aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/Kconfig5
-rw-r--r--drivers/acpi/Makefile5
-rw-r--r--drivers/acpi/ac.c2
-rw-r--r--drivers/acpi/acpi_lpss.c22
-rw-r--r--drivers/acpi/acpi_pad.c2
-rw-r--r--drivers/acpi/acpi_platform.c2
-rw-r--r--drivers/acpi/acpi_pnp.c2
-rw-r--r--drivers/acpi/acpi_processor.c20
-rw-r--r--drivers/acpi/acpi_video.c (renamed from drivers/acpi/video.c)283
-rw-r--r--drivers/acpi/acpica/acdebug.h4
-rw-r--r--drivers/acpi/acpica/aclocal.h15
-rw-r--r--drivers/acpi/acpica/acparser.h3
-rw-r--r--drivers/acpi/acpica/acpredef.h45
-rw-r--r--drivers/acpi/acpica/acutils.h2
-rw-r--r--drivers/acpi/acpica/dsmethod.c5
-rw-r--r--drivers/acpi/acpica/hwpci.c9
-rw-r--r--drivers/acpi/acpica/nsprepkg.c13
-rw-r--r--drivers/acpi/acpica/nsrepair.c2
-rw-r--r--drivers/acpi/acpica/psopinfo.c3
-rw-r--r--drivers/acpi/acpica/utfileio.c9
-rw-r--r--drivers/acpi/acpica/utglobal.c13
-rw-r--r--drivers/acpi/acpica/uthex.c4
-rw-r--r--drivers/acpi/acpica/utxferror.c11
-rw-r--r--drivers/acpi/apei/erst.c1
-rw-r--r--drivers/acpi/apei/ghes.c108
-rw-r--r--drivers/acpi/battery.c48
-rw-r--r--drivers/acpi/bus.c56
-rw-r--r--drivers/acpi/device_pm.c98
-rw-r--r--drivers/acpi/ec.c357
-rw-r--r--drivers/acpi/fan.c5
-rw-r--r--drivers/acpi/glue.c5
-rw-r--r--drivers/acpi/hed.c2
-rw-r--r--drivers/acpi/internal.h11
-rw-r--r--drivers/acpi/osl.c20
-rw-r--r--drivers/acpi/pci_irq.c3
-rw-r--r--drivers/acpi/power.c45
-rw-r--r--drivers/acpi/processor_core.c10
-rw-r--r--drivers/acpi/processor_idle.c2
-rw-r--r--drivers/acpi/processor_pdc.c5
-rw-r--r--drivers/acpi/property.c54
-rw-r--r--drivers/acpi/resource.c162
-rw-r--r--drivers/acpi/sbs.c2
-rw-r--r--drivers/acpi/sbshc.c22
-rw-r--r--drivers/acpi/scan.c147
-rw-r--r--drivers/acpi/utils.c15
-rw-r--r--drivers/acpi/video_detect.c409
-rw-r--r--drivers/ata/Kconfig10
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/ahci.c103
-rw-r--r--drivers/ata/ahci_mvebu.c2
-rw-r--r--drivers/ata/ahci_st.c49
-rw-r--r--drivers/ata/libahci.c3
-rw-r--r--drivers/ata/libata-core.c34
-rw-r--r--drivers/ata/libata-eh.c3
-rw-r--r--drivers/ata/pata_octeon_cf.c2
-rw-r--r--drivers/ata/pata_scc.c1110
-rw-r--r--drivers/base/cacheinfo.c2
-rw-r--r--drivers/base/init.c2
-rw-r--r--drivers/base/power/Makefile2
-rw-r--r--drivers/base/power/clock_ops.c43
-rw-r--r--drivers/base/power/domain.c42
-rw-r--r--drivers/base/power/main.c14
-rw-r--r--drivers/base/power/power.h48
-rw-r--r--drivers/base/power/runtime.c6
-rw-r--r--drivers/base/power/wakeirq.c273
-rw-r--r--drivers/base/power/wakeup.c146
-rw-r--r--drivers/base/property.c14
-rw-r--r--drivers/base/regmap/internal.h3
-rw-r--r--drivers/base/regmap/regcache.c45
-rw-r--r--drivers/base/regmap/regmap-irq.c11
-rw-r--r--drivers/base/regmap/regmap.c32
-rw-r--r--drivers/base/topology.c2
-rw-r--r--drivers/block/Kconfig1
-rw-r--r--drivers/block/cciss.c27
-rw-r--r--drivers/block/cciss_scsi.c1
-rw-r--r--drivers/block/loop.c2
-rw-r--r--drivers/block/nvme-core.c10
-rw-r--r--drivers/block/nvme-scsi.c6
-rw-r--r--drivers/block/pmem.c4
-rw-r--r--drivers/block/rbd.c5
-rw-r--r--drivers/block/xen-blkback/blkback.c35
-rw-r--r--drivers/block/zram/zram_drv.c25
-rw-r--r--drivers/bluetooth/ath3k.c4
-rw-r--r--drivers/bluetooth/bt3c_cs.c3
-rw-r--r--drivers/bluetooth/btbcm.c148
-rw-r--r--drivers/bluetooth/btbcm.h6
-rw-r--r--drivers/bluetooth/btusb.c406
-rw-r--r--drivers/bluetooth/hci_ath.c98
-rw-r--r--drivers/bus/arm-cci.c2
-rw-r--r--drivers/bus/mips_cdmm.c4
-rw-r--r--drivers/bus/mvebu-mbus.c120
-rw-r--r--drivers/bus/omap_l3_noc.c9
-rw-r--r--drivers/bus/omap_l3_noc.h54
-rw-r--r--drivers/char/hw_random/bcm63xx-rng.c18
-rw-r--r--drivers/char/hw_random/via-rng.c2
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c4
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c16
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c213
-rw-r--r--drivers/char/pcmcia/cm4040_cs.c5
-rw-r--r--drivers/char/random.c80
-rw-r--r--drivers/clk/at91/clk-peripheral.c8
-rw-r--r--drivers/clk/at91/clk-pll.c12
-rw-r--r--drivers/clk/at91/pmc.h2
-rw-r--r--drivers/clk/clk-s2mps11.c4
-rw-r--r--drivers/clk/clk-si5351.c63
-rw-r--r--drivers/clk/clk.c8
-rw-r--r--drivers/clk/clkdev.c83
-rw-r--r--drivers/clk/qcom/gcc-msm8916.c4
-rw-r--r--drivers/clk/samsung/Makefile2
-rw-r--r--drivers/clk/samsung/clk-exynos5420.c1
-rw-r--r--drivers/clk/samsung/clk-exynos5433.c12
-rw-r--r--drivers/clk/ti/clk-7xx.c8
-rw-r--r--drivers/clocksource/Kconfig17
-rw-r--r--drivers/clocksource/Makefile3
-rw-r--r--drivers/clocksource/armv7m_systick.c79
-rw-r--r--drivers/clocksource/asm9260_timer.c2
-rw-r--r--drivers/clocksource/exynos_mct.c22
-rw-r--r--drivers/clocksource/qcom-timer.c59
-rw-r--r--drivers/clocksource/time-lpc32xx.c272
-rw-r--r--drivers/clocksource/timer-integrator-ap.c2
-rw-r--r--drivers/clocksource/timer-stm32.c184
-rw-r--r--drivers/clocksource/timer-sun5i.c2
-rw-r--r--drivers/cpufreq/Kconfig.arm2
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c5
-rw-r--r--drivers/cpufreq/arm_big_little.c40
-rw-r--r--drivers/cpufreq/cpufreq-dt.c1
-rw-r--r--drivers/cpufreq/cpufreq-nforce2.c2
-rw-r--r--drivers/cpufreq/cpufreq.c573
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c28
-rw-r--r--drivers/cpufreq/cpufreq_governor.c345
-rw-r--r--drivers/cpufreq/cpufreq_governor.h16
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c6
-rw-r--r--drivers/cpufreq/gx-suspmod.c4
-rw-r--r--drivers/cpufreq/intel_pstate.c73
-rw-r--r--drivers/cpufreq/p4-clockmod.c2
-rw-r--r--drivers/cpufreq/powernow-k8.c13
-rw-r--r--drivers/cpufreq/pxa2xx-cpufreq.c20
-rw-r--r--drivers/cpufreq/qoriq-cpufreq.c32
-rw-r--r--drivers/cpufreq/speedstep-ich.c2
-rw-r--r--drivers/cpuidle/cpuidle-powernv.c12
-rw-r--r--drivers/cpuidle/cpuidle-pseries.c11
-rw-r--r--drivers/cpuidle/cpuidle.c52
-rw-r--r--drivers/cpuidle/governors/menu.c4
-rw-r--r--drivers/crypto/Kconfig87
-rw-r--r--drivers/crypto/Makefile1
-rw-r--r--drivers/crypto/caam/Kconfig5
-rw-r--r--drivers/crypto/caam/caamalg.c1371
-rw-r--r--drivers/crypto/caam/caamhash.c11
-rw-r--r--drivers/crypto/caam/caamrng.c2
-rw-r--r--drivers/crypto/caam/compat.h2
-rw-r--r--drivers/crypto/caam/ctrl.c4
-rw-r--r--drivers/crypto/caam/regs.h38
-rw-r--r--drivers/crypto/caam/sg_sw_sec4.h50
-rw-r--r--drivers/crypto/ccp/Kconfig1
-rw-r--r--drivers/crypto/ccp/ccp-ops.c9
-rw-r--r--drivers/crypto/ccp/ccp-platform.c62
-rw-r--r--drivers/crypto/ixp4xx_crypto.c9
-rw-r--r--drivers/crypto/marvell/Makefile2
-rw-r--r--drivers/crypto/marvell/cesa.c548
-rw-r--r--drivers/crypto/marvell/cesa.h791
-rw-r--r--drivers/crypto/marvell/cipher.c797
-rw-r--r--drivers/crypto/marvell/hash.c1441
-rw-r--r--drivers/crypto/marvell/tdma.c224
-rw-r--r--drivers/crypto/mv_cesa.c73
-rw-r--r--drivers/crypto/n2_core.c8
-rw-r--r--drivers/crypto/nx/Kconfig61
-rw-r--r--drivers/crypto/nx/Makefile9
-rw-r--r--drivers/crypto/nx/nx-842-crypto.c580
-rw-r--r--drivers/crypto/nx/nx-842-platform.c84
-rw-r--r--drivers/crypto/nx/nx-842-powernv.c637
-rw-r--r--drivers/crypto/nx/nx-842-pseries.c1140
-rw-r--r--drivers/crypto/nx/nx-842.c1610
-rw-r--r--drivers/crypto/nx/nx-842.h144
-rw-r--r--drivers/crypto/nx/nx-aes-gcm.c110
-rw-r--r--drivers/crypto/nx/nx-sha256.c84
-rw-r--r--drivers/crypto/nx/nx-sha512.c85
-rw-r--r--drivers/crypto/nx/nx.c233
-rw-r--r--drivers/crypto/nx/nx.h9
-rw-r--r--drivers/crypto/omap-sham.c27
-rw-r--r--drivers/crypto/padlock-aes.c2
-rw-r--r--drivers/crypto/padlock-sha.c2
-rw-r--r--drivers/crypto/picoxcell_crypto.c41
-rw-r--r--drivers/crypto/qat/Kconfig6
-rw-r--r--drivers/crypto/qat/qat_common/adf_accel_devices.h1
-rw-r--r--drivers/crypto/qat/qat_common/adf_cfg_user.h12
-rw-r--r--drivers/crypto/qat/qat_common/adf_common_drv.h7
-rw-r--r--drivers/crypto/qat/qat_common/adf_ctl_drv.c1
-rw-r--r--drivers/crypto/qat/qat_common/qat_algs.c39
-rw-r--r--drivers/crypto/qat/qat_dh895xcc/adf_drv.c5
-rw-r--r--drivers/crypto/sahara.c13
-rw-r--r--drivers/crypto/talitos.c743
-rw-r--r--drivers/crypto/talitos.h153
-rw-r--r--drivers/crypto/ux500/Kconfig4
-rw-r--r--drivers/crypto/vmx/Kconfig2
-rw-r--r--drivers/crypto/vmx/Makefile2
-rw-r--r--drivers/crypto/vmx/aes.c172
-rw-r--r--drivers/crypto/vmx/aes_cbc.c246
-rw-r--r--drivers/crypto/vmx/aes_ctr.c225
-rw-r--r--drivers/crypto/vmx/aesp8-ppc.h15
-rw-r--r--drivers/crypto/vmx/ghash.c290
-rw-r--r--drivers/crypto/vmx/vmx.c68
-rw-r--r--drivers/dma/Kconfig1
-rw-r--r--drivers/dma/at_xdmac.c231
-rw-r--r--drivers/dma/dmaengine.c10
-rw-r--r--drivers/dma/hsu/hsu.c5
-rw-r--r--drivers/dma/mic_x100_dma.c1
-rw-r--r--drivers/dma/pl330.c3
-rw-r--r--drivers/dma/sh/usb-dmac.c2
-rw-r--r--drivers/extcon/extcon-usb-gpio.c24
-rw-r--r--drivers/firewire/sbp2.c1
-rw-r--r--drivers/firmware/dmi_scan.c12
-rw-r--r--drivers/firmware/efi/Kconfig5
-rw-r--r--drivers/firmware/efi/Makefile1
-rw-r--r--drivers/firmware/efi/efi.c91
-rw-r--r--drivers/firmware/efi/efivars.c11
-rw-r--r--drivers/firmware/efi/esrt.c471
-rw-r--r--drivers/firmware/efi/runtime-map.c6
-rw-r--r--drivers/firmware/iscsi_ibft.c36
-rw-r--r--drivers/gpio/Kconfig39
-rw-r--r--drivers/gpio/Makefile4
-rw-r--r--drivers/gpio/gpio-altera.c3
-rw-r--r--drivers/gpio/gpio-bcm-kona.c26
-rw-r--r--drivers/gpio/gpio-brcmstb.c252
-rw-r--r--drivers/gpio/gpio-crystalcove.c5
-rw-r--r--drivers/gpio/gpio-dln2.c1
-rw-r--r--drivers/gpio/gpio-etraxfs.c176
-rw-r--r--drivers/gpio/gpio-f7188x.c4
-rw-r--r--drivers/gpio/gpio-generic.c22
-rw-r--r--drivers/gpio/gpio-it8761e.c2
-rw-r--r--drivers/gpio/gpio-kempld.c2
-rw-r--r--drivers/gpio/gpio-lpc18xx.c180
-rw-r--r--drivers/gpio/gpio-lynxpoint.c2
-rw-r--r--drivers/gpio/gpio-max732x.c21
-rw-r--r--drivers/gpio/gpio-moxart.c17
-rw-r--r--drivers/gpio/gpio-mxc.c18
-rw-r--r--drivers/gpio/gpio-mxs.c6
-rw-r--r--drivers/gpio/gpio-omap.c131
-rw-r--r--drivers/gpio/gpio-pca953x.c23
-rw-r--r--drivers/gpio/gpio-pcf857x.c56
-rw-r--r--drivers/gpio/gpio-rcar.c13
-rw-r--r--drivers/gpio/gpio-stp-xway.c29
-rw-r--r--drivers/gpio/gpio-tb10x.c1
-rw-r--r--drivers/gpio/gpio-tegra.c6
-rw-r--r--drivers/gpio/gpio-ts5500.c2
-rw-r--r--drivers/gpio/gpio-xgene-sb.c22
-rw-r--r--drivers/gpio/gpio-xilinx.c4
-rw-r--r--drivers/gpio/gpio-xlp.c427
-rw-r--r--drivers/gpio/gpio-zynq.c193
-rw-r--r--drivers/gpio/gpiolib-acpi.c42
-rw-r--r--drivers/gpio/gpiolib-of.c2
-rw-r--r--drivers/gpio/gpiolib-sysfs.c578
-rw-r--r--drivers/gpio/gpiolib.c122
-rw-r--r--drivers/gpio/gpiolib.h16
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c7
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_topology.c8
-rw-r--r--drivers/gpu/drm/drm_ioctl.c14
-rw-r--r--drivers/gpu/drm/drm_irq.c9
-rw-r--r--drivers/gpu/drm/drm_plane_helper.c3
-rw-r--r--drivers/gpu/drm/drm_sysfs.c2
-rw-r--r--drivers/gpu/drm/exynos/exynos7_drm_decon.c4
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.c13
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c10
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.h10
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h20
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c39
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c53
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.h15
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c2
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c2
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c72
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c5
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c13
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c2
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c3
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h2
-rw-r--r--drivers/gpu/drm/i915/intel_display.c3
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c14
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c20
-rw-r--r--drivers/gpu/drm/i915/intel_lrc.c6
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c26
-rw-r--r--drivers/gpu/drm/i915/intel_opregion.c12
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c24
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c14
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c2
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c8
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mode.c5
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c2
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi.c10
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_host.c21
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_manager.c6
-rw-r--r--drivers/gpu/drm/msm/edp/edp_aux.c4
-rw-r--r--drivers/gpu/drm/msm/edp/edp_connector.c2
-rw-r--r--drivers/gpu/drm/msm/edp/edp_ctrl.c3
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c34
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h9
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c12
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c2
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c24
-rw-r--r--drivers/gpu/drm/msm/msm_fb.c7
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c2
-rw-r--r--drivers/gpu/drm/msm/msm_iommu.c4
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.c2
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/class.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gm204.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h3
-rw-r--r--drivers/gpu/drm/radeon/atombios.h4
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c20
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c6
-rw-r--r--drivers/gpu/drm/radeon/cik.c2
-rw-r--r--drivers/gpu/drm/radeon/dce3_1_afmt.c2
-rw-r--r--drivers/gpu/drm/radeon/dce6_afmt.c25
-rw-r--r--drivers/gpu/drm/radeon/evergreen_hdmi.c54
-rw-r--r--drivers/gpu/drm/radeon/ni.c3
-rw-r--r--drivers/gpu/drm/radeon/r600_hdmi.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon.h1
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h4
-rw-r--r--drivers/gpu/drm/radeon/radeon_audio.c7
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c15
-rw-r--r--drivers/gpu/drm/radeon/radeon_dp_auxch.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_dp_mst.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_mn.c13
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_uvd.c144
-rw-r--r--drivers/gpu/drm/radeon/radeon_vce.c65
-rw-r--r--drivers/gpu/drm/radeon/radeon_vm.c51
-rw-r--r--drivers/gpu/drm/radeon/rv770d.h3
-rw-r--r--drivers/gpu/drm/radeon/si.c2
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c1
-rw-r--r--drivers/gpu/drm/radeon/uvd_v1_0.c14
-rw-r--r--drivers/gpu/drm/radeon/uvd_v2_2.c29
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c9
-rw-r--r--drivers/gpu/drm/tegra/drm.c1
-rw-r--r--drivers/gpu/drm/vgem/Makefile2
-rw-r--r--drivers/gpu/drm/vgem/vgem_dma_buf.c94
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.c11
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.h11
-rw-r--r--drivers/gpu/ipu-v3/ipu-common.c13
-rw-r--r--drivers/hid/Kconfig7
-rw-r--r--drivers/hid/Makefile6
-rw-r--r--drivers/hid/hid-core.c38
-rw-r--r--drivers/hid/hid-cypress.c6
-rw-r--r--drivers/hid/hid-ids.h32
-rw-r--r--drivers/hid/hid-input.c3
-rw-r--r--drivers/hid/hid-lenovo.c59
-rw-r--r--drivers/hid/hid-lg.c24
-rw-r--r--drivers/hid/hid-lg4ff.c458
-rw-r--r--drivers/hid/hid-lg4ff.h4
-rw-r--r--drivers/hid/hid-logitech-hidpp.c245
-rw-r--r--drivers/hid/hid-microsoft.c2
-rw-r--r--drivers/hid/hid-plantronics.c132
-rw-r--r--drivers/hid/hid-prodikeys.c3
-rw-r--r--drivers/hid/hid-rmi.c15
-rw-r--r--drivers/hid/hid-sensor-hub.c13
-rw-r--r--drivers/hid/hid-sjoy.c3
-rw-r--r--drivers/hid/hid-sony.c376
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c12
-rw-r--r--drivers/hid/usbhid/hid-quirks.c7
-rw-r--r--drivers/hid/wacom.h6
-rw-r--r--drivers/hid/wacom_sys.c309
-rw-r--r--drivers/hid/wacom_wac.c422
-rw-r--r--drivers/hid/wacom_wac.h27
-rw-r--r--drivers/hsi/clients/cmt_speech.c9
-rw-r--r--drivers/hsi/clients/nokia-modem.c11
-rw-r--r--drivers/hwmon/Kconfig18
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/atxp1.c58
-rw-r--r--drivers/hwmon/coretemp.c3
-rw-r--r--drivers/hwmon/max197.c2
-rw-r--r--drivers/hwmon/nct6683.c2
-rw-r--r--drivers/hwmon/nct6775.c2
-rw-r--r--drivers/hwmon/ntc_thermistor.c91
-rw-r--r--drivers/hwmon/sht15.c2
-rw-r--r--drivers/hwmon/tc74.c177
-rw-r--r--drivers/hwmon/tmp401.c2
-rw-r--r--drivers/i2c/busses/Kconfig2
-rw-r--r--drivers/i2c/busses/i2c-cros-ec-tunnel.c45
-rw-r--r--drivers/i2c/busses/i2c-hix5hd2.c2
-rw-r--r--drivers/i2c/busses/i2c-piix4.c4
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c1
-rw-r--r--drivers/i2c/i2c-core.c12
-rw-r--r--drivers/ide/Kconfig9
-rw-r--r--drivers/ide/Makefile1
-rw-r--r--drivers/ide/scc_pata.c887
-rw-r--r--drivers/iio/accel/mma9551_core.c21
-rw-r--r--drivers/iio/accel/mma9553.c18
-rw-r--r--drivers/iio/accel/st_accel_core.c1
-rw-r--r--drivers/iio/adc/axp288_adc.c12
-rw-r--r--drivers/iio/adc/cc10001_adc.c60
-rw-r--r--drivers/iio/adc/mcp320x.c6
-rw-r--r--drivers/iio/adc/qcom-spmi-vadc.c7
-rw-r--r--drivers/iio/adc/twl6030-gpadc.c2
-rw-r--r--drivers/iio/adc/xilinx-xadc-core.c5
-rw-r--r--drivers/iio/adc/xilinx-xadc.h6
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c2
-rw-r--r--drivers/iio/gyro/st_gyro_core.c1
-rw-r--r--drivers/iio/imu/adis16400.h2
-rw-r--r--drivers/iio/imu/adis16400_buffer.c26
-rw-r--r--drivers/iio/imu/adis16400_core.c41
-rw-r--r--drivers/iio/kfifo_buf.c3
-rw-r--r--drivers/iio/light/hid-sensor-prox.c12
-rw-r--r--drivers/iio/magnetometer/st_magn_core.c1
-rw-r--r--drivers/iio/pressure/bmp280.c1
-rw-r--r--drivers/iio/pressure/hid-sensor-press.c2
-rw-r--r--drivers/iio/pressure/st_pressure_core.c1
-rw-r--r--drivers/infiniband/core/addr.c17
-rw-r--r--drivers/infiniband/core/agent.c23
-rw-r--r--drivers/infiniband/core/agent.h6
-rw-r--r--drivers/infiniband/core/cache.c69
-rw-r--r--drivers/infiniband/core/cm.c51
-rw-r--r--drivers/infiniband/core/cm_msgs.h4
-rw-r--r--drivers/infiniband/core/cma.c346
-rw-r--r--drivers/infiniband/core/device.c96
-rw-r--r--drivers/infiniband/core/iwpm_msg.c75
-rw-r--r--drivers/infiniband/core/iwpm_util.c208
-rw-r--r--drivers/infiniband/core/iwpm_util.h15
-rw-r--r--drivers/infiniband/core/mad.c639
-rw-r--r--drivers/infiniband/core/mad_priv.h15
-rw-r--r--drivers/infiniband/core/mad_rmpp.c33
-rw-r--r--drivers/infiniband/core/multicast.c12
-rw-r--r--drivers/infiniband/core/opa_smi.h78
-rw-r--r--drivers/infiniband/core/sa_query.c33
-rw-r--r--drivers/infiniband/core/smi.c228
-rw-r--r--drivers/infiniband/core/sysfs.c8
-rw-r--r--drivers/infiniband/core/ucm.c3
-rw-r--r--drivers/infiniband/core/ucma.c25
-rw-r--r--drivers/infiniband/core/umem_odp.c14
-rw-r--r--drivers/infiniband/core/user_mad.c64
-rw-r--r--drivers/infiniband/core/uverbs.h1
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c188
-rw-r--r--drivers/infiniband/core/uverbs_main.c1
-rw-r--r--drivers/infiniband/core/verbs.c85
-rw-r--r--drivers/infiniband/hw/amso1100/c2_provider.c42
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_provider.c47
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c87
-rw-r--r--drivers/infiniband/hw/cxgb4/cq.c31
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c51
-rw-r--r--drivers/infiniband/hw/cxgb4/iw_cxgb4.h20
-rw-r--r--drivers/infiniband/hw/cxgb4/mem.c6
-rw-r--r--drivers/infiniband/hw/cxgb4/provider.c36
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c70
-rw-r--r--drivers/infiniband/hw/cxgb4/t4.h61
-rw-r--r--drivers/infiniband/hw/cxgb4/t4fw_ri_api.h4
-rw-r--r--drivers/infiniband/hw/ehca/ehca_cq.c7
-rw-r--r--drivers/infiniband/hw/ehca/ehca_hca.c6
-rw-r--r--drivers/infiniband/hw/ehca/ehca_iverbs.h16
-rw-r--r--drivers/infiniband/hw/ehca/ehca_main.c25
-rw-r--r--drivers/infiniband/hw/ehca/ehca_mcast.c4
-rw-r--r--drivers/infiniband/hw/ehca/ehca_sqp.c21
-rw-r--r--drivers/infiniband/hw/ipath/Kconfig3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_cq.c9
-rw-r--r--drivers/infiniband/hw/ipath/ipath_driver.c18
-rw-r--r--drivers/infiniband/hw/ipath/ipath_kernel.h4
-rw-r--r--drivers/infiniband/hw/ipath/ipath_mad.c15
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.c26
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.h11
-rw-r--r--drivers/infiniband/hw/ipath/ipath_wc_x86_64.c43
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c13
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c36
-rw-r--r--drivers/infiniband/hw/mlx4/main.c98
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h29
-rw-r--r--drivers/infiniband/hw/mlx5/cq.c10
-rw-r--r--drivers/infiniband/hw/mlx5/mad.c15
-rw-r--r--drivers/infiniband/hw/mlx5/main.c37
-rw-r--r--drivers/infiniband/hw/mlx5/mlx5_ib.h15
-rw-r--r--drivers/infiniband/hw/mlx5/qp.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cmd.c4
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cmd.h4
-rw-r--r--drivers/infiniband/hw/mthca/mthca_dev.h9
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mad.c21
-rw-r--r--drivers/infiniband/hw/mthca/mthca_profile.c8
-rw-r--r--drivers/infiniband/hw/mthca/mthca_provider.c34
-rw-r--r--drivers/infiniband/hw/nes/nes.c1
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c72
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.h2
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c41
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma.h4
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_ah.c25
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_ah.h8
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_hw.c83
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_main.c20
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_sli.h9
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.c33
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.h12
-rw-r--r--drivers/infiniband/hw/qib/qib.h3
-rw-r--r--drivers/infiniband/hw/qib/qib_cq.c11
-rw-r--r--drivers/infiniband/hw/qib/qib_file_ops.c3
-rw-r--r--drivers/infiniband/hw/qib/qib_iba6120.c8
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7220.c8
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7322.c44
-rw-r--r--drivers/infiniband/hw/qib/qib_init.c26
-rw-r--r--drivers/infiniband/hw/qib/qib_mad.c20
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.c25
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.h11
-rw-r--r--drivers/infiniband/hw/qib/qib_wc_x86_64.c32
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_main.c17
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_verbs.c16
-rw-r--r--drivers/infiniband/hw/usnic/usnic_ib_verbs.h12
-rw-r--r--drivers/infiniband/hw/usnic/usnic_uiom.c7
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c4
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c19
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_verbs.c8
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c33
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c55
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c146
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h3
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.c8
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.h2
-rw-r--r--drivers/input/joydev.c61
-rw-r--r--drivers/input/keyboard/Kconfig2
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c31
-rw-r--r--drivers/input/mouse/Kconfig2
-rw-r--r--drivers/input/mouse/alps.c10
-rw-r--r--drivers/input/mouse/elantech.c10
-rw-r--r--drivers/input/mouse/synaptics.c7
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c2
-rw-r--r--drivers/input/touchscreen/sx8654.c2
-rw-r--r--drivers/iommu/Kconfig13
-rw-r--r--drivers/iommu/Makefile1
-rw-r--r--drivers/iommu/amd_iommu.c1067
-rw-r--r--drivers/iommu/amd_iommu_init.c38
-rw-r--r--drivers/iommu/amd_iommu_proto.h11
-rw-r--r--drivers/iommu/amd_iommu_types.h16
-rw-r--r--drivers/iommu/amd_iommu_v2.c1
-rw-r--r--drivers/iommu/arm-smmu-v3.c2670
-rw-r--r--drivers/iommu/arm-smmu.c53
-rw-r--r--drivers/iommu/dmar.c47
-rw-r--r--drivers/iommu/exynos-iommu.c527
-rw-r--r--drivers/iommu/intel-iommu.c526
-rw-r--r--drivers/iommu/intel_irq_remapping.c880
-rw-r--r--drivers/iommu/iommu.c373
-rw-r--r--drivers/iommu/iova.c4
-rw-r--r--drivers/iommu/irq_remapping.c253
-rw-r--r--drivers/iommu/irq_remapping.h42
-rw-r--r--drivers/iommu/rockchip-iommu.c31
-rw-r--r--drivers/irqchip/Kconfig1
-rw-r--r--drivers/irqchip/exynos-combiner.c66
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c2
-rw-r--r--drivers/irqchip/irq-atmel-aic5.c9
-rw-r--r--drivers/irqchip/irq-bcm2835.c2
-rw-r--r--drivers/irqchip/irq-gic-common.c17
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c9
-rw-r--r--drivers/irqchip/irq-gic-v3.c1
-rw-r--r--drivers/irqchip/irq-gic.c72
-rw-r--r--drivers/irqchip/irq-hip04.c1
-rw-r--r--drivers/irqchip/irq-keystone.c5
-rw-r--r--drivers/irqchip/irq-mips-gic.c23
-rw-r--r--drivers/irqchip/irq-mtk-sysirq.c4
-rw-r--r--drivers/irqchip/irq-mxs.c2
-rw-r--r--drivers/irqchip/irq-nvic.c28
-rw-r--r--drivers/irqchip/irq-renesas-intc-irqpin.c2
-rw-r--r--drivers/irqchip/irq-renesas-irqc.c19
-rw-r--r--drivers/irqchip/irq-s3c24xx.c4
-rw-r--r--drivers/irqchip/irq-sun4i.c2
-rw-r--r--drivers/irqchip/irq-sunxi-nmi.c2
-rw-r--r--drivers/irqchip/irq-tegra.c2
-rw-r--r--drivers/irqchip/irq-versatile-fpga.c2
-rw-r--r--drivers/irqchip/irq-vf610-mscm-ir.c28
-rw-r--r--drivers/irqchip/irq-vic.c2
-rw-r--r--drivers/irqchip/irq-vt8500.c2
-rw-r--r--drivers/irqchip/spear-shirq.c3
-rw-r--r--drivers/isdn/i4l/isdn_net.c2
-rw-r--r--drivers/leds/led-class.c7
-rw-r--r--drivers/lguest/core.c2
-rw-r--r--drivers/lguest/interrupts_and_traps.c10
-rw-r--r--drivers/lguest/x86/core.c12
-rw-r--r--drivers/md/bitmap.c7
-rw-r--r--drivers/md/dm-crypt.c12
-rw-r--r--drivers/md/dm-ioctl.c17
-rw-r--r--drivers/md/dm-mpath.c4
-rw-r--r--drivers/md/dm-table.c16
-rw-r--r--drivers/md/dm.c43
-rw-r--r--drivers/md/md.c32
-rw-r--r--drivers/md/raid0.c9
-rw-r--r--drivers/md/raid10.c1
-rw-r--r--drivers/md/raid5.c270
-rw-r--r--drivers/md/raid5.h5
-rw-r--r--drivers/media/Kconfig1
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-reg.h6
-rw-r--r--drivers/media/pci/ivtv/Kconfig3
-rw-r--r--drivers/media/pci/ivtv/ivtvfb.c58
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c14
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.h8
-rw-r--r--drivers/media/platform/soc_camera/rcar_vin.c7
-rw-r--r--drivers/message/fusion/mptbase.c24
-rw-r--r--drivers/message/fusion/mptbase.h1
-rw-r--r--drivers/message/fusion/mptsas.c4
-rw-r--r--drivers/mfd/88pm860x-core.c2
-rw-r--r--drivers/mfd/Kconfig12
-rw-r--r--drivers/mfd/Makefile6
-rw-r--r--drivers/mfd/ab8500-core.c2
-rw-r--r--drivers/mfd/ab8500-debugfs.c2
-rw-r--r--drivers/mfd/ab8500-gpadc.c6
-rw-r--r--drivers/mfd/arizona-core.c361
-rw-r--r--drivers/mfd/arizona-irq.c2
-rw-r--r--drivers/mfd/axp20x.c100
-rw-r--r--drivers/mfd/cros_ec.c173
-rw-r--r--drivers/mfd/cros_ec_i2c.c170
-rw-r--r--drivers/mfd/cros_ec_spi.c408
-rw-r--r--drivers/mfd/da9052-core.c8
-rw-r--r--drivers/mfd/da9052-irq.c4
-rw-r--r--drivers/mfd/da9055-core.c6
-rw-r--r--drivers/mfd/da9063-core.c54
-rw-r--r--drivers/mfd/da9063-irq.c4
-rw-r--r--drivers/mfd/da9150-core.c4
-rw-r--r--drivers/mfd/db8500-prcmu.c2
-rw-r--r--drivers/mfd/htc-i2cpld.c3
-rw-r--r--drivers/mfd/intel_soc_pmic_core.h2
-rw-r--r--drivers/mfd/intel_soc_pmic_crc.c2
-rw-r--r--drivers/mfd/lp8788-irq.c2
-rw-r--r--drivers/mfd/lpc_ich.c8
-rw-r--r--drivers/mfd/max8925-core.c2
-rw-r--r--drivers/mfd/max8997-irq.c2
-rw-r--r--drivers/mfd/max8998-irq.c2
-rw-r--r--drivers/mfd/mc13xxx-core.c2
-rw-r--r--drivers/mfd/mfd-core.c8
-rw-r--r--drivers/mfd/mt6397-core.c5
-rw-r--r--drivers/mfd/si476x-i2c.c3
-rw-r--r--drivers/mfd/stmpe.c2
-rw-r--r--drivers/mfd/tc3589x.c2
-rw-r--r--drivers/mfd/tps6586x.c2
-rw-r--r--drivers/mfd/twl4030-irq.c2
-rw-r--r--drivers/mfd/twl4030-power.c45
-rw-r--r--drivers/mfd/twl6030-irq.c2
-rw-r--r--drivers/mfd/ucb1x00-core.c3
-rw-r--r--drivers/mfd/wm831x-auxadc.c3
-rw-r--r--drivers/mfd/wm831x-irq.c2
-rw-r--r--drivers/mfd/wm8350-core.c3
-rw-r--r--drivers/mfd/wm8994-irq.c6
-rw-r--r--drivers/mmc/card/block.c37
-rw-r--r--drivers/mmc/card/mmc_test.c104
-rw-r--r--drivers/mmc/card/queue.c8
-rw-r--r--drivers/mmc/card/queue.h3
-rw-r--r--drivers/mmc/core/core.c101
-rw-r--r--drivers/mmc/core/core.h4
-rw-r--r--drivers/mmc/core/host.c88
-rw-r--r--drivers/mmc/core/host.h6
-rw-r--r--drivers/mmc/core/mmc.c156
-rw-r--r--drivers/mmc/core/mmc_ops.c44
-rw-r--r--drivers/mmc/core/mmc_ops.h1
-rw-r--r--drivers/mmc/core/sd.c113
-rw-r--r--drivers/mmc/core/sdio.c90
-rw-r--r--drivers/mmc/core/sdio_bus.c12
-rw-r--r--drivers/mmc/host/Kconfig9
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/atmel-mci.c9
-rw-r--r--drivers/mmc/host/davinci_mmc.c2
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c2
-rw-r--r--drivers/mmc/host/dw_mmc-k3.c105
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c2
-rw-r--r--drivers/mmc/host/dw_mmc.c70
-rw-r--r--drivers/mmc/host/dw_mmc.h5
-rw-r--r--drivers/mmc/host/mtk-sd.c1462
-rw-r--r--drivers/mmc/host/mxcmmc.c6
-rw-r--r--drivers/mmc/host/mxs-mmc.c2
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c2
-rw-r--r--drivers/mmc/host/rtsx_usb_sdmmc.c2
-rw-r--r--drivers/mmc/host/s3cmci.c2
-rw-r--r--drivers/mmc/host/sdhci-bcm2835.c12
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c94
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c7
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c11
-rw-r--r--drivers/mmc/host/sdhci-pci-data.c3
-rw-r--r--drivers/mmc/host/sdhci-pci.c109
-rw-r--r--drivers/mmc/host/sdhci-pci.h4
-rw-r--r--drivers/mmc/host/sdhci-pxav2.c4
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c10
-rw-r--r--drivers/mmc/host/sdhci-s3c.c2
-rw-r--r--drivers/mmc/host/sdhci-sirf.c44
-rw-r--r--drivers/mmc/host/sdhci-st.c2
-rw-r--r--drivers/mmc/host/sdhci.c151
-rw-r--r--drivers/mmc/host/sdhci.h7
-rw-r--r--drivers/mmc/host/sdhci_f_sdh30.c9
-rw-r--r--drivers/mmc/host/sh_mmcif.c298
-rw-r--r--drivers/mmc/host/tmio_mmc.c10
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c5
-rw-r--r--drivers/mtd/chips/Kconfig1
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c2
-rw-r--r--drivers/mtd/chips/cfi_util.c188
-rw-r--r--drivers/mtd/devices/docg3.c18
-rw-r--r--drivers/mtd/devices/m25p80.c66
-rw-r--r--drivers/mtd/devices/spear_smi.c4
-rw-r--r--drivers/mtd/maps/Kconfig2
-rw-r--r--drivers/mtd/maps/amd76xrom.c2
-rw-r--r--drivers/mtd/maps/dc21285.c4
-rw-r--r--drivers/mtd/maps/esb2rom.c2
-rw-r--r--drivers/mtd/maps/ichxrom.c2
-rw-r--r--drivers/mtd/maps/lantiq-flash.c4
-rw-r--r--drivers/mtd/maps/physmap_of.c4
-rw-r--r--drivers/mtd/mtd_blkdevs.c7
-rw-r--r--drivers/mtd/mtdcore.c62
-rw-r--r--drivers/mtd/nand/Kconfig10
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/brcmnand/Makefile6
-rw-r--r--drivers/mtd/nand/brcmnand/bcm63138_nand.c111
-rw-r--r--drivers/mtd/nand/brcmnand/brcmnand.c2246
-rw-r--r--drivers/mtd/nand/brcmnand/brcmnand.h73
-rw-r--r--drivers/mtd/nand/brcmnand/brcmstb_nand.c44
-rw-r--r--drivers/mtd/nand/brcmnand/iproc_nand.c150
-rw-r--r--drivers/mtd/nand/cs553x_nand.c12
-rw-r--r--drivers/mtd/nand/diskonchip.c37
-rw-r--r--drivers/mtd/nand/fsmc_nand.c8
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c2
-rw-r--r--drivers/mtd/nand/mxc_nand.c112
-rw-r--r--drivers/mtd/nand/nand_base.c48
-rw-r--r--drivers/mtd/nand/nand_bbt.c26
-rw-r--r--drivers/mtd/nand/nand_ids.c2
-rw-r--r--drivers/mtd/nand/nandsim.c10
-rw-r--r--drivers/mtd/nand/ndfc.c2
-rw-r--r--drivers/mtd/nand/plat_nand.c4
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c43
-rw-r--r--drivers/mtd/nand/r852.c6
-rw-r--r--drivers/mtd/nand/s3c2410.c2
-rw-r--r--drivers/mtd/nand/xway_nand.c4
-rw-r--r--drivers/mtd/onenand/samsung.c2
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c2
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c18
-rw-r--r--drivers/mtd/tests/readtest.c6
-rw-r--r--drivers/mtd/ubi/block.c2
-rw-r--r--drivers/net/bonding/bond_main.c10
-rw-r--r--drivers/net/bonding/bond_options.c2
-rw-r--r--drivers/net/can/xilinx_can.c7
-rw-r--r--drivers/net/dsa/mv88e6xxx.c3
-rw-r--r--drivers/net/ethernet/altera/altera_tse_main.c6
-rw-r--r--drivers/net/ethernet/amd/Kconfig1
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c2
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-main.c27
-rw-r--r--drivers/net/ethernet/apm/xgene/Kconfig1
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_hw.h2
-rw-r--r--drivers/net/ethernet/broadcom/b44.c2
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h6
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c77
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c31
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c20
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c2
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c12
-rw-r--r--drivers/net/ethernet/brocade/bna/bfa_ioc.c4
-rw-r--r--drivers/net/ethernet/brocade/bna/bnad.c4
-rw-r--r--drivers/net/ethernet/brocade/bna/cna_fwimg.c7
-rw-r--r--drivers/net/ethernet/cadence/macb.c32
-rw-r--r--drivers/net/ethernet/cadence/macb.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c9
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_ethtool.c20
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c11
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_rq.c9
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c87
-rw-r--r--drivers/net/ethernet/emulex/benet/be_ethtool.c18
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c26
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c5
-rw-r--r--drivers/net/ethernet/ibm/emac/core.c16
-rw-r--r--drivers/net/ethernet/ibm/emac/core.h7
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h1
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_main.c4
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_debugfs.c9
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c42
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.c25
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c10
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_txrx.c25
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c4
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ptp.c4
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c4
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cmd.c9
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_port.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c26
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_tx.c8
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/resource_tracker.c22
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mad.c2
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c8
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c6
-rw-r--r--drivers/net/ethernet/qualcomm/qca_spi.c4
-rw-r--r--drivers/net/ethernet/realtek/r8169.c4
-rw-r--r--drivers/net/ethernet/rocker/rocker.c13
-rw-r--r--drivers/net/ethernet/sfc/efx.c2
-rw-r--r--drivers/net/ethernet/sfc/rx.c42
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c20
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c76
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c1
-rw-r--r--drivers/net/ethernet/sun/cassini.c1
-rw-r--r--drivers/net/ethernet/ti/netcp_ethss.c8
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_main.c4
-rw-r--r--drivers/net/hyperv/hyperv_net.h13
-rw-r--r--drivers/net/hyperv/netvsc.c18
-rw-r--r--drivers/net/hyperv/netvsc_drv.c47
-rw-r--r--drivers/net/hyperv/rndis_filter.c4
-rw-r--r--drivers/net/ieee802154/at86rf230.c390
-rw-r--r--drivers/net/macvlan.c15
-rw-r--r--drivers/net/phy/Kconfig1
-rw-r--r--drivers/net/phy/amd-xgbe-phy.c45
-rw-r--r--drivers/net/phy/bcm7xxx.c2
-rw-r--r--drivers/net/phy/dp83640.c23
-rw-r--r--drivers/net/phy/mdio-gpio.c5
-rw-r--r--drivers/net/phy/mdio-mux-gpio.c3
-rw-r--r--drivers/net/phy/micrel.c3
-rw-r--r--drivers/net/phy/phy.c34
-rw-r--r--drivers/net/ppp/pppoe.c4
-rw-r--r--drivers/net/usb/cdc_ncm.c2
-rw-r--r--drivers/net/usb/r8152.c1
-rw-r--r--drivers/net/usb/usbnet.c4
-rw-r--r--drivers/net/vxlan.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c52
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c12
-rw-r--r--drivers/net/wireless/iwlwifi/Kconfig1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-7000.c16
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-file.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-nvm-parse.c32
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h41
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/coex_legacy.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/d3.c24
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-power.h34
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h44
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h13
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c54
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c29
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h1
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c16
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.c3
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rx.c5
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/scan.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h6
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c29
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c23
-rw-r--r--drivers/net/wireless/rtlwifi/usb.c2
-rw-r--r--drivers/net/xen-netback/netback.c2
-rw-r--r--drivers/net/xen-netback/xenbus.c34
-rw-r--r--drivers/net/xen-netfront.c15
-rw-r--r--drivers/ntb/ntb_hw.c5
-rw-r--r--drivers/of/address.c2
-rw-r--r--drivers/of/base.c8
-rw-r--r--drivers/of/dynamic.c2
-rw-r--r--drivers/parisc/superio.c2
-rw-r--r--drivers/pci/Kconfig4
-rw-r--r--drivers/pci/bus.c10
-rw-r--r--drivers/pci/host/Kconfig20
-rw-r--r--drivers/pci/host/Makefile2
-rw-r--r--drivers/pci/host/pci-dra7xx.c19
-rw-r--r--drivers/pci/host/pci-exynos.c34
-rw-r--r--drivers/pci/host/pci-imx6.c88
-rw-r--r--drivers/pci/host/pci-keystone.c16
-rw-r--r--drivers/pci/host/pci-layerscape.c25
-rw-r--r--drivers/pci/host/pci-mvebu.c18
-rw-r--r--drivers/pci/host/pci-tegra.c16
-rw-r--r--drivers/pci/host/pci-xgene-msi.c596
-rw-r--r--drivers/pci/host/pci-xgene.c66
-rw-r--r--drivers/pci/host/pcie-designware.c154
-rw-r--r--drivers/pci/host/pcie-iproc-bcma.c110
-rw-r--r--drivers/pci/host/pcie-iproc-platform.c12
-rw-r--r--drivers/pci/host/pcie-iproc.c6
-rw-r--r--drivers/pci/host/pcie-iproc.h4
-rw-r--r--drivers/pci/host/pcie-spear13xx.c17
-rw-r--r--drivers/pci/hotplug/Makefile3
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c5
-rw-r--r--drivers/pci/hotplug/pciehp.h23
-rw-r--r--drivers/pci/hotplug/pciehp_acpi.c137
-rw-r--r--drivers/pci/hotplug/pciehp_core.c54
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c154
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c145
-rw-r--r--drivers/pci/htirq.c48
-rw-r--r--drivers/pci/msi.c53
-rw-r--r--drivers/pci/pci-acpi.c2
-rw-r--r--drivers/pci/pci.c44
-rw-r--r--drivers/pci/pci.h32
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c3
-rw-r--r--drivers/pci/pcie/aspm.c57
-rw-r--r--drivers/pci/probe.c69
-rw-r--r--drivers/pci/quirks.c13
-rw-r--r--drivers/pci/setup-bus.c9
-rw-r--r--drivers/pci/vc.c3
-rw-r--r--drivers/pci/xen-pcifront.c16
-rw-r--r--drivers/pcmcia/cistpl.c50
-rw-r--r--drivers/pcmcia/cs.c29
-rw-r--r--drivers/pcmcia/ds.c76
-rw-r--r--drivers/pcmcia/electra_cf.c19
-rw-r--r--drivers/pcmcia/i82365.c43
-rw-r--r--drivers/pcmcia/m32r_cfc.c7
-rw-r--r--drivers/pcmcia/m32r_pcc.c7
-rw-r--r--drivers/pcmcia/pcmcia_cis.c4
-rw-r--r--drivers/pcmcia/pcmcia_resource.c11
-rw-r--r--drivers/pcmcia/rsrc_nonstatic.c44
-rw-r--r--drivers/pcmcia/ti113x.h78
-rw-r--r--drivers/pcmcia/topic.h16
-rw-r--r--drivers/pcmcia/vrc4171_card.c30
-rw-r--r--drivers/pcmcia/yenta_socket.c94
-rw-r--r--drivers/phy/Kconfig10
-rw-r--r--drivers/phy/phy-core.c4
-rw-r--r--drivers/phy/phy-omap-usb2.c1
-rw-r--r--drivers/phy/phy-rcar-gen2.c4
-rw-r--r--drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c4
-rw-r--r--drivers/pinctrl/core.c10
-rw-r--r--drivers/pinctrl/core.h2
-rw-r--r--drivers/pinctrl/devicetree.c2
-rw-r--r--drivers/pinctrl/intel/pinctrl-cherryview.c44
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common.c2
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson.c2
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson8b.c4
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-armada-370.c2
-rw-r--r--drivers/pinctrl/qcom/pinctrl-spmi-gpio.c14
-rw-r--r--drivers/pinctrl/qcom/pinctrl-spmi-mpp.c10
-rw-r--r--drivers/platform/chrome/Kconfig9
-rw-r--r--drivers/platform/chrome/Makefile1
-rw-r--r--drivers/platform/chrome/cros_ec_dev.c189
-rw-r--r--drivers/platform/chrome/cros_ec_dev.h7
-rw-r--r--drivers/platform/chrome/cros_ec_lightbar.c217
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c85
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c382
-rw-r--r--drivers/platform/chrome/cros_ec_sysfs.c178
-rw-r--r--drivers/platform/x86/Kconfig25
-rw-r--r--drivers/platform/x86/acer-wmi.c10
-rw-r--r--drivers/platform/x86/apple-gmux.c4
-rw-r--r--drivers/platform/x86/asus-laptop.c6
-rw-r--r--drivers/platform/x86/asus-wmi.c15
-rw-r--r--drivers/platform/x86/compal-laptop.c4
-rw-r--r--drivers/platform/x86/dell-laptop.c8
-rw-r--r--drivers/platform/x86/dell-wmi.c3
-rw-r--r--drivers/platform/x86/eeepc-laptop.c5
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c6
-rw-r--r--drivers/platform/x86/ideapad-laptop.c10
-rw-r--r--drivers/platform/x86/intel_oaktrail.c7
-rw-r--r--drivers/platform/x86/msi-laptop.c6
-rw-r--r--drivers/platform/x86/msi-wmi.c4
-rw-r--r--drivers/platform/x86/samsung-laptop.c27
-rw-r--r--drivers/platform/x86/sony-laptop.c7
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c44
-rw-r--r--drivers/platform/x86/toshiba_acpi.c7
-rw-r--r--drivers/pnp/pnpacpi/rsparser.c10
-rw-r--r--drivers/pnp/system.c35
-rw-r--r--drivers/power/88pm860x_charger.c1
-rw-r--r--drivers/power/Kconfig28
-rw-r--r--drivers/power/Makefile4
-rw-r--r--drivers/power/axp288_charger.c941
-rw-r--r--drivers/power/axp288_fuel_gauge.c3
-rw-r--r--drivers/power/bq2415x_charger.c94
-rw-r--r--drivers/power/bq24190_charger.c11
-rw-r--r--drivers/power/bq24257_charger.c858
-rw-r--r--drivers/power/bq25890_charger.c994
-rw-r--r--drivers/power/bq27x00_battery.c8
-rw-r--r--drivers/power/charger-manager.c3
-rw-r--r--drivers/power/collie_battery.c2
-rw-r--r--drivers/power/max17042_battery.c199
-rw-r--r--drivers/power/power_supply_core.c106
-rw-r--r--drivers/power/power_supply_leds.c4
-rw-r--r--drivers/power/power_supply_sysfs.c4
-rw-r--r--drivers/power/reset/Kconfig1
-rw-r--r--drivers/power/reset/at91-reset.c6
-rw-r--r--drivers/power/reset/gpio-poweroff.c25
-rw-r--r--drivers/power/reset/gpio-restart.c2
-rw-r--r--drivers/power/reset/ltc2952-poweroff.c26
-rw-r--r--drivers/power/rt9455_charger.c1752
-rw-r--r--drivers/power/sbs-battery.c21
-rw-r--r--drivers/power/wm831x_power.c1
-rw-r--r--drivers/powercap/intel_rapl.c51
-rw-r--r--drivers/pwm/core.c40
-rw-r--r--drivers/pwm/pwm-atmel.c63
-rw-r--r--drivers/pwm/pwm-bcm-kona.c9
-rw-r--r--drivers/pwm/pwm-img.c76
-rw-r--r--drivers/pwm/pwm-lpss-pci.c2
-rw-r--r--drivers/pwm/pwm-samsung.c1
-rw-r--r--drivers/rapidio/rio-scan.c2
-rw-r--r--drivers/regulator/88pm8607.c2
-rw-r--r--drivers/regulator/Kconfig23
-rw-r--r--drivers/regulator/Makefile2
-rw-r--r--drivers/regulator/arizona-ldo1.c15
-rw-r--r--drivers/regulator/axp20x-regulator.c239
-rw-r--r--drivers/regulator/core.c76
-rw-r--r--drivers/regulator/da9052-regulator.c5
-rw-r--r--drivers/regulator/da9062-regulator.c842
-rw-r--r--drivers/regulator/da9063-regulator.c21
-rw-r--r--drivers/regulator/fan53555.c1
-rw-r--r--drivers/regulator/helpers.c2
-rw-r--r--drivers/regulator/lp8755.c23
-rw-r--r--drivers/regulator/max14577.c128
-rw-r--r--drivers/regulator/max77686.c8
-rw-r--r--drivers/regulator/max77693.c17
-rw-r--r--drivers/regulator/max77843.c68
-rw-r--r--drivers/regulator/max8973-regulator.c269
-rw-r--r--drivers/regulator/of_regulator.c16
-rw-r--r--drivers/regulator/pwm-regulator.c41
-rw-r--r--drivers/regulator/qcom_spmi-regulator.c1435
-rw-r--r--drivers/regulator/s2mps11.c10
-rw-r--r--drivers/regulator/wm831x-dcdc.c12
-rw-r--r--drivers/regulator/wm831x-isink.c3
-rw-r--r--drivers/regulator/wm831x-ldo.c6
-rw-r--r--drivers/rtc/Kconfig21
-rw-r--r--drivers/rtc/Makefile2
-rw-r--r--drivers/rtc/rtc-abx80x.c307
-rw-r--r--drivers/rtc/rtc-armada38x.c26
-rw-r--r--drivers/rtc/rtc-st-lpc.c354
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c1
-rw-r--r--drivers/scsi/3w-9xxx.c57
-rw-r--r--drivers/scsi/3w-9xxx.h5
-rw-r--r--drivers/scsi/3w-sas.c50
-rw-r--r--drivers/scsi/3w-sas.h4
-rw-r--r--drivers/scsi/3w-xxxx.c42
-rw-r--r--drivers/scsi/3w-xxxx.h5
-rw-r--r--drivers/scsi/Kconfig22
-rw-r--r--drivers/scsi/Makefile2
-rw-r--r--drivers/scsi/NCR53c406a.c1
-rw-r--r--drivers/scsi/a100u2w.c1
-rw-r--r--drivers/scsi/aacraid/src.c2
-rw-r--r--drivers/scsi/advansys.c1474
-rw-r--r--drivers/scsi/aha152x.c1
-rw-r--r--drivers/scsi/aha1542.c24
-rw-r--r--drivers/scsi/aha1740.c1
-rw-r--r--drivers/scsi/aha1740.h1
-rw-r--r--drivers/scsi/aic94xx/aic94xx_init.c1
-rw-r--r--drivers/scsi/arm/arxescsi.c1
-rw-r--r--drivers/scsi/arm/cumana_2.c1
-rw-r--r--drivers/scsi/arm/eesox.c1
-rw-r--r--drivers/scsi/atp870u.c1
-rw-r--r--drivers/scsi/atp870u.h1
-rw-r--r--drivers/scsi/be2iscsi/be.h6
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.c10
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.h18
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c8
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.h8
-rw-r--r--drivers/scsi/be2iscsi/be_main.c88
-rw-r--r--drivers/scsi/be2iscsi/be_main.h16
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.c77
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.h11
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c5
-rw-r--r--drivers/scsi/csiostor/csio_hw.c1
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/cxgb3i.c20
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/cxgb3i.h2
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/cxgb4i.c52
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/cxgb4i.h4
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.c20
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.h6
-rw-r--r--drivers/scsi/dpt_i2o.c1
-rw-r--r--drivers/scsi/fdomain.c1
-rw-r--r--drivers/scsi/fnic/fnic_debugfs.c1
-rw-r--r--drivers/scsi/fnic/fnic_trace.c1
-rw-r--r--drivers/scsi/hpsa.c2780
-rw-r--r--drivers/scsi/hpsa.h19
-rw-r--r--drivers/scsi/hpsa_cmd.h34
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c6
-rw-r--r--drivers/scsi/imm.c1
-rw-r--r--drivers/scsi/initio.c1
-rw-r--r--drivers/scsi/ipr.h2
-rw-r--r--drivers/scsi/ips.c9
-rw-r--r--drivers/scsi/isci/init.c1
-rw-r--r--drivers/scsi/lpfc/lpfc.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c12
-rw-r--r--drivers/scsi/lpfc/lpfc_disc.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c733
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c181
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h201
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h236
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c26
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c152
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c10
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c106
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h3
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c82
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h21
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c9
-rw-r--r--drivers/scsi/mac53c94.c1
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h342
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c741
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fp.c25
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.c556
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.h281
-rw-r--r--drivers/scsi/mvsas/mv_init.c1
-rw-r--r--drivers/scsi/nsp32.c1
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.c1
-rw-r--r--drivers/scsi/pcmcia/qlogic_stub.c1
-rw-r--r--drivers/scsi/pcmcia/sym53c500_cs.c1
-rw-r--r--drivers/scsi/pm8001/pm8001_init.c1
-rw-r--r--drivers/scsi/ppa.c1
-rw-r--r--drivers/scsi/ps3rom.c1
-rw-r--r--drivers/scsi/qla1280.c1
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c8
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c4
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_nx2.c13
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c7
-rw-r--r--drivers/scsi/qla2xxx/qla_target.c16
-rw-r--r--drivers/scsi/qla2xxx/tcm_qla2xxx.c6
-rw-r--r--drivers/scsi/qla4xxx/ql4_83xx.c2
-rw-r--r--drivers/scsi/qla4xxx/ql4_bsg.c2
-rw-r--r--drivers/scsi/qlogicfas.c1
-rw-r--r--drivers/scsi/qlogicpti.c1
-rw-r--r--drivers/scsi/scsi.c46
-rw-r--r--drivers/scsi/scsi_common.c178
-rw-r--r--drivers/scsi/scsi_devinfo.c1
-rw-r--r--drivers/scsi/scsi_error.c64
-rw-r--r--drivers/scsi/scsi_scan.c71
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c2
-rw-r--r--drivers/scsi/scsi_transport_srp.c67
-rw-r--r--drivers/scsi/sd.c22
-rw-r--r--drivers/scsi/snic/Makefile17
-rw-r--r--drivers/scsi/snic/cq_desc.h77
-rw-r--r--drivers/scsi/snic/cq_enet_desc.h38
-rw-r--r--drivers/scsi/snic/snic.h414
-rw-r--r--drivers/scsi/snic/snic_attrs.c77
-rw-r--r--drivers/scsi/snic/snic_ctl.c279
-rw-r--r--drivers/scsi/snic/snic_debugfs.c560
-rw-r--r--drivers/scsi/snic/snic_disc.c551
-rw-r--r--drivers/scsi/snic/snic_disc.h124
-rw-r--r--drivers/scsi/snic/snic_fwint.h525
-rw-r--r--drivers/scsi/snic/snic_io.c518
-rw-r--r--drivers/scsi/snic/snic_io.h118
-rw-r--r--drivers/scsi/snic/snic_isr.c204
-rw-r--r--drivers/scsi/snic/snic_main.c1044
-rw-r--r--drivers/scsi/snic/snic_res.c295
-rw-r--r--drivers/scsi/snic/snic_res.h97
-rw-r--r--drivers/scsi/snic/snic_scsi.c2632
-rw-r--r--drivers/scsi/snic/snic_stats.h123
-rw-r--r--drivers/scsi/snic/snic_trc.c181
-rw-r--r--drivers/scsi/snic/snic_trc.h121
-rw-r--r--drivers/scsi/snic/vnic_cq.c86
-rw-r--r--drivers/scsi/snic/vnic_cq.h110
-rw-r--r--drivers/scsi/snic/vnic_cq_fw.h62
-rw-r--r--drivers/scsi/snic/vnic_dev.c748
-rw-r--r--drivers/scsi/snic/vnic_dev.h110
-rw-r--r--drivers/scsi/snic/vnic_devcmd.h270
-rw-r--r--drivers/scsi/snic/vnic_intr.c59
-rw-r--r--drivers/scsi/snic/vnic_intr.h105
-rw-r--r--drivers/scsi/snic/vnic_resource.h68
-rw-r--r--drivers/scsi/snic/vnic_snic.h54
-rw-r--r--drivers/scsi/snic/vnic_stats.h68
-rw-r--r--drivers/scsi/snic/vnic_wq.c237
-rw-r--r--drivers/scsi/snic/vnic_wq.h170
-rw-r--r--drivers/scsi/snic/wq_enet_desc.h96
-rw-r--r--drivers/scsi/st.c272
-rw-r--r--drivers/scsi/st.h22
-rw-r--r--drivers/scsi/storvsc_drv.c3
-rw-r--r--drivers/scsi/sym53c416.c1
-rw-r--r--drivers/scsi/ufs/Kconfig2
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c39
-rw-r--r--drivers/scsi/ufs/ufshcd.c108
-rw-r--r--drivers/scsi/ufs/ufshcd.h53
-rw-r--r--drivers/scsi/ufs/ufshci.h8
-rw-r--r--drivers/scsi/ufs/unipro.h8
-rw-r--r--drivers/scsi/virtio_scsi.c11
-rw-r--r--drivers/scsi/wd719x.c1
-rw-r--r--drivers/scsi/wd719x.h2
-rw-r--r--drivers/sh/pm_runtime.c54
-rw-r--r--drivers/soc/mediatek/Kconfig1
-rw-r--r--drivers/soc/mediatek/mtk-pmic-wrap.c54
-rw-r--r--drivers/spi/Kconfig29
-rw-r--r--drivers/spi/Makefile3
-rw-r--r--drivers/spi/spi-ath79.c34
-rw-r--r--drivers/spi/spi-atmel.c292
-rw-r--r--drivers/spi/spi-bcm2835.c393
-rw-r--r--drivers/spi/spi-bitbang.c17
-rw-r--r--drivers/spi/spi-davinci.c2
-rw-r--r--drivers/spi/spi-fsl-cpm.c40
-rw-r--r--drivers/spi/spi-fsl-dspi.c307
-rw-r--r--drivers/spi/spi-fsl-espi.c51
-rw-r--r--drivers/spi/spi-imx.c2
-rw-r--r--drivers/spi/spi-omap2-mcspi.c270
-rw-r--r--drivers/spi/spi-orion.c70
-rw-r--r--drivers/spi/spi-pxa2xx-pci.c8
-rw-r--r--drivers/spi/spi-pxa2xx-pxadma.c487
-rw-r--r--drivers/spi/spi-pxa2xx.c159
-rw-r--r--drivers/spi/spi-pxa2xx.h6
-rw-r--r--drivers/spi/spi-rb4xx.c210
-rw-r--r--drivers/spi/spi-rspi.c23
-rw-r--r--drivers/spi/spi-s3c64xx.c2
-rw-r--r--drivers/spi/spi-sh-msiof.c2
-rw-r--r--drivers/spi/spi-sirf.c877
-rw-r--r--drivers/spi/spi-zynqmp-gqspi.c1122
-rw-r--r--drivers/spi/spi.c36
-rw-r--r--drivers/spi/spidev.c33
-rw-r--r--drivers/ssb/driver_chipcommon_pmu.c6
-rw-r--r--drivers/ssb/driver_pcicore.c7
-rw-r--r--drivers/staging/gdm724x/gdm_mux.c16
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c6
-rw-r--r--drivers/staging/lustre/lustre/include/linux/lustre_compat25.h15
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_internal.h6
-rw-r--r--drivers/staging/lustre/lustre/llite/symlink.c26
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/service.c4
-rw-r--r--drivers/staging/media/omap4iss/Kconfig1
-rw-r--r--drivers/staging/media/omap4iss/iss.c11
-rw-r--r--drivers/staging/media/omap4iss/iss.h4
-rw-r--r--drivers/staging/media/omap4iss/iss_csiphy.c12
-rw-r--r--drivers/staging/ozwpan/ozhcd.c8
-rw-r--r--drivers/staging/ozwpan/ozusbif.h4
-rw-r--r--drivers/staging/ozwpan/ozusbsvc1.c19
-rw-r--r--drivers/staging/rtl8712/rtl8712_led.c144
-rw-r--r--drivers/staging/rtl8712/rtl871x_cmd.c2
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_linux.c17
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.c6
-rw-r--r--drivers/staging/rtl8712/rtl871x_pwrctrl.c2
-rw-r--r--drivers/staging/rtl8712/rtl871x_sta_mgt.c2
-rw-r--r--drivers/staging/rts5208/rtsx.c1
-rw-r--r--drivers/staging/sm750fb/sm750.c2
-rw-r--r--drivers/staging/vt6655/card.c10
-rw-r--r--drivers/staging/vt6655/card.h2
-rw-r--r--drivers/staging/vt6655/device_main.c42
-rw-r--r--drivers/staging/vt6656/rxtx.c14
-rw-r--r--drivers/target/iscsi/iscsi_target.c4
-rw-r--r--drivers/target/iscsi/iscsi_target_device.c1
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c1
-rw-r--r--drivers/target/iscsi/iscsi_target_tmr.c2
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c5
-rw-r--r--drivers/target/sbp/sbp_target.c2
-rw-r--r--drivers/target/target_core_alua.c7
-rw-r--r--drivers/target/target_core_configfs.c40
-rw-r--r--drivers/target/target_core_device.c82
-rw-r--r--drivers/target/target_core_fabric_lib.c4
-rw-r--r--drivers/target/target_core_file.c4
-rw-r--r--drivers/target/target_core_iblock.c4
-rw-r--r--drivers/target/target_core_internal.h3
-rw-r--r--drivers/target/target_core_pr.c37
-rw-r--r--drivers/target/target_core_pscsi.c60
-rw-r--r--drivers/target/target_core_pscsi.h7
-rw-r--r--drivers/target/target_core_rd.c4
-rw-r--r--drivers/target/target_core_sbc.c4
-rw-r--r--drivers/target/target_core_spc.c3
-rw-r--r--drivers/target/target_core_stat.c3
-rw-r--r--drivers/target/target_core_tmr.c2
-rw-r--r--drivers/target/target_core_tpg.c3
-rw-r--r--drivers/target/target_core_transport.c17
-rw-r--r--drivers/target/target_core_ua.c3
-rw-r--r--drivers/target/target_core_user.c148
-rw-r--r--drivers/target/target_core_xcopy.c18
-rw-r--r--drivers/target/tcm_fc/tfc_cmd.c4
-rw-r--r--drivers/target/tcm_fc/tfc_conf.c4
-rw-r--r--drivers/target/tcm_fc/tfc_io.c4
-rw-r--r--drivers/target/tcm_fc/tfc_sess.c4
-rw-r--r--drivers/thermal/armada_thermal.c6
-rw-r--r--drivers/thermal/intel_powerclamp.c89
-rw-r--r--drivers/thermal/rockchip_thermal.c2
-rw-r--r--drivers/thermal/thermal_core.h2
-rw-r--r--drivers/thermal/ti-soc-thermal/dra752-thermal-data.c3
-rw-r--r--drivers/thermal/ti-soc-thermal/omap5-thermal-data.c3
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-bandgap.c78
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-bandgap.h6
-rw-r--r--drivers/tty/hvc/hvc_xen.c20
-rw-r--r--drivers/tty/mips_ejtag_fdc.c17
-rw-r--r--drivers/tty/n_gsm.c5
-rw-r--r--drivers/tty/n_hdlc.c4
-rw-r--r--drivers/tty/n_tty.c43
-rw-r--r--drivers/tty/pty.c5
-rw-r--r--drivers/tty/serial/8250/8250_omap.c82
-rw-r--r--drivers/tty/serial/8250/8250_pci.c25
-rw-r--r--drivers/tty/serial/amba-pl011.c21
-rw-r--r--drivers/tty/serial/atmel_serial.c2
-rw-r--r--drivers/tty/serial/earlycon.c9
-rw-r--r--drivers/tty/serial/imx.c8
-rw-r--r--drivers/tty/serial/of_serial.c1
-rw-r--r--drivers/tty/serial/omap-serial.c2
-rw-r--r--drivers/tty/serial/samsung.c5
-rw-r--r--drivers/tty/serial/serial_core.c2
-rw-r--r--drivers/tty/serial/serial_mctrl_gpio.c2
-rw-r--r--drivers/tty/serial/uartlite.c11
-rw-r--r--drivers/tty/serial/xilinx_uartps.c12
-rw-r--r--drivers/tty/tty_buffer.c41
-rw-r--r--drivers/tty/tty_ioctl.c3
-rw-r--r--drivers/usb/chipidea/debug.c6
-rw-r--r--drivers/usb/chipidea/otg_fsm.c4
-rw-r--r--drivers/usb/class/cdc-acm.c7
-rw-r--r--drivers/usb/core/quirks.c3
-rw-r--r--drivers/usb/dwc3/core.h4
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c94
-rw-r--r--drivers/usb/gadget/configfs.c1
-rw-r--r--drivers/usb/gadget/function/f_fs.c15
-rw-r--r--drivers/usb/gadget/function/f_hid.c16
-rw-r--r--drivers/usb/gadget/function/f_midi.c8
-rw-r--r--drivers/usb/gadget/function/f_uac1.c5
-rw-r--r--drivers/usb/gadget/function/u_serial.c5
-rw-r--r--drivers/usb/gadget/legacy/acm_ms.c10
-rw-r--r--drivers/usb/gadget/legacy/audio.c10
-rw-r--r--drivers/usb/gadget/legacy/cdc2.c10
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c4
-rw-r--r--drivers/usb/gadget/legacy/ether.c12
-rw-r--r--drivers/usb/gadget/legacy/g_ffs.c6
-rw-r--r--drivers/usb/gadget/legacy/gmidi.c10
-rw-r--r--drivers/usb/gadget/legacy/hid.c12
-rw-r--r--drivers/usb/gadget/legacy/mass_storage.c6
-rw-r--r--drivers/usb/gadget/legacy/multi.c10
-rw-r--r--drivers/usb/gadget/legacy/ncm.c10
-rw-r--r--drivers/usb/gadget/legacy/nokia.c10
-rw-r--r--drivers/usb/gadget/legacy/printer.c8
-rw-r--r--drivers/usb/gadget/legacy/serial.c4
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.c3
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.h1
-rw-r--r--drivers/usb/gadget/legacy/webcam.c8
-rw-r--r--drivers/usb/gadget/legacy/zero.c4
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c4
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c4
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c4
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.c4
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c4
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c4
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c2
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c4
-rw-r--r--drivers/usb/host/ehci-msm.c13
-rw-r--r--drivers/usb/host/xhci-ring.c7
-rw-r--r--drivers/usb/host/xhci.c57
-rw-r--r--drivers/usb/host/xhci.h4
-rw-r--r--drivers/usb/image/microtek.c1
-rw-r--r--drivers/usb/misc/ldusb.c10
-rw-r--r--drivers/usb/musb/musb_core.c14
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c6
-rw-r--r--drivers/usb/phy/phy-isp1301-omap.c2
-rw-r--r--drivers/usb/phy/phy-tahvo.c3
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c38
-rw-r--r--drivers/usb/serial/Kconfig2
-rw-r--r--drivers/usb/serial/cp210x.c2
-rw-r--r--drivers/usb/serial/ftdi_sio.c1
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h1
-rw-r--r--drivers/usb/serial/pl2303.c1
-rw-r--r--drivers/usb/serial/pl2303.h4
-rw-r--r--drivers/usb/serial/visor.c2
-rw-r--r--drivers/usb/storage/scsiglue.c1
-rw-r--r--drivers/usb/storage/uas-detect.h11
-rw-r--r--drivers/usb/storage/uas.c17
-rw-r--r--drivers/usb/storage/unusual_devs.h7
-rw-r--r--drivers/usb/storage/usb.c8
-rw-r--r--drivers/vfio/Kconfig2
-rw-r--r--drivers/vfio/pci/vfio_pci.c8
-rw-r--r--drivers/vfio/vfio.c21
-rw-r--r--drivers/vhost/scsi.c9
-rw-r--r--drivers/video/backlight/pwm_bl.c4
-rw-r--r--drivers/video/console/newport_con.c6
-rw-r--r--drivers/video/fbdev/Kconfig8
-rw-r--r--drivers/video/fbdev/Makefile1
-rw-r--r--drivers/video/fbdev/amifb.c8
-rw-r--r--drivers/video/fbdev/atafb.c3
-rw-r--r--drivers/video/fbdev/atmel_lcdfb.c3
-rw-r--r--drivers/video/fbdev/aty/aty128fb.c36
-rw-r--r--drivers/video/fbdev/aty/radeon_base.c29
-rw-r--r--drivers/video/fbdev/aty/radeonfb.h2
-rw-r--r--drivers/video/fbdev/core/Makefile2
-rw-r--r--drivers/video/fbdev/core/fb_defio.c2
-rw-r--r--drivers/video/fbdev/core/fbmon.c4
-rw-r--r--drivers/video/fbdev/gbefb.c25
-rw-r--r--drivers/video/fbdev/geode/gxfb_core.c3
-rw-r--r--drivers/video/fbdev/hpfb.c4
-rw-r--r--drivers/video/fbdev/i810/i810.h3
-rw-r--r--drivers/video/fbdev/i810/i810_main.c11
-rw-r--r--drivers/video/fbdev/i810/i810_main.h26
-rw-r--r--drivers/video/fbdev/imxfb.c2
-rw-r--r--drivers/video/fbdev/intelfb/intelfb.h4
-rw-r--r--drivers/video/fbdev/intelfb/intelfbdrv.c38
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_base.c42
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_base.h27
-rw-r--r--drivers/video/fbdev/msm/Makefile19
-rw-r--r--drivers/video/fbdev/msm/mddi.c821
-rw-r--r--drivers/video/fbdev/msm/mddi_client_dummy.c85
-rw-r--r--drivers/video/fbdev/msm/mddi_client_nt35399.c252
-rw-r--r--drivers/video/fbdev/msm/mddi_client_toshiba.c280
-rw-r--r--drivers/video/fbdev/msm/mddi_hw.h305
-rw-r--r--drivers/video/fbdev/msm/mdp.c520
-rw-r--r--drivers/video/fbdev/msm/mdp_csc_table.h582
-rw-r--r--drivers/video/fbdev/msm/mdp_hw.h627
-rw-r--r--drivers/video/fbdev/msm/mdp_ppp.c731
-rw-r--r--drivers/video/fbdev/msm/mdp_scale_tables.c766
-rw-r--r--drivers/video/fbdev/msm/mdp_scale_tables.h38
-rw-r--r--drivers/video/fbdev/msm/msm_fb.c659
-rw-r--r--drivers/video/fbdev/mxsfb.c70
-rw-r--r--drivers/video/fbdev/neofb.c26
-rw-r--r--drivers/video/fbdev/nvidia/nv_type.h7
-rw-r--r--drivers/video/fbdev/nvidia/nvidia.c37
-rw-r--r--drivers/video/fbdev/omap/Kconfig2
-rw-r--r--drivers/video/fbdev/omap2/displays-new/encoder-opa362.c12
-rw-r--r--drivers/video/fbdev/omap2/displays-new/panel-dpi.c13
-rw-r--r--drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c7
-rw-r--r--drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c9
-rw-r--r--drivers/video/fbdev/omap2/dss/core.c80
-rw-r--r--drivers/video/fbdev/omap2/dss/dispc.c156
-rw-r--r--drivers/video/fbdev/omap2/dss/display-sysfs.c2
-rw-r--r--drivers/video/fbdev/omap2/dss/dpi.c36
-rw-r--r--drivers/video/fbdev/omap2/dss/dsi.c27
-rw-r--r--drivers/video/fbdev/omap2/dss/dss.c223
-rw-r--r--drivers/video/fbdev/omap2/dss/dss.h32
-rw-r--r--drivers/video/fbdev/omap2/dss/hdmi4.c30
-rw-r--r--drivers/video/fbdev/omap2/dss/hdmi4_core.c12
-rw-r--r--drivers/video/fbdev/omap2/dss/hdmi5.c28
-rw-r--r--drivers/video/fbdev/omap2/dss/hdmi5_core.c5
-rw-r--r--drivers/video/fbdev/omap2/dss/hdmi_wp.c16
-rw-r--r--drivers/video/fbdev/omap2/dss/rfbi.c32
-rw-r--r--drivers/video/fbdev/omap2/dss/sdi.c35
-rw-r--r--drivers/video/fbdev/omap2/dss/venc.c31
-rw-r--r--drivers/video/fbdev/pm2fb.c31
-rw-r--r--drivers/video/fbdev/pm3fb.c30
-rw-r--r--drivers/video/fbdev/riva/fbdev.c39
-rw-r--r--drivers/video/fbdev/riva/rivafb.h4
-rw-r--r--drivers/video/fbdev/savage/savagefb.h4
-rw-r--r--drivers/video/fbdev/savage/savagefb_driver.c17
-rw-r--r--drivers/video/fbdev/sis/sis.h2
-rw-r--r--drivers/video/fbdev/sis/sis_main.c27
-rw-r--r--drivers/video/fbdev/ssd1307fb.c289
-rw-r--r--drivers/video/fbdev/tdfxfb.c41
-rw-r--r--drivers/video/fbdev/vesafb.c80
-rw-r--r--drivers/virtio/virtio_pci_common.c4
-rw-r--r--drivers/watchdog/Kconfig12
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/st_lpc_wdt.c344
-rw-r--r--drivers/xen/events/events_2l.c10
-rw-r--r--drivers/xen/events/events_base.c19
-rw-r--r--drivers/xen/gntdev.c28
-rw-r--r--drivers/xen/grant-table.c28
-rw-r--r--drivers/xen/manage.c9
-rw-r--r--drivers/xen/swiotlb-xen.c2
-rw-r--r--drivers/xen/xen-acpi-cpuhotplug.c12
-rw-r--r--drivers/xen/xen-pciback/conf_space.c6
-rw-r--r--drivers/xen/xen-pciback/conf_space.h2
-rw-r--r--drivers/xen/xen-pciback/conf_space_header.c2
-rw-r--r--drivers/xen/xen-scsiback.c5
-rw-r--r--drivers/xen/xenbus/xenbus_probe.c29
1427 files changed, 68685 insertions, 29199 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index ab2cbb51c6aa..35da507411a0 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -54,6 +54,9 @@ config ACPI_GENERIC_GSI
config ACPI_SYSTEM_POWER_STATES_SUPPORT
bool
+config ACPI_CCA_REQUIRED
+ bool
+
config ACPI_SLEEP
bool
depends on SUSPEND || HIBERNATION
@@ -62,7 +65,7 @@ config ACPI_SLEEP
config ACPI_PROCFS_POWER
bool "Deprecated power /proc/acpi directories"
- depends on PROC_FS
+ depends on X86 && PROC_FS
help
For backwards compatibility, this option allows
deprecated power /proc/acpi/ directories to exist, even when
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 8a063e276530..73d840bef455 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -52,9 +52,6 @@ acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
-ifdef CONFIG_ACPI_VIDEO
-acpi-y += video_detect.o
-endif
acpi-y += acpi_lpat.o
acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o
@@ -95,3 +92,5 @@ obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o
obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o
obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o
+
+video-objs += acpi_video.o video_detect.o
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c
index bbcc2b5a70d4..9b5354a2cd08 100644
--- a/drivers/acpi/ac.c
+++ b/drivers/acpi/ac.c
@@ -308,7 +308,7 @@ static int thinkpad_e530_quirk(const struct dmi_system_id *d)
return 0;
}
-static struct dmi_system_id ac_dmi_table[] = {
+static const struct dmi_system_id ac_dmi_table[] = {
{
.callback = thinkpad_e530_quirk,
.ident = "thinkpad e530",
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 37fb19047603..569ee090343f 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -129,50 +129,50 @@ static void byt_i2c_setup(struct lpss_private_data *pdata)
writel(0, pdata->mmio_base + LPSS_I2C_ENABLE);
}
-static struct lpss_device_desc lpt_dev_desc = {
+static const struct lpss_device_desc lpt_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
.prv_offset = 0x800,
};
-static struct lpss_device_desc lpt_i2c_dev_desc = {
+static const struct lpss_device_desc lpt_i2c_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR,
.prv_offset = 0x800,
};
-static struct lpss_device_desc lpt_uart_dev_desc = {
+static const struct lpss_device_desc lpt_uart_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
.clk_con_id = "baudclk",
.prv_offset = 0x800,
.setup = lpss_uart_setup,
};
-static struct lpss_device_desc lpt_sdio_dev_desc = {
+static const struct lpss_device_desc lpt_sdio_dev_desc = {
.flags = LPSS_LTR,
.prv_offset = 0x1000,
.prv_size_override = 0x1018,
};
-static struct lpss_device_desc byt_pwm_dev_desc = {
+static const struct lpss_device_desc byt_pwm_dev_desc = {
.flags = LPSS_SAVE_CTX,
};
-static struct lpss_device_desc byt_uart_dev_desc = {
+static const struct lpss_device_desc byt_uart_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
.clk_con_id = "baudclk",
.prv_offset = 0x800,
.setup = lpss_uart_setup,
};
-static struct lpss_device_desc byt_spi_dev_desc = {
+static const struct lpss_device_desc byt_spi_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
.prv_offset = 0x400,
};
-static struct lpss_device_desc byt_sdio_dev_desc = {
+static const struct lpss_device_desc byt_sdio_dev_desc = {
.flags = LPSS_CLK,
};
-static struct lpss_device_desc byt_i2c_dev_desc = {
+static const struct lpss_device_desc byt_i2c_dev_desc = {
.flags = LPSS_CLK | LPSS_SAVE_CTX,
.prv_offset = 0x800,
.setup = byt_i2c_setup,
@@ -323,14 +323,14 @@ out:
static int acpi_lpss_create_device(struct acpi_device *adev,
const struct acpi_device_id *id)
{
- struct lpss_device_desc *dev_desc;
+ const struct lpss_device_desc *dev_desc;
struct lpss_private_data *pdata;
struct resource_entry *rentry;
struct list_head resource_list;
struct platform_device *pdev;
int ret;
- dev_desc = (struct lpss_device_desc *)id->driver_data;
+ dev_desc = (const struct lpss_device_desc *)id->driver_data;
if (!dev_desc) {
pdev = acpi_create_platform_device(adev);
return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1;
diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c
index 6bc9cbc01ad6..00b39802d7ec 100644
--- a/drivers/acpi/acpi_pad.c
+++ b/drivers/acpi/acpi_pad.c
@@ -105,7 +105,7 @@ static void round_robin_cpu(unsigned int tsk_index)
mutex_lock(&round_robin_lock);
cpumask_clear(tmp);
for_each_cpu(cpu, pad_busy_cpus)
- cpumask_or(tmp, tmp, topology_thread_cpumask(cpu));
+ cpumask_or(tmp, tmp, topology_sibling_cpumask(cpu));
cpumask_andnot(tmp, cpu_online_mask, tmp);
/* avoid HT sibilings if possible */
if (cpumask_empty(tmp))
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index 4bf75597f732..06a67d5f2846 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -103,7 +103,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
pdevinfo.res = resources;
pdevinfo.num_res = count;
pdevinfo.fwnode = acpi_fwnode_handle(adev);
- pdevinfo.dma_mask = DMA_BIT_MASK(32);
+ pdevinfo.dma_mask = acpi_check_dma(adev, NULL) ? DMA_BIT_MASK(32) : 0;
pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev))
dev_err(&adev->dev, "platform device creation failed: %ld\n",
diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c
index b193f8425999..ff6d8adc9cda 100644
--- a/drivers/acpi/acpi_pnp.c
+++ b/drivers/acpi/acpi_pnp.c
@@ -304,6 +304,8 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = {
{"PNPb006"},
/* cs423x-pnpbios */
{"CSC0100"},
+ {"CSC0103"},
+ {"CSC0110"},
{"CSC0000"},
{"GIM0100"}, /* Guillemot Turtlebeach something appears to be cs4232 compatible */
/* es18xx-pnpbios */
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index 58f335ca2e75..92a5f738e370 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -170,7 +170,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr)
acpi_status status;
int ret;
- if (pr->phys_id == PHYS_CPUID_INVALID)
+ if (invalid_phys_cpuid(pr->phys_id))
return -ENODEV;
status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta);
@@ -215,8 +215,7 @@ static int acpi_processor_get_info(struct acpi_device *device)
union acpi_object object = { 0 };
struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
struct acpi_processor *pr = acpi_driver_data(device);
- phys_cpuid_t phys_id;
- int cpu_index, device_declaration = 0;
+ int device_declaration = 0;
acpi_status status = AE_OK;
static int cpu0_initialized;
unsigned long long value;
@@ -263,29 +262,28 @@ static int acpi_processor_get_info(struct acpi_device *device)
pr->acpi_id = value;
}
- phys_id = acpi_get_phys_id(pr->handle, device_declaration, pr->acpi_id);
- if (phys_id == PHYS_CPUID_INVALID)
+ pr->phys_id = acpi_get_phys_id(pr->handle, device_declaration,
+ pr->acpi_id);
+ if (invalid_phys_cpuid(pr->phys_id))
acpi_handle_debug(pr->handle, "failed to get CPU physical ID.\n");
- pr->phys_id = phys_id;
- cpu_index = acpi_map_cpuid(pr->phys_id, pr->acpi_id);
+ pr->id = acpi_map_cpuid(pr->phys_id, pr->acpi_id);
if (!cpu0_initialized && !acpi_has_cpu_in_madt()) {
cpu0_initialized = 1;
/*
* Handle UP system running SMP kernel, with no CPU
* entry in MADT
*/
- if ((cpu_index == -1) && (num_online_cpus() == 1))
- cpu_index = 0;
+ if (invalid_logical_cpuid(pr->id) && (num_online_cpus() == 1))
+ pr->id = 0;
}
- pr->id = cpu_index;
/*
* Extra Processor objects may be enumerated on MP systems with
* less than the max # of CPUs. They should be ignored _iff
* they are physically not present.
*/
- if (pr->id == -1) {
+ if (invalid_logical_cpuid(pr->id)) {
int ret = acpi_processor_hotadd_init(pr);
if (ret)
return ret;
diff --git a/drivers/acpi/video.c b/drivers/acpi/acpi_video.c
index cc79d3fedfb2..8c2fe2f2f9fd 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/acpi_video.c
@@ -43,7 +43,7 @@
#include <acpi/video.h>
#include <asm/uaccess.h>
-#include "internal.h"
+#define PREFIX "ACPI: "
#define ACPI_VIDEO_BUS_NAME "Video Bus"
#define ACPI_VIDEO_DEVICE_NAME "Video Device"
@@ -78,26 +78,17 @@ module_param(brightness_switch_enabled, bool, 0644);
static bool allow_duplicates;
module_param(allow_duplicates, bool, 0644);
-/*
- * For Windows 8 systems: used to decide if video module
- * should skip registering backlight interface of its own.
- */
-enum {
- NATIVE_BACKLIGHT_NOT_SET = -1,
- NATIVE_BACKLIGHT_OFF,
- NATIVE_BACKLIGHT_ON,
-};
-
-static int use_native_backlight_param = NATIVE_BACKLIGHT_NOT_SET;
-module_param_named(use_native_backlight, use_native_backlight_param, int, 0444);
-static int use_native_backlight_dmi = NATIVE_BACKLIGHT_NOT_SET;
+static int disable_backlight_sysfs_if = -1;
+module_param(disable_backlight_sysfs_if, int, 0444);
static int register_count;
+static DEFINE_MUTEX(register_count_mutex);
static struct mutex video_list_lock;
static struct list_head video_bus_head;
static int acpi_video_bus_add(struct acpi_device *device);
static int acpi_video_bus_remove(struct acpi_device *device);
static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
+void acpi_video_detect_exit(void);
static const struct acpi_device_id video_device_ids[] = {
{ACPI_VIDEO_HID, 0},
@@ -157,7 +148,6 @@ struct acpi_video_enumerated_device {
struct acpi_video_bus {
struct acpi_device *device;
bool backlight_registered;
- bool backlight_notifier_registered;
u8 dos_setting;
struct acpi_video_enumerated_device *attached_array;
u8 attached_count;
@@ -170,7 +160,6 @@ struct acpi_video_bus {
struct input_dev *input;
char phys[32]; /* for input device */
struct notifier_block pm_nb;
- struct notifier_block backlight_nb;
};
struct acpi_video_device_flags {
@@ -241,24 +230,6 @@ static int acpi_video_get_next_level(struct acpi_video_device *device,
u32 level_current, u32 event);
static void acpi_video_switch_brightness(struct work_struct *work);
-static bool acpi_video_use_native_backlight(void)
-{
- if (use_native_backlight_param != NATIVE_BACKLIGHT_NOT_SET)
- return use_native_backlight_param;
- else if (use_native_backlight_dmi != NATIVE_BACKLIGHT_NOT_SET)
- return use_native_backlight_dmi;
- return acpi_osi_is_win8();
-}
-
-bool acpi_video_verify_backlight_support(void)
-{
- if (acpi_video_use_native_backlight() &&
- backlight_device_registered(BACKLIGHT_RAW))
- return false;
- return acpi_video_backlight_support();
-}
-EXPORT_SYMBOL_GPL(acpi_video_verify_backlight_support);
-
/* backlight device sysfs support */
static int acpi_video_get_brightness(struct backlight_device *bd)
{
@@ -413,25 +384,21 @@ acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
*/
static int bqc_offset_aml_bug_workaround;
-static int __init video_set_bqc_offset(const struct dmi_system_id *d)
+static int video_set_bqc_offset(const struct dmi_system_id *d)
{
bqc_offset_aml_bug_workaround = 9;
return 0;
}
-static int __init video_disable_native_backlight(const struct dmi_system_id *d)
-{
- use_native_backlight_dmi = NATIVE_BACKLIGHT_OFF;
- return 0;
-}
-
-static int __init video_enable_native_backlight(const struct dmi_system_id *d)
+static int video_disable_backlight_sysfs_if(
+ const struct dmi_system_id *d)
{
- use_native_backlight_dmi = NATIVE_BACKLIGHT_ON;
+ if (disable_backlight_sysfs_if == -1)
+ disable_backlight_sysfs_if = 1;
return 0;
}
-static struct dmi_system_id video_dmi_table[] __initdata = {
+static struct dmi_system_id video_dmi_table[] = {
/*
* Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121
*/
@@ -477,110 +444,19 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
},
/*
- * These models have a working acpi_video backlight control, and using
- * native backlight causes a regression where backlight does not work
- * when userspace is not handling brightness key events. Disable
- * native_backlight on these to fix this:
- * https://bugzilla.kernel.org/show_bug.cgi?id=81691
+ * Some machines have a broken acpi-video interface for brightness
+ * control, but still need an acpi_video_device_lcd_set_level() call
+ * on resume to turn the backlight power on. We Enable backlight
+ * control on these systems, but do not register a backlight sysfs
+ * as brightness control does not work.
*/
{
- .callback = video_disable_native_backlight,
- .ident = "ThinkPad T420",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"),
- },
- },
- {
- .callback = video_disable_native_backlight,
- .ident = "ThinkPad T520",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"),
- },
- },
- {
- .callback = video_disable_native_backlight,
- .ident = "ThinkPad X201s",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"),
- },
- },
-
- /* The native backlight controls do not work on some older machines */
- {
- /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */
- .callback = video_disable_native_backlight,
- .ident = "HP ENVY 15 Notebook",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"),
- },
- },
-
- {
- .callback = video_disable_native_backlight,
- .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"),
- },
- },
- {
- .callback = video_disable_native_backlight,
- .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "370R4E/370R4V/370R5E/3570RE/370R5V"),
- },
- },
- {
- /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */
- .callback = video_disable_native_backlight,
- .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "3570R/370R/470R/450R/510R/4450RV"),
- },
- },
- {
- /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */
- .callback = video_disable_native_backlight,
- .ident = "SAMSUNG 730U3E/740U3E",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"),
- },
- },
- {
- /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */
- .callback = video_disable_native_backlight,
- .ident = "SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "900X3C/900X3D/900X3E/900X4C/900X4D"),
- },
- },
-
- {
- /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */
- .callback = video_disable_native_backlight,
- .ident = "Dell XPS15 L521X",
+ /* https://bugs.freedesktop.org/show_bug.cgi?id=82634 */
+ .callback = video_disable_backlight_sysfs_if,
+ .ident = "Toshiba Portege R830",
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"),
- },
- },
-
- /* Non win8 machines which need native backlight nevertheless */
- {
- /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */
- .callback = video_enable_native_backlight,
- .ident = "Lenovo Ideapad Z570",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "102434U"),
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R830"),
},
},
{}
@@ -1382,7 +1258,7 @@ acpi_video_switch_brightness(struct work_struct *work)
int result = -EINVAL;
/* no warning message if acpi_backlight=vendor or a quirk is used */
- if (!acpi_video_verify_backlight_support())
+ if (!device->backlight)
return;
if (!device->brightness)
@@ -1657,8 +1533,9 @@ static int acpi_video_resume(struct notifier_block *nb,
for (i = 0; i < video->attached_count; i++) {
video_device = video->attached_array[i].bind_info;
- if (video_device && video_device->backlight)
- acpi_video_set_brightness(video_device->backlight);
+ if (video_device && video_device->brightness)
+ acpi_video_device_lcd_set_level(video_device,
+ video_device->brightness->curr);
}
return NOTIFY_OK;
@@ -1707,6 +1584,10 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
result = acpi_video_init_brightness(device);
if (result)
return;
+
+ if (disable_backlight_sysfs_if > 0)
+ return;
+
name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
if (!name)
return;
@@ -1729,8 +1610,10 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
&acpi_backlight_ops,
&props);
kfree(name);
- if (IS_ERR(device->backlight))
+ if (IS_ERR(device->backlight)) {
+ device->backlight = NULL;
return;
+ }
/*
* Save current brightness level in case we have to restore it
@@ -1787,7 +1670,7 @@ static int acpi_video_bus_register_backlight(struct acpi_video_bus *video)
acpi_video_run_bcl_for_osi(video);
- if (!acpi_video_verify_backlight_support())
+ if (acpi_video_get_backlight_type() != acpi_backlight_video)
return 0;
mutex_lock(&video->device_list_lock);
@@ -1931,56 +1814,6 @@ static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video)
video->input = NULL;
}
-static int acpi_video_backlight_notify(struct notifier_block *nb,
- unsigned long val, void *bd)
-{
- struct backlight_device *backlight = bd;
- struct acpi_video_bus *video;
-
- /* acpi_video_verify_backlight_support only cares about raw devices */
- if (backlight->props.type != BACKLIGHT_RAW)
- return NOTIFY_DONE;
-
- video = container_of(nb, struct acpi_video_bus, backlight_nb);
-
- switch (val) {
- case BACKLIGHT_REGISTERED:
- if (!acpi_video_verify_backlight_support())
- acpi_video_bus_unregister_backlight(video);
- break;
- case BACKLIGHT_UNREGISTERED:
- acpi_video_bus_register_backlight(video);
- break;
- }
-
- return NOTIFY_OK;
-}
-
-static int acpi_video_bus_add_backlight_notify_handler(
- struct acpi_video_bus *video)
-{
- int error;
-
- video->backlight_nb.notifier_call = acpi_video_backlight_notify;
- video->backlight_nb.priority = 0;
- error = backlight_register_notifier(&video->backlight_nb);
- if (error == 0)
- video->backlight_notifier_registered = true;
-
- return error;
-}
-
-static int acpi_video_bus_remove_backlight_notify_handler(
- struct acpi_video_bus *video)
-{
- if (!video->backlight_notifier_registered)
- return 0;
-
- video->backlight_notifier_registered = false;
-
- return backlight_unregister_notifier(&video->backlight_nb);
-}
-
static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
{
struct acpi_video_device *dev, *next;
@@ -2062,7 +1895,6 @@ static int acpi_video_bus_add(struct acpi_device *device)
acpi_video_bus_register_backlight(video);
acpi_video_bus_add_notify_handler(video);
- acpi_video_bus_add_backlight_notify_handler(video);
return 0;
@@ -2086,7 +1918,6 @@ static int acpi_video_bus_remove(struct acpi_device *device)
video = acpi_driver_data(device);
- acpi_video_bus_remove_backlight_notify_handler(video);
acpi_video_bus_remove_notify_handler(video);
acpi_video_bus_unregister_backlight(video);
acpi_video_bus_put_devices(video);
@@ -2134,22 +1965,25 @@ static int __init intel_opregion_present(void)
int acpi_video_register(void)
{
- int ret;
+ int ret = 0;
+ mutex_lock(&register_count_mutex);
if (register_count) {
/*
* if the function of acpi_video_register is already called,
* don't register the acpi_vide_bus again and return no error.
*/
- return 0;
+ goto leave;
}
mutex_init(&video_list_lock);
INIT_LIST_HEAD(&video_bus_head);
+ dmi_check_system(video_dmi_table);
+
ret = acpi_bus_register_driver(&acpi_video_bus);
if (ret)
- return ret;
+ goto leave;
/*
* When the acpi_video_bus is loaded successfully, increase
@@ -2157,24 +1991,20 @@ int acpi_video_register(void)
*/
register_count = 1;
- return 0;
+leave:
+ mutex_unlock(&register_count_mutex);
+ return ret;
}
EXPORT_SYMBOL(acpi_video_register);
void acpi_video_unregister(void)
{
- if (!register_count) {
- /*
- * If the acpi video bus is already unloaded, don't
- * unload it again and return directly.
- */
- return;
+ mutex_lock(&register_count_mutex);
+ if (register_count) {
+ acpi_bus_unregister_driver(&acpi_video_bus);
+ register_count = 0;
}
- acpi_bus_unregister_driver(&acpi_video_bus);
-
- register_count = 0;
-
- return;
+ mutex_unlock(&register_count_mutex);
}
EXPORT_SYMBOL(acpi_video_unregister);
@@ -2182,15 +2012,15 @@ void acpi_video_unregister_backlight(void)
{
struct acpi_video_bus *video;
- if (!register_count)
- return;
-
- mutex_lock(&video_list_lock);
- list_for_each_entry(video, &video_bus_head, entry)
- acpi_video_bus_unregister_backlight(video);
- mutex_unlock(&video_list_lock);
+ mutex_lock(&register_count_mutex);
+ if (register_count) {
+ mutex_lock(&video_list_lock);
+ list_for_each_entry(video, &video_bus_head, entry)
+ acpi_video_bus_unregister_backlight(video);
+ mutex_unlock(&video_list_lock);
+ }
+ mutex_unlock(&register_count_mutex);
}
-EXPORT_SYMBOL(acpi_video_unregister_backlight);
/*
* This is kind of nasty. Hardware using Intel chipsets may require
@@ -2212,8 +2042,6 @@ static int __init acpi_video_init(void)
if (acpi_disabled)
return 0;
- dmi_check_system(video_dmi_table);
-
if (intel_opregion_present())
return 0;
@@ -2222,6 +2050,7 @@ static int __init acpi_video_init(void)
static void __exit acpi_video_exit(void)
{
+ acpi_video_detect_exit();
acpi_video_unregister();
return;
diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index 4169bb87a996..43685dd36c77 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -231,7 +231,9 @@ void acpi_db_open_debug_file(char *name);
acpi_status acpi_db_load_acpi_table(char *filename);
acpi_status
-acpi_db_get_table_from_file(char *filename, struct acpi_table_header **table);
+acpi_db_get_table_from_file(char *filename,
+ struct acpi_table_header **table,
+ u8 must_be_aml_table);
/*
* dbhistry - debugger HISTORY command
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 87b27521fcac..ffdb956391f6 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -352,11 +352,21 @@ struct acpi_package_info3 {
u16 reserved;
};
+struct acpi_package_info4 {
+ u8 type;
+ u8 object_type1;
+ u8 count1;
+ u8 sub_object_types;
+ u8 pkg_count;
+ u16 reserved;
+};
+
union acpi_predefined_info {
struct acpi_name_info info;
struct acpi_package_info ret_info;
struct acpi_package_info2 ret_info2;
struct acpi_package_info3 ret_info3;
+ struct acpi_package_info4 ret_info4;
};
/* Reset to default packing */
@@ -1165,4 +1175,9 @@ struct ah_uuid {
char *string;
};
+struct ah_table {
+ char *signature;
+ char *description;
+};
+
#endif /* __ACLOCAL_H__ */
diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h
index 74a390c6db16..0cdd2fce493a 100644
--- a/drivers/acpi/acpica/acparser.h
+++ b/drivers/acpi/acpica/acparser.h
@@ -70,6 +70,9 @@
*
*****************************************************************************/
+extern const u8 acpi_gbl_short_op_index[];
+extern const u8 acpi_gbl_long_op_index[];
+
/*
* psxface - Parser external interfaces
*/
diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h
index a972d11c97c9..b9474b529fcb 100644
--- a/drivers/acpi/acpica/acpredef.h
+++ b/drivers/acpi/acpica/acpredef.h
@@ -105,6 +105,11 @@
* count = 0 (optional)
* (Used for _DLM)
*
+ * ACPI_PTYPE2_VAR_VAR: Variable number of subpackages, each of either a
+ * constant or variable length. The subpackages are preceded by a
+ * constant number of objects.
+ * (Used for _LPI, _RDI)
+ *
* ACPI_PTYPE2_UUID_PAIR: Each subpackage is preceded by a UUID Buffer. The UUID
* defines the format of the package. Zero-length parent package is
* allowed.
@@ -123,7 +128,8 @@ enum acpi_return_package_types {
ACPI_PTYPE2_MIN = 8,
ACPI_PTYPE2_REV_FIXED = 9,
ACPI_PTYPE2_FIX_VAR = 10,
- ACPI_PTYPE2_UUID_PAIR = 11
+ ACPI_PTYPE2_VAR_VAR = 11,
+ ACPI_PTYPE2_UUID_PAIR = 12
};
/* Support macros for users of the predefined info table */
@@ -172,7 +178,7 @@ enum acpi_return_package_types {
* These are the names that can actually be evaluated via acpi_evaluate_object.
* Not present in this table are the following:
*
- * 1) Predefined/Reserved names that are never evaluated via
+ * 1) Predefined/Reserved names that are not usually evaluated via
* acpi_evaluate_object:
* _Lxx and _Exx GPE methods
* _Qxx EC methods
@@ -361,6 +367,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0),
+ {{"_BTH", METHOD_1ARGS(ACPI_TYPE_INTEGER), /* ACPI 6.0 */
+ METHOD_NO_RETURN_VALUE}},
+
{{"_BTM", METHOD_1ARGS(ACPI_TYPE_INTEGER),
METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
@@ -390,6 +399,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER, 0,
0, 0, 0),
+ {{"_CR3", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
{{"_CRS", METHOD_0ARGS,
METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
@@ -445,7 +457,7 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
{{"_DOS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
METHOD_NO_RETURN_VALUE}},
- {{"_DSD", METHOD_0ARGS,
+ {{"_DSD", METHOD_0ARGS, /* ACPI 6.0 */
METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: 1 Buf, 1 Pkg */
PACKAGE_INFO(ACPI_PTYPE2_UUID_PAIR, ACPI_RTYPE_BUFFER, 1,
ACPI_RTYPE_PACKAGE, 1, 0),
@@ -604,6 +616,12 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (2 Int) */
PACKAGE_INFO(ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0),
+ {{"_LPI", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (3 Int, n Pkg (10 Int/Buf) */
+ PACKAGE_INFO(ACPI_PTYPE2_VAR_VAR, ACPI_RTYPE_INTEGER, 3,
+ ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER | ACPI_RTYPE_STRING,
+ 10, 0),
+
{{"_MAT", METHOD_0ARGS,
METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
@@ -624,6 +642,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
ACPI_TYPE_INTEGER),
METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+ {{"_MTL", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
{{"_NTT", METHOD_0ARGS,
METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
@@ -716,6 +737,10 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
+ {{"_PRR", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Ref) */
+ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_REFERENCE, 1, 0, 0, 0),
+
{{"_PRS", METHOD_0ARGS,
METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
@@ -796,6 +821,11 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
{{"_PXM", METHOD_0ARGS,
METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+ {{"_RDI", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int, n Pkg (m Ref)) */
+ PACKAGE_INFO(ACPI_PTYPE2_VAR_VAR, ACPI_RTYPE_INTEGER, 1,
+ ACPI_RTYPE_REFERENCE, 0, 0),
+
{{"_REG", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
METHOD_NO_RETURN_VALUE}},
@@ -808,6 +838,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
{{"_ROM", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
+ {{"_RST", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_NO_RETURN_VALUE}},
+
{{"_RTV", METHOD_0ARGS,
METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
@@ -935,6 +968,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
{{"_TDL", METHOD_0ARGS,
METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+ {{"_TFP", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
{{"_TIP", METHOD_1ARGS(ACPI_TYPE_INTEGER),
METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
@@ -959,6 +995,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 5 Int with count */
PACKAGE_INFO(ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 5, 0, 0, 0),
+ {{"_TSN", METHOD_0ARGS, /* ACPI 6.0 */
+ METHOD_RETURNS(ACPI_RTYPE_REFERENCE)}},
+
{{"_TSP", METHOD_0ARGS,
METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index 2b3c5bd222f1..d49f5c7a20d9 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -251,7 +251,7 @@ extern const u8 _acpi_ctype[];
#define _ACPI_DI 0x04 /* '0'-'9' */
#define _ACPI_LO 0x02 /* 'a'-'z' */
#define _ACPI_PU 0x10 /* punctuation */
-#define _ACPI_SP 0x08 /* space */
+#define _ACPI_SP 0x08 /* space, tab, CR, LF, VT, FF */
#define _ACPI_UP 0x01 /* 'A'-'Z' */
#define _ACPI_XD 0x80 /* '0'-'9', 'A'-'F', 'a'-'f' */
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c
index d72565a3c646..85bb951430d9 100644
--- a/drivers/acpi/acpica/dsmethod.c
+++ b/drivers/acpi/acpica/dsmethod.c
@@ -116,6 +116,7 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
walk_state =
acpi_ds_create_walk_state(node->owner_id, NULL, NULL, NULL);
if (!walk_state) {
+ acpi_ps_free_op(op);
return_ACPI_STATUS(AE_NO_MEMORY);
}
@@ -125,6 +126,7 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
obj_desc->method.aml_length, NULL, 0);
if (ACPI_FAILURE(status)) {
acpi_ds_delete_walk_state(walk_state);
+ acpi_ps_free_op(op);
return_ACPI_STATUS(status);
}
@@ -133,9 +135,6 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
/* Parse the method, scan for creation of named objects */
status = acpi_ps_parse_aml(walk_state);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
acpi_ps_delete_parse_tree(op);
return_ACPI_STATUS(status);
diff --git a/drivers/acpi/acpica/hwpci.c b/drivers/acpi/acpica/hwpci.c
index c5214dec4988..f785ea788356 100644
--- a/drivers/acpi/acpica/hwpci.c
+++ b/drivers/acpi/acpica/hwpci.c
@@ -123,7 +123,7 @@ acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
acpi_handle root_pci_device, acpi_handle pci_region)
{
acpi_status status;
- struct acpi_pci_device *list_head = NULL;
+ struct acpi_pci_device *list_head;
ACPI_FUNCTION_TRACE(hw_derive_pci_id);
@@ -177,13 +177,13 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device,
acpi_handle parent_device;
acpi_status status;
struct acpi_pci_device *list_element;
- struct acpi_pci_device *list_head = NULL;
/*
* Ascend namespace branch until the root_pci_device is reached, building
* a list of device nodes. Loop will exit when either the PCI device is
* found, or the root of the namespace is reached.
*/
+ *return_list_head = NULL;
current_device = pci_region;
while (1) {
status = acpi_get_parent(current_device, &parent_device);
@@ -198,7 +198,6 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device,
/* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */
if (parent_device == root_pci_device) {
- *return_list_head = list_head;
return (AE_OK);
}
@@ -213,9 +212,9 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device,
/* Put new element at the head of the list */
- list_element->next = list_head;
+ list_element->next = *return_list_head;
list_element->device = parent_device;
- list_head = list_element;
+ *return_list_head = list_element;
current_device = parent_device;
}
diff --git a/drivers/acpi/acpica/nsprepkg.c b/drivers/acpi/acpica/nsprepkg.c
index 8b79958b7aca..9bb251932b45 100644
--- a/drivers/acpi/acpica/nsprepkg.c
+++ b/drivers/acpi/acpica/nsprepkg.c
@@ -316,6 +316,13 @@ acpi_ns_check_package(struct acpi_evaluate_info *info,
acpi_ns_check_package_list(info, package, elements, count);
break;
+ case ACPI_PTYPE2_VAR_VAR:
+ /*
+ * Returns a variable list of packages, each with a variable list
+ * of objects.
+ */
+ break;
+
case ACPI_PTYPE2_UUID_PAIR:
/* The package must contain pairs of (UUID + type) */
@@ -487,6 +494,12 @@ acpi_ns_check_package_list(struct acpi_evaluate_info *info,
}
break;
+ case ACPI_PTYPE2_VAR_VAR:
+ /*
+ * Each subpackage has a fixed or variable number of elements
+ */
+ break;
+
case ACPI_PTYPE2_FIXED:
/* Each subpackage has a fixed length */
diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c
index 151fcd95ba84..77d8103d0094 100644
--- a/drivers/acpi/acpica/nsrepair.c
+++ b/drivers/acpi/acpica/nsrepair.c
@@ -497,10 +497,10 @@ acpi_ns_remove_null_elements(struct acpi_evaluate_info *info,
case ACPI_PTYPE2_MIN:
case ACPI_PTYPE2_REV_FIXED:
case ACPI_PTYPE2_FIX_VAR:
-
break;
default:
+ case ACPI_PTYPE2_VAR_VAR:
case ACPI_PTYPE1_FIXED:
case ACPI_PTYPE1_OPTION:
return;
diff --git a/drivers/acpi/acpica/psopinfo.c b/drivers/acpi/acpica/psopinfo.c
index 20e1a35169fc..58310907fa7b 100644
--- a/drivers/acpi/acpica/psopinfo.c
+++ b/drivers/acpi/acpica/psopinfo.c
@@ -50,9 +50,6 @@
#define _COMPONENT ACPI_PARSER
ACPI_MODULE_NAME("psopinfo")
-extern const u8 acpi_gbl_short_op_index[];
-extern const u8 acpi_gbl_long_op_index[];
-
static const u8 acpi_gbl_argument_count[] =
{ 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 6 };
diff --git a/drivers/acpi/acpica/utfileio.c b/drivers/acpi/acpica/utfileio.c
index 7e1168be39fa..857af824337b 100644
--- a/drivers/acpi/acpica/utfileio.c
+++ b/drivers/acpi/acpica/utfileio.c
@@ -198,11 +198,8 @@ acpi_ut_read_table(FILE * fp,
table_header.length, file_size);
#ifdef ACPI_ASL_COMPILER
- status = fl_check_for_ascii(fp, NULL, FALSE);
- if (ACPI_SUCCESS(status)) {
- acpi_os_printf
- ("File appears to be ASCII only, must be binary\n");
- }
+ acpi_os_printf("File is corrupt or is ASCII text -- "
+ "it must be a binary file\n");
#endif
return (AE_BAD_HEADER);
}
@@ -315,7 +312,7 @@ acpi_ut_read_table_from_file(char *filename, struct acpi_table_header ** table)
/* Get the entire file */
fprintf(stderr,
- "Loading Acpi table from file %10s - Length %.8u (%06X)\n",
+ "Reading ACPI table from file %10s - Length %.8u (0x%06X)\n",
filename, file_size, file_size);
status = acpi_ut_read_table(file, table, &table_length);
diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c
index a72685c1e819..5e8df9177da4 100644
--- a/drivers/acpi/acpica/utglobal.c
+++ b/drivers/acpi/acpica/utglobal.c
@@ -102,19 +102,12 @@ const struct acpi_predefined_names acpi_gbl_pre_defined_names[] = {
{"_SB_", ACPI_TYPE_DEVICE, NULL},
{"_SI_", ACPI_TYPE_LOCAL_SCOPE, NULL},
{"_TZ_", ACPI_TYPE_DEVICE, NULL},
- /*
- * March, 2015:
- * The _REV object is in the process of being deprecated, because
- * other ACPI implementations permanently return 2. Thus, it
- * has little or no value. Return 2 for compatibility with
- * other ACPI implementations.
- */
- {"_REV", ACPI_TYPE_INTEGER, ACPI_CAST_PTR(char, 2)},
+ {"_REV", ACPI_TYPE_INTEGER, (char *)ACPI_CA_SUPPORT_LEVEL},
{"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME},
- {"_GL_", ACPI_TYPE_MUTEX, ACPI_CAST_PTR(char, 1)},
+ {"_GL_", ACPI_TYPE_MUTEX, (char *)1},
#if !defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY)
- {"_OSI", ACPI_TYPE_METHOD, ACPI_CAST_PTR(char, 1)},
+ {"_OSI", ACPI_TYPE_METHOD, (char *)1},
#endif
/* Table terminator */
diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c
index aa448278ba28..fda8b3def81c 100644
--- a/drivers/acpi/acpica/uthex.c
+++ b/drivers/acpi/acpica/uthex.c
@@ -75,9 +75,9 @@ char acpi_ut_hex_to_ascii_char(u64 integer, u32 position)
/*******************************************************************************
*
- * FUNCTION: acpi_ut_hex_char_to_value
+ * FUNCTION: acpi_ut_ascii_char_to_hex
*
- * PARAMETERS: ascii_char - Hex character in Ascii
+ * PARAMETERS: hex_char - Hex character in Ascii
*
* RETURN: The binary value of the ascii/hex character
*
diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c
index 306e785f9418..98d578753101 100644
--- a/drivers/acpi/acpica/utxferror.c
+++ b/drivers/acpi/acpica/utxferror.c
@@ -107,9 +107,16 @@ acpi_exception(const char *module_name,
va_list arg_list;
ACPI_MSG_REDIRECT_BEGIN;
- acpi_os_printf(ACPI_MSG_EXCEPTION "%s, ",
- acpi_format_exception(status));
+ /* For AE_OK, just print the message */
+
+ if (ACPI_SUCCESS(status)) {
+ acpi_os_printf(ACPI_MSG_EXCEPTION);
+
+ } else {
+ acpi_os_printf(ACPI_MSG_EXCEPTION "%s, ",
+ acpi_format_exception(status));
+ }
va_start(arg_list, format);
acpi_os_vprintf(format, arg_list);
ACPI_MSG_SUFFIX;
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index ed65e9c4b5b0..3670bbab57a3 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -35,6 +35,7 @@
#include <linux/nmi.h>
#include <linux/hardirq.h>
#include <linux/pstore.h>
+#include <linux/vmalloc.h>
#include <acpi/apei.h>
#include "apei-internal.h"
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index e82d0976a5d0..2bfd53cbfe80 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -729,10 +729,10 @@ static struct llist_head ghes_estatus_llist;
static struct irq_work ghes_proc_irq_work;
/*
- * NMI may be triggered on any CPU, so ghes_nmi_lock is used for
- * mutual exclusion.
+ * NMI may be triggered on any CPU, so ghes_in_nmi is used for
+ * having only one concurrent reader.
*/
-static DEFINE_RAW_SPINLOCK(ghes_nmi_lock);
+static atomic_t ghes_in_nmi = ATOMIC_INIT(0);
static LIST_HEAD(ghes_nmi);
@@ -797,73 +797,75 @@ static void ghes_print_queued_estatus(void)
}
}
+/* Save estatus for further processing in IRQ context */
+static void __process_error(struct ghes *ghes)
+{
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+ u32 len, node_len;
+ struct ghes_estatus_node *estatus_node;
+ struct acpi_hest_generic_status *estatus;
+
+ if (ghes_estatus_cached(ghes->estatus))
+ return;
+
+ len = cper_estatus_len(ghes->estatus);
+ node_len = GHES_ESTATUS_NODE_LEN(len);
+
+ estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, node_len);
+ if (!estatus_node)
+ return;
+
+ estatus_node->ghes = ghes;
+ estatus_node->generic = ghes->generic;
+ estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
+ memcpy(estatus, ghes->estatus, len);
+ llist_add(&estatus_node->llnode, &ghes_estatus_llist);
+#endif
+}
+
+static void __ghes_panic(struct ghes *ghes)
+{
+ oops_begin();
+ ghes_print_queued_estatus();
+ __ghes_print_estatus(KERN_EMERG, ghes->generic, ghes->estatus);
+
+ /* reboot to log the error! */
+ if (panic_timeout == 0)
+ panic_timeout = ghes_panic_timeout;
+ panic("Fatal hardware error!");
+}
+
static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
{
- struct ghes *ghes, *ghes_global = NULL;
- int sev, sev_global = -1;
- int ret = NMI_DONE;
+ struct ghes *ghes;
+ int sev, ret = NMI_DONE;
+
+ if (!atomic_add_unless(&ghes_in_nmi, 1, 1))
+ return ret;
- raw_spin_lock(&ghes_nmi_lock);
list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
if (ghes_read_estatus(ghes, 1)) {
ghes_clear_estatus(ghes);
continue;
}
- sev = ghes_severity(ghes->estatus->error_severity);
- if (sev > sev_global) {
- sev_global = sev;
- ghes_global = ghes;
- }
- ret = NMI_HANDLED;
- }
-
- if (ret == NMI_DONE)
- goto out;
- if (sev_global >= GHES_SEV_PANIC) {
- oops_begin();
- ghes_print_queued_estatus();
- __ghes_print_estatus(KERN_EMERG, ghes_global->generic,
- ghes_global->estatus);
- /* reboot to log the error! */
- if (panic_timeout == 0)
- panic_timeout = ghes_panic_timeout;
- panic("Fatal hardware error!");
- }
+ sev = ghes_severity(ghes->estatus->error_severity);
+ if (sev >= GHES_SEV_PANIC)
+ __ghes_panic(ghes);
- list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
-#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
- u32 len, node_len;
- struct ghes_estatus_node *estatus_node;
- struct acpi_hest_generic_status *estatus;
-#endif
if (!(ghes->flags & GHES_TO_CLEAR))
continue;
-#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
- if (ghes_estatus_cached(ghes->estatus))
- goto next;
- /* Save estatus for further processing in IRQ context */
- len = cper_estatus_len(ghes->estatus);
- node_len = GHES_ESTATUS_NODE_LEN(len);
- estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool,
- node_len);
- if (estatus_node) {
- estatus_node->ghes = ghes;
- estatus_node->generic = ghes->generic;
- estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
- memcpy(estatus, ghes->estatus, len);
- llist_add(&estatus_node->llnode, &ghes_estatus_llist);
- }
-next:
-#endif
+
+ __process_error(ghes);
ghes_clear_estatus(ghes);
+
+ ret = NMI_HANDLED;
}
+
#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
irq_work_queue(&ghes_proc_irq_work);
#endif
-
-out:
- raw_spin_unlock(&ghes_nmi_lock);
+ atomic_dec(&ghes_in_nmi);
return ret;
}
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 63d43677f644..b3628cc01a53 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -70,6 +70,7 @@ MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
MODULE_DESCRIPTION("ACPI Battery Driver");
MODULE_LICENSE("GPL");
+static async_cookie_t async_cookie;
static int battery_bix_broken_package;
static int battery_notification_delay_ms;
static unsigned int cache_time = 1000;
@@ -338,14 +339,6 @@ static enum power_supply_property energy_battery_props[] = {
POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
-#ifdef CONFIG_ACPI_PROCFS_POWER
-inline char *acpi_battery_units(struct acpi_battery *battery)
-{
- return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
- "mA" : "mW";
-}
-#endif
-
/* --------------------------------------------------------------------------
Battery Management
-------------------------------------------------------------------------- */
@@ -354,14 +347,14 @@ struct acpi_offsets {
u8 mode; /* int or string? */
};
-static struct acpi_offsets state_offsets[] = {
+static const struct acpi_offsets state_offsets[] = {
{offsetof(struct acpi_battery, state), 0},
{offsetof(struct acpi_battery, rate_now), 0},
{offsetof(struct acpi_battery, capacity_now), 0},
{offsetof(struct acpi_battery, voltage_now), 0},
};
-static struct acpi_offsets info_offsets[] = {
+static const struct acpi_offsets info_offsets[] = {
{offsetof(struct acpi_battery, power_unit), 0},
{offsetof(struct acpi_battery, design_capacity), 0},
{offsetof(struct acpi_battery, full_charge_capacity), 0},
@@ -377,7 +370,7 @@ static struct acpi_offsets info_offsets[] = {
{offsetof(struct acpi_battery, oem_info), 1},
};
-static struct acpi_offsets extended_info_offsets[] = {
+static const struct acpi_offsets extended_info_offsets[] = {
{offsetof(struct acpi_battery, revision), 0},
{offsetof(struct acpi_battery, power_unit), 0},
{offsetof(struct acpi_battery, design_capacity), 0},
@@ -402,7 +395,7 @@ static struct acpi_offsets extended_info_offsets[] = {
static int extract_package(struct acpi_battery *battery,
union acpi_object *package,
- struct acpi_offsets *offsets, int num)
+ const struct acpi_offsets *offsets, int num)
{
int i;
union acpi_object *element;
@@ -792,6 +785,12 @@ static void acpi_battery_refresh(struct acpi_battery *battery)
#ifdef CONFIG_ACPI_PROCFS_POWER
static struct proc_dir_entry *acpi_battery_dir;
+static const char *acpi_battery_units(const struct acpi_battery *battery)
+{
+ return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
+ "mA" : "mW";
+}
+
static int acpi_battery_print_info(struct seq_file *seq, int result)
{
struct acpi_battery *battery = seq->private;
@@ -1125,19 +1124,21 @@ static int battery_notify(struct notifier_block *nb,
return 0;
}
-static int battery_bix_broken_package_quirk(const struct dmi_system_id *d)
+static int __init
+battery_bix_broken_package_quirk(const struct dmi_system_id *d)
{
battery_bix_broken_package = 1;
return 0;
}
-static int battery_notification_delay_quirk(const struct dmi_system_id *d)
+static int __init
+battery_notification_delay_quirk(const struct dmi_system_id *d)
{
battery_notification_delay_ms = 1000;
return 0;
}
-static struct dmi_system_id bat_dmi_table[] = {
+static const struct dmi_system_id bat_dmi_table[] __initconst = {
{
.callback = battery_bix_broken_package_quirk,
.ident = "NEC LZ750/LS",
@@ -1292,33 +1293,34 @@ static struct acpi_driver acpi_battery_driver = {
static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
{
- if (acpi_disabled)
- return;
+ int result;
dmi_check_system(bat_dmi_table);
-
+
#ifdef CONFIG_ACPI_PROCFS_POWER
acpi_battery_dir = acpi_lock_battery_dir();
if (!acpi_battery_dir)
return;
#endif
- if (acpi_bus_register_driver(&acpi_battery_driver) < 0) {
+ result = acpi_bus_register_driver(&acpi_battery_driver);
#ifdef CONFIG_ACPI_PROCFS_POWER
+ if (result < 0)
acpi_unlock_battery_dir(acpi_battery_dir);
#endif
- return;
- }
- return;
}
static int __init acpi_battery_init(void)
{
- async_schedule(acpi_battery_init_async, NULL);
+ if (acpi_disabled)
+ return -ENODEV;
+
+ async_cookie = async_schedule(acpi_battery_init_async, NULL);
return 0;
}
static void __exit acpi_battery_exit(void)
{
+ async_synchronize_cookie(async_cookie);
acpi_bus_unregister_driver(&acpi_battery_driver);
#ifdef CONFIG_ACPI_PROCFS_POWER
acpi_unlock_battery_dir(acpi_battery_dir);
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index c412fdb28d34..513e7230e3d0 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -470,6 +470,16 @@ static int __init acpi_bus_init_irq(void)
return 0;
}
+/**
+ * acpi_early_init - Initialize ACPICA and populate the ACPI namespace.
+ *
+ * The ACPI tables are accessible after this, but the handling of events has not
+ * been initialized and the global lock is not available yet, so AML should not
+ * be executed at this point.
+ *
+ * Doing this before switching the EFI runtime services to virtual mode allows
+ * the EfiBootServices memory to be freed slightly earlier on boot.
+ */
void __init acpi_early_init(void)
{
acpi_status status;
@@ -533,26 +543,42 @@ void __init acpi_early_init(void)
acpi_gbl_FADT.sci_interrupt = acpi_sci_override_gsi;
}
#endif
+ return;
+
+ error0:
+ disable_acpi();
+}
+
+/**
+ * acpi_subsystem_init - Finalize the early initialization of ACPI.
+ *
+ * Switch over the platform to the ACPI mode (if possible), initialize the
+ * handling of ACPI events, install the interrupt and global lock handlers.
+ *
+ * Doing this too early is generally unsafe, but at the same time it needs to be
+ * done before all things that really depend on ACPI. The right spot appears to
+ * be before finalizing the EFI initialization.
+ */
+void __init acpi_subsystem_init(void)
+{
+ acpi_status status;
+
+ if (acpi_disabled)
+ return;
status = acpi_enable_subsystem(~ACPI_NO_ACPI_ENABLE);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX "Unable to enable ACPI\n");
- goto error0;
+ disable_acpi();
+ } else {
+ /*
+ * If the system is using ACPI then we can be reasonably
+ * confident that any regulators are managed by the firmware
+ * so tell the regulator core it has everything it needs to
+ * know.
+ */
+ regulator_has_full_constraints();
}
-
- /*
- * If the system is using ACPI then we can be reasonably
- * confident that any regulators are managed by the firmware
- * so tell the regulator core it has everything it needs to
- * know.
- */
- regulator_has_full_constraints();
-
- return;
-
- error0:
- disable_acpi();
- return;
}
static int __init acpi_bus_init(void)
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 735db11a9b00..717afcdb5f4a 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -98,17 +98,16 @@ int acpi_device_get_power(struct acpi_device *device, int *state)
/*
* The power resources settings may indicate a power state
- * shallower than the actual power state of the device.
+ * shallower than the actual power state of the device, because
+ * the same power resources may be referenced by other devices.
*
- * Moreover, on systems predating ACPI 4.0, if the device
- * doesn't depend on any power resources and _PSC returns 3,
- * that means "power off". We need to maintain compatibility
- * with those systems.
+ * For systems predating ACPI 4.0 we assume that D3hot is the
+ * deepest state that can be supported.
*/
if (psc > result && psc < ACPI_STATE_D3_COLD)
result = psc;
else if (result == ACPI_STATE_UNKNOWN)
- result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc;
+ result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_HOT : psc;
}
/*
@@ -153,8 +152,8 @@ static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state)
*/
int acpi_device_set_power(struct acpi_device *device, int state)
{
+ int target_state = state;
int result = 0;
- bool cut_power = false;
if (!device || !device->flags.power_manageable
|| (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
@@ -169,11 +168,21 @@ int acpi_device_set_power(struct acpi_device *device, int state)
return 0;
}
- if (!device->power.states[state].flags.valid) {
+ if (state == ACPI_STATE_D3_COLD) {
+ /*
+ * For transitions to D3cold we need to execute _PS3 and then
+ * possibly drop references to the power resources in use.
+ */
+ state = ACPI_STATE_D3_HOT;
+ /* If _PR3 is not available, use D3hot as the target state. */
+ if (!device->power.states[ACPI_STATE_D3_COLD].flags.valid)
+ target_state = state;
+ } else if (!device->power.states[state].flags.valid) {
dev_warn(&device->dev, "Power state %s not supported\n",
acpi_power_state_string(state));
return -ENODEV;
}
+
if (!device->power.flags.ignore_parent &&
device->parent && (state < device->parent->power.state)) {
dev_warn(&device->dev,
@@ -183,39 +192,38 @@ int acpi_device_set_power(struct acpi_device *device, int state)
return -ENODEV;
}
- /* For D3cold we should first transition into D3hot. */
- if (state == ACPI_STATE_D3_COLD
- && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) {
- state = ACPI_STATE_D3_HOT;
- cut_power = true;
- }
-
- if (state < device->power.state && state != ACPI_STATE_D0
- && device->power.state >= ACPI_STATE_D3_HOT) {
- dev_warn(&device->dev,
- "Cannot transition to non-D0 state from D3\n");
- return -ENODEV;
- }
-
/*
* Transition Power
* ----------------
- * In accordance with the ACPI specification first apply power (via
- * power resources) and then evaluate _PSx.
+ * In accordance with ACPI 6, _PSx is executed before manipulating power
+ * resources, unless the target state is D0, in which case _PS0 is
+ * supposed to be executed after turning the power resources on.
*/
- if (device->power.flags.power_resources) {
- result = acpi_power_transition(device, state);
+ if (state > ACPI_STATE_D0) {
+ /*
+ * According to ACPI 6, devices cannot go from lower-power
+ * (deeper) states to higher-power (shallower) states.
+ */
+ if (state < device->power.state) {
+ dev_warn(&device->dev, "Cannot transition from %s to %s\n",
+ acpi_power_state_string(device->power.state),
+ acpi_power_state_string(state));
+ return -ENODEV;
+ }
+
+ result = acpi_dev_pm_explicit_set(device, state);
if (result)
goto end;
- }
- result = acpi_dev_pm_explicit_set(device, state);
- if (result)
- goto end;
- if (cut_power) {
- device->power.state = state;
- state = ACPI_STATE_D3_COLD;
- result = acpi_power_transition(device, state);
+ if (device->power.flags.power_resources)
+ result = acpi_power_transition(device, target_state);
+ } else {
+ if (device->power.flags.power_resources) {
+ result = acpi_power_transition(device, ACPI_STATE_D0);
+ if (result)
+ goto end;
+ }
+ result = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0);
}
end:
@@ -264,13 +272,24 @@ int acpi_bus_init_power(struct acpi_device *device)
return result;
if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) {
+ /* Reference count the power resources. */
result = acpi_power_on_resources(device, state);
if (result)
return result;
- result = acpi_dev_pm_explicit_set(device, state);
- if (result)
- return result;
+ if (state == ACPI_STATE_D0) {
+ /*
+ * If _PSC is not present and the state inferred from
+ * power resources appears to be D0, it still may be
+ * necessary to execute _PS0 at this point, because
+ * another device using the same power resources may
+ * have been put into D0 previously and that's why we
+ * see D0 here.
+ */
+ result = acpi_dev_pm_explicit_set(device, state);
+ if (result)
+ return result;
+ }
} else if (state == ACPI_STATE_UNKNOWN) {
/*
* No power resources and missing _PSC? Cross fingers and make
@@ -603,12 +622,12 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD)
return -EINVAL;
- if (d_max_in > ACPI_STATE_D3_HOT) {
+ if (d_max_in > ACPI_STATE_D2) {
enum pm_qos_flags_status stat;
stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF);
if (stat == PM_QOS_FLAGS_ALL)
- d_max_in = ACPI_STATE_D3_HOT;
+ d_max_in = ACPI_STATE_D2;
}
adev = ACPI_COMPANION(dev);
@@ -953,6 +972,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_prepare);
*/
void acpi_subsys_complete(struct device *dev)
{
+ pm_generic_complete(dev);
/*
* If the device had been runtime-suspended before the system went into
* the sleep state it is going out of and it has never been resumed till
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 5e8fed448850..9d4761d2f6b7 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -59,6 +59,38 @@
#define ACPI_EC_FLAG_BURST 0x10 /* burst mode */
#define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */
+/*
+ * The SCI_EVT clearing timing is not defined by the ACPI specification.
+ * This leads to lots of practical timing issues for the host EC driver.
+ * The following variations are defined (from the target EC firmware's
+ * perspective):
+ * STATUS: After indicating SCI_EVT edge triggered IRQ to the host, the
+ * target can clear SCI_EVT at any time so long as the host can see
+ * the indication by reading the status register (EC_SC). So the
+ * host should re-check SCI_EVT after the first time the SCI_EVT
+ * indication is seen, which is the same time the query request
+ * (QR_EC) is written to the command register (EC_CMD). SCI_EVT set
+ * at any later time could indicate another event. Normally such
+ * kind of EC firmware has implemented an event queue and will
+ * return 0x00 to indicate "no outstanding event".
+ * QUERY: After seeing the query request (QR_EC) written to the command
+ * register (EC_CMD) by the host and having prepared the responding
+ * event value in the data register (EC_DATA), the target can safely
+ * clear SCI_EVT because the target can confirm that the current
+ * event is being handled by the host. The host then should check
+ * SCI_EVT right after reading the event response from the data
+ * register (EC_DATA).
+ * EVENT: After seeing the event response read from the data register
+ * (EC_DATA) by the host, the target can clear SCI_EVT. As the
+ * target requires time to notice the change in the data register
+ * (EC_DATA), the host may be required to wait additional guarding
+ * time before checking the SCI_EVT again. Such guarding may not be
+ * necessary if the host is notified via another IRQ.
+ */
+#define ACPI_EC_EVT_TIMING_STATUS 0x00
+#define ACPI_EC_EVT_TIMING_QUERY 0x01
+#define ACPI_EC_EVT_TIMING_EVENT 0x02
+
/* EC commands */
enum ec_command {
ACPI_EC_COMMAND_READ = 0x80,
@@ -70,13 +102,13 @@ enum ec_command {
#define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */
#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */
-#define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */
-#define ACPI_EC_UDELAY_POLL 1000 /* Wait 1ms for EC transaction polling */
+#define ACPI_EC_UDELAY_POLL 550 /* Wait 1ms for EC transaction polling */
#define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query
* when trying to clear the EC */
enum {
EC_FLAGS_QUERY_PENDING, /* Query is pending */
+ EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */
EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and
* OpReg are installed */
EC_FLAGS_STARTED, /* Driver is started */
@@ -93,6 +125,16 @@ static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY;
module_param(ec_delay, uint, 0644);
MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes");
+static bool ec_busy_polling __read_mostly;
+module_param(ec_busy_polling, bool, 0644);
+MODULE_PARM_DESC(ec_busy_polling, "Use busy polling to advance EC transaction");
+
+static unsigned int ec_polling_guard __read_mostly = ACPI_EC_UDELAY_POLL;
+module_param(ec_polling_guard, uint, 0644);
+MODULE_PARM_DESC(ec_polling_guard, "Guard time(us) between EC accesses in polling modes");
+
+static unsigned int ec_event_clearing __read_mostly = ACPI_EC_EVT_TIMING_QUERY;
+
/*
* If the number of false interrupts per one transaction exceeds
* this threshold, will think there is a GPE storm happened and
@@ -121,7 +163,6 @@ struct transaction {
u8 wlen;
u8 rlen;
u8 flags;
- unsigned long timestamp;
};
static int acpi_ec_query(struct acpi_ec *ec, u8 *data);
@@ -130,7 +171,6 @@ static void advance_transaction(struct acpi_ec *ec);
struct acpi_ec *boot_ec, *first_ec;
EXPORT_SYMBOL(first_ec);
-static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */
static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */
static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */
static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
@@ -218,7 +258,7 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
{
u8 x = inb(ec->data_addr);
- ec->curr->timestamp = jiffies;
+ ec->timestamp = jiffies;
ec_dbg_raw("EC_DATA(R) = 0x%2.2x", x);
return x;
}
@@ -227,14 +267,14 @@ static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
{
ec_dbg_raw("EC_SC(W) = 0x%2.2x", command);
outb(command, ec->command_addr);
- ec->curr->timestamp = jiffies;
+ ec->timestamp = jiffies;
}
static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data)
{
ec_dbg_raw("EC_DATA(W) = 0x%2.2x", data);
outb(data, ec->data_addr);
- ec->curr->timestamp = jiffies;
+ ec->timestamp = jiffies;
}
#ifdef DEBUG
@@ -267,7 +307,7 @@ static inline bool acpi_ec_is_gpe_raised(struct acpi_ec *ec)
acpi_event_status gpe_status = 0;
(void)acpi_get_gpe_status(NULL, ec->gpe, &gpe_status);
- return (gpe_status & ACPI_EVENT_FLAG_SET) ? true : false;
+ return (gpe_status & ACPI_EVENT_FLAG_STATUS_SET) ? true : false;
}
static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open)
@@ -379,19 +419,49 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec)
static void acpi_ec_submit_query(struct acpi_ec *ec)
{
if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
- ec_dbg_req("Event started");
+ ec_dbg_evt("Command(%s) submitted/blocked",
+ acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
+ ec->nr_pending_queries++;
schedule_work(&ec->work);
}
}
static void acpi_ec_complete_query(struct acpi_ec *ec)
{
- if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
+ if (test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
- ec_dbg_req("Event stopped");
+ ec_dbg_evt("Command(%s) unblocked",
+ acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
}
}
+static bool acpi_ec_guard_event(struct acpi_ec *ec)
+{
+ if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS ||
+ ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY ||
+ !test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) ||
+ (ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY))
+ return false;
+
+ /*
+ * Postpone the query submission to allow the firmware to proceed,
+ * we shouldn't check SCI_EVT before the firmware reflagging it.
+ */
+ return true;
+}
+
+static int ec_transaction_polled(struct acpi_ec *ec)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_POLL))
+ ret = 1;
+ spin_unlock_irqrestore(&ec->lock, flags);
+ return ret;
+}
+
static int ec_transaction_completed(struct acpi_ec *ec)
{
unsigned long flags;
@@ -404,6 +474,22 @@ static int ec_transaction_completed(struct acpi_ec *ec)
return ret;
}
+static inline void ec_transaction_transition(struct acpi_ec *ec, unsigned long flag)
+{
+ ec->curr->flags |= flag;
+ if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
+ if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS &&
+ flag == ACPI_EC_COMMAND_POLL)
+ acpi_ec_complete_query(ec);
+ if (ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY &&
+ flag == ACPI_EC_COMMAND_COMPLETE)
+ acpi_ec_complete_query(ec);
+ if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT &&
+ flag == ACPI_EC_COMMAND_COMPLETE)
+ set_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags);
+ }
+}
+
static void advance_transaction(struct acpi_ec *ec)
{
struct transaction *t;
@@ -420,6 +506,18 @@ static void advance_transaction(struct acpi_ec *ec)
acpi_ec_clear_gpe(ec);
status = acpi_ec_read_status(ec);
t = ec->curr;
+ /*
+ * Another IRQ or a guarded polling mode advancement is detected,
+ * the next QR_EC submission is then allowed.
+ */
+ if (!t || !(t->flags & ACPI_EC_COMMAND_POLL)) {
+ if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT &&
+ (!ec->nr_pending_queries ||
+ test_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags))) {
+ clear_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags);
+ acpi_ec_complete_query(ec);
+ }
+ }
if (!t)
goto err;
if (t->flags & ACPI_EC_COMMAND_POLL) {
@@ -432,17 +530,17 @@ static void advance_transaction(struct acpi_ec *ec)
if ((status & ACPI_EC_FLAG_OBF) == 1) {
t->rdata[t->ri++] = acpi_ec_read_data(ec);
if (t->rlen == t->ri) {
- t->flags |= ACPI_EC_COMMAND_COMPLETE;
+ ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE);
if (t->command == ACPI_EC_COMMAND_QUERY)
- ec_dbg_req("Command(%s) hardware completion",
- acpi_ec_cmd_string(t->command));
+ ec_dbg_evt("Command(%s) completed by hardware",
+ acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
wakeup = true;
}
} else
goto err;
} else if (t->wlen == t->wi &&
(status & ACPI_EC_FLAG_IBF) == 0) {
- t->flags |= ACPI_EC_COMMAND_COMPLETE;
+ ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE);
wakeup = true;
}
goto out;
@@ -450,17 +548,15 @@ static void advance_transaction(struct acpi_ec *ec)
if (EC_FLAGS_QUERY_HANDSHAKE &&
!(status & ACPI_EC_FLAG_SCI) &&
(t->command == ACPI_EC_COMMAND_QUERY)) {
- t->flags |= ACPI_EC_COMMAND_POLL;
- acpi_ec_complete_query(ec);
+ ec_transaction_transition(ec, ACPI_EC_COMMAND_POLL);
t->rdata[t->ri++] = 0x00;
- t->flags |= ACPI_EC_COMMAND_COMPLETE;
- ec_dbg_req("Command(%s) software completion",
- acpi_ec_cmd_string(t->command));
+ ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE);
+ ec_dbg_evt("Command(%s) completed by software",
+ acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
wakeup = true;
} else if ((status & ACPI_EC_FLAG_IBF) == 0) {
acpi_ec_write_cmd(ec, t->command);
- t->flags |= ACPI_EC_COMMAND_POLL;
- acpi_ec_complete_query(ec);
+ ec_transaction_transition(ec, ACPI_EC_COMMAND_POLL);
} else
goto err;
goto out;
@@ -490,8 +586,39 @@ static void start_transaction(struct acpi_ec *ec)
{
ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0;
ec->curr->flags = 0;
- ec->curr->timestamp = jiffies;
- advance_transaction(ec);
+}
+
+static int ec_guard(struct acpi_ec *ec)
+{
+ unsigned long guard = usecs_to_jiffies(ec_polling_guard);
+ unsigned long timeout = ec->timestamp + guard;
+
+ do {
+ if (ec_busy_polling) {
+ /* Perform busy polling */
+ if (ec_transaction_completed(ec))
+ return 0;
+ udelay(jiffies_to_usecs(guard));
+ } else {
+ /*
+ * Perform wait polling
+ *
+ * For SCI_EVT clearing timing of "event",
+ * performing guarding before re-checking the
+ * SCI_EVT. Otherwise, such guarding is not needed
+ * due to the old practices.
+ */
+ if (!ec_transaction_polled(ec) &&
+ !acpi_ec_guard_event(ec))
+ break;
+ if (wait_event_timeout(ec->wait,
+ ec_transaction_completed(ec),
+ guard))
+ return 0;
+ }
+ /* Guard the register accesses for the polling modes */
+ } while (time_before(jiffies, timeout));
+ return -ETIME;
}
static int ec_poll(struct acpi_ec *ec)
@@ -502,25 +629,11 @@ static int ec_poll(struct acpi_ec *ec)
while (repeat--) {
unsigned long delay = jiffies +
msecs_to_jiffies(ec_delay);
- unsigned long usecs = ACPI_EC_UDELAY_POLL;
do {
- /* don't sleep with disabled interrupts */
- if (EC_FLAGS_MSI || irqs_disabled()) {
- usecs = ACPI_EC_MSI_UDELAY;
- udelay(usecs);
- if (ec_transaction_completed(ec))
- return 0;
- } else {
- if (wait_event_timeout(ec->wait,
- ec_transaction_completed(ec),
- usecs_to_jiffies(usecs)))
- return 0;
- }
+ if (!ec_guard(ec))
+ return 0;
spin_lock_irqsave(&ec->lock, flags);
- if (time_after(jiffies,
- ec->curr->timestamp +
- usecs_to_jiffies(usecs)))
- advance_transaction(ec);
+ advance_transaction(ec);
spin_unlock_irqrestore(&ec->lock, flags);
} while (time_before(jiffies, delay));
pr_debug("controller reset, restart transaction\n");
@@ -537,8 +650,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
unsigned long tmp;
int ret = 0;
- if (EC_FLAGS_MSI)
- udelay(ACPI_EC_MSI_UDELAY);
/* start transaction */
spin_lock_irqsave(&ec->lock, tmp);
/* Enable GPE for command processing (IBF=0/OBF=1) */
@@ -552,7 +663,9 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command));
start_transaction(ec);
spin_unlock_irqrestore(&ec->lock, tmp);
+
ret = ec_poll(ec);
+
spin_lock_irqsave(&ec->lock, tmp);
if (t->irq_count == ec_storm_threshold)
acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM);
@@ -575,6 +688,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
return -EINVAL;
if (t->rdata)
memset(t->rdata, 0, t->rlen);
+
mutex_lock(&ec->mutex);
if (ec->global_lock) {
status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
@@ -586,8 +700,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
status = acpi_ec_transaction_unlocked(ec, t);
- if (test_bit(EC_FLAGS_COMMAND_STORM, &ec->flags))
- msleep(1);
if (ec->global_lock)
acpi_release_global_lock(glk);
unlock:
@@ -923,11 +1035,54 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
return result;
}
-static void acpi_ec_gpe_poller(struct work_struct *work)
+static void acpi_ec_check_event(struct acpi_ec *ec)
{
+ unsigned long flags;
+
+ if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) {
+ if (ec_guard(ec)) {
+ spin_lock_irqsave(&ec->lock, flags);
+ /*
+ * Take care of the SCI_EVT unless no one else is
+ * taking care of it.
+ */
+ if (!ec->curr)
+ advance_transaction(ec);
+ spin_unlock_irqrestore(&ec->lock, flags);
+ }
+ }
+}
+
+static void acpi_ec_event_handler(struct work_struct *work)
+{
+ unsigned long flags;
struct acpi_ec *ec = container_of(work, struct acpi_ec, work);
- acpi_ec_query(ec, NULL);
+ ec_dbg_evt("Event started");
+
+ spin_lock_irqsave(&ec->lock, flags);
+ while (ec->nr_pending_queries) {
+ spin_unlock_irqrestore(&ec->lock, flags);
+ (void)acpi_ec_query(ec, NULL);
+ spin_lock_irqsave(&ec->lock, flags);
+ ec->nr_pending_queries--;
+ /*
+ * Before exit, make sure that this work item can be
+ * scheduled again. There might be QR_EC failures, leaving
+ * EC_FLAGS_QUERY_PENDING uncleared and preventing this work
+ * item from being scheduled again.
+ */
+ if (!ec->nr_pending_queries) {
+ if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS ||
+ ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY)
+ acpi_ec_complete_query(ec);
+ }
+ }
+ spin_unlock_irqrestore(&ec->lock, flags);
+
+ ec_dbg_evt("Event stopped");
+
+ acpi_ec_check_event(ec);
}
static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
@@ -961,7 +1116,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
if (function != ACPI_READ && function != ACPI_WRITE)
return AE_BAD_PARAMETER;
- if (EC_FLAGS_MSI || bits > 8)
+ if (ec_busy_polling || bits > 8)
acpi_ec_burst_enable(ec);
for (i = 0; i < bytes; ++i, ++address, ++value)
@@ -969,7 +1124,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
acpi_ec_read(ec, address, value) :
acpi_ec_write(ec, address, *value);
- if (EC_FLAGS_MSI || bits > 8)
+ if (ec_busy_polling || bits > 8)
acpi_ec_burst_disable(ec);
switch (result) {
@@ -1002,7 +1157,8 @@ static struct acpi_ec *make_acpi_ec(void)
init_waitqueue_head(&ec->wait);
INIT_LIST_HEAD(&ec->list);
spin_lock_init(&ec->lock);
- INIT_WORK(&ec->work, acpi_ec_gpe_poller);
+ INIT_WORK(&ec->work, acpi_ec_event_handler);
+ ec->timestamp = jiffies;
return ec;
}
@@ -1237,30 +1393,13 @@ static int ec_validate_ecdt(const struct dmi_system_id *id)
return 0;
}
-/* MSI EC needs special treatment, enable it */
-static int ec_flag_msi(const struct dmi_system_id *id)
-{
- pr_debug("Detected MSI hardware, enabling workarounds.\n");
- EC_FLAGS_MSI = 1;
- EC_FLAGS_VALIDATE_ECDT = 1;
- return 0;
-}
-
-/*
- * Clevo M720 notebook actually works ok with IRQ mode, if we lifted
- * the GPE storm threshold back to 20
- */
-static int ec_enlarge_storm_threshold(const struct dmi_system_id *id)
-{
- pr_debug("Setting the EC GPE storm threshold to 20\n");
- ec_storm_threshold = 20;
- return 0;
-}
-
+#if 0
/*
- * Acer EC firmware refuses to respond QR_EC when SCI_EVT is not set, for
- * which case, we complete the QR_EC without issuing it to the firmware.
- * https://bugzilla.kernel.org/show_bug.cgi?id=86211
+ * Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not
+ * set, for which case, we complete the QR_EC without issuing it to the
+ * firmware.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=82611
+ * https://bugzilla.kernel.org/show_bug.cgi?id=97381
*/
static int ec_flag_query_handshake(const struct dmi_system_id *id)
{
@@ -1268,6 +1407,7 @@ static int ec_flag_query_handshake(const struct dmi_system_id *id)
EC_FLAGS_QUERY_HANDSHAKE = 1;
return 0;
}
+#endif
/*
* On some hardware it is necessary to clear events accumulated by the EC during
@@ -1290,6 +1430,7 @@ static int ec_clear_on_resume(const struct dmi_system_id *id)
{
pr_debug("Detected system needing EC poll on resume.\n");
EC_FLAGS_CLEAR_ON_RESUME = 1;
+ ec_event_clearing = ACPI_EC_EVT_TIMING_STATUS;
return 0;
}
@@ -1299,29 +1440,9 @@ static struct dmi_system_id ec_dmi_table[] __initdata = {
DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"),
DMI_MATCH(DMI_BOARD_NAME, "JFL92") }, NULL},
{
- ec_flag_msi, "MSI hardware", {
- DMI_MATCH(DMI_BIOS_VENDOR, "Micro-Star")}, NULL},
- {
- ec_flag_msi, "MSI hardware", {
- DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star")}, NULL},
- {
- ec_flag_msi, "MSI hardware", {
- DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-Star")}, NULL},
- {
- ec_flag_msi, "MSI hardware", {
- DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR")}, NULL},
- {
- ec_flag_msi, "Quanta hardware", {
- DMI_MATCH(DMI_SYS_VENDOR, "Quanta"),
- DMI_MATCH(DMI_PRODUCT_NAME, "TW8/SW8/DW8"),}, NULL},
- {
- ec_flag_msi, "Quanta hardware", {
- DMI_MATCH(DMI_SYS_VENDOR, "Quanta"),
- DMI_MATCH(DMI_PRODUCT_NAME, "TW9/SW9"),}, NULL},
- {
- ec_flag_msi, "Clevo W350etq", {
- DMI_MATCH(DMI_SYS_VENDOR, "CLEVO CO."),
- DMI_MATCH(DMI_PRODUCT_NAME, "W35_37ET"),}, NULL},
+ ec_validate_ecdt, "MSI MS-171F", {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),}, NULL},
{
ec_validate_ecdt, "ASUS hardware", {
DMI_MATCH(DMI_BIOS_VENDOR, "ASUS") }, NULL},
@@ -1329,10 +1450,6 @@ static struct dmi_system_id ec_dmi_table[] __initdata = {
ec_validate_ecdt, "ASUS hardware", {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc.") }, NULL},
{
- ec_enlarge_storm_threshold, "CLEVO hardware", {
- DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
- DMI_MATCH(DMI_PRODUCT_NAME, "M720T/M730T"),}, NULL},
- {
ec_skip_dsdt_scan, "HP Folio 13", {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Folio 13"),}, NULL},
@@ -1343,9 +1460,6 @@ static struct dmi_system_id ec_dmi_table[] __initdata = {
{
ec_clear_on_resume, "Samsung hardware", {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL},
- {
- ec_flag_query_handshake, "Acer hardware", {
- DMI_MATCH(DMI_SYS_VENDOR, "Acer"), }, NULL},
{},
};
@@ -1427,6 +1541,43 @@ error:
return -ENODEV;
}
+static int param_set_event_clearing(const char *val, struct kernel_param *kp)
+{
+ int result = 0;
+
+ if (!strncmp(val, "status", sizeof("status") - 1)) {
+ ec_event_clearing = ACPI_EC_EVT_TIMING_STATUS;
+ pr_info("Assuming SCI_EVT clearing on EC_SC accesses\n");
+ } else if (!strncmp(val, "query", sizeof("query") - 1)) {
+ ec_event_clearing = ACPI_EC_EVT_TIMING_QUERY;
+ pr_info("Assuming SCI_EVT clearing on QR_EC writes\n");
+ } else if (!strncmp(val, "event", sizeof("event") - 1)) {
+ ec_event_clearing = ACPI_EC_EVT_TIMING_EVENT;
+ pr_info("Assuming SCI_EVT clearing on event reads\n");
+ } else
+ result = -EINVAL;
+ return result;
+}
+
+static int param_get_event_clearing(char *buffer, struct kernel_param *kp)
+{
+ switch (ec_event_clearing) {
+ case ACPI_EC_EVT_TIMING_STATUS:
+ return sprintf(buffer, "status");
+ case ACPI_EC_EVT_TIMING_QUERY:
+ return sprintf(buffer, "query");
+ case ACPI_EC_EVT_TIMING_EVENT:
+ return sprintf(buffer, "event");
+ default:
+ return sprintf(buffer, "invalid");
+ }
+ return 0;
+}
+
+module_param_call(ec_event_clearing, param_set_event_clearing, param_get_event_clearing,
+ NULL, 0644);
+MODULE_PARM_DESC(ec_event_clearing, "Assumed SCI_EVT clearing timing");
+
static struct acpi_driver acpi_ec_driver = {
.name = "ec",
.class = ACPI_EC_CLASS,
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 7a36f02598a6..bea0bbaafa97 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -158,8 +158,9 @@ static int fan_get_state(struct acpi_device *device, unsigned long *state)
if (result)
return result;
- *state = (acpi_state == ACPI_STATE_D3_COLD ? 0 :
- (acpi_state == ACPI_STATE_D0 ? 1 : -1));
+ *state = acpi_state == ACPI_STATE_D3_COLD
+ || acpi_state == ACPI_STATE_D3_HOT ?
+ 0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1);
return 0;
}
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 39c485b0c25c..b9657af751d1 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/rwsem.h>
#include <linux/acpi.h>
+#include <linux/dma-mapping.h>
#include "internal.h"
@@ -167,6 +168,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
struct list_head *physnode_list;
unsigned int node_id;
int retval = -EINVAL;
+ bool coherent;
if (has_acpi_companion(dev)) {
if (acpi_dev) {
@@ -223,6 +225,9 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
if (!has_acpi_companion(dev))
ACPI_COMPANION_SET(dev, acpi_dev);
+ if (acpi_check_dma(acpi_dev, &coherent))
+ arch_setup_dma_ops(dev, 0, 0, NULL, coherent);
+
acpi_physnode_link_name(physical_node_name, node_id);
retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
physical_node_name);
diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c
index aafe3ca829c2..a322710b5ba4 100644
--- a/drivers/acpi/hed.c
+++ b/drivers/acpi/hed.c
@@ -27,7 +27,7 @@
#include <linux/acpi.h>
#include <acpi/hed.h>
-static struct acpi_device_id acpi_hed_ids[] = {
+static const struct acpi_device_id acpi_hed_ids[] = {
{"PNP0C33", 0},
{"", 0},
};
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index ba4a61e964be..787c629bc9b4 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -138,6 +138,8 @@ struct acpi_ec {
struct transaction *curr;
spinlock_t lock;
struct work_struct work;
+ unsigned long timestamp;
+ unsigned long nr_pending_queries;
};
extern struct acpi_ec *first_ec;
@@ -182,15 +184,10 @@ static inline void suspend_nvs_restore(void) {}
#endif
/*--------------------------------------------------------------------------
- Video
- -------------------------------------------------------------------------- */
-#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE)
-bool acpi_osi_is_win8(void);
-#endif
-
-/*--------------------------------------------------------------------------
Device properties
-------------------------------------------------------------------------- */
+#define ACPI_DT_NAMESPACE_HID "PRP0001"
+
void acpi_init_properties(struct acpi_device *adev);
void acpi_free_properties(struct acpi_device *adev);
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 39748bb3a543..a5dc9034efee 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -175,14 +175,10 @@ static void __init acpi_request_region (struct acpi_generic_address *gas,
if (!addr || !length)
return;
- /* Resources are never freed */
- if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO)
- request_region(addr, length, desc);
- else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
- request_mem_region(addr, length, desc);
+ acpi_reserve_region(addr, length, gas->space_id, 0, desc);
}
-static int __init acpi_reserve_resources(void)
+static void __init acpi_reserve_resources(void)
{
acpi_request_region(&acpi_gbl_FADT.xpm1a_event_block, acpi_gbl_FADT.pm1_event_length,
"ACPI PM1a_EVT_BLK");
@@ -211,10 +207,7 @@ static int __init acpi_reserve_resources(void)
if (!(acpi_gbl_FADT.gpe1_block_length & 0x1))
acpi_request_region(&acpi_gbl_FADT.xgpe1_block,
acpi_gbl_FADT.gpe1_block_length, "ACPI GPE1_BLK");
-
- return 0;
}
-device_initcall(acpi_reserve_resources);
void acpi_os_printf(const char *fmt, ...)
{
@@ -543,7 +536,7 @@ static char acpi_os_name[ACPI_MAX_OVERRIDE_LEN];
acpi_status
acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
- acpi_string * new_val)
+ char **new_val)
{
if (!init_val || !new_val)
return AE_BAD_PARAMETER;
@@ -1687,6 +1680,12 @@ int acpi_resources_are_enforced(void)
}
EXPORT_SYMBOL(acpi_resources_are_enforced);
+bool acpi_osi_is_win8(void)
+{
+ return acpi_gbl_osi_data >= ACPI_OSI_WIN_8;
+}
+EXPORT_SYMBOL(acpi_osi_is_win8);
+
/*
* Deallocate the memory for a spinlock.
*/
@@ -1845,6 +1844,7 @@ acpi_status __init acpi_os_initialize(void)
acpi_status __init acpi_os_initialize1(void)
{
+ acpi_reserve_resources();
kacpid_wq = alloc_workqueue("kacpid", 0, 1);
kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index b1def411c0b8..304eccb0ae5c 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -44,7 +44,6 @@
ACPI_MODULE_NAME("pci_irq");
struct acpi_prt_entry {
- struct list_head list;
struct acpi_pci_id id;
u8 pin;
acpi_handle link;
@@ -163,7 +162,7 @@ static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev,
{
int segment = pci_domain_nr(dev->bus);
int bus = dev->bus->number;
- int device = PCI_SLOT(dev->devfn);
+ int device = pci_ari_enabled(dev->bus) ? 0 : PCI_SLOT(dev->devfn);
struct acpi_prt_entry *entry;
if (((prt->address >> 16) & 0xffff) != device ||
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index e0bcfb642b52..93eac53b5110 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -684,7 +684,8 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
}
}
- *state = ACPI_STATE_D3_COLD;
+ *state = device->power.states[ACPI_STATE_D3_COLD].flags.valid ?
+ ACPI_STATE_D3_COLD : ACPI_STATE_D3_HOT;
return 0;
}
@@ -710,8 +711,6 @@ int acpi_power_transition(struct acpi_device *device, int state)
|| (device->power.state > ACPI_STATE_D3_COLD))
return -ENODEV;
- /* TBD: Resources must be ordered. */
-
/*
* First we reference all power resources required in the target list
* (e.g. so the device doesn't lose power while transitioning). Then,
@@ -761,6 +760,25 @@ static void acpi_power_sysfs_remove(struct acpi_device *device)
device_remove_file(&device->dev, &dev_attr_resource_in_use);
}
+static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource)
+{
+ mutex_lock(&power_resource_list_lock);
+
+ if (!list_empty(&acpi_power_resource_list)) {
+ struct acpi_power_resource *r;
+
+ list_for_each_entry(r, &acpi_power_resource_list, list_node)
+ if (r->order > resource->order) {
+ list_add_tail(&resource->list_node, &r->list_node);
+ goto out;
+ }
+ }
+ list_add_tail(&resource->list_node, &acpi_power_resource_list);
+
+ out:
+ mutex_unlock(&power_resource_list_lock);
+}
+
int acpi_add_power_resource(acpi_handle handle)
{
struct acpi_power_resource *resource;
@@ -811,9 +829,7 @@ int acpi_add_power_resource(acpi_handle handle)
if (!device_create_file(&device->dev, &dev_attr_resource_in_use))
device->remove = acpi_power_sysfs_remove;
- mutex_lock(&power_resource_list_lock);
- list_add(&resource->list_node, &acpi_power_resource_list);
- mutex_unlock(&power_resource_list_lock);
+ acpi_power_add_resource_to_list(resource);
acpi_device_add_finalize(device);
return 0;
@@ -844,7 +860,22 @@ void acpi_resume_power_resources(void)
&& resource->ref_count) {
dev_info(&resource->device.dev, "Turning ON\n");
__acpi_power_on(resource);
- } else if (state == ACPI_POWER_RESOURCE_STATE_ON
+ }
+
+ mutex_unlock(&resource->resource_lock);
+ }
+ list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) {
+ int result, state;
+
+ mutex_lock(&resource->resource_lock);
+
+ result = acpi_power_get_state(resource->device.handle, &state);
+ if (result) {
+ mutex_unlock(&resource->resource_lock);
+ continue;
+ }
+
+ if (state == ACPI_POWER_RESOURCE_STATE_ON
&& !resource->ref_count) {
dev_info(&resource->device.dev, "Turning OFF\n");
__acpi_power_off(resource);
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index b1ec78b8a645..33a38d604630 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -184,7 +184,7 @@ phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id)
phys_cpuid_t phys_id;
phys_id = map_mat_entry(handle, type, acpi_id);
- if (phys_id == PHYS_CPUID_INVALID)
+ if (invalid_phys_cpuid(phys_id))
phys_id = map_madt_entry(type, acpi_id);
return phys_id;
@@ -196,7 +196,7 @@ int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
int i;
#endif
- if (phys_id == PHYS_CPUID_INVALID) {
+ if (invalid_phys_cpuid(phys_id)) {
/*
* On UP processor, there is no _MAT or MADT table.
* So above phys_id is always set to PHYS_CPUID_INVALID.
@@ -215,12 +215,12 @@ int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
* Ignores phys_id and always returns 0 for the processor
* handle with acpi id 0 if nr_cpu_ids is 1.
* This should be the case if SMP tables are not found.
- * Return -1 for other CPU's handle.
+ * Return -EINVAL for other CPU's handle.
*/
if (nr_cpu_ids <= 1 && acpi_id == 0)
return acpi_id;
else
- return -1;
+ return -EINVAL;
}
#ifdef CONFIG_SMP
@@ -233,7 +233,7 @@ int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
if (phys_id == 0)
return phys_id;
#endif
- return -1;
+ return -ENODEV;
}
int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 39e0c8e36244..d540f42c9232 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -94,7 +94,7 @@ static int set_max_cstate(const struct dmi_system_id *id)
return 0;
}
-static struct dmi_system_id processor_power_dmi_table[] = {
+static const struct dmi_system_id processor_power_dmi_table[] = {
{ set_max_cstate, "Clevo 5600D", {
DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")},
diff --git a/drivers/acpi/processor_pdc.c b/drivers/acpi/processor_pdc.c
index e5dd80800930..7cfbda4d7c51 100644
--- a/drivers/acpi/processor_pdc.c
+++ b/drivers/acpi/processor_pdc.c
@@ -52,10 +52,7 @@ static bool __init processor_physically_present(acpi_handle handle)
type = (acpi_type == ACPI_TYPE_DEVICE) ? 1 : 0;
cpuid = acpi_get_cpuid(handle, type, acpi_id);
- if (cpuid == -1)
- return false;
-
- return true;
+ return !invalid_logical_cpuid(cpuid);
}
static void acpi_set_pdc_bits(u32 *buf)
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index 0d083736e25b..7836e2e980f4 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -79,50 +79,51 @@ static bool acpi_properties_format_valid(const union acpi_object *properties)
static void acpi_init_of_compatible(struct acpi_device *adev)
{
const union acpi_object *of_compatible;
- struct acpi_hardware_id *hwid;
- bool acpi_of = false;
int ret;
- /*
- * Check if the special PRP0001 ACPI ID is present and in that
- * case we fill in Device Tree compatible properties for this
- * device.
- */
- list_for_each_entry(hwid, &adev->pnp.ids, list) {
- if (!strcmp(hwid->id, "PRP0001")) {
- acpi_of = true;
- break;
- }
- }
-
- if (!acpi_of)
- return;
-
ret = acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING,
&of_compatible);
if (ret) {
ret = acpi_dev_get_property(adev, "compatible",
ACPI_TYPE_STRING, &of_compatible);
if (ret) {
- acpi_handle_warn(adev->handle,
- "PRP0001 requires compatible property\n");
+ if (adev->parent
+ && adev->parent->flags.of_compatible_ok)
+ goto out;
+
return;
}
}
adev->data.of_compatible = of_compatible;
+
+ out:
+ adev->flags.of_compatible_ok = 1;
}
void acpi_init_properties(struct acpi_device *adev)
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+ bool acpi_of = false;
+ struct acpi_hardware_id *hwid;
const union acpi_object *desc;
acpi_status status;
int i;
+ /*
+ * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in
+ * Device Tree compatible properties for this device.
+ */
+ list_for_each_entry(hwid, &adev->pnp.ids, list) {
+ if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) {
+ acpi_of = true;
+ break;
+ }
+ }
+
status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf,
ACPI_TYPE_PACKAGE);
if (ACPI_FAILURE(status))
- return;
+ goto out;
desc = buf.pointer;
if (desc->package.count % 2)
@@ -156,13 +157,20 @@ void acpi_init_properties(struct acpi_device *adev)
adev->data.pointer = buf.pointer;
adev->data.properties = properties;
- acpi_init_of_compatible(adev);
- return;
+ if (acpi_of)
+ acpi_init_of_compatible(adev);
+
+ goto out;
}
fail:
- dev_warn(&adev->dev, "Returned _DSD data is not valid, skipping\n");
+ dev_dbg(&adev->dev, "Returned _DSD data is not valid, skipping\n");
ACPI_FREE(buf.pointer);
+
+ out:
+ if (acpi_of && !adev->flags.of_compatible_ok)
+ acpi_handle_info(adev->handle,
+ ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n");
}
void acpi_free_properties(struct acpi_device *adev)
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index 5589a6e2a023..fcb7807ea8b7 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -26,6 +26,7 @@
#include <linux/device.h>
#include <linux/export.h>
#include <linux/ioport.h>
+#include <linux/list.h>
#include <linux/slab.h>
#ifdef CONFIG_X86
@@ -573,7 +574,7 @@ EXPORT_SYMBOL_GPL(acpi_dev_get_resources);
* @ares: Input ACPI resource object.
* @types: Valid resource types of IORESOURCE_XXX
*
- * This is a hepler function to support acpi_dev_get_resources(), which filters
+ * This is a helper function to support acpi_dev_get_resources(), which filters
* ACPI resource objects according to resource types.
*/
int acpi_dev_filter_resource_type(struct acpi_resource *ares,
@@ -621,3 +622,162 @@ int acpi_dev_filter_resource_type(struct acpi_resource *ares,
return (type & types) ? 0 : 1;
}
EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type);
+
+struct reserved_region {
+ struct list_head node;
+ u64 start;
+ u64 end;
+};
+
+static LIST_HEAD(reserved_io_regions);
+static LIST_HEAD(reserved_mem_regions);
+
+static int request_range(u64 start, u64 end, u8 space_id, unsigned long flags,
+ char *desc)
+{
+ unsigned int length = end - start + 1;
+ struct resource *res;
+
+ res = space_id == ACPI_ADR_SPACE_SYSTEM_IO ?
+ request_region(start, length, desc) :
+ request_mem_region(start, length, desc);
+ if (!res)
+ return -EIO;
+
+ res->flags &= ~flags;
+ return 0;
+}
+
+static int add_region_before(u64 start, u64 end, u8 space_id,
+ unsigned long flags, char *desc,
+ struct list_head *head)
+{
+ struct reserved_region *reg;
+ int error;
+
+ reg = kmalloc(sizeof(*reg), GFP_KERNEL);
+ if (!reg)
+ return -ENOMEM;
+
+ error = request_range(start, end, space_id, flags, desc);
+ if (error)
+ return error;
+
+ reg->start = start;
+ reg->end = end;
+ list_add_tail(&reg->node, head);
+ return 0;
+}
+
+/**
+ * acpi_reserve_region - Reserve an I/O or memory region as a system resource.
+ * @start: Starting address of the region.
+ * @length: Length of the region.
+ * @space_id: Identifier of address space to reserve the region from.
+ * @flags: Resource flags to clear for the region after requesting it.
+ * @desc: Region description (for messages).
+ *
+ * Reserve an I/O or memory region as a system resource to prevent others from
+ * using it. If the new region overlaps with one of the regions (in the given
+ * address space) already reserved by this routine, only the non-overlapping
+ * parts of it will be reserved.
+ *
+ * Returned is either 0 (success) or a negative error code indicating a resource
+ * reservation problem. It is the code of the first encountered error, but the
+ * routine doesn't abort until it has attempted to request all of the parts of
+ * the new region that don't overlap with other regions reserved previously.
+ *
+ * The resources requested by this routine are never released.
+ */
+int acpi_reserve_region(u64 start, unsigned int length, u8 space_id,
+ unsigned long flags, char *desc)
+{
+ struct list_head *regions;
+ struct reserved_region *reg;
+ u64 end = start + length - 1;
+ int ret = 0, error = 0;
+
+ if (space_id == ACPI_ADR_SPACE_SYSTEM_IO)
+ regions = &reserved_io_regions;
+ else if (space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ regions = &reserved_mem_regions;
+ else
+ return -EINVAL;
+
+ if (list_empty(regions))
+ return add_region_before(start, end, space_id, flags, desc, regions);
+
+ list_for_each_entry(reg, regions, node)
+ if (reg->start == end + 1) {
+ /* The new region can be prepended to this one. */
+ ret = request_range(start, end, space_id, flags, desc);
+ if (!ret)
+ reg->start = start;
+
+ return ret;
+ } else if (reg->start > end) {
+ /* No overlap. Add the new region here and get out. */
+ return add_region_before(start, end, space_id, flags,
+ desc, &reg->node);
+ } else if (reg->end == start - 1) {
+ goto combine;
+ } else if (reg->end >= start) {
+ goto overlap;
+ }
+
+ /* The new region goes after the last existing one. */
+ return add_region_before(start, end, space_id, flags, desc, regions);
+
+ overlap:
+ /*
+ * The new region overlaps an existing one.
+ *
+ * The head part of the new region immediately preceding the existing
+ * overlapping one can be combined with it right away.
+ */
+ if (reg->start > start) {
+ error = request_range(start, reg->start - 1, space_id, flags, desc);
+ if (error)
+ ret = error;
+ else
+ reg->start = start;
+ }
+
+ combine:
+ /*
+ * The new region is adjacent to an existing one. If it extends beyond
+ * that region all the way to the next one, it is possible to combine
+ * all three of them.
+ */
+ while (reg->end < end) {
+ struct reserved_region *next = NULL;
+ u64 a = reg->end + 1, b = end;
+
+ if (!list_is_last(&reg->node, regions)) {
+ next = list_next_entry(reg, node);
+ if (next->start <= end)
+ b = next->start - 1;
+ }
+ error = request_range(a, b, space_id, flags, desc);
+ if (!error) {
+ if (next && next->start == b + 1) {
+ reg->end = next->end;
+ list_del(&next->node);
+ kfree(next);
+ } else {
+ reg->end = end;
+ break;
+ }
+ } else if (next) {
+ if (!ret)
+ ret = error;
+
+ reg = next;
+ } else {
+ break;
+ }
+ }
+
+ return ret ? ret : error;
+}
+EXPORT_SYMBOL_GPL(acpi_reserve_region);
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index cd827625cf07..01504c819e8f 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -684,7 +684,7 @@ static int acpi_sbs_add(struct acpi_device *device)
if (!sbs_manager_broken) {
result = acpi_manager_get_info(sbs);
if (!result) {
- sbs->manager_present = 0;
+ sbs->manager_present = 1;
for (id = 0; id < MAX_SBS_BAT; ++id)
if ((sbs->batteries_supported & (1 << id)))
acpi_battery_add(sbs, id);
diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c
index 26e5b5060523..bf034f8b7c1a 100644
--- a/drivers/acpi/sbshc.c
+++ b/drivers/acpi/sbshc.c
@@ -14,6 +14,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/dmi.h>
#include "sbshc.h"
#define PREFIX "ACPI: "
@@ -87,6 +88,8 @@ enum acpi_smb_offset {
ACPI_SMB_ALARM_DATA = 0x26, /* 2 bytes alarm data */
};
+static bool macbook;
+
static inline int smb_hc_read(struct acpi_smb_hc *hc, u8 address, u8 *data)
{
return ec_read(hc->offset + address, data);
@@ -132,6 +135,8 @@ static int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol,
}
mutex_lock(&hc->lock);
+ if (macbook)
+ udelay(5);
if (smb_hc_read(hc, ACPI_SMB_PROTOCOL, &temp))
goto end;
if (temp) {
@@ -257,12 +262,29 @@ extern int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
acpi_handle handle, acpi_ec_query_func func,
void *data);
+static int macbook_dmi_match(const struct dmi_system_id *d)
+{
+ pr_debug("Detected MacBook, enabling workaround\n");
+ macbook = true;
+ return 0;
+}
+
+static struct dmi_system_id acpi_smbus_dmi_table[] = {
+ { macbook_dmi_match, "Apple MacBook", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBook") },
+ },
+ { },
+};
+
static int acpi_smbus_hc_add(struct acpi_device *device)
{
int status;
unsigned long long val;
struct acpi_smb_hc *hc;
+ dmi_check_system(acpi_smbus_dmi_table);
+
if (!device)
return -EINVAL;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 03141aa4ea95..2649a068671d 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -11,6 +11,7 @@
#include <linux/kthread.h>
#include <linux/dmi.h>
#include <linux/nls.h>
+#include <linux/dma-mapping.h>
#include <asm/pgtable.h>
@@ -135,12 +136,13 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
struct acpi_hardware_id *id;
/*
- * Since we skip PRP0001 from the modalias below, 0 should be returned
- * if PRP0001 is the only ACPI/PNP ID in the device's list.
+ * Since we skip ACPI_DT_NAMESPACE_HID from the modalias below, 0 should
+ * be returned if ACPI_DT_NAMESPACE_HID is the only ACPI/PNP ID in the
+ * device's list.
*/
count = 0;
list_for_each_entry(id, &acpi_dev->pnp.ids, list)
- if (strcmp(id->id, "PRP0001"))
+ if (strcmp(id->id, ACPI_DT_NAMESPACE_HID))
count++;
if (!count)
@@ -153,7 +155,7 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
size -= len;
list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
- if (!strcmp(id->id, "PRP0001"))
+ if (!strcmp(id->id, ACPI_DT_NAMESPACE_HID))
continue;
count = snprintf(&modalias[len], size, "%s:", id->id);
@@ -177,7 +179,8 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
* @size: Size of the buffer.
*
* Expose DT compatible modalias as of:NnameTCcompatible. This function should
- * only be called for devices having PRP0001 in their list of ACPI/PNP IDs.
+ * only be called for devices having ACPI_DT_NAMESPACE_HID in their list of
+ * ACPI/PNP IDs.
*/
static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
int size)
@@ -980,9 +983,9 @@ static void acpi_device_remove_files(struct acpi_device *dev)
* @adev: ACPI device object to match.
* @of_match_table: List of device IDs to match against.
*
- * If @dev has an ACPI companion which has the special PRP0001 device ID in its
- * list of identifiers and a _DSD object with the "compatible" property, use
- * that property to match against the given list of identifiers.
+ * If @dev has an ACPI companion which has ACPI_DT_NAMESPACE_HID in its list of
+ * identifiers and a _DSD object with the "compatible" property, use that
+ * property to match against the given list of identifiers.
*/
static bool acpi_of_match_device(struct acpi_device *adev,
const struct of_device_id *of_match_table)
@@ -1038,14 +1041,14 @@ static const struct acpi_device_id *__acpi_match_device(
return id;
/*
- * Next, check the special "PRP0001" ID and try to match the
+ * Next, check ACPI_DT_NAMESPACE_HID and try to match the
* "compatible" property if found.
*
* The id returned by the below is not valid, but the only
* caller passing non-NULL of_ids here is only interested in
* whether or not the return value is NULL.
*/
- if (!strcmp("PRP0001", hwid->id)
+ if (!strcmp(ACPI_DT_NAMESPACE_HID, hwid->id)
&& acpi_of_match_device(device, of_ids))
return id;
}
@@ -1671,7 +1674,7 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle,
static void acpi_wakeup_gpe_init(struct acpi_device *device)
{
- struct acpi_device_id button_device_ids[] = {
+ static const struct acpi_device_id button_device_ids[] = {
{"PNP0C0C", 0},
{"PNP0C0D", 0},
{"PNP0C0E", 0},
@@ -1766,15 +1769,9 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state)
if (acpi_has_method(device->handle, pathname))
ps->flags.explicit_set = 1;
- /*
- * State is valid if there are means to put the device into it.
- * D3hot is only valid if _PR3 present.
- */
- if (!list_empty(&ps->resources)
- || (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) {
+ /* State is valid if there are means to put the device into it. */
+ if (!list_empty(&ps->resources) || ps->flags.explicit_set)
ps->flags.valid = 1;
- ps->flags.os_accessible = 1;
- }
ps->power = -1; /* Unknown - driver assigned */
ps->latency = -1; /* Unknown - driver assigned */
@@ -1810,21 +1807,13 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)
acpi_bus_init_power_state(device, i);
INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources);
+ if (!list_empty(&device->power.states[ACPI_STATE_D3_HOT].resources))
+ device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
- /* Set defaults for D0 and D3 states (always valid) */
+ /* Set defaults for D0 and D3hot states (always valid) */
device->power.states[ACPI_STATE_D0].flags.valid = 1;
device->power.states[ACPI_STATE_D0].power = 100;
- device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
- device->power.states[ACPI_STATE_D3_COLD].power = 0;
-
- /* Set D3cold's explicit_set flag if _PS3 exists. */
- if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set)
- device->power.states[ACPI_STATE_D3_COLD].flags.explicit_set = 1;
-
- /* Presence of _PS3 or _PRx means we can put the device into D3 cold */
- if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set ||
- device->power.flags.power_resources)
- device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1;
+ device->power.states[ACPI_STATE_D3_HOT].flags.valid = 1;
if (acpi_bus_init_power(device))
device->flags.power_manageable = 0;
@@ -1947,6 +1936,62 @@ bool acpi_dock_match(acpi_handle handle)
return acpi_has_method(handle, "_DCK");
}
+static acpi_status
+acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context,
+ void **return_value)
+{
+ long *cap = context;
+
+ if (acpi_has_method(handle, "_BCM") &&
+ acpi_has_method(handle, "_BCL")) {
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight "
+ "support\n"));
+ *cap |= ACPI_VIDEO_BACKLIGHT;
+ if (!acpi_has_method(handle, "_BQC"))
+ printk(KERN_WARNING FW_BUG PREFIX "No _BQC method, "
+ "cannot determine initial brightness\n");
+ /* We have backlight support, no need to scan further */
+ return AE_CTRL_TERMINATE;
+ }
+ return 0;
+}
+
+/* Returns true if the ACPI object is a video device which can be
+ * handled by video.ko.
+ * The device will get a Linux specific CID added in scan.c to
+ * identify the device as an ACPI graphics device
+ * Be aware that the graphics device may not be physically present
+ * Use acpi_video_get_capabilities() to detect general ACPI video
+ * capabilities of present cards
+ */
+long acpi_is_video_device(acpi_handle handle)
+{
+ long video_caps = 0;
+
+ /* Is this device able to support video switching ? */
+ if (acpi_has_method(handle, "_DOD") || acpi_has_method(handle, "_DOS"))
+ video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING;
+
+ /* Is this device able to retrieve a video ROM ? */
+ if (acpi_has_method(handle, "_ROM"))
+ video_caps |= ACPI_VIDEO_ROM_AVAILABLE;
+
+ /* Is this device able to configure which video head to be POSTed ? */
+ if (acpi_has_method(handle, "_VPO") &&
+ acpi_has_method(handle, "_GPD") &&
+ acpi_has_method(handle, "_SPD"))
+ video_caps |= ACPI_VIDEO_DEVICE_POSTING;
+
+ /* Only check for backlight functionality if one of the above hit. */
+ if (video_caps)
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
+ ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL,
+ &video_caps, NULL);
+
+ return video_caps;
+}
+EXPORT_SYMBOL(acpi_is_video_device);
+
const char *acpi_device_hid(struct acpi_device *device)
{
struct acpi_hardware_id *hid;
@@ -2109,6 +2154,39 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp)
kfree(pnp->unique_id);
}
+static void acpi_init_coherency(struct acpi_device *adev)
+{
+ unsigned long long cca = 0;
+ acpi_status status;
+ struct acpi_device *parent = adev->parent;
+
+ if (parent && parent->flags.cca_seen) {
+ /*
+ * From ACPI spec, OSPM will ignore _CCA if an ancestor
+ * already saw one.
+ */
+ adev->flags.cca_seen = 1;
+ cca = parent->flags.coherent_dma;
+ } else {
+ status = acpi_evaluate_integer(adev->handle, "_CCA",
+ NULL, &cca);
+ if (ACPI_SUCCESS(status))
+ adev->flags.cca_seen = 1;
+ else if (!IS_ENABLED(CONFIG_ACPI_CCA_REQUIRED))
+ /*
+ * If architecture does not specify that _CCA is
+ * required for DMA-able devices (e.g. x86),
+ * we default to _CCA=1.
+ */
+ cca = 1;
+ else
+ acpi_handle_debug(adev->handle,
+ "ACPI device is missing _CCA.\n");
+ }
+
+ adev->flags.coherent_dma = cca;
+}
+
void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
int type, unsigned long long sta)
{
@@ -2127,6 +2205,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
device->flags.visited = false;
device_initialize(&device->dev);
dev_set_uevent_suppress(&device->dev, true);
+ acpi_init_coherency(device);
}
void acpi_device_add_finalize(struct acpi_device *device)
@@ -2405,7 +2484,7 @@ static void acpi_default_enumeration(struct acpi_device *device)
}
static const struct acpi_device_id generic_device_ids[] = {
- {"PRP0001", },
+ {ACPI_DT_NAMESPACE_HID, },
{"", },
};
@@ -2413,8 +2492,8 @@ static int acpi_generic_device_attach(struct acpi_device *adev,
const struct acpi_device_id *not_used)
{
/*
- * Since PRP0001 is the only ID handled here, the test below can be
- * unconditional.
+ * Since ACPI_DT_NAMESPACE_HID is the only ID handled here, the test
+ * below can be unconditional.
*/
if (adev->data.of_compatible)
acpi_default_enumeration(adev);
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index cd49a3982b6a..67c548ad3764 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -712,3 +712,18 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs)
return false;
}
EXPORT_SYMBOL(acpi_check_dsm);
+
+/*
+ * acpi_backlight= handling, this is done here rather then in video_detect.c
+ * because __setup cannot be used in modules.
+ */
+char acpi_video_backlight_string[16];
+EXPORT_SYMBOL(acpi_video_backlight_string);
+
+static int __init acpi_backlight(char *str)
+{
+ strlcpy(acpi_video_backlight_string, str,
+ sizeof(acpi_video_backlight_string));
+ return 1;
+}
+__setup("acpi_backlight=", acpi_backlight);
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index c42feb2bacd0..815f75ef2411 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -1,106 +1,61 @@
/*
+ * Copyright (C) 2015 Red Hat Inc.
+ * Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2008 SuSE Linux Products GmbH
* Thomas Renninger <trenn@suse.de>
*
* May be copied or modified under the terms of the GNU General Public License
*
* video_detect.c:
- * Provides acpi_is_video_device() for early scanning of ACPI devices in scan.c
- * There a Linux specific (Spec does not provide a HID for video devices) is
- * assigned
- *
* After PCI devices are glued with ACPI devices
* acpi_get_pci_dev() can be called to identify ACPI graphics
* devices for which a real graphics card is plugged in
*
- * Now acpi_video_get_capabilities() can be called to check which
- * capabilities the graphics cards plugged in support. The check for general
- * video capabilities will be triggered by the first caller of
- * acpi_video_get_capabilities(NULL); which will happen when the first
- * backlight switching supporting driver calls:
- * acpi_video_backlight_support();
- *
* Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B)
* are available, video.ko should be used to handle the device.
*
* Otherwise vendor specific drivers like thinkpad_acpi, asus-laptop,
* sony_acpi,... can take care about backlight brightness.
*
- * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m)
- * this file will not be compiled, acpi_video_get_capabilities() and
- * acpi_video_backlight_support() will always return 0 and vendor specific
- * drivers always can handle backlight.
+ * Backlight drivers can use acpi_video_get_backlight_type() to determine
+ * which driver should handle the backlight.
*
+ * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m)
+ * this file will not be compiled and acpi_video_get_backlight_type() will
+ * always return acpi_backlight_vendor.
*/
#include <linux/export.h>
#include <linux/acpi.h>
+#include <linux/backlight.h>
#include <linux/dmi.h>
+#include <linux/module.h>
#include <linux/pci.h>
-
-#include "internal.h"
+#include <linux/types.h>
+#include <acpi/video.h>
ACPI_MODULE_NAME("video");
#define _COMPONENT ACPI_VIDEO_COMPONENT
-static long acpi_video_support;
-static bool acpi_video_caps_checked;
+void acpi_video_unregister_backlight(void);
-static acpi_status
-acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context,
- void **return_value)
-{
- long *cap = context;
+static bool backlight_notifier_registered;
+static struct notifier_block backlight_nb;
- if (acpi_has_method(handle, "_BCM") &&
- acpi_has_method(handle, "_BCL")) {
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight "
- "support\n"));
- *cap |= ACPI_VIDEO_BACKLIGHT;
- if (!acpi_has_method(handle, "_BQC"))
- printk(KERN_WARNING FW_BUG PREFIX "No _BQC method, "
- "cannot determine initial brightness\n");
- /* We have backlight support, no need to scan further */
- return AE_CTRL_TERMINATE;
- }
- return 0;
-}
+static enum acpi_backlight_type acpi_backlight_cmdline = acpi_backlight_undef;
+static enum acpi_backlight_type acpi_backlight_dmi = acpi_backlight_undef;
-/* Returns true if the ACPI object is a video device which can be
- * handled by video.ko.
- * The device will get a Linux specific CID added in scan.c to
- * identify the device as an ACPI graphics device
- * Be aware that the graphics device may not be physically present
- * Use acpi_video_get_capabilities() to detect general ACPI video
- * capabilities of present cards
- */
-long acpi_is_video_device(acpi_handle handle)
+static void acpi_video_parse_cmdline(void)
{
- long video_caps = 0;
-
- /* Is this device able to support video switching ? */
- if (acpi_has_method(handle, "_DOD") || acpi_has_method(handle, "_DOS"))
- video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING;
-
- /* Is this device able to retrieve a video ROM ? */
- if (acpi_has_method(handle, "_ROM"))
- video_caps |= ACPI_VIDEO_ROM_AVAILABLE;
-
- /* Is this device able to configure which video head to be POSTed ? */
- if (acpi_has_method(handle, "_VPO") &&
- acpi_has_method(handle, "_GPD") &&
- acpi_has_method(handle, "_SPD"))
- video_caps |= ACPI_VIDEO_DEVICE_POSTING;
-
- /* Only check for backlight functionality if one of the above hit. */
- if (video_caps)
- acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
- ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL,
- &video_caps, NULL);
-
- return video_caps;
+ if (!strcmp("vendor", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_vendor;
+ if (!strcmp("video", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_video;
+ if (!strcmp("native", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_native;
+ if (!strcmp("none", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_none;
}
-EXPORT_SYMBOL(acpi_is_video_device);
static acpi_status
find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
@@ -109,7 +64,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
struct pci_dev *dev;
struct acpi_device *acpi_dev;
- const struct acpi_device_id video_ids[] = {
+ static const struct acpi_device_id video_ids[] = {
{ACPI_VIDEO_HID, 0},
{"", 0},
};
@@ -130,11 +85,23 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
* buggy */
static int video_detect_force_vendor(const struct dmi_system_id *d)
{
- acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
+ acpi_backlight_dmi = acpi_backlight_vendor;
+ return 0;
+}
+
+static int video_detect_force_video(const struct dmi_system_id *d)
+{
+ acpi_backlight_dmi = acpi_backlight_video;
return 0;
}
-static struct dmi_system_id video_detect_dmi_table[] = {
+static int video_detect_force_native(const struct dmi_system_id *d)
+{
+ acpi_backlight_dmi = acpi_backlight_native;
+ return 0;
+}
+
+static const struct dmi_system_id video_detect_dmi_table[] = {
/* On Samsung X360, the BIOS will set a flag (VDRV) if generic
* ACPI backlight device is used. This flag will definitively break
* the backlight interface (even the vendor interface) untill next
@@ -174,137 +141,209 @@ static struct dmi_system_id video_detect_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5737"),
},
},
+
+ /*
+ * These models have a working acpi_video backlight control, and using
+ * native backlight causes a regression where backlight does not work
+ * when userspace is not handling brightness key events. Disable
+ * native_backlight on these to fix this:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=81691
+ */
+ {
+ .callback = video_detect_force_video,
+ .ident = "ThinkPad T420",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"),
+ },
+ },
+ {
+ .callback = video_detect_force_video,
+ .ident = "ThinkPad T520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"),
+ },
+ },
+ {
+ .callback = video_detect_force_video,
+ .ident = "ThinkPad X201s",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"),
+ },
+ },
+
+ /* The native backlight controls do not work on some older machines */
+ {
+ /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */
+ .callback = video_detect_force_video,
+ .ident = "HP ENVY 15 Notebook",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"),
+ },
+ },
+ {
+ .callback = video_detect_force_video,
+ .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"),
+ },
+ },
+ {
+ .callback = video_detect_force_video,
+ .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "370R4E/370R4V/370R5E/3570RE/370R5V"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */
+ .callback = video_detect_force_video,
+ .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "3570R/370R/470R/450R/510R/4450RV"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */
+ .callback = video_detect_force_video,
+ .ident = "SAMSUNG 730U3E/740U3E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"),
+ },
+ },
+ {
+ /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */
+ .callback = video_detect_force_video,
+ .ident = "SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "900X3C/900X3D/900X3E/900X4C/900X4D"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */
+ .callback = video_detect_force_video,
+ .ident = "Dell XPS15 L521X",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"),
+ },
+ },
+
+ /* Non win8 machines which need native backlight nevertheless */
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */
+ .callback = video_detect_force_native,
+ .ident = "Lenovo Ideapad Z570",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "102434U"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */
+ .callback = video_detect_force_native,
+ .ident = "Apple MacBook Pro 12,1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"),
+ },
+ },
{ },
};
+static int acpi_video_backlight_notify(struct notifier_block *nb,
+ unsigned long val, void *bd)
+{
+ struct backlight_device *backlight = bd;
+
+ /* A raw bl registering may change video -> native */
+ if (backlight->props.type == BACKLIGHT_RAW &&
+ val == BACKLIGHT_REGISTERED &&
+ acpi_video_get_backlight_type() != acpi_backlight_video)
+ acpi_video_unregister_backlight();
+
+ return NOTIFY_OK;
+}
+
/*
- * Returns the video capabilities of a specific ACPI graphics device
+ * Determine which type of backlight interface to use on this system,
+ * First check cmdline, then dmi quirks, then do autodetect.
*
- * if NULL is passed as argument all ACPI devices are enumerated and
- * all graphics capabilities of physically present devices are
- * summarized and returned. This is cached and done only once.
+ * The autodetect order is:
+ * 1) Is the acpi-video backlight interface supported ->
+ * no, use a vendor interface
+ * 2) Is this a win8 "ready" BIOS and do we have a native interface ->
+ * yes, use a native interface
+ * 3) Else use the acpi-video interface
+ *
+ * Arguably the native on win8 check should be done first, but that would
+ * be a behavior change, which may causes issues.
*/
-long acpi_video_get_capabilities(acpi_handle graphics_handle)
+enum acpi_backlight_type acpi_video_get_backlight_type(void)
{
- long caps = 0;
- struct acpi_device *tmp_dev;
- acpi_status status;
-
- if (acpi_video_caps_checked && graphics_handle == NULL)
- return acpi_video_support;
-
- if (!graphics_handle) {
- /* Only do the global walk through all graphics devices once */
- acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, find_video, NULL,
- &caps, NULL);
- /* There might be boot param flags set already... */
- acpi_video_support |= caps;
- acpi_video_caps_checked = 1;
- /* Add blacklists here. Be careful to use the right *DMI* bits
- * to still be able to override logic via boot params, e.g.:
- *
- * if (dmi_name_in_vendors("XY")) {
- * acpi_video_support |=
- * ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
- *}
- */
+ static DEFINE_MUTEX(init_mutex);
+ static bool init_done;
+ static long video_caps;
+ /* Parse cmdline, dmi and acpi only once */
+ mutex_lock(&init_mutex);
+ if (!init_done) {
+ acpi_video_parse_cmdline();
dmi_check_system(video_detect_dmi_table);
- } else {
- status = acpi_bus_get_device(graphics_handle, &tmp_dev);
- if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status, "Invalid device"));
- return 0;
- }
- acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle,
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, find_video, NULL,
- &caps, NULL);
+ &video_caps, NULL);
+ backlight_nb.notifier_call = acpi_video_backlight_notify;
+ backlight_nb.priority = 0;
+ if (backlight_register_notifier(&backlight_nb) == 0)
+ backlight_notifier_registered = true;
+ init_done = true;
}
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %s %s\n",
- graphics_handle ? caps : acpi_video_support,
- graphics_handle ? "on device " : "in general",
- graphics_handle ? acpi_device_bid(tmp_dev) : ""));
- return caps;
-}
-EXPORT_SYMBOL(acpi_video_get_capabilities);
+ mutex_unlock(&init_mutex);
-static void acpi_video_caps_check(void)
-{
- /*
- * We must check whether the ACPI graphics device is physically plugged
- * in. Therefore this must be called after binding PCI and ACPI devices
- */
- if (!acpi_video_caps_checked)
- acpi_video_get_capabilities(NULL);
-}
+ if (acpi_backlight_cmdline != acpi_backlight_undef)
+ return acpi_backlight_cmdline;
-bool acpi_osi_is_win8(void)
-{
- return acpi_gbl_osi_data >= ACPI_OSI_WIN_8;
-}
-EXPORT_SYMBOL(acpi_osi_is_win8);
+ if (acpi_backlight_dmi != acpi_backlight_undef)
+ return acpi_backlight_dmi;
-/* Promote the vendor interface instead of the generic video module.
- * This function allow DMI blacklists to be implemented by externals
- * platform drivers instead of putting a big blacklist in video_detect.c
- * After calling this function you will probably want to call
- * acpi_video_unregister() to make sure the video module is not loaded
- */
-void acpi_video_dmi_promote_vendor(void)
-{
- acpi_video_caps_check();
- acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
-}
-EXPORT_SYMBOL(acpi_video_dmi_promote_vendor);
-
-/* To be called when a driver who previously promoted the vendor
- * interface */
-void acpi_video_dmi_demote_vendor(void)
-{
- acpi_video_caps_check();
- acpi_video_support &= ~ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
-}
-EXPORT_SYMBOL(acpi_video_dmi_demote_vendor);
-
-/* Returns true if video.ko can do backlight switching */
-int acpi_video_backlight_support(void)
-{
- acpi_video_caps_check();
-
- /* First check for boot param -> highest prio */
- if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR)
- return 0;
- else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO)
- return 1;
+ if (!(video_caps & ACPI_VIDEO_BACKLIGHT))
+ return acpi_backlight_vendor;
- /* Then check for DMI blacklist -> second highest prio */
- if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VENDOR)
- return 0;
- else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VIDEO)
- return 1;
+ if (acpi_osi_is_win8() && backlight_device_registered(BACKLIGHT_RAW))
+ return acpi_backlight_native;
- /* Then go the default way */
- return acpi_video_support & ACPI_VIDEO_BACKLIGHT;
+ return acpi_backlight_video;
}
-EXPORT_SYMBOL(acpi_video_backlight_support);
+EXPORT_SYMBOL(acpi_video_get_backlight_type);
/*
- * Use acpi_backlight=vendor/video to force that backlight switching
- * is processed by vendor specific acpi drivers or video.ko driver.
+ * Set the preferred backlight interface type based on DMI info.
+ * This function allows DMI blacklists to be implemented by external
+ * platform drivers instead of putting a big blacklist in video_detect.c
*/
-static int __init acpi_backlight(char *str)
+void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type)
{
- if (str == NULL || *str == '\0')
- return 1;
- else {
- if (!strcmp("vendor", str))
- acpi_video_support |=
- ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR;
- if (!strcmp("video", str))
- acpi_video_support |=
- ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO;
- }
- return 1;
+ acpi_backlight_dmi = type;
+ /* Remove acpi-video backlight interface if it is no longer desired */
+ if (acpi_video_get_backlight_type() != acpi_backlight_video)
+ acpi_video_unregister_backlight();
+}
+EXPORT_SYMBOL(acpi_video_set_dmi_backlight_type);
+
+void __exit acpi_video_detect_exit(void)
+{
+ if (backlight_notifier_registered)
+ backlight_unregister_notifier(&backlight_nb);
}
-__setup("acpi_backlight=", acpi_backlight);
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 5f601553b9b0..9dca4b995be0 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -270,6 +270,7 @@ config ATA_PIIX
config SATA_DWC
tristate "DesignWare Cores SATA support"
depends on 460EX
+ select DW_DMAC
help
This option enables support for the on-chip SATA controller of the
AppliedMicro processor 460EX.
@@ -729,15 +730,6 @@ config PATA_SC1200
If unsure, say N.
-config PATA_SCC
- tristate "Toshiba's Cell Reference Set IDE support"
- depends on PCI && PPC_CELLEB
- help
- This option enables support for the built-in IDE controller on
- Toshiba Cell Reference Board.
-
- If unsure, say N.
-
config PATA_SCH
tristate "Intel SCH PATA support"
depends on PCI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index b67e995179a9..40f7865f20a1 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -75,7 +75,6 @@ obj-$(CONFIG_PATA_PDC_OLD) += pata_pdc202xx_old.o
obj-$(CONFIG_PATA_RADISYS) += pata_radisys.o
obj-$(CONFIG_PATA_RDC) += pata_rdc.o
obj-$(CONFIG_PATA_SC1200) += pata_sc1200.o
-obj-$(CONFIG_PATA_SCC) += pata_scc.o
obj-$(CONFIG_PATA_SCH) += pata_sch.o
obj-$(CONFIG_PATA_SERVERWORKS) += pata_serverworks.o
obj-$(CONFIG_PATA_SIL680) += pata_sil680.o
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index c7a92a743ed0..65ee94454bbd 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -66,6 +66,7 @@ enum board_ids {
board_ahci_yes_fbs,
/* board IDs for specific chipsets in alphabetical order */
+ board_ahci_avn,
board_ahci_mcp65,
board_ahci_mcp77,
board_ahci_mcp89,
@@ -84,6 +85,8 @@ enum board_ids {
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
+static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline);
static void ahci_mcp89_apple_enable(struct pci_dev *pdev);
static bool is_mcp89_apple(struct pci_dev *pdev);
static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
@@ -107,6 +110,11 @@ static struct ata_port_operations ahci_p5wdh_ops = {
.hardreset = ahci_p5wdh_hardreset,
};
+static struct ata_port_operations ahci_avn_ops = {
+ .inherits = &ahci_ops,
+ .hardreset = ahci_avn_hardreset,
+};
+
static const struct ata_port_info ahci_port_info[] = {
/* by features */
[board_ahci] = {
@@ -151,6 +159,12 @@ static const struct ata_port_info ahci_port_info[] = {
.port_ops = &ahci_ops,
},
/* by chipsets */
+ [board_ahci_avn] = {
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_avn_ops,
+ },
[board_ahci_mcp65] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP |
AHCI_HFLAG_YES_NCQ),
@@ -290,14 +304,14 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x1f27), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f2e), board_ahci }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f2f), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f32), board_ahci }, /* Avoton AHCI */
- { PCI_VDEVICE(INTEL, 0x1f33), board_ahci }, /* Avoton AHCI */
- { PCI_VDEVICE(INTEL, 0x1f34), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f35), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f36), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f37), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f32), board_ahci_avn }, /* Avoton AHCI */
+ { PCI_VDEVICE(INTEL, 0x1f33), board_ahci_avn }, /* Avoton AHCI */
+ { PCI_VDEVICE(INTEL, 0x1f34), board_ahci_avn }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f35), board_ahci_avn }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f36), board_ahci_avn }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f37), board_ahci_avn }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f3e), board_ahci_avn }, /* Avoton RAID */
+ { PCI_VDEVICE(INTEL, 0x1f3f), board_ahci_avn }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */
@@ -670,6 +684,79 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
return rc;
}
+/*
+ * ahci_avn_hardreset - attempt more aggressive recovery of Avoton ports.
+ *
+ * It has been observed with some SSDs that the timing of events in the
+ * link synchronization phase can leave the port in a state that can not
+ * be recovered by a SATA-hard-reset alone. The failing signature is
+ * SStatus.DET stuck at 1 ("Device presence detected but Phy
+ * communication not established"). It was found that unloading and
+ * reloading the driver when this problem occurs allows the drive
+ * connection to be recovered (DET advanced to 0x3). The critical
+ * component of reloading the driver is that the port state machines are
+ * reset by bouncing "port enable" in the AHCI PCS configuration
+ * register. So, reproduce that effect by bouncing a port whenever we
+ * see DET==1 after a reset.
+ */
+static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
+{
+ const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+ struct ata_port *ap = link->ap;
+ struct ahci_port_priv *pp = ap->private_data;
+ struct ahci_host_priv *hpriv = ap->host->private_data;
+ u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
+ unsigned long tmo = deadline - jiffies;
+ struct ata_taskfile tf;
+ bool online;
+ int rc, i;
+
+ DPRINTK("ENTER\n");
+
+ ahci_stop_engine(ap);
+
+ for (i = 0; i < 2; i++) {
+ u16 val;
+ u32 sstatus;
+ int port = ap->port_no;
+ struct ata_host *host = ap->host;
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+
+ /* clear D2H reception area to properly wait for D2H FIS */
+ ata_tf_init(link->device, &tf);
+ tf.command = ATA_BUSY;
+ ata_tf_to_fis(&tf, 0, 0, d2h_fis);
+
+ rc = sata_link_hardreset(link, timing, deadline, &online,
+ ahci_check_ready);
+
+ if (sata_scr_read(link, SCR_STATUS, &sstatus) != 0 ||
+ (sstatus & 0xf) != 1)
+ break;
+
+ ata_link_printk(link, KERN_INFO, "avn bounce port%d\n",
+ port);
+
+ pci_read_config_word(pdev, 0x92, &val);
+ val &= ~(1 << port);
+ pci_write_config_word(pdev, 0x92, val);
+ ata_msleep(ap, 1000);
+ val |= 1 << port;
+ pci_write_config_word(pdev, 0x92, val);
+ deadline += tmo;
+ }
+
+ hpriv->start_engine(ap);
+
+ if (online)
+ *class = ahci_dev_classify(ap);
+
+ DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
+ return rc;
+}
+
+
#ifdef CONFIG_PM
static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c
index 23716dd8a7ec..5928d0746a27 100644
--- a/drivers/ata/ahci_mvebu.c
+++ b/drivers/ata/ahci_mvebu.c
@@ -45,7 +45,7 @@ static void ahci_mvebu_mbus_config(struct ahci_host_priv *hpriv,
writel((cs->mbus_attr << 8) |
(dram->mbus_dram_target_id << 4) | 1,
hpriv->mmio + AHCI_WINDOW_CTRL(i));
- writel(cs->base, hpriv->mmio + AHCI_WINDOW_BASE(i));
+ writel(cs->base >> 16, hpriv->mmio + AHCI_WINDOW_BASE(i));
writel(((cs->size - 1) & 0xffff0000),
hpriv->mmio + AHCI_WINDOW_SIZE(i));
}
diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c
index ea0ff005b86c..8ff428fe8e0f 100644
--- a/drivers/ata/ahci_st.c
+++ b/drivers/ata/ahci_st.c
@@ -37,7 +37,6 @@ struct st_ahci_drv_data {
struct reset_control *pwr;
struct reset_control *sw_rst;
struct reset_control *pwr_rst;
- struct ahci_host_priv *hpriv;
};
static void st_ahci_configure_oob(void __iomem *mmio)
@@ -55,9 +54,10 @@ static void st_ahci_configure_oob(void __iomem *mmio)
writel(new_val, mmio + ST_AHCI_OOBR);
}
-static int st_ahci_deassert_resets(struct device *dev)
+static int st_ahci_deassert_resets(struct ahci_host_priv *hpriv,
+ struct device *dev)
{
- struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
+ struct st_ahci_drv_data *drv_data = hpriv->plat_data;
int err;
if (drv_data->pwr) {
@@ -90,8 +90,8 @@ static int st_ahci_deassert_resets(struct device *dev)
static void st_ahci_host_stop(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
+ struct st_ahci_drv_data *drv_data = hpriv->plat_data;
struct device *dev = host->dev;
- struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
int err;
if (drv_data->pwr) {
@@ -103,29 +103,30 @@ static void st_ahci_host_stop(struct ata_host *host)
ahci_platform_disable_resources(hpriv);
}
-static int st_ahci_probe_resets(struct platform_device *pdev)
+static int st_ahci_probe_resets(struct ahci_host_priv *hpriv,
+ struct device *dev)
{
- struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev);
+ struct st_ahci_drv_data *drv_data = hpriv->plat_data;
- drv_data->pwr = devm_reset_control_get(&pdev->dev, "pwr-dwn");
+ drv_data->pwr = devm_reset_control_get(dev, "pwr-dwn");
if (IS_ERR(drv_data->pwr)) {
- dev_info(&pdev->dev, "power reset control not defined\n");
+ dev_info(dev, "power reset control not defined\n");
drv_data->pwr = NULL;
}
- drv_data->sw_rst = devm_reset_control_get(&pdev->dev, "sw-rst");
+ drv_data->sw_rst = devm_reset_control_get(dev, "sw-rst");
if (IS_ERR(drv_data->sw_rst)) {
- dev_info(&pdev->dev, "soft reset control not defined\n");
+ dev_info(dev, "soft reset control not defined\n");
drv_data->sw_rst = NULL;
}
- drv_data->pwr_rst = devm_reset_control_get(&pdev->dev, "pwr-rst");
+ drv_data->pwr_rst = devm_reset_control_get(dev, "pwr-rst");
if (IS_ERR(drv_data->pwr_rst)) {
- dev_dbg(&pdev->dev, "power soft reset control not defined\n");
+ dev_dbg(dev, "power soft reset control not defined\n");
drv_data->pwr_rst = NULL;
}
- return st_ahci_deassert_resets(&pdev->dev);
+ return st_ahci_deassert_resets(hpriv, dev);
}
static struct ata_port_operations st_ahci_port_ops = {
@@ -154,15 +155,12 @@ static int st_ahci_probe(struct platform_device *pdev)
if (!drv_data)
return -ENOMEM;
- platform_set_drvdata(pdev, drv_data);
-
hpriv = ahci_platform_get_resources(pdev);
if (IS_ERR(hpriv))
return PTR_ERR(hpriv);
+ hpriv->plat_data = drv_data;
- drv_data->hpriv = hpriv;
-
- err = st_ahci_probe_resets(pdev);
+ err = st_ahci_probe_resets(hpriv, &pdev->dev);
if (err)
return err;
@@ -170,7 +168,7 @@ static int st_ahci_probe(struct platform_device *pdev)
if (err)
return err;
- st_ahci_configure_oob(drv_data->hpriv->mmio);
+ st_ahci_configure_oob(hpriv->mmio);
err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info,
&ahci_platform_sht);
@@ -185,8 +183,9 @@ static int st_ahci_probe(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int st_ahci_suspend(struct device *dev)
{
- struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
- struct ahci_host_priv *hpriv = drv_data->hpriv;
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct ahci_host_priv *hpriv = host->private_data;
+ struct st_ahci_drv_data *drv_data = hpriv->plat_data;
int err;
err = ahci_platform_suspend_host(dev);
@@ -208,21 +207,21 @@ static int st_ahci_suspend(struct device *dev)
static int st_ahci_resume(struct device *dev)
{
- struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev);
- struct ahci_host_priv *hpriv = drv_data->hpriv;
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct ahci_host_priv *hpriv = host->private_data;
int err;
err = ahci_platform_enable_resources(hpriv);
if (err)
return err;
- err = st_ahci_deassert_resets(dev);
+ err = st_ahci_deassert_resets(hpriv, dev);
if (err) {
ahci_platform_disable_resources(hpriv);
return err;
}
- st_ahci_configure_oob(drv_data->hpriv->mmio);
+ st_ahci_configure_oob(hpriv->mmio);
return ahci_platform_resume_host(dev);
}
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 61a9c07e0dff..287c4ba0219f 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1707,8 +1707,7 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
if (unlikely(resetting))
status &= ~PORT_IRQ_BAD_PMP;
- /* if LPM is enabled, PHYRDY doesn't mean anything */
- if (ap->link.lpm_policy > ATA_LPM_MAX_POWER) {
+ if (sata_lpm_ignore_phy_events(&ap->link)) {
status &= ~PORT_IRQ_PHYRDY;
ahci_scr_write(&ap->link, SCR_ERROR, SERR_PHYRDY_CHG);
}
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index f6cb1f1b30b7..577849c6611a 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4235,7 +4235,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
- { "Samsung SSD 850 PRO*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ { "Samsung SSD 8*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
/*
@@ -6752,6 +6752,38 @@ u32 ata_wait_register(struct ata_port *ap, void __iomem *reg, u32 mask, u32 val,
return tmp;
}
+/**
+ * sata_lpm_ignore_phy_events - test if PHY event should be ignored
+ * @link: Link receiving the event
+ *
+ * Test whether the received PHY event has to be ignored or not.
+ *
+ * LOCKING:
+ * None:
+ *
+ * RETURNS:
+ * True if the event has to be ignored.
+ */
+bool sata_lpm_ignore_phy_events(struct ata_link *link)
+{
+ unsigned long lpm_timeout = link->last_lpm_change +
+ msecs_to_jiffies(ATA_TMOUT_SPURIOUS_PHY);
+
+ /* if LPM is enabled, PHYRDY doesn't mean anything */
+ if (link->lpm_policy > ATA_LPM_MAX_POWER)
+ return true;
+
+ /* ignore the first PHY event after the LPM policy changed
+ * as it is might be spurious
+ */
+ if ((link->flags & ATA_LFLAG_CHANGED) &&
+ time_before(jiffies, lpm_timeout))
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(sata_lpm_ignore_phy_events);
+
/*
* Dummy port_ops
*/
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 07f41be38fbe..cf0022ec07f2 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -3597,6 +3597,9 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
}
}
+ link->last_lpm_change = jiffies;
+ link->flags |= ATA_LFLAG_CHANGED;
+
return 0;
fail:
diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c
index 80a80548ad0a..27245957eee3 100644
--- a/drivers/ata/pata_octeon_cf.c
+++ b/drivers/ata/pata_octeon_cf.c
@@ -1053,7 +1053,7 @@ static struct of_device_id octeon_cf_match[] = {
},
{},
};
-MODULE_DEVICE_TABLE(of, octeon_i2c_match);
+MODULE_DEVICE_TABLE(of, octeon_cf_match);
static struct platform_driver octeon_cf_driver = {
.probe = octeon_cf_probe,
diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c
deleted file mode 100644
index 5cd60d6388ec..000000000000
--- a/drivers/ata/pata_scc.c
+++ /dev/null
@@ -1,1110 +0,0 @@
-/*
- * Support for IDE interfaces on Celleb platform
- *
- * (C) Copyright 2006 TOSHIBA CORPORATION
- *
- * This code is based on drivers/ata/ata_piix.c:
- * Copyright 2003-2005 Red Hat Inc
- * Copyright 2003-2005 Jeff Garzik
- * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
- * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
- * Copyright (C) 2003 Red Hat Inc
- *
- * and drivers/ata/ahci.c:
- * Copyright 2004-2005 Red Hat, Inc.
- *
- * and drivers/ata/libata-core.c:
- * Copyright 2003-2004 Red Hat, Inc. All rights reserved.
- * Copyright 2003-2004 Jeff Garzik
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/blkdev.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <scsi/scsi_host.h>
-#include <linux/libata.h>
-
-#define DRV_NAME "pata_scc"
-#define DRV_VERSION "0.3"
-
-#define PCI_DEVICE_ID_TOSHIBA_SCC_ATA 0x01b4
-
-/* PCI BARs */
-#define SCC_CTRL_BAR 0
-#define SCC_BMID_BAR 1
-
-/* offset of CTRL registers */
-#define SCC_CTL_PIOSHT 0x000
-#define SCC_CTL_PIOCT 0x004
-#define SCC_CTL_MDMACT 0x008
-#define SCC_CTL_MCRCST 0x00C
-#define SCC_CTL_SDMACT 0x010
-#define SCC_CTL_SCRCST 0x014
-#define SCC_CTL_UDENVT 0x018
-#define SCC_CTL_TDVHSEL 0x020
-#define SCC_CTL_MODEREG 0x024
-#define SCC_CTL_ECMODE 0xF00
-#define SCC_CTL_MAEA0 0xF50
-#define SCC_CTL_MAEC0 0xF54
-#define SCC_CTL_CCKCTRL 0xFF0
-
-/* offset of BMID registers */
-#define SCC_DMA_CMD 0x000
-#define SCC_DMA_STATUS 0x004
-#define SCC_DMA_TABLE_OFS 0x008
-#define SCC_DMA_INTMASK 0x010
-#define SCC_DMA_INTST 0x014
-#define SCC_DMA_PTERADD 0x018
-#define SCC_REG_CMD_ADDR 0x020
-#define SCC_REG_DATA 0x000
-#define SCC_REG_ERR 0x004
-#define SCC_REG_FEATURE 0x004
-#define SCC_REG_NSECT 0x008
-#define SCC_REG_LBAL 0x00C
-#define SCC_REG_LBAM 0x010
-#define SCC_REG_LBAH 0x014
-#define SCC_REG_DEVICE 0x018
-#define SCC_REG_STATUS 0x01C
-#define SCC_REG_CMD 0x01C
-#define SCC_REG_ALTSTATUS 0x020
-
-/* register value */
-#define TDVHSEL_MASTER 0x00000001
-#define TDVHSEL_SLAVE 0x00000004
-
-#define MODE_JCUSFEN 0x00000080
-
-#define ECMODE_VALUE 0x01
-
-#define CCKCTRL_ATARESET 0x00040000
-#define CCKCTRL_BUFCNT 0x00020000
-#define CCKCTRL_CRST 0x00010000
-#define CCKCTRL_OCLKEN 0x00000100
-#define CCKCTRL_ATACLKOEN 0x00000002
-#define CCKCTRL_LCLKEN 0x00000001
-
-#define QCHCD_IOS_SS 0x00000001
-
-#define QCHSD_STPDIAG 0x00020000
-
-#define INTMASK_MSK 0xD1000012
-#define INTSTS_SERROR 0x80000000
-#define INTSTS_PRERR 0x40000000
-#define INTSTS_RERR 0x10000000
-#define INTSTS_ICERR 0x01000000
-#define INTSTS_BMSINT 0x00000010
-#define INTSTS_BMHE 0x00000008
-#define INTSTS_IOIRQS 0x00000004
-#define INTSTS_INTRQ 0x00000002
-#define INTSTS_ACTEINT 0x00000001
-
-
-/* PIO transfer mode table */
-/* JCHST */
-static const unsigned long JCHSTtbl[2][7] = {
- {0x0E, 0x05, 0x02, 0x03, 0x02, 0x00, 0x00}, /* 100MHz */
- {0x13, 0x07, 0x04, 0x04, 0x03, 0x00, 0x00} /* 133MHz */
-};
-
-/* JCHHT */
-static const unsigned long JCHHTtbl[2][7] = {
- {0x0E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00}, /* 100MHz */
- {0x13, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00} /* 133MHz */
-};
-
-/* JCHCT */
-static const unsigned long JCHCTtbl[2][7] = {
- {0x1D, 0x1D, 0x1C, 0x0B, 0x06, 0x00, 0x00}, /* 100MHz */
- {0x27, 0x26, 0x26, 0x0E, 0x09, 0x00, 0x00} /* 133MHz */
-};
-
-/* DMA transfer mode table */
-/* JCHDCTM/JCHDCTS */
-static const unsigned long JCHDCTxtbl[2][7] = {
- {0x0A, 0x06, 0x04, 0x03, 0x01, 0x00, 0x00}, /* 100MHz */
- {0x0E, 0x09, 0x06, 0x04, 0x02, 0x01, 0x00} /* 133MHz */
-};
-
-/* JCSTWTM/JCSTWTS */
-static const unsigned long JCSTWTxtbl[2][7] = {
- {0x06, 0x04, 0x03, 0x02, 0x02, 0x02, 0x00}, /* 100MHz */
- {0x09, 0x06, 0x04, 0x02, 0x02, 0x02, 0x02} /* 133MHz */
-};
-
-/* JCTSS */
-static const unsigned long JCTSStbl[2][7] = {
- {0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}, /* 100MHz */
- {0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05} /* 133MHz */
-};
-
-/* JCENVT */
-static const unsigned long JCENVTtbl[2][7] = {
- {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}, /* 100MHz */
- {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02} /* 133MHz */
-};
-
-/* JCACTSELS/JCACTSELM */
-static const unsigned long JCACTSELtbl[2][7] = {
- {0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}, /* 100MHz */
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} /* 133MHz */
-};
-
-static const struct pci_device_id scc_pci_tbl[] = {
- { PCI_VDEVICE(TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SCC_ATA), 0},
- { } /* terminate list */
-};
-
-/**
- * scc_set_piomode - Initialize host controller PATA PIO timings
- * @ap: Port whose timings we are configuring
- * @adev: um
- *
- * Set PIO mode for device.
- *
- * LOCKING:
- * None (inherited from caller).
- */
-
-static void scc_set_piomode (struct ata_port *ap, struct ata_device *adev)
-{
- unsigned int pio = adev->pio_mode - XFER_PIO_0;
- void __iomem *ctrl_base = ap->host->iomap[SCC_CTRL_BAR];
- void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL;
- void __iomem *piosht_port = ctrl_base + SCC_CTL_PIOSHT;
- void __iomem *pioct_port = ctrl_base + SCC_CTL_PIOCT;
- unsigned long reg;
- int offset;
-
- reg = in_be32(cckctrl_port);
- if (reg & CCKCTRL_ATACLKOEN)
- offset = 1; /* 133MHz */
- else
- offset = 0; /* 100MHz */
-
- reg = JCHSTtbl[offset][pio] << 16 | JCHHTtbl[offset][pio];
- out_be32(piosht_port, reg);
- reg = JCHCTtbl[offset][pio];
- out_be32(pioct_port, reg);
-}
-
-/**
- * scc_set_dmamode - Initialize host controller PATA DMA timings
- * @ap: Port whose timings we are configuring
- * @adev: um
- *
- * Set UDMA mode for device.
- *
- * LOCKING:
- * None (inherited from caller).
- */
-
-static void scc_set_dmamode (struct ata_port *ap, struct ata_device *adev)
-{
- unsigned int udma = adev->dma_mode;
- unsigned int is_slave = (adev->devno != 0);
- u8 speed = udma;
- void __iomem *ctrl_base = ap->host->iomap[SCC_CTRL_BAR];
- void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL;
- void __iomem *mdmact_port = ctrl_base + SCC_CTL_MDMACT;
- void __iomem *mcrcst_port = ctrl_base + SCC_CTL_MCRCST;
- void __iomem *sdmact_port = ctrl_base + SCC_CTL_SDMACT;
- void __iomem *scrcst_port = ctrl_base + SCC_CTL_SCRCST;
- void __iomem *udenvt_port = ctrl_base + SCC_CTL_UDENVT;
- void __iomem *tdvhsel_port = ctrl_base + SCC_CTL_TDVHSEL;
- int offset, idx;
-
- if (in_be32(cckctrl_port) & CCKCTRL_ATACLKOEN)
- offset = 1; /* 133MHz */
- else
- offset = 0; /* 100MHz */
-
- if (speed >= XFER_UDMA_0)
- idx = speed - XFER_UDMA_0;
- else
- return;
-
- if (is_slave) {
- out_be32(sdmact_port, JCHDCTxtbl[offset][idx]);
- out_be32(scrcst_port, JCSTWTxtbl[offset][idx]);
- out_be32(tdvhsel_port,
- (in_be32(tdvhsel_port) & ~TDVHSEL_SLAVE) | (JCACTSELtbl[offset][idx] << 2));
- } else {
- out_be32(mdmact_port, JCHDCTxtbl[offset][idx]);
- out_be32(mcrcst_port, JCSTWTxtbl[offset][idx]);
- out_be32(tdvhsel_port,
- (in_be32(tdvhsel_port) & ~TDVHSEL_MASTER) | JCACTSELtbl[offset][idx]);
- }
- out_be32(udenvt_port,
- JCTSStbl[offset][idx] << 16 | JCENVTtbl[offset][idx]);
-}
-
-unsigned long scc_mode_filter(struct ata_device *adev, unsigned long mask)
-{
- /* errata A308 workaround: limit ATAPI UDMA mode to UDMA4 */
- if (adev->class == ATA_DEV_ATAPI &&
- (mask & (0xE0 << ATA_SHIFT_UDMA))) {
- printk(KERN_INFO "%s: limit ATAPI UDMA to UDMA4\n", DRV_NAME);
- mask &= ~(0xE0 << ATA_SHIFT_UDMA);
- }
- return mask;
-}
-
-/**
- * scc_tf_load - send taskfile registers to host controller
- * @ap: Port to which output is sent
- * @tf: ATA taskfile register set
- *
- * Note: Original code is ata_sff_tf_load().
- */
-
-static void scc_tf_load (struct ata_port *ap, const struct ata_taskfile *tf)
-{
- struct ata_ioports *ioaddr = &ap->ioaddr;
- unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
-
- if (tf->ctl != ap->last_ctl) {
- out_be32(ioaddr->ctl_addr, tf->ctl);
- ap->last_ctl = tf->ctl;
- ata_wait_idle(ap);
- }
-
- if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
- out_be32(ioaddr->feature_addr, tf->hob_feature);
- out_be32(ioaddr->nsect_addr, tf->hob_nsect);
- out_be32(ioaddr->lbal_addr, tf->hob_lbal);
- out_be32(ioaddr->lbam_addr, tf->hob_lbam);
- out_be32(ioaddr->lbah_addr, tf->hob_lbah);
- VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",
- tf->hob_feature,
- tf->hob_nsect,
- tf->hob_lbal,
- tf->hob_lbam,
- tf->hob_lbah);
- }
-
- if (is_addr) {
- out_be32(ioaddr->feature_addr, tf->feature);
- out_be32(ioaddr->nsect_addr, tf->nsect);
- out_be32(ioaddr->lbal_addr, tf->lbal);
- out_be32(ioaddr->lbam_addr, tf->lbam);
- out_be32(ioaddr->lbah_addr, tf->lbah);
- VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",
- tf->feature,
- tf->nsect,
- tf->lbal,
- tf->lbam,
- tf->lbah);
- }
-
- if (tf->flags & ATA_TFLAG_DEVICE) {
- out_be32(ioaddr->device_addr, tf->device);
- VPRINTK("device 0x%X\n", tf->device);
- }
-
- ata_wait_idle(ap);
-}
-
-/**
- * scc_check_status - Read device status reg & clear interrupt
- * @ap: port where the device is
- *
- * Note: Original code is ata_check_status().
- */
-
-static u8 scc_check_status (struct ata_port *ap)
-{
- return in_be32(ap->ioaddr.status_addr);
-}
-
-/**
- * scc_tf_read - input device's ATA taskfile shadow registers
- * @ap: Port from which input is read
- * @tf: ATA taskfile register set for storing input
- *
- * Note: Original code is ata_sff_tf_read().
- */
-
-static void scc_tf_read (struct ata_port *ap, struct ata_taskfile *tf)
-{
- struct ata_ioports *ioaddr = &ap->ioaddr;
-
- tf->command = scc_check_status(ap);
- tf->feature = in_be32(ioaddr->error_addr);
- tf->nsect = in_be32(ioaddr->nsect_addr);
- tf->lbal = in_be32(ioaddr->lbal_addr);
- tf->lbam = in_be32(ioaddr->lbam_addr);
- tf->lbah = in_be32(ioaddr->lbah_addr);
- tf->device = in_be32(ioaddr->device_addr);
-
- if (tf->flags & ATA_TFLAG_LBA48) {
- out_be32(ioaddr->ctl_addr, tf->ctl | ATA_HOB);
- tf->hob_feature = in_be32(ioaddr->error_addr);
- tf->hob_nsect = in_be32(ioaddr->nsect_addr);
- tf->hob_lbal = in_be32(ioaddr->lbal_addr);
- tf->hob_lbam = in_be32(ioaddr->lbam_addr);
- tf->hob_lbah = in_be32(ioaddr->lbah_addr);
- out_be32(ioaddr->ctl_addr, tf->ctl);
- ap->last_ctl = tf->ctl;
- }
-}
-
-/**
- * scc_exec_command - issue ATA command to host controller
- * @ap: port to which command is being issued
- * @tf: ATA taskfile register set
- *
- * Note: Original code is ata_sff_exec_command().
- */
-
-static void scc_exec_command (struct ata_port *ap,
- const struct ata_taskfile *tf)
-{
- DPRINTK("ata%u: cmd 0x%X\n", ap->print_id, tf->command);
-
- out_be32(ap->ioaddr.command_addr, tf->command);
- ata_sff_pause(ap);
-}
-
-/**
- * scc_check_altstatus - Read device alternate status reg
- * @ap: port where the device is
- */
-
-static u8 scc_check_altstatus (struct ata_port *ap)
-{
- return in_be32(ap->ioaddr.altstatus_addr);
-}
-
-/**
- * scc_dev_select - Select device 0/1 on ATA bus
- * @ap: ATA channel to manipulate
- * @device: ATA device (numbered from zero) to select
- *
- * Note: Original code is ata_sff_dev_select().
- */
-
-static void scc_dev_select (struct ata_port *ap, unsigned int device)
-{
- u8 tmp;
-
- if (device == 0)
- tmp = ATA_DEVICE_OBS;
- else
- tmp = ATA_DEVICE_OBS | ATA_DEV1;
-
- out_be32(ap->ioaddr.device_addr, tmp);
- ata_sff_pause(ap);
-}
-
-/**
- * scc_set_devctl - Write device control reg
- * @ap: port where the device is
- * @ctl: value to write
- */
-
-static void scc_set_devctl(struct ata_port *ap, u8 ctl)
-{
- out_be32(ap->ioaddr.ctl_addr, ctl);
-}
-
-/**
- * scc_bmdma_setup - Set up PCI IDE BMDMA transaction
- * @qc: Info associated with this ATA transaction.
- *
- * Note: Original code is ata_bmdma_setup().
- */
-
-static void scc_bmdma_setup (struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
- unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
- u8 dmactl;
- void __iomem *mmio = ap->ioaddr.bmdma_addr;
-
- /* load PRD table addr */
- out_be32(mmio + SCC_DMA_TABLE_OFS, ap->bmdma_prd_dma);
-
- /* specify data direction, triple-check start bit is clear */
- dmactl = in_be32(mmio + SCC_DMA_CMD);
- dmactl &= ~(ATA_DMA_WR | ATA_DMA_START);
- if (!rw)
- dmactl |= ATA_DMA_WR;
- out_be32(mmio + SCC_DMA_CMD, dmactl);
-
- /* issue r/w command */
- ap->ops->sff_exec_command(ap, &qc->tf);
-}
-
-/**
- * scc_bmdma_start - Start a PCI IDE BMDMA transaction
- * @qc: Info associated with this ATA transaction.
- *
- * Note: Original code is ata_bmdma_start().
- */
-
-static void scc_bmdma_start (struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
- u8 dmactl;
- void __iomem *mmio = ap->ioaddr.bmdma_addr;
-
- /* start host DMA transaction */
- dmactl = in_be32(mmio + SCC_DMA_CMD);
- out_be32(mmio + SCC_DMA_CMD, dmactl | ATA_DMA_START);
-}
-
-/**
- * scc_devchk - PATA device presence detection
- * @ap: ATA channel to examine
- * @device: Device to examine (starting at zero)
- *
- * Note: Original code is ata_devchk().
- */
-
-static unsigned int scc_devchk (struct ata_port *ap,
- unsigned int device)
-{
- struct ata_ioports *ioaddr = &ap->ioaddr;
- u8 nsect, lbal;
-
- ap->ops->sff_dev_select(ap, device);
-
- out_be32(ioaddr->nsect_addr, 0x55);
- out_be32(ioaddr->lbal_addr, 0xaa);
-
- out_be32(ioaddr->nsect_addr, 0xaa);
- out_be32(ioaddr->lbal_addr, 0x55);
-
- out_be32(ioaddr->nsect_addr, 0x55);
- out_be32(ioaddr->lbal_addr, 0xaa);
-
- nsect = in_be32(ioaddr->nsect_addr);
- lbal = in_be32(ioaddr->lbal_addr);
-
- if ((nsect == 0x55) && (lbal == 0xaa))
- return 1; /* we found a device */
-
- return 0; /* nothing found */
-}
-
-/**
- * scc_wait_after_reset - wait for devices to become ready after reset
- *
- * Note: Original code is ata_sff_wait_after_reset
- */
-
-static int scc_wait_after_reset(struct ata_link *link, unsigned int devmask,
- unsigned long deadline)
-{
- struct ata_port *ap = link->ap;
- struct ata_ioports *ioaddr = &ap->ioaddr;
- unsigned int dev0 = devmask & (1 << 0);
- unsigned int dev1 = devmask & (1 << 1);
- int rc, ret = 0;
-
- /* Spec mandates ">= 2ms" before checking status. We wait
- * 150ms, because that was the magic delay used for ATAPI
- * devices in Hale Landis's ATADRVR, for the period of time
- * between when the ATA command register is written, and then
- * status is checked. Because waiting for "a while" before
- * checking status is fine, post SRST, we perform this magic
- * delay here as well.
- *
- * Old drivers/ide uses the 2mS rule and then waits for ready.
- */
- ata_msleep(ap, 150);
-
- /* always check readiness of the master device */
- rc = ata_sff_wait_ready(link, deadline);
- /* -ENODEV means the odd clown forgot the D7 pulldown resistor
- * and TF status is 0xff, bail out on it too.
- */
- if (rc)
- return rc;
-
- /* if device 1 was found in ata_devchk, wait for register
- * access briefly, then wait for BSY to clear.
- */
- if (dev1) {
- int i;
-
- ap->ops->sff_dev_select(ap, 1);
-
- /* Wait for register access. Some ATAPI devices fail
- * to set nsect/lbal after reset, so don't waste too
- * much time on it. We're gonna wait for !BSY anyway.
- */
- for (i = 0; i < 2; i++) {
- u8 nsect, lbal;
-
- nsect = in_be32(ioaddr->nsect_addr);
- lbal = in_be32(ioaddr->lbal_addr);
- if ((nsect == 1) && (lbal == 1))
- break;
- ata_msleep(ap, 50); /* give drive a breather */
- }
-
- rc = ata_sff_wait_ready(link, deadline);
- if (rc) {
- if (rc != -ENODEV)
- return rc;
- ret = rc;
- }
- }
-
- /* is all this really necessary? */
- ap->ops->sff_dev_select(ap, 0);
- if (dev1)
- ap->ops->sff_dev_select(ap, 1);
- if (dev0)
- ap->ops->sff_dev_select(ap, 0);
-
- return ret;
-}
-
-/**
- * scc_bus_softreset - PATA device software reset
- *
- * Note: Original code is ata_bus_softreset().
- */
-
-static int scc_bus_softreset(struct ata_port *ap, unsigned int devmask,
- unsigned long deadline)
-{
- struct ata_ioports *ioaddr = &ap->ioaddr;
-
- DPRINTK("ata%u: bus reset via SRST\n", ap->print_id);
-
- /* software reset. causes dev0 to be selected */
- out_be32(ioaddr->ctl_addr, ap->ctl);
- udelay(20);
- out_be32(ioaddr->ctl_addr, ap->ctl | ATA_SRST);
- udelay(20);
- out_be32(ioaddr->ctl_addr, ap->ctl);
-
- return scc_wait_after_reset(&ap->link, devmask, deadline);
-}
-
-/**
- * scc_softreset - reset host port via ATA SRST
- * @ap: port to reset
- * @classes: resulting classes of attached devices
- * @deadline: deadline jiffies for the operation
- *
- * Note: Original code is ata_sff_softreset().
- */
-
-static int scc_softreset(struct ata_link *link, unsigned int *classes,
- unsigned long deadline)
-{
- struct ata_port *ap = link->ap;
- unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
- unsigned int devmask = 0;
- int rc;
- u8 err;
-
- DPRINTK("ENTER\n");
-
- /* determine if device 0/1 are present */
- if (scc_devchk(ap, 0))
- devmask |= (1 << 0);
- if (slave_possible && scc_devchk(ap, 1))
- devmask |= (1 << 1);
-
- /* select device 0 again */
- ap->ops->sff_dev_select(ap, 0);
-
- /* issue bus reset */
- DPRINTK("about to softreset, devmask=%x\n", devmask);
- rc = scc_bus_softreset(ap, devmask, deadline);
- if (rc) {
- ata_port_err(ap, "SRST failed (err_mask=0x%x)\n", rc);
- return -EIO;
- }
-
- /* determine by signature whether we have ATA or ATAPI devices */
- classes[0] = ata_sff_dev_classify(&ap->link.device[0],
- devmask & (1 << 0), &err);
- if (slave_possible && err != 0x81)
- classes[1] = ata_sff_dev_classify(&ap->link.device[1],
- devmask & (1 << 1), &err);
-
- DPRINTK("EXIT, classes[0]=%u [1]=%u\n", classes[0], classes[1]);
- return 0;
-}
-
-/**
- * scc_bmdma_stop - Stop PCI IDE BMDMA transfer
- * @qc: Command we are ending DMA for
- */
-
-static void scc_bmdma_stop (struct ata_queued_cmd *qc)
-{
- struct ata_port *ap = qc->ap;
- void __iomem *ctrl_base = ap->host->iomap[SCC_CTRL_BAR];
- void __iomem *bmid_base = ap->host->iomap[SCC_BMID_BAR];
- u32 reg;
-
- while (1) {
- reg = in_be32(bmid_base + SCC_DMA_INTST);
-
- if (reg & INTSTS_SERROR) {
- printk(KERN_WARNING "%s: SERROR\n", DRV_NAME);
- out_be32(bmid_base + SCC_DMA_INTST, INTSTS_SERROR|INTSTS_BMSINT);
- out_be32(bmid_base + SCC_DMA_CMD,
- in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START);
- continue;
- }
-
- if (reg & INTSTS_PRERR) {
- u32 maea0, maec0;
- maea0 = in_be32(ctrl_base + SCC_CTL_MAEA0);
- maec0 = in_be32(ctrl_base + SCC_CTL_MAEC0);
- printk(KERN_WARNING "%s: PRERR [addr:%x cmd:%x]\n", DRV_NAME, maea0, maec0);
- out_be32(bmid_base + SCC_DMA_INTST, INTSTS_PRERR|INTSTS_BMSINT);
- out_be32(bmid_base + SCC_DMA_CMD,
- in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START);
- continue;
- }
-
- if (reg & INTSTS_RERR) {
- printk(KERN_WARNING "%s: Response Error\n", DRV_NAME);
- out_be32(bmid_base + SCC_DMA_INTST, INTSTS_RERR|INTSTS_BMSINT);
- out_be32(bmid_base + SCC_DMA_CMD,
- in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START);
- continue;
- }
-
- if (reg & INTSTS_ICERR) {
- out_be32(bmid_base + SCC_DMA_CMD,
- in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START);
- printk(KERN_WARNING "%s: Illegal Configuration\n", DRV_NAME);
- out_be32(bmid_base + SCC_DMA_INTST, INTSTS_ICERR|INTSTS_BMSINT);
- continue;
- }
-
- if (reg & INTSTS_BMSINT) {
- unsigned int classes;
- unsigned long deadline = ata_deadline(jiffies, ATA_TMOUT_BOOT);
- printk(KERN_WARNING "%s: Internal Bus Error\n", DRV_NAME);
- out_be32(bmid_base + SCC_DMA_INTST, INTSTS_BMSINT);
- /* TBD: SW reset */
- scc_softreset(&ap->link, &classes, deadline);
- continue;
- }
-
- if (reg & INTSTS_BMHE) {
- out_be32(bmid_base + SCC_DMA_INTST, INTSTS_BMHE);
- continue;
- }
-
- if (reg & INTSTS_ACTEINT) {
- out_be32(bmid_base + SCC_DMA_INTST, INTSTS_ACTEINT);
- continue;
- }
-
- if (reg & INTSTS_IOIRQS) {
- out_be32(bmid_base + SCC_DMA_INTST, INTSTS_IOIRQS);
- continue;
- }
- break;
- }
-
- /* clear start/stop bit */
- out_be32(bmid_base + SCC_DMA_CMD,
- in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START);
-
- /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */
- ata_sff_dma_pause(ap); /* dummy read */
-}
-
-/**
- * scc_bmdma_status - Read PCI IDE BMDMA status
- * @ap: Port associated with this ATA transaction.
- */
-
-static u8 scc_bmdma_status (struct ata_port *ap)
-{
- void __iomem *mmio = ap->ioaddr.bmdma_addr;
- u8 host_stat = in_be32(mmio + SCC_DMA_STATUS);
- u32 int_status = in_be32(mmio + SCC_DMA_INTST);
- struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->link.active_tag);
- static int retry = 0;
-
- /* return if IOS_SS is cleared */
- if (!(in_be32(mmio + SCC_DMA_CMD) & ATA_DMA_START))
- return host_stat;
-
- /* errata A252,A308 workaround: Step4 */
- if ((scc_check_altstatus(ap) & ATA_ERR)
- && (int_status & INTSTS_INTRQ))
- return (host_stat | ATA_DMA_INTR);
-
- /* errata A308 workaround Step5 */
- if (int_status & INTSTS_IOIRQS) {
- host_stat |= ATA_DMA_INTR;
-
- /* We don't check ATAPI DMA because it is limited to UDMA4 */
- if ((qc->tf.protocol == ATA_PROT_DMA &&
- qc->dev->xfer_mode > XFER_UDMA_4)) {
- if (!(int_status & INTSTS_ACTEINT)) {
- printk(KERN_WARNING "ata%u: operation failed (transfer data loss)\n",
- ap->print_id);
- host_stat |= ATA_DMA_ERR;
- if (retry++)
- ap->udma_mask &= ~(1 << qc->dev->xfer_mode);
- } else
- retry = 0;
- }
- }
-
- return host_stat;
-}
-
-/**
- * scc_data_xfer - Transfer data by PIO
- * @dev: device for this I/O
- * @buf: data buffer
- * @buflen: buffer length
- * @rw: read/write
- *
- * Note: Original code is ata_sff_data_xfer().
- */
-
-static unsigned int scc_data_xfer (struct ata_device *dev, unsigned char *buf,
- unsigned int buflen, int rw)
-{
- struct ata_port *ap = dev->link->ap;
- unsigned int words = buflen >> 1;
- unsigned int i;
- __le16 *buf16 = (__le16 *) buf;
- void __iomem *mmio = ap->ioaddr.data_addr;
-
- /* Transfer multiple of 2 bytes */
- if (rw == READ)
- for (i = 0; i < words; i++)
- buf16[i] = cpu_to_le16(in_be32(mmio));
- else
- for (i = 0; i < words; i++)
- out_be32(mmio, le16_to_cpu(buf16[i]));
-
- /* Transfer trailing 1 byte, if any. */
- if (unlikely(buflen & 0x01)) {
- __le16 align_buf[1] = { 0 };
- unsigned char *trailing_buf = buf + buflen - 1;
-
- if (rw == READ) {
- align_buf[0] = cpu_to_le16(in_be32(mmio));
- memcpy(trailing_buf, align_buf, 1);
- } else {
- memcpy(align_buf, trailing_buf, 1);
- out_be32(mmio, le16_to_cpu(align_buf[0]));
- }
- words++;
- }
-
- return words << 1;
-}
-
-/**
- * scc_postreset - standard postreset callback
- * @ap: the target ata_port
- * @classes: classes of attached devices
- *
- * Note: Original code is ata_sff_postreset().
- */
-
-static void scc_postreset(struct ata_link *link, unsigned int *classes)
-{
- struct ata_port *ap = link->ap;
-
- DPRINTK("ENTER\n");
-
- /* is double-select really necessary? */
- if (classes[0] != ATA_DEV_NONE)
- ap->ops->sff_dev_select(ap, 1);
- if (classes[1] != ATA_DEV_NONE)
- ap->ops->sff_dev_select(ap, 0);
-
- /* bail out if no device is present */
- if (classes[0] == ATA_DEV_NONE && classes[1] == ATA_DEV_NONE) {
- DPRINTK("EXIT, no device\n");
- return;
- }
-
- /* set up device control */
- out_be32(ap->ioaddr.ctl_addr, ap->ctl);
-
- DPRINTK("EXIT\n");
-}
-
-/**
- * scc_irq_clear - Clear PCI IDE BMDMA interrupt.
- * @ap: Port associated with this ATA transaction.
- *
- * Note: Original code is ata_bmdma_irq_clear().
- */
-
-static void scc_irq_clear (struct ata_port *ap)
-{
- void __iomem *mmio = ap->ioaddr.bmdma_addr;
-
- if (!mmio)
- return;
-
- out_be32(mmio + SCC_DMA_STATUS, in_be32(mmio + SCC_DMA_STATUS));
-}
-
-/**
- * scc_port_start - Set port up for dma.
- * @ap: Port to initialize
- *
- * Allocate space for PRD table using ata_bmdma_port_start().
- * Set PRD table address for PTERADD. (PRD Transfer End Read)
- */
-
-static int scc_port_start (struct ata_port *ap)
-{
- void __iomem *mmio = ap->ioaddr.bmdma_addr;
- int rc;
-
- rc = ata_bmdma_port_start(ap);
- if (rc)
- return rc;
-
- out_be32(mmio + SCC_DMA_PTERADD, ap->bmdma_prd_dma);
- return 0;
-}
-
-/**
- * scc_port_stop - Undo scc_port_start()
- * @ap: Port to shut down
- *
- * Reset PTERADD.
- */
-
-static void scc_port_stop (struct ata_port *ap)
-{
- void __iomem *mmio = ap->ioaddr.bmdma_addr;
-
- out_be32(mmio + SCC_DMA_PTERADD, 0);
-}
-
-static struct scsi_host_template scc_sht = {
- ATA_BMDMA_SHT(DRV_NAME),
-};
-
-static struct ata_port_operations scc_pata_ops = {
- .inherits = &ata_bmdma_port_ops,
-
- .set_piomode = scc_set_piomode,
- .set_dmamode = scc_set_dmamode,
- .mode_filter = scc_mode_filter,
-
- .sff_tf_load = scc_tf_load,
- .sff_tf_read = scc_tf_read,
- .sff_exec_command = scc_exec_command,
- .sff_check_status = scc_check_status,
- .sff_check_altstatus = scc_check_altstatus,
- .sff_dev_select = scc_dev_select,
- .sff_set_devctl = scc_set_devctl,
-
- .bmdma_setup = scc_bmdma_setup,
- .bmdma_start = scc_bmdma_start,
- .bmdma_stop = scc_bmdma_stop,
- .bmdma_status = scc_bmdma_status,
- .sff_data_xfer = scc_data_xfer,
-
- .cable_detect = ata_cable_80wire,
- .softreset = scc_softreset,
- .postreset = scc_postreset,
-
- .sff_irq_clear = scc_irq_clear,
-
- .port_start = scc_port_start,
- .port_stop = scc_port_stop,
-};
-
-static struct ata_port_info scc_port_info[] = {
- {
- .flags = ATA_FLAG_SLAVE_POSS,
- .pio_mask = ATA_PIO4,
- /* No MWDMA */
- .udma_mask = ATA_UDMA6,
- .port_ops = &scc_pata_ops,
- },
-};
-
-/**
- * scc_reset_controller - initialize SCC PATA controller.
- */
-
-static int scc_reset_controller(struct ata_host *host)
-{
- void __iomem *ctrl_base = host->iomap[SCC_CTRL_BAR];
- void __iomem *bmid_base = host->iomap[SCC_BMID_BAR];
- void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL;
- void __iomem *mode_port = ctrl_base + SCC_CTL_MODEREG;
- void __iomem *ecmode_port = ctrl_base + SCC_CTL_ECMODE;
- void __iomem *intmask_port = bmid_base + SCC_DMA_INTMASK;
- void __iomem *dmastatus_port = bmid_base + SCC_DMA_STATUS;
- u32 reg = 0;
-
- out_be32(cckctrl_port, reg);
- reg |= CCKCTRL_ATACLKOEN;
- out_be32(cckctrl_port, reg);
- reg |= CCKCTRL_LCLKEN | CCKCTRL_OCLKEN;
- out_be32(cckctrl_port, reg);
- reg |= CCKCTRL_CRST;
- out_be32(cckctrl_port, reg);
-
- for (;;) {
- reg = in_be32(cckctrl_port);
- if (reg & CCKCTRL_CRST)
- break;
- udelay(5000);
- }
-
- reg |= CCKCTRL_ATARESET;
- out_be32(cckctrl_port, reg);
- out_be32(ecmode_port, ECMODE_VALUE);
- out_be32(mode_port, MODE_JCUSFEN);
- out_be32(intmask_port, INTMASK_MSK);
-
- if (in_be32(dmastatus_port) & QCHSD_STPDIAG) {
- printk(KERN_WARNING "%s: failed to detect 80c cable. (PDIAG# is high)\n", DRV_NAME);
- return -EIO;
- }
-
- return 0;
-}
-
-/**
- * scc_setup_ports - initialize ioaddr with SCC PATA port offsets.
- * @ioaddr: IO address structure to be initialized
- * @base: base address of BMID region
- */
-
-static void scc_setup_ports (struct ata_ioports *ioaddr, void __iomem *base)
-{
- ioaddr->cmd_addr = base + SCC_REG_CMD_ADDR;
- ioaddr->altstatus_addr = ioaddr->cmd_addr + SCC_REG_ALTSTATUS;
- ioaddr->ctl_addr = ioaddr->cmd_addr + SCC_REG_ALTSTATUS;
- ioaddr->bmdma_addr = base;
- ioaddr->data_addr = ioaddr->cmd_addr + SCC_REG_DATA;
- ioaddr->error_addr = ioaddr->cmd_addr + SCC_REG_ERR;
- ioaddr->feature_addr = ioaddr->cmd_addr + SCC_REG_FEATURE;
- ioaddr->nsect_addr = ioaddr->cmd_addr + SCC_REG_NSECT;
- ioaddr->lbal_addr = ioaddr->cmd_addr + SCC_REG_LBAL;
- ioaddr->lbam_addr = ioaddr->cmd_addr + SCC_REG_LBAM;
- ioaddr->lbah_addr = ioaddr->cmd_addr + SCC_REG_LBAH;
- ioaddr->device_addr = ioaddr->cmd_addr + SCC_REG_DEVICE;
- ioaddr->status_addr = ioaddr->cmd_addr + SCC_REG_STATUS;
- ioaddr->command_addr = ioaddr->cmd_addr + SCC_REG_CMD;
-}
-
-static int scc_host_init(struct ata_host *host)
-{
- struct pci_dev *pdev = to_pci_dev(host->dev);
- int rc;
-
- rc = scc_reset_controller(host);
- if (rc)
- return rc;
-
- rc = dma_set_mask(&pdev->dev, ATA_DMA_MASK);
- if (rc)
- return rc;
- rc = dma_set_coherent_mask(&pdev->dev, ATA_DMA_MASK);
- if (rc)
- return rc;
-
- scc_setup_ports(&host->ports[0]->ioaddr, host->iomap[SCC_BMID_BAR]);
-
- pci_set_master(pdev);
-
- return 0;
-}
-
-/**
- * scc_init_one - Register SCC PATA device with kernel services
- * @pdev: PCI device to register
- * @ent: Entry in scc_pci_tbl matching with @pdev
- *
- * LOCKING:
- * Inherited from PCI layer (may sleep).
- *
- * RETURNS:
- * Zero on success, or -ERRNO value.
- */
-
-static int scc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
-{
- unsigned int board_idx = (unsigned int) ent->driver_data;
- const struct ata_port_info *ppi[] = { &scc_port_info[board_idx], NULL };
- struct ata_host *host;
- int rc;
-
- ata_print_version_once(&pdev->dev, DRV_VERSION);
-
- host = ata_host_alloc_pinfo(&pdev->dev, ppi, 1);
- if (!host)
- return -ENOMEM;
-
- rc = pcim_enable_device(pdev);
- if (rc)
- return rc;
-
- rc = pcim_iomap_regions(pdev, (1 << SCC_CTRL_BAR) | (1 << SCC_BMID_BAR), DRV_NAME);
- if (rc == -EBUSY)
- pcim_pin_device(pdev);
- if (rc)
- return rc;
- host->iomap = pcim_iomap_table(pdev);
-
- ata_port_pbar_desc(host->ports[0], SCC_CTRL_BAR, -1, "ctrl");
- ata_port_pbar_desc(host->ports[0], SCC_BMID_BAR, -1, "bmid");
-
- rc = scc_host_init(host);
- if (rc)
- return rc;
-
- return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
- IRQF_SHARED, &scc_sht);
-}
-
-static struct pci_driver scc_pci_driver = {
- .name = DRV_NAME,
- .id_table = scc_pci_tbl,
- .probe = scc_init_one,
- .remove = ata_pci_remove_one,
-#ifdef CONFIG_PM_SLEEP
- .suspend = ata_pci_device_suspend,
- .resume = ata_pci_device_resume,
-#endif
-};
-
-module_pci_driver(scc_pci_driver);
-
-MODULE_AUTHOR("Toshiba corp");
-MODULE_DESCRIPTION("SCSI low-level driver for Toshiba SCC PATA controller");
-MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(pci, scc_pci_tbl);
-MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c
index 9c2ba1c97c42..df0c66cb7ad3 100644
--- a/drivers/base/cacheinfo.c
+++ b/drivers/base/cacheinfo.c
@@ -179,7 +179,7 @@ static int detect_cache_attributes(unsigned int cpu)
{
int ret;
- if (init_cache_level(cpu))
+ if (init_cache_level(cpu) || !cache_leaves(cpu))
return -ENOENT;
per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu),
diff --git a/drivers/base/init.c b/drivers/base/init.c
index da033d3bab3c..48c0e220acc0 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/init.h>
#include <linux/memory.h>
+#include <linux/of.h>
#include "base.h"
@@ -34,4 +35,5 @@ void __init driver_init(void)
cpu_dev_init();
memory_dev_init();
container_dev_init();
+ of_core_init();
}
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 1cb8544598d5..f94a6ccfe787 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o
+obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
obj-$(CONFIG_PM_OPP) += opp.o
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index 7fdd0172605a..acef9f9f759a 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -15,6 +15,7 @@
#include <linux/clkdev.h>
#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/pm_runtime.h>
#ifdef CONFIG_PM
@@ -67,7 +68,8 @@ static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce)
} else {
clk_prepare(ce->clk);
ce->status = PCE_STATUS_ACQUIRED;
- dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
+ dev_dbg(dev, "Clock %pC con_id %s managed by runtime PM.\n",
+ ce->clk, ce->con_id);
}
}
@@ -93,7 +95,7 @@ static int __pm_clk_add(struct device *dev, const char *con_id,
return -ENOMEM;
}
} else {
- if (IS_ERR(ce->clk) || !__clk_get(clk)) {
+ if (IS_ERR(clk) || !__clk_get(clk)) {
kfree(ce);
return -ENOENT;
}
@@ -367,6 +369,43 @@ static int pm_clk_notify(struct notifier_block *nb,
return 0;
}
+int pm_clk_runtime_suspend(struct device *dev)
+{
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ ret = pm_generic_runtime_suspend(dev);
+ if (ret) {
+ dev_err(dev, "failed to suspend device\n");
+ return ret;
+ }
+
+ ret = pm_clk_suspend(dev);
+ if (ret) {
+ dev_err(dev, "failed to suspend clock\n");
+ pm_generic_runtime_resume(dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+int pm_clk_runtime_resume(struct device *dev)
+{
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ ret = pm_clk_resume(dev);
+ if (ret) {
+ dev_err(dev, "failed to resume clock\n");
+ return ret;
+ }
+
+ return pm_generic_runtime_resume(dev);
+}
+
#else /* !CONFIG_PM */
/**
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 2327613d4539..cdd547bd67df 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -181,7 +181,7 @@ static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd)
genpd->cpuidle_data->idle_state->exit_latency = usecs64;
}
-static int genpd_power_on(struct generic_pm_domain *genpd)
+static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)
{
ktime_t time_start;
s64 elapsed_ns;
@@ -190,6 +190,9 @@ static int genpd_power_on(struct generic_pm_domain *genpd)
if (!genpd->power_on)
return 0;
+ if (!timed)
+ return genpd->power_on(genpd);
+
time_start = ktime_get();
ret = genpd->power_on(genpd);
if (ret)
@@ -208,7 +211,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd)
return ret;
}
-static int genpd_power_off(struct generic_pm_domain *genpd)
+static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)
{
ktime_t time_start;
s64 elapsed_ns;
@@ -217,6 +220,9 @@ static int genpd_power_off(struct generic_pm_domain *genpd)
if (!genpd->power_off)
return 0;
+ if (!timed)
+ return genpd->power_off(genpd);
+
time_start = ktime_get();
ret = genpd->power_off(genpd);
if (ret == -EBUSY)
@@ -305,7 +311,7 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd)
}
}
- ret = genpd_power_on(genpd);
+ ret = genpd_power_on(genpd, true);
if (ret)
goto err;
@@ -615,7 +621,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
* the pm_genpd_poweron() restore power for us (this shouldn't
* happen very often).
*/
- ret = genpd_power_off(genpd);
+ ret = genpd_power_off(genpd, true);
if (ret == -EBUSY) {
genpd_set_active(genpd);
goto out;
@@ -827,6 +833,7 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
/**
* pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
* @genpd: PM domain to power off, if possible.
+ * @timed: True if latency measurements are allowed.
*
* Check if the given PM domain can be powered off (during system suspend or
* hibernation) and do that if so. Also, in that case propagate to its masters.
@@ -836,7 +843,8 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
* executed sequentially, so it is guaranteed that it will never run twice in
* parallel).
*/
-static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd,
+ bool timed)
{
struct gpd_link *link;
@@ -847,26 +855,28 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
|| atomic_read(&genpd->sd_count) > 0)
return;
- genpd_power_off(genpd);
+ genpd_power_off(genpd, timed);
genpd->status = GPD_STATE_POWER_OFF;
list_for_each_entry(link, &genpd->slave_links, slave_node) {
genpd_sd_counter_dec(link->master);
- pm_genpd_sync_poweroff(link->master);
+ pm_genpd_sync_poweroff(link->master, timed);
}
}
/**
* pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters.
* @genpd: PM domain to power on.
+ * @timed: True if latency measurements are allowed.
*
* This function is only called in "noirq" and "syscore" stages of system power
* transitions, so it need not acquire locks (all of the "noirq" callbacks are
* executed sequentially, so it is guaranteed that it will never run twice in
* parallel).
*/
-static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd)
+static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd,
+ bool timed)
{
struct gpd_link *link;
@@ -874,11 +884,11 @@ static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd)
return;
list_for_each_entry(link, &genpd->slave_links, slave_node) {
- pm_genpd_sync_poweron(link->master);
+ pm_genpd_sync_poweron(link->master, timed);
genpd_sd_counter_inc(link->master);
}
- genpd_power_on(genpd);
+ genpd_power_on(genpd, timed);
genpd->status = GPD_STATE_ACTIVE;
}
@@ -1056,7 +1066,7 @@ static int pm_genpd_suspend_noirq(struct device *dev)
* the same PM domain, so it is not necessary to use locking here.
*/
genpd->suspended_count++;
- pm_genpd_sync_poweroff(genpd);
+ pm_genpd_sync_poweroff(genpd, true);
return 0;
}
@@ -1086,7 +1096,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
* guaranteed that this function will never run twice in parallel for
* the same PM domain, so it is not necessary to use locking here.
*/
- pm_genpd_sync_poweron(genpd);
+ pm_genpd_sync_poweron(genpd, true);
genpd->suspended_count--;
return genpd_start_dev(genpd, dev);
@@ -1300,7 +1310,7 @@ static int pm_genpd_restore_noirq(struct device *dev)
* If the domain was off before the hibernation, make
* sure it will be off going forward.
*/
- genpd_power_off(genpd);
+ genpd_power_off(genpd, true);
return 0;
}
@@ -1309,7 +1319,7 @@ static int pm_genpd_restore_noirq(struct device *dev)
if (genpd->suspend_power_off)
return 0;
- pm_genpd_sync_poweron(genpd);
+ pm_genpd_sync_poweron(genpd, true);
return genpd_start_dev(genpd, dev);
}
@@ -1367,9 +1377,9 @@ static void genpd_syscore_switch(struct device *dev, bool suspend)
if (suspend) {
genpd->suspended_count++;
- pm_genpd_sync_poweroff(genpd);
+ pm_genpd_sync_poweroff(genpd, false);
} else {
- pm_genpd_sync_poweron(genpd);
+ pm_genpd_sync_poweron(genpd, false);
genpd->suspended_count--;
}
}
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 3d874eca7104..30b7bbfdc558 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -24,6 +24,7 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/pm-trace.h>
+#include <linux/pm_wakeirq.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/async.h>
@@ -587,6 +588,7 @@ void dpm_resume_noirq(pm_message_t state)
async_synchronize_full();
dpm_show_time(starttime, state, "noirq");
resume_device_irqs();
+ device_wakeup_disarm_wake_irqs();
cpuidle_resume();
trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
}
@@ -920,9 +922,7 @@ static void device_complete(struct device *dev, pm_message_t state)
if (callback) {
pm_dev_dbg(dev, state, info);
- trace_device_pm_callback_start(dev, info, state.event);
callback(dev);
- trace_device_pm_callback_end(dev, 0);
}
device_unlock(dev);
@@ -954,7 +954,9 @@ void dpm_complete(pm_message_t state)
list_move(&dev->power.entry, &list);
mutex_unlock(&dpm_list_mtx);
+ trace_device_pm_callback_start(dev, "", state.event);
device_complete(dev, state);
+ trace_device_pm_callback_end(dev, 0);
mutex_lock(&dpm_list_mtx);
put_device(dev);
@@ -1104,6 +1106,7 @@ int dpm_suspend_noirq(pm_message_t state)
trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
cpuidle_pause();
+ device_wakeup_arm_wake_irqs();
suspend_device_irqs();
mutex_lock(&dpm_list_mtx);
pm_transition = state;
@@ -1585,11 +1588,8 @@ static int device_prepare(struct device *dev, pm_message_t state)
callback = dev->driver->pm->prepare;
}
- if (callback) {
- trace_device_pm_callback_start(dev, info, state.event);
+ if (callback)
ret = callback(dev);
- trace_device_pm_callback_end(dev, ret);
- }
device_unlock(dev);
@@ -1631,7 +1631,9 @@ int dpm_prepare(pm_message_t state)
get_device(dev);
mutex_unlock(&dpm_list_mtx);
+ trace_device_pm_callback_start(dev, "", state.event);
error = device_prepare(dev, state);
+ trace_device_pm_callback_end(dev, error);
mutex_lock(&dpm_list_mtx);
if (error) {
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index b6b8a273c5da..f1a5d95e7b20 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -20,6 +20,46 @@ static inline void pm_runtime_early_init(struct device *dev)
extern void pm_runtime_init(struct device *dev);
extern void pm_runtime_remove(struct device *dev);
+struct wake_irq {
+ struct device *dev;
+ int irq;
+ bool dedicated_irq:1;
+};
+
+extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
+extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
+
+#ifdef CONFIG_PM_SLEEP
+
+extern int device_wakeup_attach_irq(struct device *dev,
+ struct wake_irq *wakeirq);
+extern void device_wakeup_detach_irq(struct device *dev);
+extern void device_wakeup_arm_wake_irqs(void);
+extern void device_wakeup_disarm_wake_irqs(void);
+
+#else
+
+static inline int
+device_wakeup_attach_irq(struct device *dev,
+ struct wake_irq *wakeirq)
+{
+ return 0;
+}
+
+static inline void device_wakeup_detach_irq(struct device *dev)
+{
+}
+
+static inline void device_wakeup_arm_wake_irqs(void)
+{
+}
+
+static inline void device_wakeup_disarm_wake_irqs(void)
+{
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
/*
* sysfs.c
*/
@@ -52,6 +92,14 @@ static inline void wakeup_sysfs_remove(struct device *dev) {}
static inline int pm_qos_sysfs_add(struct device *dev) { return 0; }
static inline void pm_qos_sysfs_remove(struct device *dev) {}
+static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+}
+
#endif
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 5070c4fe8542..e1a10a03df8e 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -10,6 +10,7 @@
#include <linux/sched.h>
#include <linux/export.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
#include <trace/events/rpm.h>
#include "power.h"
@@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
callback = RPM_GET_CALLBACK(dev, runtime_suspend);
+ dev_pm_enable_wake_irq(dev);
retval = rpm_callback(callback, dev);
if (retval)
goto fail;
@@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
return retval;
fail:
+ dev_pm_disable_wake_irq(dev);
__update_runtime_status(dev, RPM_ACTIVE);
dev->power.deferred_resume = false;
wake_up_all(&dev->power.wait_queue);
@@ -734,13 +737,16 @@ static int rpm_resume(struct device *dev, int rpmflags)
callback = RPM_GET_CALLBACK(dev, runtime_resume);
+ dev_pm_disable_wake_irq(dev);
retval = rpm_callback(callback, dev);
if (retval) {
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_cancel_pending(dev);
+ dev_pm_enable_wake_irq(dev);
} else {
no_callback:
__update_runtime_status(dev, RPM_ACTIVE);
+ pm_runtime_mark_last_busy(dev);
if (parent)
atomic_inc(&parent->power.child_count);
}
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
new file mode 100644
index 000000000000..7470004ca810
--- /dev/null
+++ b/drivers/base/power/wakeirq.c
@@ -0,0 +1,273 @@
+/*
+ * wakeirq.c - Device wakeirq helper functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
+
+#include "power.h"
+
+/**
+ * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
+ * @dev: Device entry
+ * @irq: Device wake-up capable interrupt
+ * @wirq: Wake irq specific data
+ *
+ * Internal function to attach either a device IO interrupt or a
+ * dedicated wake-up interrupt as a wake IRQ.
+ */
+static int dev_pm_attach_wake_irq(struct device *dev, int irq,
+ struct wake_irq *wirq)
+{
+ unsigned long flags;
+ int err;
+
+ if (!dev || !wirq)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->power.lock, flags);
+ if (dev_WARN_ONCE(dev, dev->power.wakeirq,
+ "wake irq already initialized\n")) {
+ spin_unlock_irqrestore(&dev->power.lock, flags);
+ return -EEXIST;
+ }
+
+ dev->power.wakeirq = wirq;
+ spin_unlock_irqrestore(&dev->power.lock, flags);
+
+ err = device_wakeup_attach_irq(dev, wirq);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/**
+ * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
+ * @dev: Device entry
+ * @irq: Device IO interrupt
+ *
+ * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
+ * automatically configured for wake-up from suspend based
+ * on the device specific sysfs wakeup entry. Typically called
+ * during driver probe after calling device_init_wakeup().
+ */
+int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+ struct wake_irq *wirq;
+ int err;
+
+ wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+ if (!wirq)
+ return -ENOMEM;
+
+ wirq->dev = dev;
+ wirq->irq = irq;
+
+ err = dev_pm_attach_wake_irq(dev, irq, wirq);
+ if (err)
+ kfree(wirq);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
+
+/**
+ * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ
+ * @dev: Device entry
+ *
+ * Detach a device wake IRQ and free resources.
+ *
+ * Note that it's OK for drivers to call this without calling
+ * dev_pm_set_wake_irq() as all the driver instances may not have
+ * a wake IRQ configured. This avoid adding wake IRQ specific
+ * checks into the drivers.
+ */
+void dev_pm_clear_wake_irq(struct device *dev)
+{
+ struct wake_irq *wirq = dev->power.wakeirq;
+ unsigned long flags;
+
+ if (!wirq)
+ return;
+
+ spin_lock_irqsave(&dev->power.lock, flags);
+ dev->power.wakeirq = NULL;
+ spin_unlock_irqrestore(&dev->power.lock, flags);
+
+ device_wakeup_detach_irq(dev);
+ if (wirq->dedicated_irq)
+ free_irq(wirq->irq, wirq);
+ kfree(wirq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
+
+/**
+ * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
+ * @irq: Device specific dedicated wake-up interrupt
+ * @_wirq: Wake IRQ data
+ *
+ * Some devices have a separate wake-up interrupt in addition to the
+ * device IO interrupt. The wake-up interrupt signals that a device
+ * should be woken up from it's idle state. This handler uses device
+ * specific pm_runtime functions to wake the device, and then it's
+ * up to the device to do whatever it needs to. Note that as the
+ * device may need to restore context and start up regulators, we
+ * use a threaded IRQ.
+ *
+ * Also note that we are not resending the lost device interrupts.
+ * We assume that the wake-up interrupt just needs to wake-up the
+ * device, and then device's pm_runtime_resume() can deal with the
+ * situation.
+ */
+static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
+{
+ struct wake_irq *wirq = _wirq;
+ int res;
+
+ /* We don't want RPM_ASYNC or RPM_NOWAIT here */
+ res = pm_runtime_resume(wirq->dev);
+ if (res < 0)
+ dev_warn(wirq->dev,
+ "wake IRQ with no resume: %i\n", res);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt
+ * @dev: Device entry
+ * @irq: Device wake-up interrupt
+ *
+ * Unless your hardware has separate wake-up interrupts in addition
+ * to the device IO interrupts, you don't need this.
+ *
+ * Sets up a threaded interrupt handler for a device that has
+ * a dedicated wake-up interrupt in addition to the device IO
+ * interrupt.
+ *
+ * The interrupt starts disabled, and needs to be managed for
+ * the device by the bus code or the device driver using
+ * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq()
+ * functions.
+ */
+int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
+{
+ struct wake_irq *wirq;
+ int err;
+
+ wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+ if (!wirq)
+ return -ENOMEM;
+
+ wirq->dev = dev;
+ wirq->irq = irq;
+ wirq->dedicated_irq = true;
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+ /*
+ * Consumer device may need to power up and restore state
+ * so we use a threaded irq.
+ */
+ err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq,
+ IRQF_ONESHOT, dev_name(dev), wirq);
+ if (err)
+ goto err_free;
+
+ err = dev_pm_attach_wake_irq(dev, irq, wirq);
+ if (err)
+ goto err_free_irq;
+
+ return err;
+
+err_free_irq:
+ free_irq(irq, wirq);
+err_free:
+ kfree(wirq);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
+
+/**
+ * dev_pm_enable_wake_irq - Enable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_suspend() to enable the wake-up interrupt while
+ * the device is running.
+ *
+ * Note that for runtime_suspend()) the wake-up interrupts
+ * should be unconditionally enabled unlike for suspend()
+ * that is conditional.
+ */
+void dev_pm_enable_wake_irq(struct device *dev)
+{
+ struct wake_irq *wirq = dev->power.wakeirq;
+
+ if (wirq && wirq->dedicated_irq)
+ enable_irq(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
+
+/**
+ * dev_pm_disable_wake_irq - Disable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_resume() to disable the wake-up interrupt while
+ * the device is running.
+ */
+void dev_pm_disable_wake_irq(struct device *dev)
+{
+ struct wake_irq *wirq = dev->power.wakeirq;
+
+ if (wirq && wirq->dedicated_irq)
+ disable_irq_nosync(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
+
+/**
+ * dev_pm_arm_wake_irq - Arm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Sets up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+ if (!wirq)
+ return;
+
+ if (device_may_wakeup(wirq->dev))
+ enable_irq_wake(wirq->irq);
+}
+
+/**
+ * dev_pm_disarm_wake_irq - Disarm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Clears up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+ if (!wirq)
+ return;
+
+ if (device_may_wakeup(wirq->dev))
+ disable_irq_wake(wirq->irq);
+}
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 77262009f89d..40f71603378c 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -14,6 +14,7 @@
#include <linux/suspend.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
+#include <linux/pm_wakeirq.h>
#include <trace/events/power.h>
#include "power.h"
@@ -56,6 +57,11 @@ static LIST_HEAD(wakeup_sources);
static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue);
+static struct wakeup_source deleted_ws = {
+ .name = "deleted",
+ .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock),
+};
+
/**
* wakeup_source_prepare - Prepare a new wakeup source for initialization.
* @ws: Wakeup source to prepare.
@@ -107,6 +113,34 @@ void wakeup_source_drop(struct wakeup_source *ws)
}
EXPORT_SYMBOL_GPL(wakeup_source_drop);
+/*
+ * Record wakeup_source statistics being deleted into a dummy wakeup_source.
+ */
+static void wakeup_source_record(struct wakeup_source *ws)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&deleted_ws.lock, flags);
+
+ if (ws->event_count) {
+ deleted_ws.total_time =
+ ktime_add(deleted_ws.total_time, ws->total_time);
+ deleted_ws.prevent_sleep_time =
+ ktime_add(deleted_ws.prevent_sleep_time,
+ ws->prevent_sleep_time);
+ deleted_ws.max_time =
+ ktime_compare(deleted_ws.max_time, ws->max_time) > 0 ?
+ deleted_ws.max_time : ws->max_time;
+ deleted_ws.event_count += ws->event_count;
+ deleted_ws.active_count += ws->active_count;
+ deleted_ws.relax_count += ws->relax_count;
+ deleted_ws.expire_count += ws->expire_count;
+ deleted_ws.wakeup_count += ws->wakeup_count;
+ }
+
+ spin_unlock_irqrestore(&deleted_ws.lock, flags);
+}
+
/**
* wakeup_source_destroy - Destroy a struct wakeup_source object.
* @ws: Wakeup source to destroy.
@@ -119,6 +153,7 @@ void wakeup_source_destroy(struct wakeup_source *ws)
return;
wakeup_source_drop(ws);
+ wakeup_source_record(ws);
kfree(ws->name);
kfree(ws);
}
@@ -239,6 +274,97 @@ int device_wakeup_enable(struct device *dev)
EXPORT_SYMBOL_GPL(device_wakeup_enable);
/**
+ * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source
+ * @dev: Device to handle
+ * @wakeirq: Device specific wakeirq entry
+ *
+ * Attach a device wakeirq to the wakeup source so the device
+ * wake IRQ can be configured automatically for suspend and
+ * resume.
+ */
+int device_wakeup_attach_irq(struct device *dev,
+ struct wake_irq *wakeirq)
+{
+ struct wakeup_source *ws;
+ int ret = 0;
+
+ spin_lock_irq(&dev->power.lock);
+ ws = dev->power.wakeup;
+ if (!ws) {
+ dev_err(dev, "forgot to call call device_init_wakeup?\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (ws->wakeirq) {
+ ret = -EEXIST;
+ goto unlock;
+ }
+
+ ws->wakeirq = wakeirq;
+
+unlock:
+ spin_unlock_irq(&dev->power.lock);
+
+ return ret;
+}
+
+/**
+ * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source
+ * @dev: Device to handle
+ *
+ * Removes a device wakeirq from the wakeup source.
+ */
+void device_wakeup_detach_irq(struct device *dev)
+{
+ struct wakeup_source *ws;
+
+ spin_lock_irq(&dev->power.lock);
+ ws = dev->power.wakeup;
+ if (!ws)
+ goto unlock;
+
+ ws->wakeirq = NULL;
+
+unlock:
+ spin_unlock_irq(&dev->power.lock);
+}
+
+/**
+ * device_wakeup_arm_wake_irqs(void)
+ *
+ * Itereates over the list of device wakeirqs to arm them.
+ */
+void device_wakeup_arm_wake_irqs(void)
+{
+ struct wakeup_source *ws;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+ if (ws->wakeirq)
+ dev_pm_arm_wake_irq(ws->wakeirq);
+ }
+ rcu_read_unlock();
+}
+
+/**
+ * device_wakeup_disarm_wake_irqs(void)
+ *
+ * Itereates over the list of device wakeirqs to disarm them.
+ */
+void device_wakeup_disarm_wake_irqs(void)
+{
+ struct wakeup_source *ws;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+ if (ws->wakeirq)
+ dev_pm_disarm_wake_irq(ws->wakeirq);
+ }
+ rcu_read_unlock();
+}
+
+/**
* device_wakeup_detach - Detach a device's wakeup source object from it.
* @dev: Device to detach the wakeup source object from.
*
@@ -351,6 +477,20 @@ int device_set_wakeup_enable(struct device *dev, bool enable)
}
EXPORT_SYMBOL_GPL(device_set_wakeup_enable);
+/**
+ * wakeup_source_not_registered - validate the given wakeup source.
+ * @ws: Wakeup source to be validated.
+ */
+static bool wakeup_source_not_registered(struct wakeup_source *ws)
+{
+ /*
+ * Use timer struct to check if the given source is initialized
+ * by wakeup_source_add.
+ */
+ return ws->timer.function != pm_wakeup_timer_fn ||
+ ws->timer.data != (unsigned long)ws;
+}
+
/*
* The functions below use the observation that each wakeup event starts a
* period in which the system should not be suspended. The moment this period
@@ -391,6 +531,10 @@ static void wakeup_source_activate(struct wakeup_source *ws)
{
unsigned int cec;
+ if (WARN_ONCE(wakeup_source_not_registered(ws),
+ "unregistered wakeup source\n"))
+ return;
+
/*
* active wakeup source should bring the system
* out of PM_SUSPEND_FREEZE state
@@ -894,6 +1038,8 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused)
print_wakeup_source_stats(m, ws);
rcu_read_unlock();
+ print_wakeup_source_stats(m, &deleted_ws);
+
return 0;
}
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 1d0b116cae95..e645852396ba 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -14,6 +14,7 @@
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/property.h>
/**
@@ -519,3 +520,16 @@ unsigned int device_get_child_node_count(struct device *dev)
return count;
}
EXPORT_SYMBOL_GPL(device_get_child_node_count);
+
+bool device_dma_is_coherent(struct device *dev)
+{
+ bool coherent = false;
+
+ if (IS_ENABLED(CONFIG_OF) && dev->of_node)
+ coherent = of_dma_is_coherent(dev->of_node);
+ else
+ acpi_check_dma(ACPI_COMPANION(dev), &coherent);
+
+ return coherent;
+}
+EXPORT_SYMBOL_GPL(device_dma_is_coherent);
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index a13587b5c2be..b2b2849fc6d3 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -131,7 +131,10 @@ struct regmap {
struct reg_default *reg_defaults;
const void *reg_defaults_raw;
void *cache;
+ /* if set, the cache contains newer data than the HW */
u32 cache_dirty;
+ /* if set, the HW registers are known to match map->reg_defaults */
+ bool no_sync_defaults;
struct reg_default *patch;
int patch_regs;
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 7eb7b3b98794..b9862d741a56 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -249,6 +249,22 @@ int regcache_write(struct regmap *map,
return 0;
}
+static bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
+ unsigned int val)
+{
+ int ret;
+
+ /* If we don't know the chip just got reset, then sync everything. */
+ if (!map->no_sync_defaults)
+ return true;
+
+ /* Is this the hardware default? If so skip. */
+ ret = regcache_lookup_reg(map, reg);
+ if (ret >= 0 && val == map->reg_defaults[ret].def)
+ return false;
+ return true;
+}
+
static int regcache_default_sync(struct regmap *map, unsigned int min,
unsigned int max)
{
@@ -266,9 +282,7 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,
if (ret)
return ret;
- /* Is this the hardware default? If so skip. */
- ret = regcache_lookup_reg(map, reg);
- if (ret >= 0 && val == map->reg_defaults[ret].def)
+ if (!regcache_reg_needs_sync(map, reg, val))
continue;
map->cache_bypass = 1;
@@ -342,6 +356,7 @@ out:
/* Restore the bypass state */
map->async = false;
map->cache_bypass = bypass;
+ map->no_sync_defaults = false;
map->unlock(map->lock_arg);
regmap_async_complete(map);
@@ -397,6 +412,7 @@ out:
/* Restore the bypass state */
map->cache_bypass = bypass;
map->async = false;
+ map->no_sync_defaults = false;
map->unlock(map->lock_arg);
regmap_async_complete(map);
@@ -461,18 +477,23 @@ void regcache_cache_only(struct regmap *map, bool enable)
EXPORT_SYMBOL_GPL(regcache_cache_only);
/**
- * regcache_mark_dirty: Mark the register cache as dirty
+ * regcache_mark_dirty: Indicate that HW registers were reset to default values
*
* @map: map to mark
*
- * Mark the register cache as dirty, for example due to the device
- * having been powered down for suspend. If the cache is not marked
- * as dirty then the cache sync will be suppressed.
+ * Inform regcache that the device has been powered down or reset, so that
+ * on resume, regcache_sync() knows to write out all non-default values
+ * stored in the cache.
+ *
+ * If this function is not called, regcache_sync() will assume that
+ * the hardware state still matches the cache state, modulo any writes that
+ * happened when cache_only was true.
*/
void regcache_mark_dirty(struct regmap *map)
{
map->lock(map->lock_arg);
map->cache_dirty = true;
+ map->no_sync_defaults = true;
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_mark_dirty);
@@ -613,10 +634,7 @@ static int regcache_sync_block_single(struct regmap *map, void *block,
continue;
val = regcache_get_val(map, block, i);
-
- /* Is this the hardware default? If so skip. */
- ret = regcache_lookup_reg(map, regtmp);
- if (ret >= 0 && val == map->reg_defaults[ret].def)
+ if (!regcache_reg_needs_sync(map, regtmp, val))
continue;
map->cache_bypass = 1;
@@ -688,10 +706,7 @@ static int regcache_sync_block_raw(struct regmap *map, void *block,
}
val = regcache_get_val(map, block, i);
-
- /* Is this the hardware default? If so skip. */
- ret = regcache_lookup_reg(map, regtmp);
- if (ret >= 0 && val == map->reg_defaults[ret].def) {
+ if (!regcache_reg_needs_sync(map, regtmp, val)) {
ret = regcache_sync_block_raw_flush(map, &data,
base, regtmp);
if (ret != 0)
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index a6c3f75b4b01..2597600a5d26 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -109,7 +109,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
if (!d->chip->init_ack_masked)
continue;
/*
- * Ack all the masked interrupts uncondictionly,
+ * Ack all the masked interrupts unconditionally,
* OR if there is masked interrupt which hasn't been Acked,
* it'll be ignored in irq handler, then may introduce irq storm
*/
@@ -306,19 +306,12 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
irq_set_chip_data(virq, data);
irq_set_chip(virq, &data->irq_chip);
irq_set_nested_thread(virq, 1);
-
- /* ARM needs us to explicitly flag the IRQ as valid
- * and will set them noprobe when we do so. */
-#ifdef CONFIG_ARM
- set_irq_flags(virq, IRQF_VALID);
-#else
irq_set_noprobe(virq);
-#endif
return 0;
}
-static struct irq_domain_ops regmap_domain_ops = {
+static const struct irq_domain_ops regmap_domain_ops = {
.map = regmap_irq_map,
.xlate = irq_domain_xlate_twocell,
};
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 6273ff072f3e..7111d04f2621 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -945,11 +945,10 @@ EXPORT_SYMBOL_GPL(devm_regmap_init);
static void regmap_field_init(struct regmap_field *rm_field,
struct regmap *regmap, struct reg_field reg_field)
{
- int field_bits = reg_field.msb - reg_field.lsb + 1;
rm_field->regmap = regmap;
rm_field->reg = reg_field.reg;
rm_field->shift = reg_field.lsb;
- rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb);
+ rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb);
rm_field->id_size = reg_field.id_size;
rm_field->id_offset = reg_field.id_offset;
}
@@ -2318,7 +2317,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
&ival);
if (ret != 0)
return ret;
- memcpy(val + (i * val_bytes), &ival, val_bytes);
+ map->format.format_val(val + (i * val_bytes), ival, 0);
}
}
@@ -2583,10 +2582,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
map->async = true;
ret = _regmap_multi_reg_write(map, regs, num_regs);
- if (ret != 0)
- goto out;
-out:
map->async = false;
map->cache_bypass = bypass;
@@ -2613,6 +2609,30 @@ int regmap_get_val_bytes(struct regmap *map)
}
EXPORT_SYMBOL_GPL(regmap_get_val_bytes);
+/**
+ * regmap_get_max_register(): Report the max register value
+ *
+ * Report the max register value, mainly intended to for use by
+ * generic infrastructure built on top of regmap.
+ */
+int regmap_get_max_register(struct regmap *map)
+{
+ return map->max_register ? map->max_register : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(regmap_get_max_register);
+
+/**
+ * regmap_get_reg_stride(): Report the register address stride
+ *
+ * Report the register address stride, mainly intended to for use by
+ * generic infrastructure built on top of regmap.
+ */
+int regmap_get_reg_stride(struct regmap *map)
+{
+ return map->reg_stride;
+}
+EXPORT_SYMBOL_GPL(regmap_get_reg_stride);
+
int regmap_parse_val(struct regmap *map, const void *buf,
unsigned int *val)
{
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index 6491f45200a7..8b7d7f8e5851 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -61,7 +61,7 @@ static DEVICE_ATTR_RO(physical_package_id);
define_id_show_func(core_id);
static DEVICE_ATTR_RO(core_id);
-define_siblings_show_func(thread_siblings, thread_cpumask);
+define_siblings_show_func(thread_siblings, sibling_cpumask);
static DEVICE_ATTR_RO(thread_siblings);
static DEVICE_ATTR_RO(thread_siblings_list);
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index eb1fed5bd516..3ccef9eba6f9 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -406,6 +406,7 @@ config BLK_DEV_RAM_DAX
config BLK_DEV_PMEM
tristate "Persistent memory block device support"
+ depends on HAS_IOMEM
help
Saying Y here will allow you to use a contiguous range of reserved
memory as one or more persistent block devices.
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index ff20f192b0f6..0422c47261c3 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -139,8 +139,6 @@ static struct board_type products[] = {
{0x3214103C, "Smart Array E200i", &SA5_access},
{0x3215103C, "Smart Array E200i", &SA5_access},
{0x3237103C, "Smart Array E500", &SA5_access},
- {0x3223103C, "Smart Array P800", &SA5_access},
- {0x3234103C, "Smart Array P400", &SA5_access},
{0x323D103C, "Smart Array P700m", &SA5_access},
};
@@ -574,8 +572,6 @@ static void cciss_procinit(ctlr_info_t *h)
/* List of controllers which cannot be hard reset on kexec with reset_devices */
static u32 unresettable_controller[] = {
- 0x324a103C, /* Smart Array P712m */
- 0x324b103C, /* SmartArray P711m */
0x3223103C, /* Smart Array P800 */
0x3234103C, /* Smart Array P400 */
0x3235103C, /* Smart Array P400i */
@@ -586,12 +582,32 @@ static u32 unresettable_controller[] = {
0x3215103C, /* Smart Array E200i */
0x3237103C, /* Smart Array E500 */
0x323D103C, /* Smart Array P700m */
+ 0x40800E11, /* Smart Array 5i */
0x409C0E11, /* Smart Array 6400 */
0x409D0E11, /* Smart Array 6400 EM */
+ 0x40700E11, /* Smart Array 5300 */
+ 0x40820E11, /* Smart Array 532 */
+ 0x40830E11, /* Smart Array 5312 */
+ 0x409A0E11, /* Smart Array 641 */
+ 0x409B0E11, /* Smart Array 642 */
+ 0x40910E11, /* Smart Array 6i */
};
/* List of controllers which cannot even be soft reset */
static u32 soft_unresettable_controller[] = {
+ 0x40800E11, /* Smart Array 5i */
+ 0x40700E11, /* Smart Array 5300 */
+ 0x40820E11, /* Smart Array 532 */
+ 0x40830E11, /* Smart Array 5312 */
+ 0x409A0E11, /* Smart Array 641 */
+ 0x409B0E11, /* Smart Array 642 */
+ 0x40910E11, /* Smart Array 6i */
+ /* Exclude 640x boards. These are two pci devices in one slot
+ * which share a battery backed cache module. One controls the
+ * cache, the other accesses the cache through the one that controls
+ * it. If we reset the one controlling the cache, the other will
+ * likely not be happy. Just forbid resetting this conjoined mess.
+ */
0x409C0E11, /* Smart Array 6400 */
0x409D0E11, /* Smart Array 6400 EM */
};
@@ -4667,8 +4683,7 @@ static int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
*/
cciss_lookup_board_id(pdev, &board_id);
if (!ctlr_is_resettable(board_id)) {
- dev_warn(&pdev->dev, "Cannot reset Smart Array 640x "
- "due to shared cache module.");
+ dev_warn(&pdev->dev, "Controller not resettable\n");
return -ENODEV;
}
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index ecd845cd28d8..1537302e56e3 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -84,7 +84,6 @@ static struct scsi_host_template cciss_driver_template = {
.show_info = cciss_scsi_show_info,
.queuecommand = cciss_scsi_queue_command,
.this_id = 7,
- .cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
/* Can't have eh_bus_reset_handler or eh_host_reset_handler for cciss */
.eh_device_reset_handler= cciss_eh_device_reset_handler,
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index ae3fcb4199e9..d7173cb1ea76 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1620,8 +1620,8 @@ out:
static void loop_remove(struct loop_device *lo)
{
- del_gendisk(lo->lo_disk);
blk_cleanup_queue(lo->lo_queue);
+ del_gendisk(lo->lo_disk);
blk_mq_free_tag_set(&lo->tag_set);
put_disk(lo->lo_disk);
kfree(lo);
diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c
index 85b8036deaa3..683dff272562 100644
--- a/drivers/block/nvme-core.c
+++ b/drivers/block/nvme-core.c
@@ -1750,6 +1750,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
struct nvme_iod *iod;
dma_addr_t meta_dma = 0;
void *meta = NULL;
+ void __user *metadata;
if (copy_from_user(&io, uio, sizeof(io)))
return -EFAULT;
@@ -1763,6 +1764,8 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
meta_len = 0;
}
+ metadata = (void __user *)(unsigned long)io.metadata;
+
write = io.opcode & 1;
switch (io.opcode) {
@@ -1786,13 +1789,13 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
if (meta_len) {
meta = dma_alloc_coherent(&dev->pci_dev->dev, meta_len,
&meta_dma, GFP_KERNEL);
+
if (!meta) {
status = -ENOMEM;
goto unmap;
}
if (write) {
- if (copy_from_user(meta, (void __user *)io.metadata,
- meta_len)) {
+ if (copy_from_user(meta, metadata, meta_len)) {
status = -EFAULT;
goto unmap;
}
@@ -1819,8 +1822,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
nvme_free_iod(dev, iod);
if (meta) {
if (status == NVME_SC_SUCCESS && !write) {
- if (copy_to_user((void __user *)io.metadata, meta,
- meta_len))
+ if (copy_to_user(metadata, meta, meta_len))
status = -EFAULT;
}
dma_free_coherent(&dev->pci_dev->dev, meta_len, meta, meta_dma);
diff --git a/drivers/block/nvme-scsi.c b/drivers/block/nvme-scsi.c
index 6b736b00f63e..44f2514fb775 100644
--- a/drivers/block/nvme-scsi.c
+++ b/drivers/block/nvme-scsi.c
@@ -944,7 +944,8 @@ static int nvme_trans_ext_inq_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
static int nvme_trans_bdev_limits_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *inq_response, int alloc_len)
{
- __be32 max_sectors = cpu_to_be32(queue_max_hw_sectors(ns->queue));
+ __be32 max_sectors = cpu_to_be32(
+ nvme_block_nr(ns, queue_max_hw_sectors(ns->queue)));
__be32 max_discard = cpu_to_be32(ns->queue->limits.max_discard_sectors);
__be32 discard_desc_count = cpu_to_be32(0x100);
@@ -2256,7 +2257,8 @@ static int nvme_trans_inquiry(struct nvme_ns *ns, struct sg_io_hdr *hdr,
page_code = GET_INQ_PAGE_CODE(cmd);
alloc_len = GET_INQ_ALLOC_LENGTH(cmd);
- inq_response = kmalloc(alloc_len, GFP_KERNEL);
+ inq_response = kmalloc(max(alloc_len, STANDARD_INQUIRY_LENGTH),
+ GFP_KERNEL);
if (inq_response == NULL) {
res = -ENOMEM;
goto out_mem;
diff --git a/drivers/block/pmem.c b/drivers/block/pmem.c
index eabf4a8d0085..095dfaadcaa5 100644
--- a/drivers/block/pmem.c
+++ b/drivers/block/pmem.c
@@ -139,11 +139,11 @@ static struct pmem_device *pmem_alloc(struct device *dev, struct resource *res)
}
/*
- * Map the memory as non-cachable, as we can't write back the contents
+ * Map the memory as write-through, as we can't write back the contents
* of the CPU caches in case of a crash.
*/
err = -ENOMEM;
- pmem->virt_addr = ioremap_nocache(pmem->phys_addr, pmem->size);
+ pmem->virt_addr = ioremap_wt(pmem->phys_addr, pmem->size);
if (!pmem->virt_addr)
goto out_release_region;
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 812523330a78..ec6c5c6e1ac9 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -2264,6 +2264,11 @@ static bool rbd_img_obj_end_request(struct rbd_obj_request *obj_request)
result, xferred);
if (!img_request->result)
img_request->result = result;
+ /*
+ * Need to end I/O on the entire obj_request worth of
+ * bytes in case of error.
+ */
+ xferred = obj_request->length;
}
/* Image object requests don't own their page array */
diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c
index bd2b3bbbb22c..713fc9ff1149 100644
--- a/drivers/block/xen-blkback/blkback.c
+++ b/drivers/block/xen-blkback/blkback.c
@@ -265,17 +265,6 @@ static void put_persistent_gnt(struct xen_blkif *blkif,
atomic_dec(&blkif->persistent_gnt_in_use);
}
-static void free_persistent_gnts_unmap_callback(int result,
- struct gntab_unmap_queue_data *data)
-{
- struct completion *c = data->data;
-
- /* BUG_ON used to reproduce existing behaviour,
- but is this the best way to deal with this? */
- BUG_ON(result);
- complete(c);
-}
-
static void free_persistent_gnts(struct xen_blkif *blkif, struct rb_root *root,
unsigned int num)
{
@@ -285,12 +274,7 @@ static void free_persistent_gnts(struct xen_blkif *blkif, struct rb_root *root,
struct rb_node *n;
int segs_to_unmap = 0;
struct gntab_unmap_queue_data unmap_data;
- struct completion unmap_completion;
- init_completion(&unmap_completion);
-
- unmap_data.data = &unmap_completion;
- unmap_data.done = &free_persistent_gnts_unmap_callback;
unmap_data.pages = pages;
unmap_data.unmap_ops = unmap;
unmap_data.kunmap_ops = NULL;
@@ -310,8 +294,7 @@ static void free_persistent_gnts(struct xen_blkif *blkif, struct rb_root *root,
!rb_next(&persistent_gnt->node)) {
unmap_data.count = segs_to_unmap;
- gnttab_unmap_refs_async(&unmap_data);
- wait_for_completion(&unmap_completion);
+ BUG_ON(gnttab_unmap_refs_sync(&unmap_data));
put_free_pages(blkif, pages, segs_to_unmap);
segs_to_unmap = 0;
@@ -329,8 +312,13 @@ void xen_blkbk_unmap_purged_grants(struct work_struct *work)
struct gnttab_unmap_grant_ref unmap[BLKIF_MAX_SEGMENTS_PER_REQUEST];
struct page *pages[BLKIF_MAX_SEGMENTS_PER_REQUEST];
struct persistent_gnt *persistent_gnt;
- int ret, segs_to_unmap = 0;
+ int segs_to_unmap = 0;
struct xen_blkif *blkif = container_of(work, typeof(*blkif), persistent_purge_work);
+ struct gntab_unmap_queue_data unmap_data;
+
+ unmap_data.pages = pages;
+ unmap_data.unmap_ops = unmap;
+ unmap_data.kunmap_ops = NULL;
while(!list_empty(&blkif->persistent_purge_list)) {
persistent_gnt = list_first_entry(&blkif->persistent_purge_list,
@@ -346,17 +334,16 @@ void xen_blkbk_unmap_purged_grants(struct work_struct *work)
pages[segs_to_unmap] = persistent_gnt->page;
if (++segs_to_unmap == BLKIF_MAX_SEGMENTS_PER_REQUEST) {
- ret = gnttab_unmap_refs(unmap, NULL, pages,
- segs_to_unmap);
- BUG_ON(ret);
+ unmap_data.count = segs_to_unmap;
+ BUG_ON(gnttab_unmap_refs_sync(&unmap_data));
put_free_pages(blkif, pages, segs_to_unmap);
segs_to_unmap = 0;
}
kfree(persistent_gnt);
}
if (segs_to_unmap > 0) {
- ret = gnttab_unmap_refs(unmap, NULL, pages, segs_to_unmap);
- BUG_ON(ret);
+ unmap_data.count = segs_to_unmap;
+ BUG_ON(gnttab_unmap_refs_sync(&unmap_data));
put_free_pages(blkif, pages, segs_to_unmap);
}
}
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index c94386aa563d..6e134f4759c0 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -74,6 +74,27 @@ static inline struct zram *dev_to_zram(struct device *dev)
return (struct zram *)dev_to_disk(dev)->private_data;
}
+static ssize_t compact_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long nr_migrated;
+ struct zram *zram = dev_to_zram(dev);
+ struct zram_meta *meta;
+
+ down_read(&zram->init_lock);
+ if (!init_done(zram)) {
+ up_read(&zram->init_lock);
+ return -EINVAL;
+ }
+
+ meta = zram->meta;
+ nr_migrated = zs_compact(meta->mem_pool);
+ atomic64_add(nr_migrated, &zram->stats.num_migrated);
+ up_read(&zram->init_lock);
+
+ return len;
+}
+
static ssize_t disksize_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -784,7 +805,9 @@ static void zram_reset_device(struct zram *zram)
memset(&zram->stats, 0, sizeof(zram->stats));
zram->disksize = 0;
zram->max_comp_streams = 1;
+
set_capacity(zram->disk, 0);
+ part_stat_set_all(&zram->disk->part0, 0);
up_write(&zram->init_lock);
/* I/O operation under all of CPU are done so let's free */
@@ -1038,6 +1061,7 @@ static const struct block_device_operations zram_devops = {
.owner = THIS_MODULE
};
+static DEVICE_ATTR_WO(compact);
static DEVICE_ATTR_RW(disksize);
static DEVICE_ATTR_RO(initstate);
static DEVICE_ATTR_WO(reset);
@@ -1114,6 +1138,7 @@ static struct attribute *zram_disk_attrs[] = {
&dev_attr_num_writes.attr,
&dev_attr_failed_reads.attr,
&dev_attr_failed_writes.attr,
+ &dev_attr_compact.attr,
&dev_attr_invalid_io.attr,
&dev_attr_notify_free.attr,
&dev_attr_zero_pages.attr,
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 288547a3c566..8c81af6dbe06 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -88,6 +88,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x04CA, 0x3007) },
{ USB_DEVICE(0x04CA, 0x3008) },
{ USB_DEVICE(0x04CA, 0x300b) },
+ { USB_DEVICE(0x04CA, 0x300f) },
{ USB_DEVICE(0x04CA, 0x3010) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0930, 0x0220) },
@@ -104,6 +105,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x0cf3, 0xe003) },
{ USB_DEVICE(0x0CF3, 0xE004) },
{ USB_DEVICE(0x0CF3, 0xE005) },
+ { USB_DEVICE(0x0CF3, 0xE006) },
{ USB_DEVICE(0x13d3, 0x3362) },
{ USB_DEVICE(0x13d3, 0x3375) },
{ USB_DEVICE(0x13d3, 0x3393) },
@@ -143,6 +145,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
@@ -158,6 +161,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 4f7e8d400bc0..6de97b3871b0 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -227,7 +227,6 @@ static void bt3c_receive(struct bt3c_info *info)
iobase = info->p_dev->resource[0]->start;
avail = bt3c_read(iobase, 0x7006);
- //printk("bt3c_cs: receiving %d bytes\n", avail);
bt3c_address(iobase, 0x7480);
while (size < avail) {
@@ -250,7 +249,6 @@ static void bt3c_receive(struct bt3c_info *info)
bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
inb(iobase + DATA_H);
- //printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type);
switch (bt_cb(info->rx_skb)->pkt_type) {
@@ -364,7 +362,6 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
if (stat & 0x0001)
bt3c_receive(info);
if (stat & 0x0002) {
- //BT_ERR("Ack (stat=0x%04x)", stat);
clear_bit(XMIT_SENDING, &(info->tx_state));
bt3c_write_wakeup(info);
}
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
index d0741f3ed7ec..4bba86677adc 100644
--- a/drivers/bluetooth/btbcm.c
+++ b/drivers/bluetooth/btbcm.c
@@ -95,6 +95,78 @@ int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
}
EXPORT_SYMBOL_GPL(btbcm_set_bdaddr);
+int btbcm_patchram(struct hci_dev *hdev, const char *firmware)
+{
+ const struct hci_command_hdr *cmd;
+ const struct firmware *fw;
+ const u8 *fw_ptr;
+ size_t fw_size;
+ struct sk_buff *skb;
+ u16 opcode;
+ int err;
+
+ err = request_firmware(&fw, firmware, &hdev->dev);
+ if (err < 0) {
+ BT_INFO("%s: BCM: Patch %s not found", hdev->name, firmware);
+ return err;
+ }
+
+ /* Start Download */
+ skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ BT_ERR("%s: BCM: Download Minidrv command failed (%d)",
+ hdev->name, err);
+ goto done;
+ }
+ kfree_skb(skb);
+
+ /* 50 msec delay after Download Minidrv completes */
+ msleep(50);
+
+ fw_ptr = fw->data;
+ fw_size = fw->size;
+
+ while (fw_size >= sizeof(*cmd)) {
+ const u8 *cmd_param;
+
+ cmd = (struct hci_command_hdr *)fw_ptr;
+ fw_ptr += sizeof(*cmd);
+ fw_size -= sizeof(*cmd);
+
+ if (fw_size < cmd->plen) {
+ BT_ERR("%s: BCM: Patch %s is corrupted", hdev->name,
+ firmware);
+ err = -EINVAL;
+ goto done;
+ }
+
+ cmd_param = fw_ptr;
+ fw_ptr += cmd->plen;
+ fw_size -= cmd->plen;
+
+ opcode = le16_to_cpu(cmd->opcode);
+
+ skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ BT_ERR("%s: BCM: Patch command %04x failed (%d)",
+ hdev->name, opcode, err);
+ goto done;
+ }
+ kfree_skb(skb);
+ }
+
+ /* 250 msec delay after Launch Ram completes */
+ msleep(250);
+
+done:
+ release_firmware(fw);
+ return err;
+}
+EXPORT_SYMBOL(btbcm_patchram);
+
static int btbcm_reset(struct hci_dev *hdev)
{
struct sk_buff *skb;
@@ -198,12 +270,8 @@ static const struct {
int btbcm_setup_patchram(struct hci_dev *hdev)
{
- const struct hci_command_hdr *cmd;
- const struct firmware *fw;
- const u8 *fw_ptr;
- size_t fw_size;
char fw_name[64];
- u16 opcode, subver, rev, pid, vid;
+ u16 subver, rev, pid, vid;
const char *hw_name = NULL;
struct sk_buff *skb;
struct hci_rp_read_local_version *ver;
@@ -273,74 +341,19 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
hw_name ? : "BCM", (subver & 0x7000) >> 13,
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
- err = request_firmware(&fw, fw_name, &hdev->dev);
- if (err < 0) {
- BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name);
+ err = btbcm_patchram(hdev, fw_name);
+ if (err == -ENOENT)
return 0;
- }
-
- /* Start Download */
- skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: BCM: Download Minidrv command failed (%d)",
- hdev->name, err);
- goto reset;
- }
- kfree_skb(skb);
-
- /* 50 msec delay after Download Minidrv completes */
- msleep(50);
-
- fw_ptr = fw->data;
- fw_size = fw->size;
-
- while (fw_size >= sizeof(*cmd)) {
- const u8 *cmd_param;
-
- cmd = (struct hci_command_hdr *)fw_ptr;
- fw_ptr += sizeof(*cmd);
- fw_size -= sizeof(*cmd);
-
- if (fw_size < cmd->plen) {
- BT_ERR("%s: BCM: patch %s is corrupted", hdev->name,
- fw_name);
- err = -EINVAL;
- goto reset;
- }
- cmd_param = fw_ptr;
- fw_ptr += cmd->plen;
- fw_size -= cmd->plen;
-
- opcode = le16_to_cpu(cmd->opcode);
-
- skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: BCM: patch command %04x failed (%d)",
- hdev->name, opcode, err);
- goto reset;
- }
- kfree_skb(skb);
- }
-
- /* 250 msec delay after Launch Ram completes */
- msleep(250);
-
-reset:
/* Reset */
err = btbcm_reset(hdev);
if (err)
- goto done;
+ return err;
/* Read Local Version Info */
skb = btbcm_read_local_version(hdev);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- goto done;
- }
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
ver = (struct hci_rp_read_local_version *)skb->data;
rev = le16_to_cpu(ver->hci_rev);
@@ -355,10 +368,7 @@ reset:
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
-done:
- release_firmware(fw);
-
- return err;
+ return 0;
}
EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h
index 34268ae3eb46..eb6ab5f9483d 100644
--- a/drivers/bluetooth/btbcm.h
+++ b/drivers/bluetooth/btbcm.h
@@ -25,6 +25,7 @@
int btbcm_check_bdaddr(struct hci_dev *hdev);
int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
+int btbcm_patchram(struct hci_dev *hdev, const char *firmware);
int btbcm_setup_patchram(struct hci_dev *hdev);
int btbcm_setup_apple(struct hci_dev *hdev);
@@ -41,6 +42,11 @@ static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
return -EOPNOTSUPP;
}
+static inline int btbcm_patchram(struct hci_dev *hdev, const char *firmware)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int btbcm_setup_patchram(struct hci_dev *hdev)
{
return 0;
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index de7b236eeae7..3c10d4dfe9a7 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/firmware.h>
+#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -57,6 +58,7 @@ static struct usb_driver btusb_driver;
#define BTUSB_AMP 0x4000
#define BTUSB_QCA_ROME 0x8000
#define BTUSB_BCM_APPLE 0x10000
+#define BTUSB_REALTEK 0x20000
static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
@@ -184,6 +186,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
@@ -200,6 +203,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
@@ -216,6 +220,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
/* QCA ROME chipset */
+ { USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME },
@@ -288,6 +293,28 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(0x8087, 0xe0, 0x01, 0x01),
.driver_info = BTUSB_IGNORE },
+ /* Realtek Bluetooth devices */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
+ .driver_info = BTUSB_REALTEK },
+
+ /* Additional Realtek 8723AE Bluetooth devices */
+ { USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK },
+
+ /* Additional Realtek 8723BE Bluetooth devices */
+ { USB_DEVICE(0x0489, 0xe085), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x0489, 0xe08b), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3410), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3416), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK },
+
+ /* Additional Realtek 8821AE Bluetooth devices */
+ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3458), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3461), .driver_info = BTUSB_REALTEK },
+ { USB_DEVICE(0x13d3, 0x3462), .driver_info = BTUSB_REALTEK },
+
{ } /* Terminating entry */
};
@@ -892,7 +919,7 @@ static int btusb_open(struct hci_dev *hdev)
*/
if (data->setup_on_usb) {
err = data->setup_on_usb(hdev);
- if (err <0)
+ if (err < 0)
return err;
}
@@ -1345,6 +1372,378 @@ static int btusb_setup_csr(struct hci_dev *hdev)
return ret;
}
+#define RTL_FRAG_LEN 252
+
+struct rtl_download_cmd {
+ __u8 index;
+ __u8 data[RTL_FRAG_LEN];
+} __packed;
+
+struct rtl_download_response {
+ __u8 status;
+ __u8 index;
+} __packed;
+
+struct rtl_rom_version_evt {
+ __u8 status;
+ __u8 version;
+} __packed;
+
+struct rtl_epatch_header {
+ __u8 signature[8];
+ __le32 fw_version;
+ __le16 num_patches;
+} __packed;
+
+#define RTL_EPATCH_SIGNATURE "Realtech"
+#define RTL_ROM_LMP_3499 0x3499
+#define RTL_ROM_LMP_8723A 0x1200
+#define RTL_ROM_LMP_8723B 0x8723
+#define RTL_ROM_LMP_8821A 0x8821
+#define RTL_ROM_LMP_8761A 0x8761
+
+static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
+{
+ struct rtl_rom_version_evt *rom_version;
+ struct sk_buff *skb;
+ int ret;
+
+ /* Read RTL ROM version command */
+ skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: Read ROM version failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+
+ if (skb->len != sizeof(*rom_version)) {
+ BT_ERR("%s: RTL version event length mismatch", hdev->name);
+ kfree_skb(skb);
+ return -EIO;
+ }
+
+ rom_version = (struct rtl_rom_version_evt *)skb->data;
+ BT_INFO("%s: rom_version status=%x version=%x",
+ hdev->name, rom_version->status, rom_version->version);
+
+ ret = rom_version->status;
+ if (ret == 0)
+ *version = rom_version->version;
+
+ kfree_skb(skb);
+ return ret;
+}
+
+static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
+ const struct firmware *fw,
+ unsigned char **_buf)
+{
+ const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
+ struct rtl_epatch_header *epatch_info;
+ unsigned char *buf;
+ int i, ret, len;
+ size_t min_size;
+ u8 opcode, length, data, rom_version = 0;
+ int project_id = -1;
+ const unsigned char *fwptr, *chip_id_base;
+ const unsigned char *patch_length_base, *patch_offset_base;
+ u32 patch_offset = 0;
+ u16 patch_length, num_patches;
+ const u16 project_id_to_lmp_subver[] = {
+ RTL_ROM_LMP_8723A,
+ RTL_ROM_LMP_8723B,
+ RTL_ROM_LMP_8821A,
+ RTL_ROM_LMP_8761A
+ };
+
+ ret = rtl_read_rom_version(hdev, &rom_version);
+ if (ret)
+ return -bt_to_errno(ret);
+
+ min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
+ if (fw->size < min_size)
+ return -EINVAL;
+
+ fwptr = fw->data + fw->size - sizeof(extension_sig);
+ if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) {
+ BT_ERR("%s: extension section signature mismatch", hdev->name);
+ return -EINVAL;
+ }
+
+ /* Loop from the end of the firmware parsing instructions, until
+ * we find an instruction that identifies the "project ID" for the
+ * hardware supported by this firwmare file.
+ * Once we have that, we double-check that that project_id is suitable
+ * for the hardware we are working with.
+ */
+ while (fwptr >= fw->data + (sizeof(struct rtl_epatch_header) + 3)) {
+ opcode = *--fwptr;
+ length = *--fwptr;
+ data = *--fwptr;
+
+ BT_DBG("check op=%x len=%x data=%x", opcode, length, data);
+
+ if (opcode == 0xff) /* EOF */
+ break;
+
+ if (length == 0) {
+ BT_ERR("%s: found instruction with length 0",
+ hdev->name);
+ return -EINVAL;
+ }
+
+ if (opcode == 0 && length == 1) {
+ project_id = data;
+ break;
+ }
+
+ fwptr -= length;
+ }
+
+ if (project_id < 0) {
+ BT_ERR("%s: failed to find version instruction", hdev->name);
+ return -EINVAL;
+ }
+
+ if (project_id >= ARRAY_SIZE(project_id_to_lmp_subver)) {
+ BT_ERR("%s: unknown project id %d", hdev->name, project_id);
+ return -EINVAL;
+ }
+
+ if (lmp_subver != project_id_to_lmp_subver[project_id]) {
+ BT_ERR("%s: firmware is for %x but this is a %x", hdev->name,
+ project_id_to_lmp_subver[project_id], lmp_subver);
+ return -EINVAL;
+ }
+
+ epatch_info = (struct rtl_epatch_header *)fw->data;
+ if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) {
+ BT_ERR("%s: bad EPATCH signature", hdev->name);
+ return -EINVAL;
+ }
+
+ num_patches = le16_to_cpu(epatch_info->num_patches);
+ BT_DBG("fw_version=%x, num_patches=%d",
+ le32_to_cpu(epatch_info->fw_version), num_patches);
+
+ /* After the rtl_epatch_header there is a funky patch metadata section.
+ * Assuming 2 patches, the layout is:
+ * ChipID1 ChipID2 PatchLength1 PatchLength2 PatchOffset1 PatchOffset2
+ *
+ * Find the right patch for this chip.
+ */
+ min_size += 8 * num_patches;
+ if (fw->size < min_size)
+ return -EINVAL;
+
+ chip_id_base = fw->data + sizeof(struct rtl_epatch_header);
+ patch_length_base = chip_id_base + (sizeof(u16) * num_patches);
+ patch_offset_base = patch_length_base + (sizeof(u16) * num_patches);
+ for (i = 0; i < num_patches; i++) {
+ u16 chip_id = get_unaligned_le16(chip_id_base +
+ (i * sizeof(u16)));
+ if (chip_id == rom_version + 1) {
+ patch_length = get_unaligned_le16(patch_length_base +
+ (i * sizeof(u16)));
+ patch_offset = get_unaligned_le32(patch_offset_base +
+ (i * sizeof(u32)));
+ break;
+ }
+ }
+
+ if (!patch_offset) {
+ BT_ERR("%s: didn't find patch for chip id %d",
+ hdev->name, rom_version);
+ return -EINVAL;
+ }
+
+ BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
+ min_size = patch_offset + patch_length;
+ if (fw->size < min_size)
+ return -EINVAL;
+
+ /* Copy the firmware into a new buffer and write the version at
+ * the end.
+ */
+ len = patch_length;
+ buf = kmemdup(fw->data + patch_offset, patch_length, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
+
+ *_buf = buf;
+ return len;
+}
+
+static int rtl_download_firmware(struct hci_dev *hdev,
+ const unsigned char *data, int fw_len)
+{
+ struct rtl_download_cmd *dl_cmd;
+ int frag_num = fw_len / RTL_FRAG_LEN + 1;
+ int frag_len = RTL_FRAG_LEN;
+ int ret = 0;
+ int i;
+
+ dl_cmd = kmalloc(sizeof(struct rtl_download_cmd), GFP_KERNEL);
+ if (!dl_cmd)
+ return -ENOMEM;
+
+ for (i = 0; i < frag_num; i++) {
+ struct rtl_download_response *dl_resp;
+ struct sk_buff *skb;
+
+ BT_DBG("download fw (%d/%d)", i, frag_num);
+
+ dl_cmd->index = i;
+ if (i == (frag_num - 1)) {
+ dl_cmd->index |= 0x80; /* data end */
+ frag_len = fw_len % RTL_FRAG_LEN;
+ }
+ memcpy(dl_cmd->data, data, frag_len);
+
+ /* Send download command */
+ skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: download fw command failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ ret = -PTR_ERR(skb);
+ goto out;
+ }
+
+ if (skb->len != sizeof(*dl_resp)) {
+ BT_ERR("%s: download fw event length mismatch",
+ hdev->name);
+ kfree_skb(skb);
+ ret = -EIO;
+ goto out;
+ }
+
+ dl_resp = (struct rtl_download_response *)skb->data;
+ if (dl_resp->status != 0) {
+ kfree_skb(skb);
+ ret = bt_to_errno(dl_resp->status);
+ goto out;
+ }
+
+ kfree_skb(skb);
+ data += RTL_FRAG_LEN;
+ }
+
+out:
+ kfree(dl_cmd);
+ return ret;
+}
+
+static int btusb_setup_rtl8723a(struct hci_dev *hdev)
+{
+ struct btusb_data *data = dev_get_drvdata(&hdev->dev);
+ struct usb_device *udev = interface_to_usbdev(data->intf);
+ const struct firmware *fw;
+ int ret;
+
+ BT_INFO("%s: rtl: loading rtl_bt/rtl8723a_fw.bin", hdev->name);
+ ret = request_firmware(&fw, "rtl_bt/rtl8723a_fw.bin", &udev->dev);
+ if (ret < 0) {
+ BT_ERR("%s: Failed to load rtl_bt/rtl8723a_fw.bin", hdev->name);
+ return ret;
+ }
+
+ if (fw->size < 8) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Check that the firmware doesn't have the epatch signature
+ * (which is only for RTL8723B and newer).
+ */
+ if (!memcmp(fw->data, RTL_EPATCH_SIGNATURE, 8)) {
+ BT_ERR("%s: unexpected EPATCH signature!", hdev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = rtl_download_firmware(hdev, fw->data, fw->size);
+
+out:
+ release_firmware(fw);
+ return ret;
+}
+
+static int btusb_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
+ const char *fw_name)
+{
+ struct btusb_data *data = dev_get_drvdata(&hdev->dev);
+ struct usb_device *udev = interface_to_usbdev(data->intf);
+ unsigned char *fw_data = NULL;
+ const struct firmware *fw;
+ int ret;
+
+ BT_INFO("%s: rtl: loading %s", hdev->name, fw_name);
+ ret = request_firmware(&fw, fw_name, &udev->dev);
+ if (ret < 0) {
+ BT_ERR("%s: Failed to load %s", hdev->name, fw_name);
+ return ret;
+ }
+
+ ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data);
+ if (ret < 0)
+ goto out;
+
+ ret = rtl_download_firmware(hdev, fw_data, ret);
+ kfree(fw_data);
+ if (ret < 0)
+ goto out;
+
+out:
+ release_firmware(fw);
+ return ret;
+}
+
+static int btusb_setup_realtek(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ struct hci_rp_read_local_version *resp;
+ u16 lmp_subver;
+
+ skb = btusb_read_local_version(hdev);
+ if (IS_ERR(skb))
+ return -PTR_ERR(skb);
+
+ resp = (struct hci_rp_read_local_version *)skb->data;
+ BT_INFO("%s: rtl: examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
+ "lmp_subver=%04x", hdev->name, resp->hci_ver, resp->hci_rev,
+ resp->lmp_ver, resp->lmp_subver);
+
+ lmp_subver = le16_to_cpu(resp->lmp_subver);
+ kfree_skb(skb);
+
+ /* Match a set of subver values that correspond to stock firmware,
+ * which is not compatible with standard btusb.
+ * If matched, upload an alternative firmware that does conform to
+ * standard btusb. Once that firmware is uploaded, the subver changes
+ * to a different value.
+ */
+ switch (lmp_subver) {
+ case RTL_ROM_LMP_8723A:
+ case RTL_ROM_LMP_3499:
+ return btusb_setup_rtl8723a(hdev);
+ case RTL_ROM_LMP_8723B:
+ return btusb_setup_rtl8723b(hdev, lmp_subver,
+ "rtl_bt/rtl8723b_fw.bin");
+ case RTL_ROM_LMP_8821A:
+ return btusb_setup_rtl8723b(hdev, lmp_subver,
+ "rtl_bt/rtl8821a_fw.bin");
+ case RTL_ROM_LMP_8761A:
+ return btusb_setup_rtl8723b(hdev, lmp_subver,
+ "rtl_bt/rtl8761a_fw.bin");
+ default:
+ BT_INFO("rtl: assuming no firmware upload needed.");
+ return 0;
+ }
+}
+
static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
struct intel_version *ver)
{
@@ -2577,7 +2976,7 @@ static int btusb_setup_qca(struct hci_dev *hdev)
int i, err;
err = btusb_qca_send_vendor_req(hdev, QCA_GET_TARGET_VERSION, &ver,
- sizeof(ver));
+ sizeof(ver));
if (err < 0)
return err;
@@ -2776,6 +3175,9 @@ static int btusb_probe(struct usb_interface *intf,
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
}
+ if (id->driver_info & BTUSB_REALTEK)
+ hdev->setup = btusb_setup_realtek;
+
if (id->driver_info & BTUSB_AMP) {
/* AMP controllers do not support SCO packets */
data->isoc = NULL;
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index 1b3f8647ea2f..ec8fa0e0f036 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -95,7 +95,6 @@ static void ath_hci_uart_work(struct work_struct *work)
hci_uart_tx_wakeup(hu);
}
-/* Initialize protocol */
static int ath_open(struct hci_uart *hu)
{
struct ath_struct *ath;
@@ -116,8 +115,7 @@ static int ath_open(struct hci_uart *hu)
return 0;
}
-/* Flush protocol data */
-static int ath_flush(struct hci_uart *hu)
+static int ath_close(struct hci_uart *hu)
{
struct ath_struct *ath = hu->priv;
@@ -125,11 +123,17 @@ static int ath_flush(struct hci_uart *hu)
skb_queue_purge(&ath->txq);
+ kfree_skb(ath->rx_skb);
+
+ cancel_work_sync(&ath->ctxtsw);
+
+ hu->priv = NULL;
+ kfree(ath);
+
return 0;
}
-/* Close protocol */
-static int ath_close(struct hci_uart *hu)
+static int ath_flush(struct hci_uart *hu)
{
struct ath_struct *ath = hu->priv;
@@ -137,19 +141,65 @@ static int ath_close(struct hci_uart *hu)
skb_queue_purge(&ath->txq);
- kfree_skb(ath->rx_skb);
+ return 0;
+}
- cancel_work_sync(&ath->ctxtsw);
+static int ath_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ struct sk_buff *skb;
+ u8 buf[10];
+ int err;
+
+ buf[0] = 0x01;
+ buf[1] = 0x01;
+ buf[2] = 0x00;
+ buf[3] = sizeof(bdaddr_t);
+ memcpy(buf + 4, bdaddr, sizeof(bdaddr_t));
+
+ skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ BT_ERR("%s: Change address command failed (%d)",
+ hdev->name, err);
+ return err;
+ }
+ kfree_skb(skb);
- hu->priv = NULL;
- kfree(ath);
+ return 0;
+}
+
+static int ath_setup(struct hci_uart *hu)
+{
+ BT_DBG("hu %p", hu);
+
+ hu->hdev->set_bdaddr = ath_set_bdaddr;
return 0;
}
+static const struct h4_recv_pkt ath_recv_pkts[] = {
+ { H4_RECV_ACL, .recv = hci_recv_frame },
+ { H4_RECV_SCO, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
+static int ath_recv(struct hci_uart *hu, const void *data, int count)
+{
+ struct ath_struct *ath = hu->priv;
+
+ ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count,
+ ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts));
+ if (IS_ERR(ath->rx_skb)) {
+ int err = PTR_ERR(ath->rx_skb);
+ BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+ return err;
+ }
+
+ return count;
+}
+
#define HCI_OP_ATH_SLEEP 0xFC04
-/* Enqueue frame for transmittion */
static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct ath_struct *ath = hu->priv;
@@ -159,8 +209,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
return 0;
}
- /*
- * Update power management enable flag with parameters of
+ /* Update power management enable flag with parameters of
* HCI sleep enable vendor specific HCI command.
*/
if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
@@ -190,37 +239,16 @@ static struct sk_buff *ath_dequeue(struct hci_uart *hu)
return skb_dequeue(&ath->txq);
}
-static const struct h4_recv_pkt ath_recv_pkts[] = {
- { H4_RECV_ACL, .recv = hci_recv_frame },
- { H4_RECV_SCO, .recv = hci_recv_frame },
- { H4_RECV_EVENT, .recv = hci_recv_frame },
-};
-
-/* Recv data */
-static int ath_recv(struct hci_uart *hu, const void *data, int count)
-{
- struct ath_struct *ath = hu->priv;
-
- ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count,
- ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts));
- if (IS_ERR(ath->rx_skb)) {
- int err = PTR_ERR(ath->rx_skb);
- BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
- return err;
- }
-
- return count;
-}
-
static const struct hci_uart_proto athp = {
.id = HCI_UART_ATH3K,
.name = "ATH3K",
.open = ath_open,
.close = ath_close,
+ .flush = ath_flush,
+ .setup = ath_setup,
.recv = ath_recv,
.enqueue = ath_enqueue,
.dequeue = ath_dequeue,
- .flush = ath_flush,
};
int __init ath_init(void)
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
index b854125e4831..5340604b23a4 100644
--- a/drivers/bus/arm-cci.c
+++ b/drivers/bus/arm-cci.c
@@ -660,7 +660,7 @@ validate_group(struct perf_event *event)
* Initialise the fake PMU. We only need to populate the
* used_mask for the purposes of validation.
*/
- .used_mask = CPU_BITS_NONE,
+ .used_mask = { 0 },
};
if (!validate_event(event->pmu, &fake_pmu, leader))
diff --git a/drivers/bus/mips_cdmm.c b/drivers/bus/mips_cdmm.c
index 5bd792c68f9b..ab3bde16ecb4 100644
--- a/drivers/bus/mips_cdmm.c
+++ b/drivers/bus/mips_cdmm.c
@@ -453,7 +453,7 @@ void __iomem *mips_cdmm_early_probe(unsigned int dev_type)
/* Look for a specific device type */
for (; drb < bus->drbs; drb += size + 1) {
- acsr = readl(cdmm + drb * CDMM_DRB_SIZE);
+ acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
if (type == dev_type)
return cdmm + drb * CDMM_DRB_SIZE;
@@ -500,7 +500,7 @@ static void mips_cdmm_bus_discover(struct mips_cdmm_bus *bus)
bus->discovered = true;
pr_info("cdmm%u discovery (%u blocks)\n", cpu, bus->drbs);
for (; drb < bus->drbs; drb += size + 1) {
- acsr = readl(cdmm + drb * CDMM_DRB_SIZE);
+ acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
rev = (acsr & CDMM_ACSR_DEVREV) >> CDMM_ACSR_DEVREV_SHIFT;
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index fb9ec6221730..c43c3d2baf73 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -57,8 +57,8 @@
#include <linux/of_address.h>
#include <linux/debugfs.h>
#include <linux/log2.h>
-#include <linux/syscore_ops.h>
#include <linux/memblock.h>
+#include <linux/syscore_ops.h>
/*
* DDR target is the same on all platforms.
@@ -70,6 +70,7 @@
*/
#define WIN_CTRL_OFF 0x0000
#define WIN_CTRL_ENABLE BIT(0)
+/* Only on HW I/O coherency capable platforms */
#define WIN_CTRL_SYNCBARRIER BIT(1)
#define WIN_CTRL_TGT_MASK 0xf0
#define WIN_CTRL_TGT_SHIFT 4
@@ -102,9 +103,7 @@
/* Relative to mbusbridge_base */
#define MBUS_BRIDGE_CTRL_OFF 0x0
-#define MBUS_BRIDGE_SIZE_MASK 0xffff0000
#define MBUS_BRIDGE_BASE_OFF 0x4
-#define MBUS_BRIDGE_BASE_MASK 0xffff0000
/* Maximum number of windows, for all known platforms */
#define MBUS_WINS_MAX 20
@@ -154,13 +153,39 @@ struct mvebu_mbus_state {
static struct mvebu_mbus_state mbus_state;
+/*
+ * We provide two variants of the mv_mbus_dram_info() function:
+ *
+ * - The normal one, where the described DRAM ranges may overlap with
+ * the I/O windows, but for which the DRAM ranges are guaranteed to
+ * have a power of two size. Such ranges are suitable for the DMA
+ * masters that only DMA between the RAM and the device, which is
+ * actually all devices except the crypto engines.
+ *
+ * - The 'nooverlap' one, where the described DRAM ranges are
+ * guaranteed to not overlap with the I/O windows, but for which the
+ * DRAM ranges will not have power of two sizes. They will only be
+ * aligned on a 64 KB boundary, and have a size multiple of 64
+ * KB. Such ranges are suitable for the DMA masters that DMA between
+ * the crypto SRAM (which is mapped through an I/O window) and a
+ * device. This is the case for the crypto engines.
+ */
+
static struct mbus_dram_target_info mvebu_mbus_dram_info;
+static struct mbus_dram_target_info mvebu_mbus_dram_info_nooverlap;
+
const struct mbus_dram_target_info *mv_mbus_dram_info(void)
{
return &mvebu_mbus_dram_info;
}
EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
+const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void)
+{
+ return &mvebu_mbus_dram_info_nooverlap;
+}
+EXPORT_SYMBOL_GPL(mv_mbus_dram_info_nooverlap);
+
/* Checks whether the given window has remap capability */
static bool mvebu_mbus_window_is_remappable(struct mvebu_mbus_state *mbus,
const int win)
@@ -323,8 +348,9 @@ static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus,
ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) |
(attr << WIN_CTRL_ATTR_SHIFT) |
(target << WIN_CTRL_TGT_SHIFT) |
- WIN_CTRL_SYNCBARRIER |
WIN_CTRL_ENABLE;
+ if (mbus->hw_io_coherency)
+ ctrl |= WIN_CTRL_SYNCBARRIER;
writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF);
writel(ctrl, addr + WIN_CTRL_OFF);
@@ -592,7 +618,7 @@ mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end)
* This part of the memory is above 4 GB, so we don't
* care for the MBus bridge hole.
*/
- if (r->base >= 0x100000000)
+ if (r->base >= 0x100000000ULL)
continue;
/*
@@ -604,50 +630,33 @@ mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end)
}
*start = s;
- *end = 0x100000000;
+ *end = 0x100000000ULL;
}
+/*
+ * This function fills in the mvebu_mbus_dram_info_nooverlap data
+ * structure, by looking at the mvebu_mbus_dram_info data, and
+ * removing the parts of it that overlap with I/O windows.
+ */
static void __init
-mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
+mvebu_mbus_setup_cpu_target_nooverlap(struct mvebu_mbus_state *mbus)
{
- int i;
- int cs;
uint64_t mbus_bridge_base, mbus_bridge_end;
-
- mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
+ int cs_nooverlap = 0;
+ int i;
mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end);
- for (i = 0, cs = 0; i < 4; i++) {
- u64 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
- u64 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
- u64 end;
+ for (i = 0; i < mvebu_mbus_dram_info.num_cs; i++) {
struct mbus_dram_window *w;
+ u64 base, size, end;
- /* Ignore entries that are not enabled */
- if (!(size & DDR_SIZE_ENABLED))
- continue;
-
- /*
- * Ignore entries whose base address is above 2^32,
- * since devices cannot DMA to such high addresses
- */
- if (base & DDR_BASE_CS_HIGH_MASK)
- continue;
-
- base = base & DDR_BASE_CS_LOW_MASK;
- size = (size | ~DDR_SIZE_MASK) + 1;
+ w = &mvebu_mbus_dram_info.cs[i];
+ base = w->base;
+ size = w->size;
end = base + size;
/*
- * Adjust base/size of the current CS to make sure it
- * doesn't overlap with the MBus bridge hole. This is
- * particularly important for devices that do DMA from
- * DRAM to a SRAM mapped in a MBus window, such as the
- * CESA cryptographic engine.
- */
-
- /*
* The CS is fully enclosed inside the MBus bridge
* area, so ignore it.
*/
@@ -670,7 +679,7 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
if (base < mbus_bridge_base && end > mbus_bridge_base)
size -= end - mbus_bridge_base;
- w = &mvebu_mbus_dram_info.cs[cs++];
+ w = &mvebu_mbus_dram_info_nooverlap.cs[cs_nooverlap++];
w->cs_index = i;
w->mbus_attr = 0xf & ~(1 << i);
if (mbus->hw_io_coherency)
@@ -678,6 +687,42 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
w->base = base;
w->size = size;
}
+
+ mvebu_mbus_dram_info_nooverlap.mbus_dram_target_id = TARGET_DDR;
+ mvebu_mbus_dram_info_nooverlap.num_cs = cs_nooverlap;
+}
+
+static void __init
+mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
+{
+ int i;
+ int cs;
+
+ mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
+
+ for (i = 0, cs = 0; i < 4; i++) {
+ u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
+ u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
+
+ /*
+ * We only take care of entries for which the chip
+ * select is enabled, and that don't have high base
+ * address bits set (devices can only access the first
+ * 32 bits of the memory).
+ */
+ if ((size & DDR_SIZE_ENABLED) &&
+ !(base & DDR_BASE_CS_HIGH_MASK)) {
+ struct mbus_dram_window *w;
+
+ w = &mvebu_mbus_dram_info.cs[cs++];
+ w->cs_index = i;
+ w->mbus_attr = 0xf & ~(1 << i);
+ if (mbus->hw_io_coherency)
+ w->mbus_attr |= ATTR_HW_COHERENCY;
+ w->base = base & DDR_BASE_CS_LOW_MASK;
+ w->size = (size | ~DDR_SIZE_MASK) + 1;
+ }
+ }
mvebu_mbus_dram_info.num_cs = cs;
}
@@ -1035,6 +1080,7 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
mvebu_mbus_disable_window(mbus, win);
mbus->soc->setup_cpu_target(mbus);
+ mvebu_mbus_setup_cpu_target_nooverlap(mbus);
if (is_coherent)
writel(UNIT_SYNC_BARRIER_ALL,
diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c
index 11f7982cbdb3..5012e3ad1225 100644
--- a/drivers/bus/omap_l3_noc.c
+++ b/drivers/bus/omap_l3_noc.c
@@ -1,7 +1,7 @@
/*
* OMAP L3 Interconnect error handling driver
*
- * Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2011-2015 Texas Instruments Incorporated - http://www.ti.com/
* Santosh Shilimkar <santosh.shilimkar@ti.com>
* Sricharan <r.sricharan@ti.com>
*
@@ -233,7 +233,8 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
}
static const struct of_device_id l3_noc_match[] = {
- {.compatible = "ti,omap4-l3-noc", .data = &omap_l3_data},
+ {.compatible = "ti,omap4-l3-noc", .data = &omap4_l3_data},
+ {.compatible = "ti,omap5-l3-noc", .data = &omap5_l3_data},
{.compatible = "ti,dra7-l3-noc", .data = &dra_l3_data},
{.compatible = "ti,am4372-l3-noc", .data = &am4372_l3_data},
{},
@@ -300,7 +301,7 @@ static int omap_l3_probe(struct platform_device *pdev)
return ret;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/**
* l3_resume_noirq() - resume function for l3_noc
@@ -346,7 +347,7 @@ static int l3_resume_noirq(struct device *dev)
}
static const struct dev_pm_ops l3_dev_pm_ops = {
- .resume_noirq = l3_resume_noirq,
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, l3_resume_noirq)
};
#define L3_DEV_PM_OPS (&l3_dev_pm_ops)
diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h
index 95254585db86..73431f81da28 100644
--- a/drivers/bus/omap_l3_noc.h
+++ b/drivers/bus/omap_l3_noc.h
@@ -1,7 +1,7 @@
/*
* OMAP L3 Interconnect error handling driver header
*
- * Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2011-2015 Texas Instruments Incorporated - http://www.ti.com/
* Santosh Shilimkar <santosh.shilimkar@ti.com>
* sricharan <r.sricharan@ti.com>
*
@@ -175,16 +175,14 @@ static struct l3_flagmux_data omap_l3_flagmux_clk2 = {
};
-static struct l3_target_data omap_l3_target_data_clk3[] = {
- {0x0100, "EMUSS",},
- {0x0300, "DEBUG SOURCE",},
- {0x0, "HOST CLK3",},
+static struct l3_target_data omap4_l3_target_data_clk3[] = {
+ {0x0100, "DEBUGSS",},
};
-static struct l3_flagmux_data omap_l3_flagmux_clk3 = {
+static struct l3_flagmux_data omap4_l3_flagmux_clk3 = {
.offset = 0x0200,
- .l3_targ = omap_l3_target_data_clk3,
- .num_targ_data = ARRAY_SIZE(omap_l3_target_data_clk3),
+ .l3_targ = omap4_l3_target_data_clk3,
+ .num_targ_data = ARRAY_SIZE(omap4_l3_target_data_clk3),
};
static struct l3_masters_data omap_l3_masters[] = {
@@ -215,21 +213,49 @@ static struct l3_masters_data omap_l3_masters[] = {
{ 0x32, "USBHOSTFS"}
};
-static struct l3_flagmux_data *omap_l3_flagmux[] = {
+static struct l3_flagmux_data *omap4_l3_flagmux[] = {
&omap_l3_flagmux_clk1,
&omap_l3_flagmux_clk2,
- &omap_l3_flagmux_clk3,
+ &omap4_l3_flagmux_clk3,
};
-static const struct omap_l3 omap_l3_data = {
- .l3_flagmux = omap_l3_flagmux,
- .num_modules = ARRAY_SIZE(omap_l3_flagmux),
+static const struct omap_l3 omap4_l3_data = {
+ .l3_flagmux = omap4_l3_flagmux,
+ .num_modules = ARRAY_SIZE(omap4_l3_flagmux),
.l3_masters = omap_l3_masters,
.num_masters = ARRAY_SIZE(omap_l3_masters),
/* The 6 MSBs of register field used to distinguish initiator */
.mst_addr_mask = 0xFC,
};
+/* OMAP5 data */
+static struct l3_target_data omap5_l3_target_data_clk3[] = {
+ {0x0100, "L3INSTR",},
+ {0x0300, "DEBUGSS",},
+ {0x0, "HOSTCLK3",},
+};
+
+static struct l3_flagmux_data omap5_l3_flagmux_clk3 = {
+ .offset = 0x0200,
+ .l3_targ = omap5_l3_target_data_clk3,
+ .num_targ_data = ARRAY_SIZE(omap5_l3_target_data_clk3),
+};
+
+static struct l3_flagmux_data *omap5_l3_flagmux[] = {
+ &omap_l3_flagmux_clk1,
+ &omap_l3_flagmux_clk2,
+ &omap5_l3_flagmux_clk3,
+};
+
+static const struct omap_l3 omap5_l3_data = {
+ .l3_flagmux = omap5_l3_flagmux,
+ .num_modules = ARRAY_SIZE(omap5_l3_flagmux),
+ .l3_masters = omap_l3_masters,
+ .num_masters = ARRAY_SIZE(omap_l3_masters),
+ /* The 6 MSBs of register field used to distinguish initiator */
+ .mst_addr_mask = 0x7E0,
+};
+
/* DRA7 data */
static struct l3_target_data dra_l3_target_data_clk1[] = {
{0x2a00, "AES1",},
@@ -274,7 +300,7 @@ static struct l3_flagmux_data dra_l3_flagmux_clk1 = {
static struct l3_target_data dra_l3_target_data_clk2[] = {
{0x0, "HOST CLK1",},
- {0x0, "HOST CLK2",},
+ {0x800000, "HOST CLK2",},
{0xdead, L3_TARGET_NOT_SUPPORTED,},
{0x3400, "SHA2_2",},
{0x0900, "BB2D",},
diff --git a/drivers/char/hw_random/bcm63xx-rng.c b/drivers/char/hw_random/bcm63xx-rng.c
index d1494ecd9e11..4b31f1387f37 100644
--- a/drivers/char/hw_random/bcm63xx-rng.c
+++ b/drivers/char/hw_random/bcm63xx-rng.c
@@ -57,7 +57,7 @@ static void bcm63xx_rng_cleanup(struct hwrng *rng)
val &= ~RNG_EN;
__raw_writel(val, priv->regs + RNG_CTRL);
- clk_didsable_unprepare(prov->clk);
+ clk_disable_unprepare(priv->clk);
}
static int bcm63xx_rng_data_present(struct hwrng *rng, int wait)
@@ -97,14 +97,14 @@ static int bcm63xx_rng_probe(struct platform_device *pdev)
priv->rng.name = pdev->name;
priv->rng.init = bcm63xx_rng_init;
priv->rng.cleanup = bcm63xx_rng_cleanup;
- prov->rng.data_present = bcm63xx_rng_data_present;
+ priv->rng.data_present = bcm63xx_rng_data_present;
priv->rng.data_read = bcm63xx_rng_data_read;
priv->clk = devm_clk_get(&pdev->dev, "ipsec");
if (IS_ERR(priv->clk)) {
- error = PTR_ERR(priv->clk);
- dev_err(&pdev->dev, "no clock for device: %d\n", error);
- return error;
+ ret = PTR_ERR(priv->clk);
+ dev_err(&pdev->dev, "no clock for device: %d\n", ret);
+ return ret;
}
if (!devm_request_mem_region(&pdev->dev, r->start,
@@ -120,11 +120,11 @@ static int bcm63xx_rng_probe(struct platform_device *pdev)
return -ENOMEM;
}
- error = devm_hwrng_register(&pdev->dev, &priv->rng);
- if (error) {
+ ret = devm_hwrng_register(&pdev->dev, &priv->rng);
+ if (ret) {
dev_err(&pdev->dev, "failed to register rng device: %d\n",
- error);
- return error;
+ ret);
+ return ret;
}
dev_info(&pdev->dev, "registered RNG driver\n");
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c
index a3bebef255ad..0c98a9d51a24 100644
--- a/drivers/char/hw_random/via-rng.c
+++ b/drivers/char/hw_random/via-rng.c
@@ -33,7 +33,7 @@
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/cpufeature.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 9bb592872532..bf75f6361773 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -2000,7 +2000,7 @@ static int smi_ipmb_proc_show(struct seq_file *m, void *v)
seq_printf(m, " %x", intf->channels[i].address);
seq_putc(m, '\n');
- return seq_has_overflowed(m);
+ return 0;
}
static int smi_ipmb_proc_open(struct inode *inode, struct file *file)
@@ -2023,7 +2023,7 @@ static int smi_version_proc_show(struct seq_file *m, void *v)
ipmi_version_major(&intf->bmc->id),
ipmi_version_minor(&intf->bmc->id));
- return seq_has_overflowed(m);
+ return 0;
}
static int smi_version_proc_open(struct inode *inode, struct file *file)
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 5e90a18afbaf..8a45e92ff60c 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -942,8 +942,7 @@ static void sender(void *send_info,
* If we are running to completion, start it and run
* transactions until everything is clear.
*/
- smi_info->curr_msg = msg;
- smi_info->waiting_msg = NULL;
+ smi_info->waiting_msg = msg;
/*
* Run to completion means we are single-threaded, no
@@ -2244,7 +2243,7 @@ static int ipmi_pnp_probe(struct pnp_dev *dev,
acpi_handle handle;
acpi_status status;
unsigned long long tmp;
- int rv;
+ int rv = -EINVAL;
acpi_dev = pnp_acpi_device(dev);
if (!acpi_dev)
@@ -2262,8 +2261,10 @@ static int ipmi_pnp_probe(struct pnp_dev *dev,
/* _IFT tells us the interface type: KCS, BT, etc */
status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp);
- if (ACPI_FAILURE(status))
+ if (ACPI_FAILURE(status)) {
+ dev_err(&dev->dev, "Could not find ACPI IPMI interface type\n");
goto err_free;
+ }
switch (tmp) {
case 1:
@@ -2276,6 +2277,7 @@ static int ipmi_pnp_probe(struct pnp_dev *dev,
info->si_type = SI_BT;
break;
case 4: /* SSIF, just ignore */
+ rv = -ENODEV;
goto err_free;
default:
dev_info(&dev->dev, "unknown IPMI type %lld\n", tmp);
@@ -2336,7 +2338,7 @@ static int ipmi_pnp_probe(struct pnp_dev *dev,
err_free:
kfree(info);
- return -EINVAL;
+ return rv;
}
static void ipmi_pnp_remove(struct pnp_dev *dev)
@@ -3080,7 +3082,7 @@ static int smi_type_proc_show(struct seq_file *m, void *v)
seq_printf(m, "%s\n", si_to_str[smi->si_type]);
- return seq_has_overflowed(m);
+ return 0;
}
static int smi_type_proc_open(struct inode *inode, struct file *file)
@@ -3153,7 +3155,7 @@ static int smi_params_proc_show(struct seq_file *m, void *v)
smi->irq,
smi->slave_addr);
- return seq_has_overflowed(m);
+ return 0;
}
static int smi_params_proc_open(struct inode *inode, struct file *file)
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index f40e3bd2c69c..207689c444a8 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -31,7 +31,6 @@
* interface into the I2C driver, I believe.
*/
-#include <linux/version.h>
#if defined(MODVERSIONS)
#include <linux/modversions.h>
#endif
@@ -166,6 +165,9 @@ enum ssif_stat_indexes {
/* Number of watchdog pretimeouts. */
SSIF_STAT_watchdog_pretimeouts,
+ /* Number of alers received. */
+ SSIF_STAT_alerts,
+
/* Always add statistics before this value, it must be last. */
SSIF_NUM_STATS
};
@@ -214,7 +216,16 @@ struct ssif_info {
#define WDT_PRE_TIMEOUT_INT 0x08
unsigned char msg_flags;
+ u8 global_enables;
bool has_event_buffer;
+ bool supports_alert;
+
+ /*
+ * Used to tell what we should do with alerts. If we are
+ * waiting on a response, read the data immediately.
+ */
+ bool got_alert;
+ bool waiting_alert;
/*
* If set to true, this will request events the next time the
@@ -478,13 +489,13 @@ static int ipmi_ssif_thread(void *data)
if (ssif_info->i2c_read_write == I2C_SMBUS_WRITE) {
result = i2c_smbus_write_block_data(
- ssif_info->client, SSIF_IPMI_REQUEST,
+ ssif_info->client, ssif_info->i2c_command,
ssif_info->i2c_data[0],
ssif_info->i2c_data + 1);
ssif_info->done_handler(ssif_info, result, NULL, 0);
} else {
result = i2c_smbus_read_block_data(
- ssif_info->client, SSIF_IPMI_RESPONSE,
+ ssif_info->client, ssif_info->i2c_command,
ssif_info->i2c_data);
if (result < 0)
ssif_info->done_handler(ssif_info, result,
@@ -518,15 +529,12 @@ static int ssif_i2c_send(struct ssif_info *ssif_info,
static void msg_done_handler(struct ssif_info *ssif_info, int result,
unsigned char *data, unsigned int len);
-static void retry_timeout(unsigned long data)
+static void start_get(struct ssif_info *ssif_info)
{
- struct ssif_info *ssif_info = (void *) data;
int rv;
- if (ssif_info->stopping)
- return;
-
ssif_info->rtc_us_timer = 0;
+ ssif_info->multi_pos = 0;
rv = ssif_i2c_send(ssif_info, msg_done_handler, I2C_SMBUS_READ,
SSIF_IPMI_RESPONSE,
@@ -540,6 +548,46 @@ static void retry_timeout(unsigned long data)
}
}
+static void retry_timeout(unsigned long data)
+{
+ struct ssif_info *ssif_info = (void *) data;
+ unsigned long oflags, *flags;
+ bool waiting;
+
+ if (ssif_info->stopping)
+ return;
+
+ flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+ waiting = ssif_info->waiting_alert;
+ ssif_info->waiting_alert = false;
+ ipmi_ssif_unlock_cond(ssif_info, flags);
+
+ if (waiting)
+ start_get(ssif_info);
+}
+
+
+static void ssif_alert(struct i2c_client *client, unsigned int data)
+{
+ struct ssif_info *ssif_info = i2c_get_clientdata(client);
+ unsigned long oflags, *flags;
+ bool do_get = false;
+
+ ssif_inc_stat(ssif_info, alerts);
+
+ flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+ if (ssif_info->waiting_alert) {
+ ssif_info->waiting_alert = false;
+ del_timer(&ssif_info->retry_timer);
+ do_get = true;
+ } else if (ssif_info->curr_msg) {
+ ssif_info->got_alert = true;
+ }
+ ipmi_ssif_unlock_cond(ssif_info, flags);
+ if (do_get)
+ start_get(ssif_info);
+}
+
static int start_resend(struct ssif_info *ssif_info);
static void msg_done_handler(struct ssif_info *ssif_info, int result,
@@ -559,9 +607,12 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
if (ssif_info->retries_left > 0) {
ssif_inc_stat(ssif_info, receive_retries);
+ flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+ ssif_info->waiting_alert = true;
+ ssif_info->rtc_us_timer = SSIF_MSG_USEC;
mod_timer(&ssif_info->retry_timer,
jiffies + SSIF_MSG_JIFFIES);
- ssif_info->rtc_us_timer = SSIF_MSG_USEC;
+ ipmi_ssif_unlock_cond(ssif_info, flags);
return;
}
@@ -581,9 +632,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ssif_inc_stat(ssif_info, received_message_parts);
/* Remove the multi-part read marker. */
- for (i = 0; i < (len-2); i++)
- ssif_info->data[i] = data[i+2];
len -= 2;
+ for (i = 0; i < len; i++)
+ ssif_info->data[i] = data[i+2];
ssif_info->multi_len = len;
ssif_info->multi_pos = 1;
@@ -610,9 +661,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
goto continue_op;
}
- blocknum = data[ssif_info->multi_len];
+ blocknum = data[0];
- if (ssif_info->multi_len+len-1 > IPMI_MAX_MSG_LENGTH) {
+ if (ssif_info->multi_len + len - 1 > IPMI_MAX_MSG_LENGTH) {
/* Received message too big, abort the operation. */
result = -E2BIG;
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
@@ -622,15 +673,15 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
}
/* Remove the blocknum from the data. */
- for (i = 0; i < (len-1); i++)
- ssif_info->data[i+ssif_info->multi_len] = data[i+1];
len--;
+ for (i = 0; i < len; i++)
+ ssif_info->data[i + ssif_info->multi_len] = data[i + 1];
ssif_info->multi_len += len;
if (blocknum == 0xff) {
/* End of read */
len = ssif_info->multi_len;
data = ssif_info->data;
- } else if ((blocknum+1) != ssif_info->multi_pos) {
+ } else if (blocknum + 1 != ssif_info->multi_pos) {
/*
* Out of sequence block, just abort. Block
* numbers start at zero for the second block,
@@ -650,7 +701,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
if (rv < 0) {
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info(PFX
- "Error from i2c_non_blocking_op(2)\n");
+ "Error from ssif_i2c_send\n");
result = -EIO;
} else
@@ -830,7 +881,11 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
}
if (ssif_info->multi_data) {
- /* In the middle of a multi-data write. */
+ /*
+ * In the middle of a multi-data write. See the comment
+ * in the SSIF_MULTI_n_PART case in the probe function
+ * for details on the intricacies of this.
+ */
int left;
ssif_inc_stat(ssif_info, sent_messages_parts);
@@ -864,15 +919,32 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
msg_done_handler(ssif_info, -EIO, NULL, 0);
}
} else {
+ unsigned long oflags, *flags;
+ bool got_alert;
+
ssif_inc_stat(ssif_info, sent_messages);
ssif_inc_stat(ssif_info, sent_messages_parts);
- /* Wait a jiffie then request the next message */
- ssif_info->retries_left = SSIF_RECV_RETRIES;
- ssif_info->rtc_us_timer = SSIF_MSG_PART_USEC;
- mod_timer(&ssif_info->retry_timer,
- jiffies + SSIF_MSG_PART_JIFFIES);
- return;
+ flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
+ got_alert = ssif_info->got_alert;
+ if (got_alert) {
+ ssif_info->got_alert = false;
+ ssif_info->waiting_alert = false;
+ }
+
+ if (got_alert) {
+ ipmi_ssif_unlock_cond(ssif_info, flags);
+ /* The alert already happened, try now. */
+ retry_timeout((unsigned long) ssif_info);
+ } else {
+ /* Wait a jiffie then request the next message */
+ ssif_info->waiting_alert = true;
+ ssif_info->retries_left = SSIF_RECV_RETRIES;
+ ssif_info->rtc_us_timer = SSIF_MSG_PART_USEC;
+ mod_timer(&ssif_info->retry_timer,
+ jiffies + SSIF_MSG_PART_JIFFIES);
+ ipmi_ssif_unlock_cond(ssif_info, flags);
+ }
}
}
@@ -881,6 +953,8 @@ static int start_resend(struct ssif_info *ssif_info)
int rv;
int command;
+ ssif_info->got_alert = false;
+
if (ssif_info->data_len > 32) {
command = SSIF_IPMI_MULTI_PART_REQUEST_START;
ssif_info->multi_data = ssif_info->data;
@@ -915,7 +989,7 @@ static int start_send(struct ssif_info *ssif_info,
return -E2BIG;
ssif_info->retries_left = SSIF_SEND_RETRIES;
- memcpy(ssif_info->data+1, data, len);
+ memcpy(ssif_info->data + 1, data, len);
ssif_info->data_len = len;
return start_resend(ssif_info);
}
@@ -1200,7 +1274,7 @@ static int smi_type_proc_show(struct seq_file *m, void *v)
{
seq_puts(m, "ssif\n");
- return seq_has_overflowed(m);
+ return 0;
}
static int smi_type_proc_open(struct inode *inode, struct file *file)
@@ -1243,6 +1317,8 @@ static int smi_stats_proc_show(struct seq_file *m, void *v)
ssif_get_stat(ssif_info, events));
seq_printf(m, "watchdog_pretimeouts: %u\n",
ssif_get_stat(ssif_info, watchdog_pretimeouts));
+ seq_printf(m, "alerts: %u\n",
+ ssif_get_stat(ssif_info, alerts));
return 0;
}
@@ -1258,6 +1334,23 @@ static const struct file_operations smi_stats_proc_ops = {
.release = single_release,
};
+static int strcmp_nospace(char *s1, char *s2)
+{
+ while (*s1 && *s2) {
+ while (isspace(*s1))
+ s1++;
+ while (isspace(*s2))
+ s2++;
+ if (*s1 > *s2)
+ return 1;
+ if (*s1 < *s2)
+ return -1;
+ s1++;
+ s2++;
+ }
+ return 0;
+}
+
static struct ssif_addr_info *ssif_info_find(unsigned short addr,
char *adapter_name,
bool match_null_name)
@@ -1272,8 +1365,10 @@ restart:
/* One is NULL and one is not */
continue;
}
- if (strcmp(info->adapter_name, adapter_name))
- /* Names to not match */
+ if (adapter_name &&
+ strcmp_nospace(info->adapter_name,
+ adapter_name))
+ /* Names do not match */
continue;
}
found = info;
@@ -1306,6 +1401,12 @@ static bool check_acpi(struct ssif_info *ssif_info, struct device *dev)
return false;
}
+/*
+ * Global enables we care about.
+ */
+#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \
+ IPMI_BMC_EVT_MSG_INTR)
+
static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
unsigned char msg[3];
@@ -1391,13 +1492,33 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
break;
case SSIF_MULTI_2_PART:
- if (ssif_info->max_xmit_msg_size > 64)
- ssif_info->max_xmit_msg_size = 64;
+ if (ssif_info->max_xmit_msg_size > 63)
+ ssif_info->max_xmit_msg_size = 63;
if (ssif_info->max_recv_msg_size > 62)
ssif_info->max_recv_msg_size = 62;
break;
case SSIF_MULTI_n_PART:
+ /*
+ * The specification is rather confusing at
+ * this point, but I think I understand what
+ * is meant. At least I have a workable
+ * solution. With multi-part messages, you
+ * cannot send a message that is a multiple of
+ * 32-bytes in length, because the start and
+ * middle messages are 32-bytes and the end
+ * message must be at least one byte. You
+ * can't fudge on an extra byte, that would
+ * screw up things like fru data writes. So
+ * we limit the length to 63 bytes. That way
+ * a 32-byte message gets sent as a single
+ * part. A larger message will be a 32-byte
+ * start and the next message is always going
+ * to be 1-31 bytes in length. Not ideal, but
+ * it should work.
+ */
+ if (ssif_info->max_xmit_msg_size > 63)
+ ssif_info->max_xmit_msg_size = 63;
break;
default:
@@ -1407,7 +1528,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
} else {
no_support:
/* Assume no multi-part or PEC support */
- pr_info(PFX "Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n",
+ pr_info(PFX "Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n",
rv, len, resp[2]);
ssif_info->max_xmit_msg_size = 32;
@@ -1436,6 +1557,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
goto found;
}
+ ssif_info->global_enables = resp[3];
+
if (resp[3] & IPMI_BMC_EVT_MSG_BUFF) {
ssif_info->has_event_buffer = true;
/* buffer is already enabled, nothing to do. */
@@ -1444,18 +1567,37 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
msg[0] = IPMI_NETFN_APP_REQUEST << 2;
msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
- msg[2] = resp[3] | IPMI_BMC_EVT_MSG_BUFF;
+ msg[2] = ssif_info->global_enables | IPMI_BMC_EVT_MSG_BUFF;
rv = do_cmd(client, 3, msg, &len, resp);
if (rv || (len < 2)) {
- pr_warn(PFX "Error getting global enables: %d %d %2.2x\n",
+ pr_warn(PFX "Error setting global enables: %d %d %2.2x\n",
rv, len, resp[2]);
rv = 0; /* Not fatal */
goto found;
}
- if (resp[2] == 0)
+ if (resp[2] == 0) {
/* A successful return means the event buffer is supported. */
ssif_info->has_event_buffer = true;
+ ssif_info->global_enables |= IPMI_BMC_EVT_MSG_BUFF;
+ }
+
+ msg[0] = IPMI_NETFN_APP_REQUEST << 2;
+ msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
+ msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR;
+ rv = do_cmd(client, 3, msg, &len, resp);
+ if (rv || (len < 2)) {
+ pr_warn(PFX "Error setting global enables: %d %d %2.2x\n",
+ rv, len, resp[2]);
+ rv = 0; /* Not fatal */
+ goto found;
+ }
+
+ if (resp[2] == 0) {
+ /* A successful return means the alert is supported. */
+ ssif_info->supports_alert = true;
+ ssif_info->global_enables |= IPMI_BMC_RCV_MSG_INTR;
+ }
found:
ssif_info->intf_num = atomic_inc_return(&next_intf);
@@ -1813,6 +1955,7 @@ static struct i2c_driver ssif_i2c_driver = {
},
.probe = ssif_probe,
.remove = ssif_remove,
+ .alert = ssif_alert,
.id_table = ssif_id,
.detect = ssif_detect
};
@@ -1832,7 +1975,7 @@ static int init_ipmi_ssif(void)
rv = new_ssif_client(addr[i], adapter_name[i],
dbg[i], slave_addrs[i],
SI_HARDCODED);
- if (!rv)
+ if (rv)
pr_err(PFX
"Couldn't add hardcoded device at addr 0x%x\n",
addr[i]);
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
index 8dd48a2be911..fc061f7c2bd1 100644
--- a/drivers/char/pcmcia/cm4040_cs.c
+++ b/drivers/char/pcmcia/cm4040_cs.c
@@ -532,9 +532,8 @@ static int reader_config(struct pcmcia_device *link, int devno)
fail_rc = pcmcia_enable_device(link);
if (fail_rc != 0) {
- dev_printk(KERN_INFO, &link->dev,
- "pcmcia_enable_device failed 0x%x\n",
- fail_rc);
+ dev_info(&link->dev, "pcmcia_enable_device failed 0x%x\n",
+ fail_rc);
goto cs_release;
}
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 9cd6968e2f92..d0da5d852d41 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -409,6 +409,9 @@ static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
static DECLARE_WAIT_QUEUE_HEAD(urandom_init_wait);
static struct fasync_struct *fasync;
+static DEFINE_SPINLOCK(random_ready_list_lock);
+static LIST_HEAD(random_ready_list);
+
/**********************************************************************
*
* OS independent entropy store. Here are the functions which handle
@@ -589,6 +592,22 @@ static void fast_mix(struct fast_pool *f)
f->count++;
}
+static void process_random_ready_list(void)
+{
+ unsigned long flags;
+ struct random_ready_callback *rdy, *tmp;
+
+ spin_lock_irqsave(&random_ready_list_lock, flags);
+ list_for_each_entry_safe(rdy, tmp, &random_ready_list, list) {
+ struct module *owner = rdy->owner;
+
+ list_del_init(&rdy->list);
+ rdy->func(rdy);
+ module_put(owner);
+ }
+ spin_unlock_irqrestore(&random_ready_list_lock, flags);
+}
+
/*
* Credit (or debit) the entropy store with n bits of entropy.
* Use credit_entropy_bits_safe() if the value comes from userspace
@@ -660,7 +679,8 @@ retry:
r->entropy_total = 0;
if (r == &nonblocking_pool) {
prandom_reseed_late();
- wake_up_interruptible(&urandom_init_wait);
+ process_random_ready_list();
+ wake_up_all(&urandom_init_wait);
pr_notice("random: %s pool is initialized\n", r->name);
}
}
@@ -1245,6 +1265,64 @@ void get_random_bytes(void *buf, int nbytes)
EXPORT_SYMBOL(get_random_bytes);
/*
+ * Add a callback function that will be invoked when the nonblocking
+ * pool is initialised.
+ *
+ * returns: 0 if callback is successfully added
+ * -EALREADY if pool is already initialised (callback not called)
+ * -ENOENT if module for callback is not alive
+ */
+int add_random_ready_callback(struct random_ready_callback *rdy)
+{
+ struct module *owner;
+ unsigned long flags;
+ int err = -EALREADY;
+
+ if (likely(nonblocking_pool.initialized))
+ return err;
+
+ owner = rdy->owner;
+ if (!try_module_get(owner))
+ return -ENOENT;
+
+ spin_lock_irqsave(&random_ready_list_lock, flags);
+ if (nonblocking_pool.initialized)
+ goto out;
+
+ owner = NULL;
+
+ list_add(&rdy->list, &random_ready_list);
+ err = 0;
+
+out:
+ spin_unlock_irqrestore(&random_ready_list_lock, flags);
+
+ module_put(owner);
+
+ return err;
+}
+EXPORT_SYMBOL(add_random_ready_callback);
+
+/*
+ * Delete a previously registered readiness callback function.
+ */
+void del_random_ready_callback(struct random_ready_callback *rdy)
+{
+ unsigned long flags;
+ struct module *owner = NULL;
+
+ spin_lock_irqsave(&random_ready_list_lock, flags);
+ if (!list_empty(&rdy->list)) {
+ list_del_init(&rdy->list);
+ owner = rdy->owner;
+ }
+ spin_unlock_irqrestore(&random_ready_list_lock, flags);
+
+ module_put(owner);
+}
+EXPORT_SYMBOL(del_random_ready_callback);
+
+/*
* This function will use the architecture-specific hardware random
* number generator if it is available. The arch-specific hw RNG will
* almost certainly be faster than what we can do in software, but it
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
index 597fed423d7d..df2c1afa52b4 100644
--- a/drivers/clk/at91/clk-peripheral.c
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -29,7 +29,7 @@
#define PERIPHERAL_RSHIFT_MASK 0x3
#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
-#define PERIPHERAL_MAX_SHIFT 4
+#define PERIPHERAL_MAX_SHIFT 3
struct clk_peripheral {
struct clk_hw hw;
@@ -242,7 +242,7 @@ static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
return *parent_rate;
if (periph->range.max) {
- for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+ for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
cur_rate = *parent_rate >> shift;
if (cur_rate <= periph->range.max)
break;
@@ -254,7 +254,7 @@ static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
best_diff = cur_rate - rate;
best_rate = cur_rate;
- for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+ for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
cur_rate = *parent_rate >> shift;
if (cur_rate < rate)
cur_diff = rate - cur_rate;
@@ -289,7 +289,7 @@ static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
if (periph->range.max && rate > periph->range.max)
return -EINVAL;
- for (shift = 0; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+ for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
if (parent_rate >> shift == rate) {
periph->auto_div = false;
periph->div = shift;
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index 6ec79dbc0840..cbbe40377ad6 100644
--- a/drivers/clk/at91/clk-pll.c
+++ b/drivers/clk/at91/clk-pll.c
@@ -173,8 +173,7 @@ static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
int i = 0;
/* Check if parent_rate is a valid input rate */
- if (parent_rate < characteristics->input.min ||
- parent_rate > characteristics->input.max)
+ if (parent_rate < characteristics->input.min)
return -ERANGE;
/*
@@ -187,6 +186,15 @@ static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
if (!mindiv)
mindiv = 1;
+ if (parent_rate > characteristics->input.max) {
+ tmpdiv = DIV_ROUND_UP(parent_rate, characteristics->input.max);
+ if (tmpdiv > PLL_DIV_MAX)
+ return -ERANGE;
+
+ if (tmpdiv > mindiv)
+ mindiv = tmpdiv;
+ }
+
/*
* Calculate the maximum divider which is limited by PLL register
* layout (limited by the MUL or DIV field size).
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 69abb08cf146..eb8e5dc9076d 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -121,7 +121,7 @@ extern void __init of_at91sam9x5_clk_smd_setup(struct device_node *np,
struct at91_pmc *pmc);
#endif
-#if defined(CONFIG_HAVE_AT91_SMD)
+#if defined(CONFIG_HAVE_AT91_H32MX)
extern void __init of_sama5d4_clk_h32mx_setup(struct device_node *np,
struct at91_pmc *pmc);
#endif
diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c
index bfa1e64e267d..9b13a303d3f8 100644
--- a/drivers/clk/clk-s2mps11.c
+++ b/drivers/clk/clk-s2mps11.c
@@ -242,14 +242,12 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
goto err_reg;
}
- s2mps11_clk->lookup = clkdev_alloc(s2mps11_clk->clk,
+ s2mps11_clk->lookup = clkdev_create(s2mps11_clk->clk,
s2mps11_name(s2mps11_clk), NULL);
if (!s2mps11_clk->lookup) {
ret = -ENOMEM;
goto err_lup;
}
-
- clkdev_add(s2mps11_clk->lookup);
}
for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
index 44ea107cfc67..30335d3b99af 100644
--- a/drivers/clk/clk-si5351.c
+++ b/drivers/clk/clk-si5351.c
@@ -1128,13 +1128,6 @@ static int si5351_dt_parse(struct i2c_client *client,
if (!pdata)
return -ENOMEM;
- pdata->clk_xtal = of_clk_get(np, 0);
- if (!IS_ERR(pdata->clk_xtal))
- clk_put(pdata->clk_xtal);
- pdata->clk_clkin = of_clk_get(np, 1);
- if (!IS_ERR(pdata->clk_clkin))
- clk_put(pdata->clk_clkin);
-
/*
* property silabs,pll-source : <num src>, [<..>]
* allow to selectively set pll source
@@ -1328,8 +1321,22 @@ static int si5351_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, drvdata);
drvdata->client = client;
drvdata->variant = variant;
- drvdata->pxtal = pdata->clk_xtal;
- drvdata->pclkin = pdata->clk_clkin;
+ drvdata->pxtal = devm_clk_get(&client->dev, "xtal");
+ drvdata->pclkin = devm_clk_get(&client->dev, "clkin");
+
+ if (PTR_ERR(drvdata->pxtal) == -EPROBE_DEFER ||
+ PTR_ERR(drvdata->pclkin) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ /*
+ * Check for valid parent clock: VARIANT_A and VARIANT_B need XTAL,
+ * VARIANT_C can have CLKIN instead.
+ */
+ if (IS_ERR(drvdata->pxtal) &&
+ (drvdata->variant != SI5351_VARIANT_C || IS_ERR(drvdata->pclkin))) {
+ dev_err(&client->dev, "missing parent clock\n");
+ return -EINVAL;
+ }
drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config);
if (IS_ERR(drvdata->regmap)) {
@@ -1393,6 +1400,11 @@ static int si5351_i2c_probe(struct i2c_client *client,
}
}
+ if (!IS_ERR(drvdata->pxtal))
+ clk_prepare_enable(drvdata->pxtal);
+ if (!IS_ERR(drvdata->pclkin))
+ clk_prepare_enable(drvdata->pclkin);
+
/* register xtal input clock gate */
memset(&init, 0, sizeof(init));
init.name = si5351_input_names[0];
@@ -1407,7 +1419,8 @@ static int si5351_i2c_probe(struct i2c_client *client,
clk = devm_clk_register(&client->dev, &drvdata->xtal);
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n", init.name);
- return PTR_ERR(clk);
+ ret = PTR_ERR(clk);
+ goto err_clk;
}
/* register clkin input clock gate */
@@ -1425,7 +1438,8 @@ static int si5351_i2c_probe(struct i2c_client *client,
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n",
init.name);
- return PTR_ERR(clk);
+ ret = PTR_ERR(clk);
+ goto err_clk;
}
}
@@ -1447,7 +1461,8 @@ static int si5351_i2c_probe(struct i2c_client *client,
clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw);
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n", init.name);
- return -EINVAL;
+ ret = PTR_ERR(clk);
+ goto err_clk;
}
/* register PLLB or VXCO (Si5351B) */
@@ -1471,7 +1486,8 @@ static int si5351_i2c_probe(struct i2c_client *client,
clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw);
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n", init.name);
- return -EINVAL;
+ ret = PTR_ERR(clk);
+ goto err_clk;
}
/* register clk multisync and clk out divider */
@@ -1492,8 +1508,10 @@ static int si5351_i2c_probe(struct i2c_client *client,
num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL);
if (WARN_ON(!drvdata->msynth || !drvdata->clkout ||
- !drvdata->onecell.clks))
- return -ENOMEM;
+ !drvdata->onecell.clks)) {
+ ret = -ENOMEM;
+ goto err_clk;
+ }
for (n = 0; n < num_clocks; n++) {
drvdata->msynth[n].num = n;
@@ -1511,7 +1529,8 @@ static int si5351_i2c_probe(struct i2c_client *client,
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n",
init.name);
- return -EINVAL;
+ ret = PTR_ERR(clk);
+ goto err_clk;
}
}
@@ -1538,7 +1557,8 @@ static int si5351_i2c_probe(struct i2c_client *client,
if (IS_ERR(clk)) {
dev_err(&client->dev, "unable to register %s\n",
init.name);
- return -EINVAL;
+ ret = PTR_ERR(clk);
+ goto err_clk;
}
drvdata->onecell.clks[n] = clk;
@@ -1557,10 +1577,17 @@ static int si5351_i2c_probe(struct i2c_client *client,
&drvdata->onecell);
if (ret) {
dev_err(&client->dev, "unable to add clk provider\n");
- return ret;
+ goto err_clk;
}
return 0;
+
+err_clk:
+ if (!IS_ERR(drvdata->pxtal))
+ clk_disable_unprepare(drvdata->pxtal);
+ if (!IS_ERR(drvdata->pclkin))
+ clk_disable_unprepare(drvdata->pclkin);
+ return ret;
}
static const struct i2c_device_id si5351_i2c_ids[] = {
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 459ce9da13e0..5b0f41868b42 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1475,8 +1475,10 @@ static struct clk_core *__clk_set_parent_before(struct clk_core *clk,
*/
if (clk->prepare_count) {
clk_core_prepare(parent);
+ flags = clk_enable_lock();
clk_core_enable(parent);
clk_core_enable(clk);
+ clk_enable_unlock(flags);
}
/* update the clk tree topology */
@@ -1491,13 +1493,17 @@ static void __clk_set_parent_after(struct clk_core *core,
struct clk_core *parent,
struct clk_core *old_parent)
{
+ unsigned long flags;
+
/*
* Finish the migration of prepare state and undo the changes done
* for preventing a race with clk_enable().
*/
if (core->prepare_count) {
+ flags = clk_enable_lock();
clk_core_disable(core);
clk_core_disable(old_parent);
+ clk_enable_unlock(flags);
clk_core_unprepare(old_parent);
}
}
@@ -1525,8 +1531,10 @@ static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent,
clk_enable_unlock(flags);
if (clk->prepare_count) {
+ flags = clk_enable_lock();
clk_core_disable(clk);
clk_core_disable(parent);
+ clk_enable_unlock(flags);
clk_core_unprepare(parent);
}
return ret;
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 1fcb6ef2cdac..c0eaf0973bd2 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -177,7 +177,7 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id)
if (!cl)
goto out;
- clk = __clk_create_clk(__clk_get_hw(cl->clk), dev_id, con_id);
+ clk = __clk_create_clk(cl->clk_hw, dev_id, con_id);
if (IS_ERR(clk))
goto out;
@@ -215,18 +215,26 @@ void clk_put(struct clk *clk)
}
EXPORT_SYMBOL(clk_put);
-void clkdev_add(struct clk_lookup *cl)
+static void __clkdev_add(struct clk_lookup *cl)
{
mutex_lock(&clocks_mutex);
list_add_tail(&cl->node, &clocks);
mutex_unlock(&clocks_mutex);
}
+
+void clkdev_add(struct clk_lookup *cl)
+{
+ if (!cl->clk_hw)
+ cl->clk_hw = __clk_get_hw(cl->clk);
+ __clkdev_add(cl);
+}
EXPORT_SYMBOL(clkdev_add);
-void __init clkdev_add_table(struct clk_lookup *cl, size_t num)
+void clkdev_add_table(struct clk_lookup *cl, size_t num)
{
mutex_lock(&clocks_mutex);
while (num--) {
+ cl->clk_hw = __clk_get_hw(cl->clk);
list_add_tail(&cl->node, &clocks);
cl++;
}
@@ -243,7 +251,7 @@ struct clk_lookup_alloc {
};
static struct clk_lookup * __init_refok
-vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
+vclkdev_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
va_list ap)
{
struct clk_lookup_alloc *cla;
@@ -252,7 +260,7 @@ vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
if (!cla)
return NULL;
- cla->cl.clk = clk;
+ cla->cl.clk_hw = hw;
if (con_id) {
strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
cla->cl.con_id = cla->con_id;
@@ -266,6 +274,19 @@ vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
return &cla->cl;
}
+static struct clk_lookup *
+vclkdev_create(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
+ va_list ap)
+{
+ struct clk_lookup *cl;
+
+ cl = vclkdev_alloc(hw, con_id, dev_fmt, ap);
+ if (cl)
+ __clkdev_add(cl);
+
+ return cl;
+}
+
struct clk_lookup * __init_refok
clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
{
@@ -273,28 +294,49 @@ clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
va_list ap;
va_start(ap, dev_fmt);
- cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);
+ cl = vclkdev_alloc(__clk_get_hw(clk), con_id, dev_fmt, ap);
va_end(ap);
return cl;
}
EXPORT_SYMBOL(clkdev_alloc);
-int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
- struct device *dev)
+/**
+ * clkdev_create - allocate and add a clkdev lookup structure
+ * @clk: struct clk to associate with all clk_lookups
+ * @con_id: connection ID string on device
+ * @dev_fmt: format string describing device name
+ *
+ * Returns a clk_lookup structure, which can be later unregistered and
+ * freed.
+ */
+struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id,
+ const char *dev_fmt, ...)
{
- struct clk *r = clk_get(dev, id);
+ struct clk_lookup *cl;
+ va_list ap;
+
+ va_start(ap, dev_fmt);
+ cl = vclkdev_create(__clk_get_hw(clk), con_id, dev_fmt, ap);
+ va_end(ap);
+
+ return cl;
+}
+EXPORT_SYMBOL_GPL(clkdev_create);
+
+int clk_add_alias(const char *alias, const char *alias_dev_name,
+ const char *con_id, struct device *dev)
+{
+ struct clk *r = clk_get(dev, con_id);
struct clk_lookup *l;
if (IS_ERR(r))
return PTR_ERR(r);
- l = clkdev_alloc(r, alias, alias_dev_name);
+ l = clkdev_create(r, alias, "%s", alias_dev_name);
clk_put(r);
- if (!l)
- return -ENODEV;
- clkdev_add(l);
- return 0;
+
+ return l ? 0 : -ENODEV;
}
EXPORT_SYMBOL(clk_add_alias);
@@ -334,15 +376,10 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
return PTR_ERR(clk);
va_start(ap, dev_fmt);
- cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);
+ cl = vclkdev_create(__clk_get_hw(clk), con_id, dev_fmt, ap);
va_end(ap);
- if (!cl)
- return -ENOMEM;
-
- clkdev_add(cl);
-
- return 0;
+ return cl ? 0 : -ENOMEM;
}
EXPORT_SYMBOL(clk_register_clkdev);
@@ -365,8 +402,8 @@ int clk_register_clkdevs(struct clk *clk, struct clk_lookup *cl, size_t num)
return PTR_ERR(clk);
for (i = 0; i < num; i++, cl++) {
- cl->clk = clk;
- clkdev_add(cl);
+ cl->clk_hw = __clk_get_hw(clk);
+ __clkdev_add(cl);
}
return 0;
diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c
index d3458474eb3a..c66f7bc2ae87 100644
--- a/drivers/clk/qcom/gcc-msm8916.c
+++ b/drivers/clk/qcom/gcc-msm8916.c
@@ -71,8 +71,8 @@ static const char *gcc_xo_gpll0_bimc[] = {
static const struct parent_map gcc_xo_gpll0a_gpll1_gpll2a_map[] = {
{ P_XO, 0 },
{ P_GPLL0_AUX, 3 },
- { P_GPLL2_AUX, 2 },
{ P_GPLL1, 1 },
+ { P_GPLL2_AUX, 2 },
};
static const char *gcc_xo_gpll0a_gpll1_gpll2a[] = {
@@ -1115,7 +1115,7 @@ static struct clk_rcg2 usb_hs_system_clk_src = {
static const struct freq_tbl ftbl_gcc_venus0_vcodec0_clk[] = {
F(100000000, P_GPLL0, 8, 0, 0),
F(160000000, P_GPLL0, 5, 0, 0),
- F(228570000, P_GPLL0, 5, 0, 0),
+ F(228570000, P_GPLL0, 3.5, 0, 0),
{ }
};
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
index 17e9af7fe81f..a17683b2cf27 100644
--- a/drivers/clk/samsung/Makefile
+++ b/drivers/clk/samsung/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o
obj-$(CONFIG_SOC_EXYNOS5260) += clk-exynos5260.o
obj-$(CONFIG_SOC_EXYNOS5410) += clk-exynos5410.o
obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o
-obj-$(CONFIG_ARCH_EXYNOS5433) += clk-exynos5433.o
+obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos5433.o
obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o
obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o
obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-clkout.o
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index 07d666cc6a29..bea4a173eef5 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -271,6 +271,7 @@ static const struct samsung_clk_reg_dump exynos5420_set_clksrc[] = {
{ .offset = SRC_MASK_PERIC0, .value = 0x11111110, },
{ .offset = SRC_MASK_PERIC1, .value = 0x11111100, },
{ .offset = SRC_MASK_ISP, .value = 0x11111000, },
+ { .offset = GATE_BUS_TOP, .value = 0xffffffff, },
{ .offset = GATE_BUS_DISP1, .value = 0xffffffff, },
{ .offset = GATE_IP_PERIC, .value = 0xffffffff, },
};
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index 387e3e39e635..9e04ae2bb4d7 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -748,7 +748,7 @@ static struct samsung_pll_rate_table exynos5443_pll_rates[] = {
PLL_35XX_RATE(825000000U, 275, 4, 1),
PLL_35XX_RATE(800000000U, 400, 6, 1),
PLL_35XX_RATE(733000000U, 733, 12, 1),
- PLL_35XX_RATE(700000000U, 360, 6, 1),
+ PLL_35XX_RATE(700000000U, 175, 3, 1),
PLL_35XX_RATE(667000000U, 222, 4, 1),
PLL_35XX_RATE(633000000U, 211, 4, 1),
PLL_35XX_RATE(600000000U, 500, 5, 2),
@@ -760,14 +760,14 @@ static struct samsung_pll_rate_table exynos5443_pll_rates[] = {
PLL_35XX_RATE(444000000U, 370, 5, 2),
PLL_35XX_RATE(420000000U, 350, 5, 2),
PLL_35XX_RATE(400000000U, 400, 6, 2),
- PLL_35XX_RATE(350000000U, 360, 6, 2),
+ PLL_35XX_RATE(350000000U, 350, 6, 2),
PLL_35XX_RATE(333000000U, 222, 4, 2),
PLL_35XX_RATE(300000000U, 500, 5, 3),
PLL_35XX_RATE(266000000U, 532, 6, 3),
PLL_35XX_RATE(200000000U, 400, 6, 3),
PLL_35XX_RATE(166000000U, 332, 6, 3),
PLL_35XX_RATE(160000000U, 320, 6, 3),
- PLL_35XX_RATE(133000000U, 552, 6, 4),
+ PLL_35XX_RATE(133000000U, 532, 6, 4),
PLL_35XX_RATE(100000000U, 400, 6, 4),
{ /* sentinel */ }
};
@@ -1490,7 +1490,7 @@ static struct samsung_gate_clock mif_gate_clks[] __initdata = {
/* ENABLE_PCLK_MIF_SECURE_MONOTONIC_CNT */
GATE(CLK_PCLK_MONOTONIC_CNT, "pclk_monotonic_cnt", "div_aclk_mif_133",
- ENABLE_PCLK_MIF_SECURE_RTC, 0, 0, 0),
+ ENABLE_PCLK_MIF_SECURE_MONOTONIC_CNT, 0, 0, 0),
/* ENABLE_PCLK_MIF_SECURE_RTC */
GATE(CLK_PCLK_RTC, "pclk_rtc", "div_aclk_mif_133",
@@ -3665,7 +3665,7 @@ static struct samsung_gate_clock apollo_gate_clks[] __initdata = {
ENABLE_SCLK_APOLLO, 3, CLK_IGNORE_UNUSED, 0),
GATE(CLK_SCLK_HPM_APOLLO, "sclk_hpm_apollo", "div_sclk_hpm_apollo",
ENABLE_SCLK_APOLLO, 1, CLK_IGNORE_UNUSED, 0),
- GATE(CLK_SCLK_APOLLO, "sclk_apollo", "div_apollo_pll",
+ GATE(CLK_SCLK_APOLLO, "sclk_apollo", "div_apollo2",
ENABLE_SCLK_APOLLO, 0, CLK_IGNORE_UNUSED, 0),
};
@@ -3927,7 +3927,7 @@ CLK_OF_DECLARE(exynos5433_cmu_atlas, "samsung,exynos5433-cmu-atlas",
#define ENABLE_PCLK_MSCL 0x0900
#define ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER0 0x0904
#define ENABLE_PCLK_MSCL_SECURE_SMMU_M2MSCALER1 0x0908
-#define ENABLE_PCLK_MSCL_SECURE_SMMU_JPEG 0x000c
+#define ENABLE_PCLK_MSCL_SECURE_SMMU_JPEG 0x090c
#define ENABLE_SCLK_MSCL 0x0a00
#define ENABLE_IP_MSCL0 0x0b00
#define ENABLE_IP_MSCL1 0x0b04
diff --git a/drivers/clk/ti/clk-7xx.c b/drivers/clk/ti/clk-7xx.c
index 5d2217ae4478..63b8323df918 100644
--- a/drivers/clk/ti/clk-7xx.c
+++ b/drivers/clk/ti/clk-7xx.c
@@ -305,13 +305,14 @@ static struct ti_dt_clk dra7xx_clks[] = {
DT_CLK("4882c000.timer", "timer_sys_ck", "timer_sys_clk_div"),
DT_CLK("4882e000.timer", "timer_sys_ck", "timer_sys_clk_div"),
DT_CLK(NULL, "sys_clkin", "sys_clkin1"),
+ DT_CLK(NULL, "dss_deshdcp_clk", "dss_deshdcp_clk"),
{ .node_name = NULL },
};
int __init dra7xx_dt_clk_init(void)
{
int rc;
- struct clk *abe_dpll_mux, *sys_clkin2, *dpll_ck;
+ struct clk *abe_dpll_mux, *sys_clkin2, *dpll_ck, *hdcp_ck;
ti_dt_clocks_register(dra7xx_clks);
@@ -347,5 +348,10 @@ int __init dra7xx_dt_clk_init(void)
if (rc)
pr_err("%s: failed to set USB_DPLL M2 OUT\n", __func__);
+ hdcp_ck = clk_get_sys(NULL, "dss_deshdcp_clk");
+ rc = clk_prepare_enable(hdcp_ck);
+ if (rc)
+ pr_err("%s: failed to set dss_deshdcp_clk\n", __func__);
+
return rc;
}
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 51d7865fdddb..32164ba3d36a 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -106,6 +106,16 @@ config CLKSRC_EFM32
Support to use the timers of EFM32 SoCs as clock source and clock
event device.
+config CLKSRC_LPC32XX
+ bool
+ select CLKSRC_MMIO
+ select CLKSRC_OF
+
+config CLKSRC_STM32
+ bool "Clocksource for STM32 SoCs" if !ARCH_STM32
+ depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
+ select CLKSRC_MMIO
+
config ARM_ARCH_TIMER
bool
select CLKSRC_OF if OF
@@ -139,6 +149,13 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
help
Use ARM global timer clock source as sched_clock
+config ARMV7M_SYSTICK
+ bool
+ select CLKSRC_OF if OF
+ select CLKSRC_MMIO
+ help
+ This options enables support for the ARMv7M system timer unit
+
config ATMEL_PIT
select CLKSRC_OF if OF
def_bool SOC_AT91SAM9 || SOC_SAMA5
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 5b85f6adb258..1831a588b988 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -36,7 +36,9 @@ obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o
obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm_kona_timer.o
obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o
obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o
+obj-$(CONFIG_CLKSRC_STM32) += timer-stm32.o
obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
+obj-$(CONFIG_CLKSRC_LPC32XX) += time-lpc32xx.o
obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
obj-$(CONFIG_FSL_FTM_TIMER) += fsl_ftm_timer.o
obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
@@ -45,6 +47,7 @@ obj-$(CONFIG_MTK_TIMER) += mtk_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
+obj-$(CONFIG_ARMV7M_SYSTICK) += armv7m_systick.o
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o
obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o
diff --git a/drivers/clocksource/armv7m_systick.c b/drivers/clocksource/armv7m_systick.c
new file mode 100644
index 000000000000..addfd2c64f54
--- /dev/null
+++ b/drivers/clocksource/armv7m_systick.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) Maxime Coquelin 2015
+ * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/bitops.h>
+
+#define SYST_CSR 0x00
+#define SYST_RVR 0x04
+#define SYST_CVR 0x08
+#define SYST_CALIB 0x0c
+
+#define SYST_CSR_ENABLE BIT(0)
+
+#define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF
+
+static void __init system_timer_of_register(struct device_node *np)
+{
+ struct clk *clk = NULL;
+ void __iomem *base;
+ u32 rate;
+ int ret;
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_warn("system-timer: invalid base address\n");
+ return;
+ }
+
+ ret = of_property_read_u32(np, "clock-frequency", &rate);
+ if (ret) {
+ clk = of_clk_get(np, 0);
+ if (IS_ERR(clk))
+ goto out_unmap;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto out_clk_put;
+
+ rate = clk_get_rate(clk);
+ if (!rate)
+ goto out_clk_disable;
+ }
+
+ writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
+ writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
+
+ ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer", rate,
+ 200, 24, clocksource_mmio_readl_down);
+ if (ret) {
+ pr_err("failed to init clocksource (%d)\n", ret);
+ if (clk)
+ goto out_clk_disable;
+ else
+ goto out_unmap;
+ }
+
+ pr_info("ARM System timer initialized as clocksource\n");
+
+ return;
+
+out_clk_disable:
+ clk_disable_unprepare(clk);
+out_clk_put:
+ clk_put(clk);
+out_unmap:
+ iounmap(base);
+ pr_warn("ARM System timer register failed (%d)\n", ret);
+}
+
+CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
+ system_timer_of_register);
diff --git a/drivers/clocksource/asm9260_timer.c b/drivers/clocksource/asm9260_timer.c
index 2c9c993727c8..4c2ba59897e8 100644
--- a/drivers/clocksource/asm9260_timer.c
+++ b/drivers/clocksource/asm9260_timer.c
@@ -178,7 +178,7 @@ static void __init asm9260_timer_init(struct device_node *np)
unsigned long rate;
priv.base = of_io_request_and_map(np, 0, np->name);
- if (!priv.base)
+ if (IS_ERR(priv.base))
panic("%s: unable to map resource", np->name);
clk = of_clk_get(np, 0);
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index 83564c9cfdbe..935b05936dbd 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -209,7 +209,7 @@ static void exynos4_frc_resume(struct clocksource *cs)
exynos4_mct_frc_start();
}
-struct clocksource mct_frc = {
+static struct clocksource mct_frc = {
.name = "mct-frc",
.rating = 400,
.read = exynos4_frc_read,
@@ -413,7 +413,7 @@ static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
}
}
-static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
+static void exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
{
struct clock_event_device *evt = &mevt->evt;
@@ -426,12 +426,8 @@ static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
exynos4_mct_tick_stop(mevt);
/* Clear the MCT tick interrupt */
- if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
+ if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1)
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
- return 1;
- } else {
- return 0;
- }
}
static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
@@ -564,18 +560,6 @@ out_irq:
free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick);
}
-void __init mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1)
-{
- mct_irqs[MCT_G0_IRQ] = irq_g0;
- mct_irqs[MCT_L0_IRQ] = irq_l0;
- mct_irqs[MCT_L1_IRQ] = irq_l1;
- mct_int_type = MCT_INT_SPI;
-
- exynos4_timer_resources(NULL, base);
- exynos4_clocksource_init();
- exynos4_clockevent_init();
-}
-
static void __init mct_init_dt(struct device_node *np, unsigned int int_type)
{
u32 nr_irqs, i;
diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
index 098c542e5c53..cba2d015564c 100644
--- a/drivers/clocksource/qcom-timer.c
+++ b/drivers/clocksource/qcom-timer.c
@@ -40,8 +40,6 @@
#define GPT_HZ 32768
-#define MSM_DGT_SHIFT 5
-
static void __iomem *event_base;
static void __iomem *sts_base;
@@ -232,7 +230,6 @@ err:
register_current_timer_delay(&msm_delay_timer);
}
-#ifdef CONFIG_ARCH_QCOM
static void __init msm_dt_timer_init(struct device_node *np)
{
u32 freq;
@@ -285,59 +282,3 @@ static void __init msm_dt_timer_init(struct device_node *np)
}
CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
-#else
-
-static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
- u32 sts)
-{
- void __iomem *base;
-
- base = ioremap(addr, SZ_256);
- if (!base) {
- pr_err("Failed to map timer base\n");
- return -ENOMEM;
- }
- event_base = base + event;
- source_base = base + source;
- if (sts)
- sts_base = base + sts;
-
- return 0;
-}
-
-static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
-{
- /*
- * Shift timer count down by a constant due to unreliable lower bits
- * on some targets.
- */
- return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
-}
-
-void __init msm7x01_timer_init(void)
-{
- struct clocksource *cs = &msm_clocksource;
-
- if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0))
- return;
- cs->read = msm_read_timer_count_shift;
- cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
- /* 600 KHz */
- msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7,
- false);
-}
-
-void __init msm7x30_timer_init(void)
-{
- if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80))
- return;
- msm_timer_init(24576000 / 4, 32, 1, false);
-}
-
-void __init qsd8x50_timer_init(void)
-{
- if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34))
- return;
- msm_timer_init(19200000 / 4, 32, 7, false);
-}
-#endif
diff --git a/drivers/clocksource/time-lpc32xx.c b/drivers/clocksource/time-lpc32xx.c
new file mode 100644
index 000000000000..a1c06a2bc77c
--- /dev/null
+++ b/drivers/clocksource/time-lpc32xx.c
@@ -0,0 +1,272 @@
+/*
+ * Clocksource driver for NXP LPC32xx/18xx/43xx timer
+ *
+ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
+ *
+ * Based on:
+ * time-efm32 Copyright (C) 2013 Pengutronix
+ * mach-lpc32xx/timer.c Copyright (C) 2009 - 2010 NXP Semiconductors
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/sched_clock.h>
+
+#define LPC32XX_TIMER_IR 0x000
+#define LPC32XX_TIMER_IR_MR0INT BIT(0)
+#define LPC32XX_TIMER_TCR 0x004
+#define LPC32XX_TIMER_TCR_CEN BIT(0)
+#define LPC32XX_TIMER_TCR_CRST BIT(1)
+#define LPC32XX_TIMER_TC 0x008
+#define LPC32XX_TIMER_PR 0x00c
+#define LPC32XX_TIMER_MCR 0x014
+#define LPC32XX_TIMER_MCR_MR0I BIT(0)
+#define LPC32XX_TIMER_MCR_MR0R BIT(1)
+#define LPC32XX_TIMER_MCR_MR0S BIT(2)
+#define LPC32XX_TIMER_MR0 0x018
+#define LPC32XX_TIMER_CTCR 0x070
+
+struct lpc32xx_clock_event_ddata {
+ struct clock_event_device evtdev;
+ void __iomem *base;
+};
+
+/* Needed for the sched clock */
+static void __iomem *clocksource_timer_counter;
+
+static u64 notrace lpc32xx_read_sched_clock(void)
+{
+ return readl(clocksource_timer_counter);
+}
+
+static int lpc32xx_clkevt_next_event(unsigned long delta,
+ struct clock_event_device *evtdev)
+{
+ struct lpc32xx_clock_event_ddata *ddata =
+ container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev);
+
+ /*
+ * Place timer in reset and program the delta in the prescale
+ * register (PR). When the prescale counter matches the value
+ * in PR the counter register is incremented and the compare
+ * match will trigger. After setup the timer is released from
+ * reset and enabled.
+ */
+ writel_relaxed(LPC32XX_TIMER_TCR_CRST, ddata->base + LPC32XX_TIMER_TCR);
+ writel_relaxed(delta, ddata->base + LPC32XX_TIMER_PR);
+ writel_relaxed(LPC32XX_TIMER_TCR_CEN, ddata->base + LPC32XX_TIMER_TCR);
+
+ return 0;
+}
+
+static int lpc32xx_clkevt_shutdown(struct clock_event_device *evtdev)
+{
+ struct lpc32xx_clock_event_ddata *ddata =
+ container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev);
+
+ /* Disable the timer */
+ writel_relaxed(0, ddata->base + LPC32XX_TIMER_TCR);
+
+ return 0;
+}
+
+static int lpc32xx_clkevt_oneshot(struct clock_event_device *evtdev)
+{
+ /*
+ * When using oneshot, we must also disable the timer
+ * to wait for the first call to set_next_event().
+ */
+ return lpc32xx_clkevt_shutdown(evtdev);
+}
+
+static irqreturn_t lpc32xx_clock_event_handler(int irq, void *dev_id)
+{
+ struct lpc32xx_clock_event_ddata *ddata = dev_id;
+
+ /* Clear match on channel 0 */
+ writel_relaxed(LPC32XX_TIMER_IR_MR0INT, ddata->base + LPC32XX_TIMER_IR);
+
+ ddata->evtdev.event_handler(&ddata->evtdev);
+
+ return IRQ_HANDLED;
+}
+
+static struct lpc32xx_clock_event_ddata lpc32xx_clk_event_ddata = {
+ .evtdev = {
+ .name = "lpc3220 clockevent",
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .rating = 300,
+ .set_next_event = lpc32xx_clkevt_next_event,
+ .set_state_shutdown = lpc32xx_clkevt_shutdown,
+ .set_state_oneshot = lpc32xx_clkevt_oneshot,
+ },
+};
+
+static int __init lpc32xx_clocksource_init(struct device_node *np)
+{
+ void __iomem *base;
+ unsigned long rate;
+ struct clk *clk;
+ int ret;
+
+ clk = of_clk_get_by_name(np, "timerclk");
+ if (IS_ERR(clk)) {
+ pr_err("clock get failed (%lu)\n", PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ pr_err("clock enable failed (%d)\n", ret);
+ goto err_clk_enable;
+ }
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("unable to map registers\n");
+ ret = -EADDRNOTAVAIL;
+ goto err_iomap;
+ }
+
+ /*
+ * Disable and reset timer then set it to free running timer
+ * mode (CTCR) with no prescaler (PR) or match operations (MCR).
+ * After setup the timer is released from reset and enabled.
+ */
+ writel_relaxed(LPC32XX_TIMER_TCR_CRST, base + LPC32XX_TIMER_TCR);
+ writel_relaxed(0, base + LPC32XX_TIMER_PR);
+ writel_relaxed(0, base + LPC32XX_TIMER_MCR);
+ writel_relaxed(0, base + LPC32XX_TIMER_CTCR);
+ writel_relaxed(LPC32XX_TIMER_TCR_CEN, base + LPC32XX_TIMER_TCR);
+
+ rate = clk_get_rate(clk);
+ ret = clocksource_mmio_init(base + LPC32XX_TIMER_TC, "lpc3220 timer",
+ rate, 300, 32, clocksource_mmio_readl_up);
+ if (ret) {
+ pr_err("failed to init clocksource (%d)\n", ret);
+ goto err_clocksource_init;
+ }
+
+ clocksource_timer_counter = base + LPC32XX_TIMER_TC;
+ sched_clock_register(lpc32xx_read_sched_clock, 32, rate);
+
+ return 0;
+
+err_clocksource_init:
+ iounmap(base);
+err_iomap:
+ clk_disable_unprepare(clk);
+err_clk_enable:
+ clk_put(clk);
+ return ret;
+}
+
+static int __init lpc32xx_clockevent_init(struct device_node *np)
+{
+ void __iomem *base;
+ unsigned long rate;
+ struct clk *clk;
+ int ret, irq;
+
+ clk = of_clk_get_by_name(np, "timerclk");
+ if (IS_ERR(clk)) {
+ pr_err("clock get failed (%lu)\n", PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ pr_err("clock enable failed (%d)\n", ret);
+ goto err_clk_enable;
+ }
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("unable to map registers\n");
+ ret = -EADDRNOTAVAIL;
+ goto err_iomap;
+ }
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (!irq) {
+ pr_err("get irq failed\n");
+ ret = -ENOENT;
+ goto err_irq;
+ }
+
+ /*
+ * Disable timer and clear any pending interrupt (IR) on match
+ * channel 0 (MR0). Configure a compare match value of 1 on MR0
+ * and enable interrupt, reset on match and stop on match (MCR).
+ */
+ writel_relaxed(0, base + LPC32XX_TIMER_TCR);
+ writel_relaxed(0, base + LPC32XX_TIMER_CTCR);
+ writel_relaxed(LPC32XX_TIMER_IR_MR0INT, base + LPC32XX_TIMER_IR);
+ writel_relaxed(1, base + LPC32XX_TIMER_MR0);
+ writel_relaxed(LPC32XX_TIMER_MCR_MR0I | LPC32XX_TIMER_MCR_MR0R |
+ LPC32XX_TIMER_MCR_MR0S, base + LPC32XX_TIMER_MCR);
+
+ rate = clk_get_rate(clk);
+ lpc32xx_clk_event_ddata.base = base;
+ clockevents_config_and_register(&lpc32xx_clk_event_ddata.evtdev,
+ rate, 1, -1);
+
+ ret = request_irq(irq, lpc32xx_clock_event_handler,
+ IRQF_TIMER | IRQF_IRQPOLL, "lpc3220 clockevent",
+ &lpc32xx_clk_event_ddata);
+ if (ret) {
+ pr_err("request irq failed\n");
+ goto err_irq;
+ }
+
+ return 0;
+
+err_irq:
+ iounmap(base);
+err_iomap:
+ clk_disable_unprepare(clk);
+err_clk_enable:
+ clk_put(clk);
+ return ret;
+}
+
+/*
+ * This function asserts that we have exactly one clocksource and one
+ * clock_event_device in the end.
+ */
+static void __init lpc32xx_timer_init(struct device_node *np)
+{
+ static int has_clocksource, has_clockevent;
+ int ret;
+
+ if (!has_clocksource) {
+ ret = lpc32xx_clocksource_init(np);
+ if (!ret) {
+ has_clocksource = 1;
+ return;
+ }
+ }
+
+ if (!has_clockevent) {
+ ret = lpc32xx_clockevent_init(np);
+ if (!ret) {
+ has_clockevent = 1;
+ return;
+ }
+ }
+}
+CLOCKSOURCE_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
diff --git a/drivers/clocksource/timer-integrator-ap.c b/drivers/clocksource/timer-integrator-ap.c
index b9efd30513d5..c97d1980c0f8 100644
--- a/drivers/clocksource/timer-integrator-ap.c
+++ b/drivers/clocksource/timer-integrator-ap.c
@@ -166,7 +166,7 @@ static void __init integrator_ap_timer_init_of(struct device_node *node)
struct device_node *sec_node;
base = of_io_request_and_map(node, 0, "integrator-timer");
- if (!base)
+ if (IS_ERR(base))
return;
clk = of_clk_get(node, 0);
diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
new file mode 100644
index 000000000000..a97e8b50701c
--- /dev/null
+++ b/drivers/clocksource/timer-stm32.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) Maxime Coquelin 2015
+ * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Inspired by time-efm32.c from Uwe Kleine-Koenig
+ */
+
+#include <linux/kernel.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+#define TIM_CR1 0x00
+#define TIM_DIER 0x0c
+#define TIM_SR 0x10
+#define TIM_EGR 0x14
+#define TIM_PSC 0x28
+#define TIM_ARR 0x2c
+
+#define TIM_CR1_CEN BIT(0)
+#define TIM_CR1_OPM BIT(3)
+#define TIM_CR1_ARPE BIT(7)
+
+#define TIM_DIER_UIE BIT(0)
+
+#define TIM_SR_UIF BIT(0)
+
+#define TIM_EGR_UG BIT(0)
+
+struct stm32_clock_event_ddata {
+ struct clock_event_device evtdev;
+ unsigned periodic_top;
+ void __iomem *base;
+};
+
+static void stm32_clock_event_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evtdev)
+{
+ struct stm32_clock_event_ddata *data =
+ container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
+ void *base = data->base;
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ writel_relaxed(data->periodic_top, base + TIM_ARR);
+ writel_relaxed(TIM_CR1_ARPE | TIM_CR1_CEN, base + TIM_CR1);
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ default:
+ writel_relaxed(0, base + TIM_CR1);
+ break;
+ }
+}
+
+static int stm32_clock_event_set_next_event(unsigned long evt,
+ struct clock_event_device *evtdev)
+{
+ struct stm32_clock_event_ddata *data =
+ container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
+
+ writel_relaxed(evt, data->base + TIM_ARR);
+ writel_relaxed(TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN,
+ data->base + TIM_CR1);
+
+ return 0;
+}
+
+static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
+{
+ struct stm32_clock_event_ddata *data = dev_id;
+
+ writel_relaxed(0, data->base + TIM_SR);
+
+ data->evtdev.event_handler(&data->evtdev);
+
+ return IRQ_HANDLED;
+}
+
+static struct stm32_clock_event_ddata clock_event_ddata = {
+ .evtdev = {
+ .name = "stm32 clockevent",
+ .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
+ .set_mode = stm32_clock_event_set_mode,
+ .set_next_event = stm32_clock_event_set_next_event,
+ .rating = 200,
+ },
+};
+
+static void __init stm32_clockevent_init(struct device_node *np)
+{
+ struct stm32_clock_event_ddata *data = &clock_event_ddata;
+ struct clk *clk;
+ struct reset_control *rstc;
+ unsigned long rate, max_delta;
+ int irq, ret, bits, prescaler = 1;
+
+ clk = of_clk_get(np, 0);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ pr_err("failed to get clock for clockevent (%d)\n", ret);
+ goto err_clk_get;
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ pr_err("failed to enable timer clock for clockevent (%d)\n",
+ ret);
+ goto err_clk_enable;
+ }
+
+ rate = clk_get_rate(clk);
+
+ rstc = of_reset_control_get(np, NULL);
+ if (!IS_ERR(rstc)) {
+ reset_control_assert(rstc);
+ reset_control_deassert(rstc);
+ }
+
+ data->base = of_iomap(np, 0);
+ if (!data->base) {
+ pr_err("failed to map registers for clockevent\n");
+ goto err_iomap;
+ }
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (!irq) {
+ pr_err("%s: failed to get irq.\n", np->full_name);
+ goto err_get_irq;
+ }
+
+ /* Detect whether the timer is 16 or 32 bits */
+ writel_relaxed(~0U, data->base + TIM_ARR);
+ max_delta = readl_relaxed(data->base + TIM_ARR);
+ if (max_delta == ~0U) {
+ prescaler = 1;
+ bits = 32;
+ } else {
+ prescaler = 1024;
+ bits = 16;
+ }
+ writel_relaxed(0, data->base + TIM_ARR);
+
+ writel_relaxed(prescaler - 1, data->base + TIM_PSC);
+ writel_relaxed(TIM_EGR_UG, data->base + TIM_EGR);
+ writel_relaxed(TIM_DIER_UIE, data->base + TIM_DIER);
+ writel_relaxed(0, data->base + TIM_SR);
+
+ data->periodic_top = DIV_ROUND_CLOSEST(rate, prescaler * HZ);
+
+ clockevents_config_and_register(&data->evtdev,
+ DIV_ROUND_CLOSEST(rate, prescaler),
+ 0x1, max_delta);
+
+ ret = request_irq(irq, stm32_clock_event_handler, IRQF_TIMER,
+ "stm32 clockevent", data);
+ if (ret) {
+ pr_err("%s: failed to request irq.\n", np->full_name);
+ goto err_get_irq;
+ }
+
+ pr_info("%s: STM32 clockevent driver initialized (%d bits)\n",
+ np->full_name, bits);
+
+ return;
+
+err_get_irq:
+ iounmap(data->base);
+err_iomap:
+ clk_disable_unprepare(clk);
+err_clk_enable:
+ clk_put(clk);
+err_clk_get:
+ return;
+}
+
+CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);
diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c
index 28aa4b7bb602..0ffb4ea7c925 100644
--- a/drivers/clocksource/timer-sun5i.c
+++ b/drivers/clocksource/timer-sun5i.c
@@ -324,7 +324,7 @@ static void __init sun5i_timer_init(struct device_node *node)
int irq;
timer_base = of_io_request_and_map(node, 0, of_node_full_name(node));
- if (!timer_base)
+ if (IS_ERR(timer_base))
panic("Can't map registers");
irq = irq_of_parse_and_map(node, 0);
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 4f3dbc8cf729..611cb09239eb 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -5,7 +5,7 @@
# big LITTLE core layer and glue drivers
config ARM_BIG_LITTLE_CPUFREQ
tristate "Generic ARM big LITTLE CPUfreq driver"
- depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK
+ depends on (ARM_CPU_TOPOLOGY || ARM64) && HAVE_CLK
select PM_OPP
help
This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index b0c18ed8d83f..0136dfcdabf0 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -699,13 +699,14 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
dmi_check_system(sw_any_bug_dmi_table);
if (bios_with_sw_any_bug && !policy_is_shared(policy)) {
policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
- cpumask_copy(policy->cpus, cpu_core_mask(cpu));
+ cpumask_copy(policy->cpus, topology_core_cpumask(cpu));
}
if (check_amd_hwpstate_cpu(cpu) && !acpi_pstate_strict) {
cpumask_clear(policy->cpus);
cpumask_set_cpu(cpu, policy->cpus);
- cpumask_copy(data->freqdomain_cpus, cpu_sibling_mask(cpu));
+ cpumask_copy(data->freqdomain_cpus,
+ topology_sibling_cpumask(cpu));
policy->shared_type = CPUFREQ_SHARED_TYPE_HW;
pr_info_once(PFX "overriding BIOS provided _PSD data\n");
}
diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c
index e1a6ba66a7f5..f1e42f8ce0fc 100644
--- a/drivers/cpufreq/arm_big_little.c
+++ b/drivers/cpufreq/arm_big_little.c
@@ -31,7 +31,6 @@
#include <linux/slab.h>
#include <linux/topology.h>
#include <linux/types.h>
-#include <asm/bL_switcher.h>
#include "arm_big_little.h"
@@ -41,12 +40,16 @@
#define MAX_CLUSTERS 2
#ifdef CONFIG_BL_SWITCHER
+#include <asm/bL_switcher.h>
static bool bL_switching_enabled;
#define is_bL_switching_enabled() bL_switching_enabled
#define set_switching_enabled(x) (bL_switching_enabled = (x))
#else
#define is_bL_switching_enabled() false
#define set_switching_enabled(x) do { } while (0)
+#define bL_switch_request(...) do { } while (0)
+#define bL_switcher_put_enabled() do { } while (0)
+#define bL_switcher_get_enabled() do { } while (0)
#endif
#define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq)
@@ -186,6 +189,15 @@ bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
mutex_unlock(&cluster_lock[old_cluster]);
}
+ /*
+ * FIXME: clk_set_rate has to handle the case where clk_change_rate
+ * can fail due to hardware or firmware issues. Until the clk core
+ * layer is fixed, we can check here. In most of the cases we will
+ * be reading only the cached value anyway. This needs to be removed
+ * once clk core is fixed.
+ */
+ if (bL_cpufreq_get_rate(cpu) != new_rate)
+ return -EIO;
return 0;
}
@@ -322,7 +334,6 @@ static void put_cluster_clk_and_freq_table(struct device *cpu_dev)
static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
{
u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
- char name[14] = "cpu-cluster.";
int ret;
if (freq_table[cluster])
@@ -342,8 +353,7 @@ static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
goto free_opp_table;
}
- name[12] = cluster + '0';
- clk[cluster] = clk_get(cpu_dev, name);
+ clk[cluster] = clk_get(cpu_dev, NULL);
if (!IS_ERR(clk[cluster])) {
dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
__func__, clk[cluster], freq_table[cluster],
@@ -506,6 +516,7 @@ static struct cpufreq_driver bL_cpufreq_driver = {
.attr = cpufreq_generic_attr,
};
+#ifdef CONFIG_BL_SWITCHER
static int bL_cpufreq_switcher_notifier(struct notifier_block *nfb,
unsigned long action, void *_arg)
{
@@ -538,6 +549,20 @@ static struct notifier_block bL_switcher_notifier = {
.notifier_call = bL_cpufreq_switcher_notifier,
};
+static int __bLs_register_notifier(void)
+{
+ return bL_switcher_register_notifier(&bL_switcher_notifier);
+}
+
+static int __bLs_unregister_notifier(void)
+{
+ return bL_switcher_unregister_notifier(&bL_switcher_notifier);
+}
+#else
+static int __bLs_register_notifier(void) { return 0; }
+static int __bLs_unregister_notifier(void) { return 0; }
+#endif
+
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
{
int ret, i;
@@ -555,8 +580,7 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
arm_bL_ops = ops;
- ret = bL_switcher_get_enabled();
- set_switching_enabled(ret);
+ set_switching_enabled(bL_switcher_get_enabled());
for (i = 0; i < MAX_CLUSTERS; i++)
mutex_init(&cluster_lock[i]);
@@ -567,7 +591,7 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
__func__, ops->name, ret);
arm_bL_ops = NULL;
} else {
- ret = bL_switcher_register_notifier(&bL_switcher_notifier);
+ ret = __bLs_register_notifier();
if (ret) {
cpufreq_unregister_driver(&bL_cpufreq_driver);
arm_bL_ops = NULL;
@@ -591,7 +615,7 @@ void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops)
}
bL_switcher_get_enabled();
- bL_switcher_unregister_notifier(&bL_switcher_notifier);
+ __bLs_unregister_notifier();
cpufreq_unregister_driver(&bL_cpufreq_driver);
bL_switcher_put_enabled();
pr_info("%s: Un-registered platform driver: %s\n", __func__,
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index bab67db54b7e..528a82bf5038 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -416,6 +416,7 @@ static struct platform_driver dt_cpufreq_platdrv = {
};
module_platform_driver(dt_cpufreq_platdrv);
+MODULE_ALIAS("platform:cpufreq-dt");
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
MODULE_DESCRIPTION("Generic cpufreq driver");
diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c
index a2258090b58b..db69eeb501a7 100644
--- a/drivers/cpufreq/cpufreq-nforce2.c
+++ b/drivers/cpufreq/cpufreq-nforce2.c
@@ -414,7 +414,7 @@ static int nforce2_detect_chipset(void)
* nforce2_init - initializes the nForce2 CPUFreq driver
*
* Initializes the nForce2 FSB support. Returns -ENODEV on unsupported
- * devices, -EINVAL on problems during initiatization, and zero on
+ * devices, -EINVAL on problems during initialization, and zero on
* success.
*/
static int __init nforce2_init(void)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 8ae655c364f4..b612411655f9 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -31,10 +31,62 @@
#include <linux/tick.h>
#include <trace/events/power.h>
-/* Macros to iterate over lists */
-/* Iterate over online CPUs policies */
static LIST_HEAD(cpufreq_policy_list);
-#define for_each_policy(__policy) \
+
+static inline bool policy_is_inactive(struct cpufreq_policy *policy)
+{
+ return cpumask_empty(policy->cpus);
+}
+
+static bool suitable_policy(struct cpufreq_policy *policy, bool active)
+{
+ return active == !policy_is_inactive(policy);
+}
+
+/* Finds Next Acive/Inactive policy */
+static struct cpufreq_policy *next_policy(struct cpufreq_policy *policy,
+ bool active)
+{
+ do {
+ policy = list_next_entry(policy, policy_list);
+
+ /* No more policies in the list */
+ if (&policy->policy_list == &cpufreq_policy_list)
+ return NULL;
+ } while (!suitable_policy(policy, active));
+
+ return policy;
+}
+
+static struct cpufreq_policy *first_policy(bool active)
+{
+ struct cpufreq_policy *policy;
+
+ /* No policies in the list */
+ if (list_empty(&cpufreq_policy_list))
+ return NULL;
+
+ policy = list_first_entry(&cpufreq_policy_list, typeof(*policy),
+ policy_list);
+
+ if (!suitable_policy(policy, active))
+ policy = next_policy(policy, active);
+
+ return policy;
+}
+
+/* Macros to iterate over CPU policies */
+#define for_each_suitable_policy(__policy, __active) \
+ for (__policy = first_policy(__active); \
+ __policy; \
+ __policy = next_policy(__policy, __active))
+
+#define for_each_active_policy(__policy) \
+ for_each_suitable_policy(__policy, true)
+#define for_each_inactive_policy(__policy) \
+ for_each_suitable_policy(__policy, false)
+
+#define for_each_policy(__policy) \
list_for_each_entry(__policy, &cpufreq_policy_list, policy_list)
/* Iterate over governors */
@@ -49,13 +101,9 @@ static LIST_HEAD(cpufreq_governor_list);
*/
static struct cpufreq_driver *cpufreq_driver;
static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
-static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback);
static DEFINE_RWLOCK(cpufreq_driver_lock);
DEFINE_MUTEX(cpufreq_governor_lock);
-/* This one keeps track of the previously set governor of a removed CPU */
-static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor);
-
/* Flag to suspend/resume CPUFreq governors */
static bool cpufreq_suspended;
@@ -178,7 +226,7 @@ int cpufreq_generic_init(struct cpufreq_policy *policy,
policy->cpuinfo.transition_latency = transition_latency;
/*
- * The driver only supports the SMP configuartion where all processors
+ * The driver only supports the SMP configuration where all processors
* share the clock and voltage and clock.
*/
cpumask_setall(policy->cpus);
@@ -187,10 +235,18 @@ int cpufreq_generic_init(struct cpufreq_policy *policy,
}
EXPORT_SYMBOL_GPL(cpufreq_generic_init);
-unsigned int cpufreq_generic_get(unsigned int cpu)
+/* Only for cpufreq core internal use */
+struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu)
{
struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu);
+ return policy && cpumask_test_cpu(cpu, policy->cpus) ? policy : NULL;
+}
+
+unsigned int cpufreq_generic_get(unsigned int cpu)
+{
+ struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
+
if (!policy || IS_ERR(policy->clk)) {
pr_err("%s: No %s associated to cpu: %d\n",
__func__, policy ? "clk" : "policy", cpu);
@@ -201,18 +257,29 @@ unsigned int cpufreq_generic_get(unsigned int cpu)
}
EXPORT_SYMBOL_GPL(cpufreq_generic_get);
-/* Only for cpufreq core internal use */
-struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu)
-{
- return per_cpu(cpufreq_cpu_data, cpu);
-}
-
+/**
+ * cpufreq_cpu_get: returns policy for a cpu and marks it busy.
+ *
+ * @cpu: cpu to find policy for.
+ *
+ * This returns policy for 'cpu', returns NULL if it doesn't exist.
+ * It also increments the kobject reference count to mark it busy and so would
+ * require a corresponding call to cpufreq_cpu_put() to decrement it back.
+ * If corresponding call cpufreq_cpu_put() isn't made, the policy wouldn't be
+ * freed as that depends on the kobj count.
+ *
+ * It also takes a read-lock of 'cpufreq_rwsem' and doesn't put it back if a
+ * valid policy is found. This is done to make sure the driver doesn't get
+ * unregistered while the policy is being used.
+ *
+ * Return: A valid policy on success, otherwise NULL on failure.
+ */
struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
{
struct cpufreq_policy *policy = NULL;
unsigned long flags;
- if (cpu >= nr_cpu_ids)
+ if (WARN_ON(cpu >= nr_cpu_ids))
return NULL;
if (!down_read_trylock(&cpufreq_rwsem))
@@ -223,7 +290,7 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
if (cpufreq_driver) {
/* get the CPU */
- policy = per_cpu(cpufreq_cpu_data, cpu);
+ policy = cpufreq_cpu_get_raw(cpu);
if (policy)
kobject_get(&policy->kobj);
}
@@ -237,6 +304,16 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
}
EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
+/**
+ * cpufreq_cpu_put: Decrements the usage count of a policy
+ *
+ * @policy: policy earlier returned by cpufreq_cpu_get().
+ *
+ * This decrements the kobject reference count incremented earlier by calling
+ * cpufreq_cpu_get().
+ *
+ * It also drops the read-lock of 'cpufreq_rwsem' taken at cpufreq_cpu_get().
+ */
void cpufreq_cpu_put(struct cpufreq_policy *policy)
{
kobject_put(&policy->kobj);
@@ -798,11 +875,18 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
down_write(&policy->rwsem);
+ /* Updating inactive policies is invalid, so avoid doing that. */
+ if (unlikely(policy_is_inactive(policy))) {
+ ret = -EBUSY;
+ goto unlock_policy_rwsem;
+ }
+
if (fattr->store)
ret = fattr->store(policy, buf, count);
else
ret = -EIO;
+unlock_policy_rwsem:
up_write(&policy->rwsem);
up_read(&cpufreq_rwsem);
@@ -873,28 +957,67 @@ void cpufreq_sysfs_remove_file(const struct attribute *attr)
}
EXPORT_SYMBOL(cpufreq_sysfs_remove_file);
-/* symlink affected CPUs */
+static int add_cpu_dev_symlink(struct cpufreq_policy *policy, int cpu)
+{
+ struct device *cpu_dev;
+
+ pr_debug("%s: Adding symlink for CPU: %u\n", __func__, cpu);
+
+ if (!policy)
+ return 0;
+
+ cpu_dev = get_cpu_device(cpu);
+ if (WARN_ON(!cpu_dev))
+ return 0;
+
+ return sysfs_create_link(&cpu_dev->kobj, &policy->kobj, "cpufreq");
+}
+
+static void remove_cpu_dev_symlink(struct cpufreq_policy *policy, int cpu)
+{
+ struct device *cpu_dev;
+
+ pr_debug("%s: Removing symlink for CPU: %u\n", __func__, cpu);
+
+ cpu_dev = get_cpu_device(cpu);
+ if (WARN_ON(!cpu_dev))
+ return;
+
+ sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
+}
+
+/* Add/remove symlinks for all related CPUs */
static int cpufreq_add_dev_symlink(struct cpufreq_policy *policy)
{
unsigned int j;
int ret = 0;
- for_each_cpu(j, policy->cpus) {
- struct device *cpu_dev;
-
- if (j == policy->cpu)
+ /* Some related CPUs might not be present (physically hotplugged) */
+ for_each_cpu_and(j, policy->related_cpus, cpu_present_mask) {
+ if (j == policy->kobj_cpu)
continue;
- pr_debug("Adding link for CPU: %u\n", j);
- cpu_dev = get_cpu_device(j);
- ret = sysfs_create_link(&cpu_dev->kobj, &policy->kobj,
- "cpufreq");
+ ret = add_cpu_dev_symlink(policy, j);
if (ret)
break;
}
+
return ret;
}
+static void cpufreq_remove_dev_symlink(struct cpufreq_policy *policy)
+{
+ unsigned int j;
+
+ /* Some related CPUs might not be present (physically hotplugged) */
+ for_each_cpu_and(j, policy->related_cpus, cpu_present_mask) {
+ if (j == policy->kobj_cpu)
+ continue;
+
+ remove_cpu_dev_symlink(policy, j);
+ }
+}
+
static int cpufreq_add_dev_interface(struct cpufreq_policy *policy,
struct device *dev)
{
@@ -937,7 +1060,7 @@ static void cpufreq_init_policy(struct cpufreq_policy *policy)
memcpy(&new_policy, policy, sizeof(*policy));
/* Update governor of new_policy to the governor used before hotplug */
- gov = find_governor(per_cpu(cpufreq_cpu_governor, policy->cpu));
+ gov = find_governor(policy->last_governor);
if (gov)
pr_debug("Restoring governor %s for cpu %d\n",
policy->governor->name, policy->cpu);
@@ -963,7 +1086,10 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
unsigned int cpu, struct device *dev)
{
int ret = 0;
- unsigned long flags;
+
+ /* Has this CPU been taken care of already? */
+ if (cpumask_test_cpu(cpu, policy->cpus))
+ return 0;
if (has_target()) {
ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
@@ -974,13 +1100,7 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
}
down_write(&policy->rwsem);
-
- write_lock_irqsave(&cpufreq_driver_lock, flags);
-
cpumask_set_cpu(cpu, policy->cpus);
- per_cpu(cpufreq_cpu_data, cpu) = policy;
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
up_write(&policy->rwsem);
if (has_target()) {
@@ -994,7 +1114,7 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
}
}
- return sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
+ return 0;
}
static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu)
@@ -1003,20 +1123,25 @@ static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu)
unsigned long flags;
read_lock_irqsave(&cpufreq_driver_lock, flags);
-
- policy = per_cpu(cpufreq_cpu_data_fallback, cpu);
-
+ policy = per_cpu(cpufreq_cpu_data, cpu);
read_unlock_irqrestore(&cpufreq_driver_lock, flags);
- if (policy)
- policy->governor = NULL;
+ if (likely(policy)) {
+ /* Policy should be inactive here */
+ WARN_ON(!policy_is_inactive(policy));
+
+ down_write(&policy->rwsem);
+ policy->cpu = cpu;
+ up_write(&policy->rwsem);
+ }
return policy;
}
-static struct cpufreq_policy *cpufreq_policy_alloc(void)
+static struct cpufreq_policy *cpufreq_policy_alloc(struct device *dev)
{
struct cpufreq_policy *policy;
+ int ret;
policy = kzalloc(sizeof(*policy), GFP_KERNEL);
if (!policy)
@@ -1028,6 +1153,13 @@ static struct cpufreq_policy *cpufreq_policy_alloc(void)
if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL))
goto err_free_cpumask;
+ ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, &dev->kobj,
+ "cpufreq");
+ if (ret) {
+ pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret);
+ goto err_free_rcpumask;
+ }
+
INIT_LIST_HEAD(&policy->policy_list);
init_rwsem(&policy->rwsem);
spin_lock_init(&policy->transition_lock);
@@ -1035,8 +1167,15 @@ static struct cpufreq_policy *cpufreq_policy_alloc(void)
init_completion(&policy->kobj_unregister);
INIT_WORK(&policy->update, handle_update);
+ policy->cpu = dev->id;
+
+ /* Set this once on allocation */
+ policy->kobj_cpu = dev->id;
+
return policy;
+err_free_rcpumask:
+ free_cpumask_var(policy->related_cpus);
err_free_cpumask:
free_cpumask_var(policy->cpus);
err_free_policy:
@@ -1045,18 +1184,20 @@ err_free_policy:
return NULL;
}
-static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
+static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify)
{
struct kobject *kobj;
struct completion *cmp;
- blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
- CPUFREQ_REMOVE_POLICY, policy);
+ if (notify)
+ blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
+ CPUFREQ_REMOVE_POLICY, policy);
- down_read(&policy->rwsem);
+ down_write(&policy->rwsem);
+ cpufreq_remove_dev_symlink(policy);
kobj = &policy->kobj;
cmp = &policy->kobj_unregister;
- up_read(&policy->rwsem);
+ up_write(&policy->rwsem);
kobject_put(kobj);
/*
@@ -1069,68 +1210,64 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
pr_debug("wait complete\n");
}
-static void cpufreq_policy_free(struct cpufreq_policy *policy)
-{
- free_cpumask_var(policy->related_cpus);
- free_cpumask_var(policy->cpus);
- kfree(policy);
-}
-
-static int update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu,
- struct device *cpu_dev)
+static void cpufreq_policy_free(struct cpufreq_policy *policy, bool notify)
{
- int ret;
-
- if (WARN_ON(cpu == policy->cpu))
- return 0;
+ unsigned long flags;
+ int cpu;
- /* Move kobject to the new policy->cpu */
- ret = kobject_move(&policy->kobj, &cpu_dev->kobj);
- if (ret) {
- pr_err("%s: Failed to move kobj: %d\n", __func__, ret);
- return ret;
- }
+ /* Remove policy from list */
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
+ list_del(&policy->policy_list);
- down_write(&policy->rwsem);
- policy->cpu = cpu;
- up_write(&policy->rwsem);
+ for_each_cpu(cpu, policy->related_cpus)
+ per_cpu(cpufreq_cpu_data, cpu) = NULL;
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
- return 0;
+ cpufreq_policy_put_kobj(policy, notify);
+ free_cpumask_var(policy->related_cpus);
+ free_cpumask_var(policy->cpus);
+ kfree(policy);
}
-static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
+/**
+ * cpufreq_add_dev - add a CPU device
+ *
+ * Adds the cpufreq interface for a CPU device.
+ *
+ * The Oracle says: try running cpufreq registration/unregistration concurrently
+ * with with cpu hotplugging and all hell will break loose. Tried to clean this
+ * mess up, but more thorough testing is needed. - Mathieu
+ */
+static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
{
unsigned int j, cpu = dev->id;
int ret = -ENOMEM;
struct cpufreq_policy *policy;
unsigned long flags;
- bool recover_policy = cpufreq_suspended;
-
- if (cpu_is_offline(cpu))
- return 0;
+ bool recover_policy = !sif;
pr_debug("adding CPU %u\n", cpu);
- /* check whether a different CPU already registered this
- * CPU because it is in the same boat. */
- policy = cpufreq_cpu_get_raw(cpu);
- if (unlikely(policy))
- return 0;
+ /*
+ * Only possible if 'cpu' wasn't physically present earlier and we are
+ * here from subsys_interface add callback. A hotplug notifier will
+ * follow and we will handle it like logical CPU hotplug then. For now,
+ * just create the sysfs link.
+ */
+ if (cpu_is_offline(cpu))
+ return add_cpu_dev_symlink(per_cpu(cpufreq_cpu_data, cpu), cpu);
if (!down_read_trylock(&cpufreq_rwsem))
return 0;
- /* Check if this cpu was hot-unplugged earlier and has siblings */
- read_lock_irqsave(&cpufreq_driver_lock, flags);
- for_each_policy(policy) {
- if (cpumask_test_cpu(cpu, policy->related_cpus)) {
- read_unlock_irqrestore(&cpufreq_driver_lock, flags);
- ret = cpufreq_add_policy_cpu(policy, cpu, dev);
- up_read(&cpufreq_rwsem);
- return ret;
- }
+ /* Check if this CPU already has a policy to manage it */
+ policy = per_cpu(cpufreq_cpu_data, cpu);
+ if (policy && !policy_is_inactive(policy)) {
+ WARN_ON(!cpumask_test_cpu(cpu, policy->related_cpus));
+ ret = cpufreq_add_policy_cpu(policy, cpu, dev);
+ up_read(&cpufreq_rwsem);
+ return ret;
}
- read_unlock_irqrestore(&cpufreq_driver_lock, flags);
/*
* Restore the saved policy when doing light-weight init and fall back
@@ -1139,22 +1276,11 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
policy = recover_policy ? cpufreq_policy_restore(cpu) : NULL;
if (!policy) {
recover_policy = false;
- policy = cpufreq_policy_alloc();
+ policy = cpufreq_policy_alloc(dev);
if (!policy)
goto nomem_out;
}
- /*
- * In the resume path, since we restore a saved policy, the assignment
- * to policy->cpu is like an update of the existing policy, rather than
- * the creation of a brand new one. So we need to perform this update
- * by invoking update_policy_cpu().
- */
- if (recover_policy && cpu != policy->cpu)
- WARN_ON(update_policy_cpu(policy, cpu, dev));
- else
- policy->cpu = cpu;
-
cpumask_copy(policy->cpus, cpumask_of(cpu));
/* call driver. From then on the cpufreq must be able
@@ -1181,21 +1307,12 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
policy->user_policy.min = policy->min;
policy->user_policy.max = policy->max;
- /* prepare interface data */
- ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq,
- &dev->kobj, "cpufreq");
- if (ret) {
- pr_err("%s: failed to init policy->kobj: %d\n",
- __func__, ret);
- goto err_init_policy_kobj;
- }
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
+ for_each_cpu(j, policy->related_cpus)
+ per_cpu(cpufreq_cpu_data, j) = policy;
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
}
- write_lock_irqsave(&cpufreq_driver_lock, flags);
- for_each_cpu(j, policy->cpus)
- per_cpu(cpufreq_cpu_data, j) = policy;
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
if (cpufreq_driver->get && !cpufreq_driver->setpolicy) {
policy->cur = cpufreq_driver->get(policy->cpu);
if (!policy->cur) {
@@ -1253,11 +1370,11 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
goto err_out_unregister;
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
CPUFREQ_CREATE_POLICY, policy);
- }
- write_lock_irqsave(&cpufreq_driver_lock, flags);
- list_add(&policy->policy_list, &cpufreq_policy_list);
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
+ list_add(&policy->policy_list, &cpufreq_policy_list);
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ }
cpufreq_init_policy(policy);
@@ -1281,68 +1398,28 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
err_out_unregister:
err_get_freq:
- write_lock_irqsave(&cpufreq_driver_lock, flags);
- for_each_cpu(j, policy->cpus)
- per_cpu(cpufreq_cpu_data, j) = NULL;
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
- if (!recover_policy) {
- kobject_put(&policy->kobj);
- wait_for_completion(&policy->kobj_unregister);
- }
-err_init_policy_kobj:
up_write(&policy->rwsem);
if (cpufreq_driver->exit)
cpufreq_driver->exit(policy);
err_set_policy_cpu:
- if (recover_policy) {
- /* Do not leave stale fallback data behind. */
- per_cpu(cpufreq_cpu_data_fallback, cpu) = NULL;
- cpufreq_policy_put_kobj(policy);
- }
- cpufreq_policy_free(policy);
-
+ cpufreq_policy_free(policy, recover_policy);
nomem_out:
up_read(&cpufreq_rwsem);
return ret;
}
-/**
- * cpufreq_add_dev - add a CPU device
- *
- * Adds the cpufreq interface for a CPU device.
- *
- * The Oracle says: try running cpufreq registration/unregistration concurrently
- * with with cpu hotplugging and all hell will break loose. Tried to clean this
- * mess up, but more thorough testing is needed. - Mathieu
- */
-static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
-{
- return __cpufreq_add_dev(dev, sif);
-}
-
static int __cpufreq_remove_dev_prepare(struct device *dev,
struct subsys_interface *sif)
{
- unsigned int cpu = dev->id, cpus;
- int ret;
- unsigned long flags;
+ unsigned int cpu = dev->id;
+ int ret = 0;
struct cpufreq_policy *policy;
pr_debug("%s: unregistering CPU %u\n", __func__, cpu);
- write_lock_irqsave(&cpufreq_driver_lock, flags);
-
- policy = per_cpu(cpufreq_cpu_data, cpu);
-
- /* Save the policy somewhere when doing a light-weight tear-down */
- if (cpufreq_suspended)
- per_cpu(cpufreq_cpu_data_fallback, cpu) = policy;
-
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
+ policy = cpufreq_cpu_get_raw(cpu);
if (!policy) {
pr_debug("%s: No cpu_data found\n", __func__);
return -EINVAL;
@@ -1354,108 +1431,75 @@ static int __cpufreq_remove_dev_prepare(struct device *dev,
pr_err("%s: Failed to stop governor\n", __func__);
return ret;
}
-
- strncpy(per_cpu(cpufreq_cpu_governor, cpu),
- policy->governor->name, CPUFREQ_NAME_LEN);
}
- down_read(&policy->rwsem);
- cpus = cpumask_weight(policy->cpus);
- up_read(&policy->rwsem);
+ down_write(&policy->rwsem);
+ cpumask_clear_cpu(cpu, policy->cpus);
- if (cpu != policy->cpu) {
- sysfs_remove_link(&dev->kobj, "cpufreq");
- } else if (cpus > 1) {
+ if (policy_is_inactive(policy)) {
+ if (has_target())
+ strncpy(policy->last_governor, policy->governor->name,
+ CPUFREQ_NAME_LEN);
+ } else if (cpu == policy->cpu) {
/* Nominate new CPU */
- int new_cpu = cpumask_any_but(policy->cpus, cpu);
- struct device *cpu_dev = get_cpu_device(new_cpu);
+ policy->cpu = cpumask_any(policy->cpus);
+ }
+ up_write(&policy->rwsem);
- sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
- ret = update_policy_cpu(policy, new_cpu, cpu_dev);
- if (ret) {
- if (sysfs_create_link(&cpu_dev->kobj, &policy->kobj,
- "cpufreq"))
- pr_err("%s: Failed to restore kobj link to cpu:%d\n",
- __func__, cpu_dev->id);
- return ret;
- }
+ /* Start governor again for active policy */
+ if (!policy_is_inactive(policy)) {
+ if (has_target()) {
+ ret = __cpufreq_governor(policy, CPUFREQ_GOV_START);
+ if (!ret)
+ ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
- if (!cpufreq_suspended)
- pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n",
- __func__, new_cpu, cpu);
+ if (ret)
+ pr_err("%s: Failed to start governor\n", __func__);
+ }
} else if (cpufreq_driver->stop_cpu) {
cpufreq_driver->stop_cpu(policy);
}
- return 0;
+ return ret;
}
static int __cpufreq_remove_dev_finish(struct device *dev,
struct subsys_interface *sif)
{
- unsigned int cpu = dev->id, cpus;
+ unsigned int cpu = dev->id;
int ret;
- unsigned long flags;
- struct cpufreq_policy *policy;
-
- write_lock_irqsave(&cpufreq_driver_lock, flags);
- policy = per_cpu(cpufreq_cpu_data, cpu);
- per_cpu(cpufreq_cpu_data, cpu) = NULL;
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu);
if (!policy) {
pr_debug("%s: No cpu_data found\n", __func__);
return -EINVAL;
}
- down_write(&policy->rwsem);
- cpus = cpumask_weight(policy->cpus);
-
- if (cpus > 1)
- cpumask_clear_cpu(cpu, policy->cpus);
- up_write(&policy->rwsem);
+ /* Only proceed for inactive policies */
+ if (!policy_is_inactive(policy))
+ return 0;
/* If cpu is last user of policy, free policy */
- if (cpus == 1) {
- if (has_target()) {
- ret = __cpufreq_governor(policy,
- CPUFREQ_GOV_POLICY_EXIT);
- if (ret) {
- pr_err("%s: Failed to exit governor\n",
- __func__);
- return ret;
- }
- }
-
- if (!cpufreq_suspended)
- cpufreq_policy_put_kobj(policy);
-
- /*
- * Perform the ->exit() even during light-weight tear-down,
- * since this is a core component, and is essential for the
- * subsequent light-weight ->init() to succeed.
- */
- if (cpufreq_driver->exit)
- cpufreq_driver->exit(policy);
-
- /* Remove policy from list of active policies */
- write_lock_irqsave(&cpufreq_driver_lock, flags);
- list_del(&policy->policy_list);
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
- if (!cpufreq_suspended)
- cpufreq_policy_free(policy);
- } else if (has_target()) {
- ret = __cpufreq_governor(policy, CPUFREQ_GOV_START);
- if (!ret)
- ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
-
+ if (has_target()) {
+ ret = __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT);
if (ret) {
- pr_err("%s: Failed to start governor\n", __func__);
+ pr_err("%s: Failed to exit governor\n", __func__);
return ret;
}
}
+ /*
+ * Perform the ->exit() even during light-weight tear-down,
+ * since this is a core component, and is essential for the
+ * subsequent light-weight ->init() to succeed.
+ */
+ if (cpufreq_driver->exit)
+ cpufreq_driver->exit(policy);
+
+ /* Free the policy only if the driver is getting removed. */
+ if (sif)
+ cpufreq_policy_free(policy, true);
+
return 0;
}
@@ -1469,8 +1513,33 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
unsigned int cpu = dev->id;
int ret;
- if (cpu_is_offline(cpu))
+ /*
+ * Only possible if 'cpu' is getting physically removed now. A hotplug
+ * notifier should have already been called and we just need to remove
+ * link or free policy here.
+ */
+ if (cpu_is_offline(cpu)) {
+ struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu);
+ struct cpumask mask;
+
+ if (!policy)
+ return 0;
+
+ cpumask_copy(&mask, policy->related_cpus);
+ cpumask_clear_cpu(cpu, &mask);
+
+ /*
+ * Free policy only if all policy->related_cpus are removed
+ * physically.
+ */
+ if (cpumask_intersects(&mask, cpu_present_mask)) {
+ remove_cpu_dev_symlink(policy, cpu);
+ return 0;
+ }
+
+ cpufreq_policy_free(policy, true);
return 0;
+ }
ret = __cpufreq_remove_dev_prepare(dev, sif);
@@ -1567,6 +1636,10 @@ static unsigned int __cpufreq_get(struct cpufreq_policy *policy)
ret_freq = cpufreq_driver->get(policy->cpu);
+ /* Updating inactive policies is invalid, so avoid doing that. */
+ if (unlikely(policy_is_inactive(policy)))
+ return ret_freq;
+
if (ret_freq && policy->cur &&
!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {
/* verify no discrepancy between actual and
@@ -1656,7 +1729,7 @@ void cpufreq_suspend(void)
pr_debug("%s: Suspending Governors\n", __func__);
- for_each_policy(policy) {
+ for_each_active_policy(policy) {
if (__cpufreq_governor(policy, CPUFREQ_GOV_STOP))
pr_err("%s: Failed to stop governor for policy: %p\n",
__func__, policy);
@@ -1690,7 +1763,7 @@ void cpufreq_resume(void)
pr_debug("%s: Resuming Governors\n", __func__);
- for_each_policy(policy) {
+ for_each_active_policy(policy) {
if (cpufreq_driver->resume && cpufreq_driver->resume(policy))
pr_err("%s: Failed to resume driver: %p\n", __func__,
policy);
@@ -1891,7 +1964,7 @@ static int __target_index(struct cpufreq_policy *policy,
* Failed after setting to intermediate freq? Driver should have
* reverted back to initial frequency and so should we. Check
* here for intermediate_freq instead of get_intermediate, in
- * case we have't switched to intermediate freq at all.
+ * case we haven't switched to intermediate freq at all.
*/
if (unlikely(retval && intermediate_freq)) {
freqs.old = intermediate_freq;
@@ -2092,7 +2165,8 @@ EXPORT_SYMBOL_GPL(cpufreq_register_governor);
void cpufreq_unregister_governor(struct cpufreq_governor *governor)
{
- int cpu;
+ struct cpufreq_policy *policy;
+ unsigned long flags;
if (!governor)
return;
@@ -2100,12 +2174,15 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
if (cpufreq_disabled())
return;
- for_each_present_cpu(cpu) {
- if (cpu_online(cpu))
- continue;
- if (!strcmp(per_cpu(cpufreq_cpu_governor, cpu), governor->name))
- strcpy(per_cpu(cpufreq_cpu_governor, cpu), "\0");
+ /* clear last_governor for all inactive policies */
+ read_lock_irqsave(&cpufreq_driver_lock, flags);
+ for_each_inactive_policy(policy) {
+ if (!strcmp(policy->last_governor, governor->name)) {
+ policy->governor = NULL;
+ strcpy(policy->last_governor, "\0");
+ }
}
+ read_unlock_irqrestore(&cpufreq_driver_lock, flags);
mutex_lock(&cpufreq_governor_mutex);
list_del(&governor->governor_list);
@@ -2304,7 +2381,7 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb,
if (dev) {
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_ONLINE:
- __cpufreq_add_dev(dev, NULL);
+ cpufreq_add_dev(dev, NULL);
break;
case CPU_DOWN_PREPARE:
@@ -2316,7 +2393,7 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb,
break;
case CPU_DOWN_FAILED:
- __cpufreq_add_dev(dev, NULL);
+ cpufreq_add_dev(dev, NULL);
break;
}
}
@@ -2336,7 +2413,7 @@ static int cpufreq_boost_set_sw(int state)
struct cpufreq_policy *policy;
int ret = -EINVAL;
- for_each_policy(policy) {
+ for_each_active_policy(policy) {
freq_table = cpufreq_frequency_get_table(policy->cpu);
if (freq_table) {
ret = cpufreq_frequency_table_cpuinfo(policy,
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 25a70d06c5bf..c86a10c30912 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -148,6 +148,10 @@ static int dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
return 0;
}
+static struct notifier_block cs_cpufreq_notifier_block = {
+ .notifier_call = dbs_cpufreq_notifier,
+};
+
/************************** sysfs interface ************************/
static struct common_dbs_data cs_dbs_cdata;
@@ -317,7 +321,7 @@ static struct attribute_group cs_attr_group_gov_pol = {
/************************** sysfs end ************************/
-static int cs_init(struct dbs_data *dbs_data)
+static int cs_init(struct dbs_data *dbs_data, bool notify)
{
struct cs_dbs_tuners *tuners;
@@ -336,25 +340,25 @@ static int cs_init(struct dbs_data *dbs_data)
dbs_data->tuners = tuners;
dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO *
jiffies_to_usecs(10);
- mutex_init(&dbs_data->mutex);
+
+ if (notify)
+ cpufreq_register_notifier(&cs_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
return 0;
}
-static void cs_exit(struct dbs_data *dbs_data)
+static void cs_exit(struct dbs_data *dbs_data, bool notify)
{
+ if (notify)
+ cpufreq_unregister_notifier(&cs_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
kfree(dbs_data->tuners);
}
define_get_cpu_dbs_routines(cs_cpu_dbs_info);
-static struct notifier_block cs_cpufreq_notifier_block = {
- .notifier_call = dbs_cpufreq_notifier,
-};
-
-static struct cs_ops cs_ops = {
- .notifier_block = &cs_cpufreq_notifier_block,
-};
-
static struct common_dbs_data cs_dbs_cdata = {
.governor = GOV_CONSERVATIVE,
.attr_group_gov_sys = &cs_attr_group_gov_sys,
@@ -363,9 +367,9 @@ static struct common_dbs_data cs_dbs_cdata = {
.get_cpu_dbs_info_s = get_cpu_dbs_info_s,
.gov_dbs_timer = cs_dbs_timer,
.gov_check_cpu = cs_check_cpu,
- .gov_ops = &cs_ops,
.init = cs_init,
.exit = cs_exit,
+ .mutex = __MUTEX_INITIALIZER(cs_dbs_cdata.mutex),
};
static int cs_cpufreq_governor_dbs(struct cpufreq_policy *policy,
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index 1b44496b2d2b..57a39f8a92b7 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -239,211 +239,242 @@ static void set_sampling_rate(struct dbs_data *dbs_data,
}
}
-int cpufreq_governor_dbs(struct cpufreq_policy *policy,
- struct common_dbs_data *cdata, unsigned int event)
+static int cpufreq_governor_init(struct cpufreq_policy *policy,
+ struct dbs_data *dbs_data,
+ struct common_dbs_data *cdata)
{
- struct dbs_data *dbs_data;
- struct od_cpu_dbs_info_s *od_dbs_info = NULL;
- struct cs_cpu_dbs_info_s *cs_dbs_info = NULL;
- struct od_ops *od_ops = NULL;
- struct od_dbs_tuners *od_tuners = NULL;
- struct cs_dbs_tuners *cs_tuners = NULL;
- struct cpu_dbs_common_info *cpu_cdbs;
- unsigned int sampling_rate, latency, ignore_nice, j, cpu = policy->cpu;
- int io_busy = 0;
- int rc;
-
- if (have_governor_per_policy())
- dbs_data = policy->governor_data;
- else
- dbs_data = cdata->gdbs_data;
-
- WARN_ON(!dbs_data && (event != CPUFREQ_GOV_POLICY_INIT));
-
- switch (event) {
- case CPUFREQ_GOV_POLICY_INIT:
- if (have_governor_per_policy()) {
- WARN_ON(dbs_data);
- } else if (dbs_data) {
- dbs_data->usage_count++;
- policy->governor_data = dbs_data;
- return 0;
- }
-
- dbs_data = kzalloc(sizeof(*dbs_data), GFP_KERNEL);
- if (!dbs_data) {
- pr_err("%s: POLICY_INIT: kzalloc failed\n", __func__);
- return -ENOMEM;
- }
+ unsigned int latency;
+ int ret;
- dbs_data->cdata = cdata;
- dbs_data->usage_count = 1;
- rc = cdata->init(dbs_data);
- if (rc) {
- pr_err("%s: POLICY_INIT: init() failed\n", __func__);
- kfree(dbs_data);
- return rc;
- }
+ if (dbs_data) {
+ if (WARN_ON(have_governor_per_policy()))
+ return -EINVAL;
+ dbs_data->usage_count++;
+ policy->governor_data = dbs_data;
+ return 0;
+ }
- if (!have_governor_per_policy())
- WARN_ON(cpufreq_get_global_kobject());
+ dbs_data = kzalloc(sizeof(*dbs_data), GFP_KERNEL);
+ if (!dbs_data)
+ return -ENOMEM;
- rc = sysfs_create_group(get_governor_parent_kobj(policy),
- get_sysfs_attr(dbs_data));
- if (rc) {
- cdata->exit(dbs_data);
- kfree(dbs_data);
- return rc;
- }
+ dbs_data->cdata = cdata;
+ dbs_data->usage_count = 1;
- policy->governor_data = dbs_data;
+ ret = cdata->init(dbs_data, !policy->governor->initialized);
+ if (ret)
+ goto free_dbs_data;
- /* policy latency is in ns. Convert it to us first */
- latency = policy->cpuinfo.transition_latency / 1000;
- if (latency == 0)
- latency = 1;
+ /* policy latency is in ns. Convert it to us first */
+ latency = policy->cpuinfo.transition_latency / 1000;
+ if (latency == 0)
+ latency = 1;
- /* Bring kernel and HW constraints together */
- dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate,
- MIN_LATENCY_MULTIPLIER * latency);
- set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate,
+ /* Bring kernel and HW constraints together */
+ dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate,
+ MIN_LATENCY_MULTIPLIER * latency);
+ set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate,
latency * LATENCY_MULTIPLIER));
- if ((cdata->governor == GOV_CONSERVATIVE) &&
- (!policy->governor->initialized)) {
- struct cs_ops *cs_ops = dbs_data->cdata->gov_ops;
-
- cpufreq_register_notifier(cs_ops->notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
+ if (!have_governor_per_policy()) {
+ if (WARN_ON(cpufreq_get_global_kobject())) {
+ ret = -EINVAL;
+ goto cdata_exit;
}
+ cdata->gdbs_data = dbs_data;
+ }
- if (!have_governor_per_policy())
- cdata->gdbs_data = dbs_data;
+ ret = sysfs_create_group(get_governor_parent_kobj(policy),
+ get_sysfs_attr(dbs_data));
+ if (ret)
+ goto put_kobj;
- return 0;
- case CPUFREQ_GOV_POLICY_EXIT:
- if (!--dbs_data->usage_count) {
- sysfs_remove_group(get_governor_parent_kobj(policy),
- get_sysfs_attr(dbs_data));
+ policy->governor_data = dbs_data;
- if (!have_governor_per_policy())
- cpufreq_put_global_kobject();
+ return 0;
- if ((dbs_data->cdata->governor == GOV_CONSERVATIVE) &&
- (policy->governor->initialized == 1)) {
- struct cs_ops *cs_ops = dbs_data->cdata->gov_ops;
+put_kobj:
+ if (!have_governor_per_policy()) {
+ cdata->gdbs_data = NULL;
+ cpufreq_put_global_kobject();
+ }
+cdata_exit:
+ cdata->exit(dbs_data, !policy->governor->initialized);
+free_dbs_data:
+ kfree(dbs_data);
+ return ret;
+}
+
+static void cpufreq_governor_exit(struct cpufreq_policy *policy,
+ struct dbs_data *dbs_data)
+{
+ struct common_dbs_data *cdata = dbs_data->cdata;
- cpufreq_unregister_notifier(cs_ops->notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
- }
+ policy->governor_data = NULL;
+ if (!--dbs_data->usage_count) {
+ sysfs_remove_group(get_governor_parent_kobj(policy),
+ get_sysfs_attr(dbs_data));
- cdata->exit(dbs_data);
- kfree(dbs_data);
+ if (!have_governor_per_policy()) {
cdata->gdbs_data = NULL;
+ cpufreq_put_global_kobject();
}
- policy->governor_data = NULL;
- return 0;
+ cdata->exit(dbs_data, policy->governor->initialized == 1);
+ kfree(dbs_data);
}
+}
- cpu_cdbs = dbs_data->cdata->get_cpu_cdbs(cpu);
+static int cpufreq_governor_start(struct cpufreq_policy *policy,
+ struct dbs_data *dbs_data)
+{
+ struct common_dbs_data *cdata = dbs_data->cdata;
+ unsigned int sampling_rate, ignore_nice, j, cpu = policy->cpu;
+ struct cpu_dbs_common_info *cpu_cdbs = cdata->get_cpu_cdbs(cpu);
+ int io_busy = 0;
+
+ if (!policy->cur)
+ return -EINVAL;
+
+ if (cdata->governor == GOV_CONSERVATIVE) {
+ struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
- if (dbs_data->cdata->governor == GOV_CONSERVATIVE) {
- cs_tuners = dbs_data->tuners;
- cs_dbs_info = dbs_data->cdata->get_cpu_dbs_info_s(cpu);
sampling_rate = cs_tuners->sampling_rate;
ignore_nice = cs_tuners->ignore_nice_load;
} else {
- od_tuners = dbs_data->tuners;
- od_dbs_info = dbs_data->cdata->get_cpu_dbs_info_s(cpu);
+ struct od_dbs_tuners *od_tuners = dbs_data->tuners;
+
sampling_rate = od_tuners->sampling_rate;
ignore_nice = od_tuners->ignore_nice_load;
- od_ops = dbs_data->cdata->gov_ops;
io_busy = od_tuners->io_is_busy;
}
- switch (event) {
- case CPUFREQ_GOV_START:
- if (!policy->cur)
- return -EINVAL;
+ for_each_cpu(j, policy->cpus) {
+ struct cpu_dbs_common_info *j_cdbs = cdata->get_cpu_cdbs(j);
+ unsigned int prev_load;
- mutex_lock(&dbs_data->mutex);
+ j_cdbs->cpu = j;
+ j_cdbs->cur_policy = policy;
+ j_cdbs->prev_cpu_idle =
+ get_cpu_idle_time(j, &j_cdbs->prev_cpu_wall, io_busy);
- for_each_cpu(j, policy->cpus) {
- struct cpu_dbs_common_info *j_cdbs =
- dbs_data->cdata->get_cpu_cdbs(j);
- unsigned int prev_load;
+ prev_load = (unsigned int)(j_cdbs->prev_cpu_wall -
+ j_cdbs->prev_cpu_idle);
+ j_cdbs->prev_load = 100 * prev_load /
+ (unsigned int)j_cdbs->prev_cpu_wall;
- j_cdbs->cpu = j;
- j_cdbs->cur_policy = policy;
- j_cdbs->prev_cpu_idle = get_cpu_idle_time(j,
- &j_cdbs->prev_cpu_wall, io_busy);
+ if (ignore_nice)
+ j_cdbs->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE];
- prev_load = (unsigned int)
- (j_cdbs->prev_cpu_wall - j_cdbs->prev_cpu_idle);
- j_cdbs->prev_load = 100 * prev_load /
- (unsigned int) j_cdbs->prev_cpu_wall;
+ mutex_init(&j_cdbs->timer_mutex);
+ INIT_DEFERRABLE_WORK(&j_cdbs->work, cdata->gov_dbs_timer);
+ }
- if (ignore_nice)
- j_cdbs->prev_cpu_nice =
- kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+ if (cdata->governor == GOV_CONSERVATIVE) {
+ struct cs_cpu_dbs_info_s *cs_dbs_info =
+ cdata->get_cpu_dbs_info_s(cpu);
- mutex_init(&j_cdbs->timer_mutex);
- INIT_DEFERRABLE_WORK(&j_cdbs->work,
- dbs_data->cdata->gov_dbs_timer);
- }
+ cs_dbs_info->down_skip = 0;
+ cs_dbs_info->enable = 1;
+ cs_dbs_info->requested_freq = policy->cur;
+ } else {
+ struct od_ops *od_ops = cdata->gov_ops;
+ struct od_cpu_dbs_info_s *od_dbs_info = cdata->get_cpu_dbs_info_s(cpu);
- if (dbs_data->cdata->governor == GOV_CONSERVATIVE) {
- cs_dbs_info->down_skip = 0;
- cs_dbs_info->enable = 1;
- cs_dbs_info->requested_freq = policy->cur;
- } else {
- od_dbs_info->rate_mult = 1;
- od_dbs_info->sample_type = OD_NORMAL_SAMPLE;
- od_ops->powersave_bias_init_cpu(cpu);
- }
+ od_dbs_info->rate_mult = 1;
+ od_dbs_info->sample_type = OD_NORMAL_SAMPLE;
+ od_ops->powersave_bias_init_cpu(cpu);
+ }
- mutex_unlock(&dbs_data->mutex);
+ /* Initiate timer time stamp */
+ cpu_cdbs->time_stamp = ktime_get();
- /* Initiate timer time stamp */
- cpu_cdbs->time_stamp = ktime_get();
+ gov_queue_work(dbs_data, policy, delay_for_sampling_rate(sampling_rate),
+ true);
+ return 0;
+}
- gov_queue_work(dbs_data, policy,
- delay_for_sampling_rate(sampling_rate), true);
- break;
+static void cpufreq_governor_stop(struct cpufreq_policy *policy,
+ struct dbs_data *dbs_data)
+{
+ struct common_dbs_data *cdata = dbs_data->cdata;
+ unsigned int cpu = policy->cpu;
+ struct cpu_dbs_common_info *cpu_cdbs = cdata->get_cpu_cdbs(cpu);
- case CPUFREQ_GOV_STOP:
- if (dbs_data->cdata->governor == GOV_CONSERVATIVE)
- cs_dbs_info->enable = 0;
+ if (cdata->governor == GOV_CONSERVATIVE) {
+ struct cs_cpu_dbs_info_s *cs_dbs_info =
+ cdata->get_cpu_dbs_info_s(cpu);
- gov_cancel_work(dbs_data, policy);
+ cs_dbs_info->enable = 0;
+ }
- mutex_lock(&dbs_data->mutex);
- mutex_destroy(&cpu_cdbs->timer_mutex);
- cpu_cdbs->cur_policy = NULL;
+ gov_cancel_work(dbs_data, policy);
- mutex_unlock(&dbs_data->mutex);
+ mutex_destroy(&cpu_cdbs->timer_mutex);
+ cpu_cdbs->cur_policy = NULL;
+}
- break;
+static void cpufreq_governor_limits(struct cpufreq_policy *policy,
+ struct dbs_data *dbs_data)
+{
+ struct common_dbs_data *cdata = dbs_data->cdata;
+ unsigned int cpu = policy->cpu;
+ struct cpu_dbs_common_info *cpu_cdbs = cdata->get_cpu_cdbs(cpu);
+
+ if (!cpu_cdbs->cur_policy)
+ return;
+
+ mutex_lock(&cpu_cdbs->timer_mutex);
+ if (policy->max < cpu_cdbs->cur_policy->cur)
+ __cpufreq_driver_target(cpu_cdbs->cur_policy, policy->max,
+ CPUFREQ_RELATION_H);
+ else if (policy->min > cpu_cdbs->cur_policy->cur)
+ __cpufreq_driver_target(cpu_cdbs->cur_policy, policy->min,
+ CPUFREQ_RELATION_L);
+ dbs_check_cpu(dbs_data, cpu);
+ mutex_unlock(&cpu_cdbs->timer_mutex);
+}
+
+int cpufreq_governor_dbs(struct cpufreq_policy *policy,
+ struct common_dbs_data *cdata, unsigned int event)
+{
+ struct dbs_data *dbs_data;
+ int ret = 0;
+ /* Lock governor to block concurrent initialization of governor */
+ mutex_lock(&cdata->mutex);
+
+ if (have_governor_per_policy())
+ dbs_data = policy->governor_data;
+ else
+ dbs_data = cdata->gdbs_data;
+
+ if (WARN_ON(!dbs_data && (event != CPUFREQ_GOV_POLICY_INIT))) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ switch (event) {
+ case CPUFREQ_GOV_POLICY_INIT:
+ ret = cpufreq_governor_init(policy, dbs_data, cdata);
+ break;
+ case CPUFREQ_GOV_POLICY_EXIT:
+ cpufreq_governor_exit(policy, dbs_data);
+ break;
+ case CPUFREQ_GOV_START:
+ ret = cpufreq_governor_start(policy, dbs_data);
+ break;
+ case CPUFREQ_GOV_STOP:
+ cpufreq_governor_stop(policy, dbs_data);
+ break;
case CPUFREQ_GOV_LIMITS:
- mutex_lock(&dbs_data->mutex);
- if (!cpu_cdbs->cur_policy) {
- mutex_unlock(&dbs_data->mutex);
- break;
- }
- mutex_lock(&cpu_cdbs->timer_mutex);
- if (policy->max < cpu_cdbs->cur_policy->cur)
- __cpufreq_driver_target(cpu_cdbs->cur_policy,
- policy->max, CPUFREQ_RELATION_H);
- else if (policy->min > cpu_cdbs->cur_policy->cur)
- __cpufreq_driver_target(cpu_cdbs->cur_policy,
- policy->min, CPUFREQ_RELATION_L);
- dbs_check_cpu(dbs_data, cpu);
- mutex_unlock(&cpu_cdbs->timer_mutex);
- mutex_unlock(&dbs_data->mutex);
+ cpufreq_governor_limits(policy, dbs_data);
break;
}
- return 0;
+
+unlock:
+ mutex_unlock(&cdata->mutex);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(cpufreq_governor_dbs);
diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h
index cc401d147e72..34736f5e869d 100644
--- a/drivers/cpufreq/cpufreq_governor.h
+++ b/drivers/cpufreq/cpufreq_governor.h
@@ -208,11 +208,16 @@ struct common_dbs_data {
void *(*get_cpu_dbs_info_s)(int cpu);
void (*gov_dbs_timer)(struct work_struct *work);
void (*gov_check_cpu)(int cpu, unsigned int load);
- int (*init)(struct dbs_data *dbs_data);
- void (*exit)(struct dbs_data *dbs_data);
+ int (*init)(struct dbs_data *dbs_data, bool notify);
+ void (*exit)(struct dbs_data *dbs_data, bool notify);
/* Governor specific ops, see below */
void *gov_ops;
+
+ /*
+ * Protects governor's data (struct dbs_data and struct common_dbs_data)
+ */
+ struct mutex mutex;
};
/* Governor Per policy data */
@@ -221,9 +226,6 @@ struct dbs_data {
unsigned int min_sampling_rate;
int usage_count;
void *tuners;
-
- /* dbs_mutex protects dbs_enable in governor start/stop */
- struct mutex mutex;
};
/* Governor specific ops, will be passed to dbs_data->gov_ops */
@@ -234,10 +236,6 @@ struct od_ops {
void (*freq_increase)(struct cpufreq_policy *policy, unsigned int freq);
};
-struct cs_ops {
- struct notifier_block *notifier_block;
-};
-
static inline int delay_for_sampling_rate(unsigned int sampling_rate)
{
int delay = usecs_to_jiffies(sampling_rate);
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index ad3f38fd3eb9..3c1e10f2304c 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -475,7 +475,7 @@ static struct attribute_group od_attr_group_gov_pol = {
/************************** sysfs end ************************/
-static int od_init(struct dbs_data *dbs_data)
+static int od_init(struct dbs_data *dbs_data, bool notify)
{
struct od_dbs_tuners *tuners;
u64 idle_time;
@@ -513,11 +513,10 @@ static int od_init(struct dbs_data *dbs_data)
tuners->io_is_busy = should_io_be_busy();
dbs_data->tuners = tuners;
- mutex_init(&dbs_data->mutex);
return 0;
}
-static void od_exit(struct dbs_data *dbs_data)
+static void od_exit(struct dbs_data *dbs_data, bool notify)
{
kfree(dbs_data->tuners);
}
@@ -541,6 +540,7 @@ static struct common_dbs_data od_dbs_cdata = {
.gov_ops = &od_ops,
.init = od_init,
.exit = od_exit,
+ .mutex = __MUTEX_INITIALIZER(od_dbs_cdata.mutex),
};
static void od_set_powersave_bias(unsigned int powersave_bias)
diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c
index 1d723dc8880c..3488c9c175eb 100644
--- a/drivers/cpufreq/gx-suspmod.c
+++ b/drivers/cpufreq/gx-suspmod.c
@@ -144,7 +144,7 @@ module_param(max_duration, int, 0444);
/**
- * we can detect a core multipiler from dir0_lsb
+ * we can detect a core multiplier from dir0_lsb
* from GX1 datasheet p.56,
* MULT[3:0]:
* 0000 = SYSCLK multiplied by 4 (test only)
@@ -346,7 +346,7 @@ static int cpufreq_gx_verify(struct cpufreq_policy *policy)
/* it needs to be assured that at least one supported frequency is
* within policy->min and policy->max. If it is not, policy->max
- * needs to be increased until one freuqency is supported.
+ * needs to be increased until one frequency is supported.
* policy->min may not be decreased, though. This way we guarantee a
* specific processing capacity.
*/
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 6414661ac1c4..15ada47bb720 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -26,6 +26,7 @@
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/acpi.h>
+#include <linux/vmalloc.h>
#include <trace/events/power.h>
#include <asm/div64.h>
@@ -48,9 +49,9 @@ static inline int32_t mul_fp(int32_t x, int32_t y)
return ((int64_t)x * (int64_t)y) >> FRAC_BITS;
}
-static inline int32_t div_fp(int32_t x, int32_t y)
+static inline int32_t div_fp(s64 x, s64 y)
{
- return div_s64((int64_t)x << FRAC_BITS, y);
+ return div64_s64((int64_t)x << FRAC_BITS, y);
}
static inline int ceiling_fp(int32_t x)
@@ -68,6 +69,7 @@ struct sample {
int32_t core_pct_busy;
u64 aperf;
u64 mperf;
+ u64 tsc;
int freq;
ktime_t time;
};
@@ -109,6 +111,7 @@ struct cpudata {
ktime_t last_sample_time;
u64 prev_aperf;
u64 prev_mperf;
+ u64 prev_tsc;
struct sample sample;
};
@@ -396,7 +399,7 @@ static ssize_t store_no_turbo(struct kobject *a, struct attribute *b,
update_turbo_state();
if (limits.turbo_disabled) {
- pr_warn("Turbo disabled by BIOS or unavailable on processor\n");
+ pr_warn("intel_pstate: Turbo disabled by BIOS or unavailable on processor\n");
return -EPERM;
}
@@ -484,7 +487,7 @@ static void __init intel_pstate_sysfs_expose_params(void)
static void intel_pstate_hwp_enable(void)
{
hwp_active++;
- pr_info("intel_pstate HWP enabled\n");
+ pr_info("intel_pstate: HWP enabled\n");
wrmsrl( MSR_PM_ENABLE, 0x1);
}
@@ -535,7 +538,7 @@ static void byt_set_pstate(struct cpudata *cpudata, int pstate)
val |= vid;
- wrmsrl(MSR_IA32_PERF_CTL, val);
+ wrmsrl_on_cpu(cpudata->cpu, MSR_IA32_PERF_CTL, val);
}
#define BYT_BCLK_FREQS 5
@@ -704,19 +707,20 @@ static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max)
*min = clamp_t(int, min_perf, cpu->pstate.min_pstate, max_perf);
}
-static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate)
+static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate, bool force)
{
int max_perf, min_perf;
- update_turbo_state();
-
- intel_pstate_get_min_max(cpu, &min_perf, &max_perf);
+ if (force) {
+ update_turbo_state();
- pstate = clamp_t(int, pstate, min_perf, max_perf);
+ intel_pstate_get_min_max(cpu, &min_perf, &max_perf);
- if (pstate == cpu->pstate.current_pstate)
- return;
+ pstate = clamp_t(int, pstate, min_perf, max_perf);
+ if (pstate == cpu->pstate.current_pstate)
+ return;
+ }
trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu);
cpu->pstate.current_pstate = pstate;
@@ -733,7 +737,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
if (pstate_funcs.get_vid)
pstate_funcs.get_vid(cpu);
- intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate);
+ intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate, false);
}
static inline void intel_pstate_calc_busy(struct cpudata *cpu)
@@ -756,23 +760,28 @@ static inline void intel_pstate_sample(struct cpudata *cpu)
{
u64 aperf, mperf;
unsigned long flags;
+ u64 tsc;
local_irq_save(flags);
rdmsrl(MSR_IA32_APERF, aperf);
rdmsrl(MSR_IA32_MPERF, mperf);
+ tsc = native_read_tsc();
local_irq_restore(flags);
cpu->last_sample_time = cpu->sample.time;
cpu->sample.time = ktime_get();
cpu->sample.aperf = aperf;
cpu->sample.mperf = mperf;
+ cpu->sample.tsc = tsc;
cpu->sample.aperf -= cpu->prev_aperf;
cpu->sample.mperf -= cpu->prev_mperf;
+ cpu->sample.tsc -= cpu->prev_tsc;
intel_pstate_calc_busy(cpu);
cpu->prev_aperf = aperf;
cpu->prev_mperf = mperf;
+ cpu->prev_tsc = tsc;
}
static inline void intel_hwp_set_sample_time(struct cpudata *cpu)
@@ -794,7 +803,7 @@ static inline void intel_pstate_set_sample_time(struct cpudata *cpu)
static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu)
{
int32_t core_busy, max_pstate, current_pstate, sample_ratio;
- u32 duration_us;
+ s64 duration_us;
u32 sample_time;
/*
@@ -821,8 +830,8 @@ static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu)
* to adjust our busyness.
*/
sample_time = pid_params.sample_rate_ms * USEC_PER_MSEC;
- duration_us = (u32) ktime_us_delta(cpu->sample.time,
- cpu->last_sample_time);
+ duration_us = ktime_us_delta(cpu->sample.time,
+ cpu->last_sample_time);
if (duration_us > sample_time * 3) {
sample_ratio = div_fp(int_tofp(sample_time),
int_tofp(duration_us));
@@ -837,6 +846,10 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu)
int32_t busy_scaled;
struct _pid *pid;
signed int ctl;
+ int from;
+ struct sample *sample;
+
+ from = cpu->pstate.current_pstate;
pid = &cpu->pid;
busy_scaled = intel_pstate_get_scaled_busy(cpu);
@@ -844,7 +857,17 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu)
ctl = pid_calc(pid, busy_scaled);
/* Negative values of ctl increase the pstate and vice versa */
- intel_pstate_set_pstate(cpu, cpu->pstate.current_pstate - ctl);
+ intel_pstate_set_pstate(cpu, cpu->pstate.current_pstate - ctl, true);
+
+ sample = &cpu->sample;
+ trace_pstate_sample(fp_toint(sample->core_pct_busy),
+ fp_toint(busy_scaled),
+ from,
+ cpu->pstate.current_pstate,
+ sample->mperf,
+ sample->aperf,
+ sample->tsc,
+ sample->freq);
}
static void intel_hwp_timer_func(unsigned long __data)
@@ -858,21 +881,11 @@ static void intel_hwp_timer_func(unsigned long __data)
static void intel_pstate_timer_func(unsigned long __data)
{
struct cpudata *cpu = (struct cpudata *) __data;
- struct sample *sample;
intel_pstate_sample(cpu);
- sample = &cpu->sample;
-
intel_pstate_adjust_busy_pstate(cpu);
- trace_pstate_sample(fp_toint(sample->core_pct_busy),
- fp_toint(intel_pstate_get_scaled_busy(cpu)),
- cpu->pstate.current_pstate,
- sample->mperf,
- sample->aperf,
- sample->freq);
-
intel_pstate_set_sample_time(cpu);
}
@@ -935,7 +948,7 @@ static int intel_pstate_init_cpu(unsigned int cpunum)
add_timer_on(&cpu->timer, cpunum);
- pr_debug("Intel pstate controlling: cpu %d\n", cpunum);
+ pr_debug("intel_pstate: controlling: cpu %d\n", cpunum);
return 0;
}
@@ -1001,13 +1014,13 @@ static void intel_pstate_stop_cpu(struct cpufreq_policy *policy)
int cpu_num = policy->cpu;
struct cpudata *cpu = all_cpu_data[cpu_num];
- pr_info("intel_pstate CPU %d exiting\n", cpu_num);
+ pr_debug("intel_pstate: CPU %d exiting\n", cpu_num);
del_timer_sync(&all_cpu_data[cpu_num]->timer);
if (hwp_active)
return;
- intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate);
+ intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate, false);
}
static int intel_pstate_cpu_init(struct cpufreq_policy *policy)
diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c
index 529cfd92158f..5dd95dab580d 100644
--- a/drivers/cpufreq/p4-clockmod.c
+++ b/drivers/cpufreq/p4-clockmod.c
@@ -172,7 +172,7 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
unsigned int i;
#ifdef CONFIG_SMP
- cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
+ cpumask_copy(policy->cpus, topology_sibling_cpumask(policy->cpu));
#endif
/* Errata workaround */
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c
index f9ce7e4bf0fe..5c035d04d827 100644
--- a/drivers/cpufreq/powernow-k8.c
+++ b/drivers/cpufreq/powernow-k8.c
@@ -57,13 +57,6 @@ static DEFINE_PER_CPU(struct powernow_k8_data *, powernow_data);
static struct cpufreq_driver cpufreq_amd64_driver;
-#ifndef CONFIG_SMP
-static inline const struct cpumask *cpu_core_mask(int cpu)
-{
- return cpumask_of(0);
-}
-#endif
-
/* Return a frequency in MHz, given an input fid */
static u32 find_freq_from_fid(u32 fid)
{
@@ -620,7 +613,7 @@ static int fill_powernow_table(struct powernow_k8_data *data,
pr_debug("cfid 0x%x, cvid 0x%x\n", data->currfid, data->currvid);
data->powernow_table = powernow_table;
- if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu)
+ if (cpumask_first(topology_core_cpumask(data->cpu)) == data->cpu)
print_basics(data);
for (j = 0; j < data->numps; j++)
@@ -784,7 +777,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
CPUFREQ_TABLE_END;
data->powernow_table = powernow_table;
- if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu)
+ if (cpumask_first(topology_core_cpumask(data->cpu)) == data->cpu)
print_basics(data);
/* notify BIOS that we exist */
@@ -1090,7 +1083,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
if (rc != 0)
goto err_out_exit_acpi;
- cpumask_copy(pol->cpus, cpu_core_mask(pol->cpu));
+ cpumask_copy(pol->cpus, topology_core_cpumask(pol->cpu));
data->available_cores = pol->cpus;
/* min/max the cpu is capable of */
diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c
index e24269ab4e9b..1d99c97defa9 100644
--- a/drivers/cpufreq/pxa2xx-cpufreq.c
+++ b/drivers/cpufreq/pxa2xx-cpufreq.c
@@ -56,7 +56,7 @@ module_param(pxa27x_maxfreq, uint, 0);
MODULE_PARM_DESC(pxa27x_maxfreq, "Set the pxa27x maxfreq in MHz"
"(typically 624=>pxa270, 416=>pxa271, 520=>pxa272)");
-typedef struct {
+struct pxa_freqs {
unsigned int khz;
unsigned int membus;
unsigned int cccr;
@@ -64,7 +64,7 @@ typedef struct {
unsigned int cclkcfg;
int vmin;
int vmax;
-} pxa_freqs_t;
+};
/* Define the refresh period in mSec for the SDRAM and the number of rows */
#define SDRAM_TREF 64 /* standard 64ms SDRAM */
@@ -86,7 +86,7 @@ static unsigned int sdram_rows;
/* Use the run mode frequencies for the CPUFREQ_POLICY_PERFORMANCE policy */
#define CCLKCFG CCLKCFG_TURBO | CCLKCFG_FCS
-static pxa_freqs_t pxa255_run_freqs[] =
+static const struct pxa_freqs pxa255_run_freqs[] =
{
/* CPU MEMBUS CCCR DIV2 CCLKCFG run turbo PXbus SDRAM */
{ 99500, 99500, 0x121, 1, CCLKCFG, -1, -1}, /* 99, 99, 50, 50 */
@@ -98,7 +98,7 @@ static pxa_freqs_t pxa255_run_freqs[] =
};
/* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */
-static pxa_freqs_t pxa255_turbo_freqs[] =
+static const struct pxa_freqs pxa255_turbo_freqs[] =
{
/* CPU MEMBUS CCCR DIV2 CCLKCFG run turbo PXbus SDRAM */
{ 99500, 99500, 0x121, 1, CCLKCFG, -1, -1}, /* 99, 99, 50, 50 */
@@ -153,7 +153,7 @@ MODULE_PARM_DESC(pxa255_turbo_table, "Selects the frequency table (0 = run table
((HT) ? CCLKCFG_HALFTURBO : 0) | \
((T) ? CCLKCFG_TURBO : 0))
-static pxa_freqs_t pxa27x_freqs[] = {
+static struct pxa_freqs pxa27x_freqs[] = {
{104000, 104000, PXA27x_CCCR(1, 8, 2), 0, CCLKCFG2(1, 0, 1), 900000, 1705000 },
{156000, 104000, PXA27x_CCCR(1, 8, 3), 0, CCLKCFG2(1, 0, 1), 1000000, 1705000 },
{208000, 208000, PXA27x_CCCR(0, 16, 2), 1, CCLKCFG2(0, 0, 1), 1180000, 1705000 },
@@ -171,7 +171,7 @@ extern unsigned get_clk_frequency_khz(int info);
#ifdef CONFIG_REGULATOR
-static int pxa_cpufreq_change_voltage(pxa_freqs_t *pxa_freq)
+static int pxa_cpufreq_change_voltage(const struct pxa_freqs *pxa_freq)
{
int ret = 0;
int vmin, vmax;
@@ -202,7 +202,7 @@ static void __init pxa_cpufreq_init_voltages(void)
}
}
#else
-static int pxa_cpufreq_change_voltage(pxa_freqs_t *pxa_freq)
+static int pxa_cpufreq_change_voltage(struct pxa_freqs *pxa_freq)
{
return 0;
}
@@ -211,7 +211,7 @@ static void __init pxa_cpufreq_init_voltages(void) { }
#endif
static void find_freq_tables(struct cpufreq_frequency_table **freq_table,
- pxa_freqs_t **pxa_freqs)
+ const struct pxa_freqs **pxa_freqs)
{
if (cpu_is_pxa25x()) {
if (!pxa255_turbo_table) {
@@ -270,7 +270,7 @@ static unsigned int pxa_cpufreq_get(unsigned int cpu)
static int pxa_set_target(struct cpufreq_policy *policy, unsigned int idx)
{
struct cpufreq_frequency_table *pxa_freqs_table;
- pxa_freqs_t *pxa_freq_settings;
+ const struct pxa_freqs *pxa_freq_settings;
unsigned long flags;
unsigned int new_freq_cpu, new_freq_mem;
unsigned int unused, preset_mdrefr, postset_mdrefr, cclkcfg;
@@ -361,7 +361,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy)
int i;
unsigned int freq;
struct cpufreq_frequency_table *pxa255_freq_table;
- pxa_freqs_t *pxa255_freqs;
+ const struct pxa_freqs *pxa255_freqs;
/* try to guess pxa27x cpu */
if (cpu_is_pxa27x())
diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c
index 88b21ae0d6b0..358f0752c31e 100644
--- a/drivers/cpufreq/qoriq-cpufreq.c
+++ b/drivers/cpufreq/qoriq-cpufreq.c
@@ -27,11 +27,11 @@
/**
* struct cpu_data
- * @parent: the parent node of cpu clock
+ * @pclk: the parent clock of cpu
* @table: frequency table
*/
struct cpu_data {
- struct device_node *parent;
+ struct clk **pclk;
struct cpufreq_frequency_table *table;
};
@@ -196,7 +196,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
- struct device_node *np;
+ struct device_node *np, *pnode;
int i, count, ret;
u32 freq, mask;
struct clk *clk;
@@ -219,17 +219,23 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
goto err_nomem2;
}
- data->parent = of_parse_phandle(np, "clocks", 0);
- if (!data->parent) {
+ pnode = of_parse_phandle(np, "clocks", 0);
+ if (!pnode) {
pr_err("%s: could not get clock information\n", __func__);
goto err_nomem2;
}
- count = of_property_count_strings(data->parent, "clock-names");
+ count = of_property_count_strings(pnode, "clock-names");
+ data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
+ if (!data->pclk) {
+ pr_err("%s: no memory\n", __func__);
+ goto err_node;
+ }
+
table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
if (!table) {
pr_err("%s: no memory\n", __func__);
- goto err_node;
+ goto err_pclk;
}
if (fmask)
@@ -238,7 +244,8 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
mask = 0x0;
for (i = 0; i < count; i++) {
- clk = of_clk_get(data->parent, i);
+ clk = of_clk_get(pnode, i);
+ data->pclk[i] = clk;
freq = clk_get_rate(clk);
/*
* the clock is valid if its frequency is not masked
@@ -273,13 +280,16 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->cpuinfo.transition_latency = u64temp + 1;
of_node_put(np);
+ of_node_put(pnode);
return 0;
err_nomem1:
kfree(table);
+err_pclk:
+ kfree(data->pclk);
err_node:
- of_node_put(data->parent);
+ of_node_put(pnode);
err_nomem2:
policy->driver_data = NULL;
kfree(data);
@@ -293,7 +303,7 @@ static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
struct cpu_data *data = policy->driver_data;
- of_node_put(data->parent);
+ kfree(data->pclk);
kfree(data->table);
kfree(data);
policy->driver_data = NULL;
@@ -307,7 +317,7 @@ static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
struct clk *parent;
struct cpu_data *data = policy->driver_data;
- parent = of_clk_get(data->parent, data->table[index].driver_data);
+ parent = data->pclk[data->table[index].driver_data];
return clk_set_parent(policy->clk, parent);
}
diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c
index e56d632a8b21..37555c6b86a7 100644
--- a/drivers/cpufreq/speedstep-ich.c
+++ b/drivers/cpufreq/speedstep-ich.c
@@ -292,7 +292,7 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
/* only run on CPU to be set, or on its sibling */
#ifdef CONFIG_SMP
- cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
+ cpumask_copy(policy->cpus, topology_sibling_cpumask(policy->cpu));
#endif
policy_cpu = cpumask_any_and(policy->cpus, cpu_online_mask);
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
index 59372077ec7c..1e3ef5ec4784 100644
--- a/drivers/cpuidle/cpuidle-powernv.c
+++ b/drivers/cpuidle/cpuidle-powernv.c
@@ -29,18 +29,25 @@ struct cpuidle_driver powernv_idle_driver = {
static int max_idle_state;
static struct cpuidle_state *cpuidle_state_table;
+static u64 snooze_timeout;
+static bool snooze_timeout_en;
static int snooze_loop(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
+ u64 snooze_exit_time;
+
local_irq_enable();
set_thread_flag(TIF_POLLING_NRFLAG);
+ snooze_exit_time = get_tb() + snooze_timeout;
ppc64_runlatch_off();
while (!need_resched()) {
HMT_low();
HMT_very_low();
+ if (snooze_timeout_en && get_tb() > snooze_exit_time)
+ break;
}
HMT_medium();
@@ -252,6 +259,11 @@ static int powernv_idle_probe(void)
cpuidle_state_table = powernv_states;
/* Device tree can indicate more idle states */
max_idle_state = powernv_add_idle_states();
+ if (max_idle_state > 1) {
+ snooze_timeout_en = true;
+ snooze_timeout = powernv_states[1].target_residency *
+ tb_ticks_per_usec;
+ }
} else
return -ENODEV;
diff --git a/drivers/cpuidle/cpuidle-pseries.c b/drivers/cpuidle/cpuidle-pseries.c
index bb9e2b6f3ecc..07135e009d8b 100644
--- a/drivers/cpuidle/cpuidle-pseries.c
+++ b/drivers/cpuidle/cpuidle-pseries.c
@@ -27,6 +27,8 @@ struct cpuidle_driver pseries_idle_driver = {
static int max_idle_state;
static struct cpuidle_state *cpuidle_state_table;
+static u64 snooze_timeout;
+static bool snooze_timeout_en;
static inline void idle_loop_prolog(unsigned long *in_purr)
{
@@ -58,14 +60,18 @@ static int snooze_loop(struct cpuidle_device *dev,
int index)
{
unsigned long in_purr;
+ u64 snooze_exit_time;
idle_loop_prolog(&in_purr);
local_irq_enable();
set_thread_flag(TIF_POLLING_NRFLAG);
+ snooze_exit_time = get_tb() + snooze_timeout;
while (!need_resched()) {
HMT_low();
HMT_very_low();
+ if (snooze_timeout_en && get_tb() > snooze_exit_time)
+ break;
}
HMT_medium();
@@ -244,6 +250,11 @@ static int pseries_idle_probe(void)
} else
return -ENODEV;
+ if (max_idle_state > 1) {
+ snooze_timeout_en = true;
+ snooze_timeout = cpuidle_state_table[1].target_residency *
+ tb_ticks_per_usec;
+ }
return 0;
}
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 7a73a279e179..e8e2775c3821 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -65,7 +65,7 @@ int cpuidle_play_dead(void)
return -ENODEV;
/* Find lowest-power state that supports long-term idle */
- for (i = drv->state_count - 1; i >= CPUIDLE_DRIVER_STATE_START; i--)
+ for (i = drv->state_count - 1; i >= 0; i--)
if (drv->states[i].enter_dead)
return drv->states[i].enter_dead(dev, i);
@@ -73,16 +73,21 @@ int cpuidle_play_dead(void)
}
static int find_deepest_state(struct cpuidle_driver *drv,
- struct cpuidle_device *dev, bool freeze)
+ struct cpuidle_device *dev,
+ unsigned int max_latency,
+ unsigned int forbidden_flags,
+ bool freeze)
{
unsigned int latency_req = 0;
- int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1;
+ int i, ret = -ENXIO;
- for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
+ for (i = 0; i < drv->state_count; i++) {
struct cpuidle_state *s = &drv->states[i];
struct cpuidle_state_usage *su = &dev->states_usage[i];
if (s->disabled || su->disable || s->exit_latency <= latency_req
+ || s->exit_latency > max_latency
+ || (s->flags & forbidden_flags)
|| (freeze && !s->enter_freeze))
continue;
@@ -92,6 +97,7 @@ static int find_deepest_state(struct cpuidle_driver *drv,
return ret;
}
+#ifdef CONFIG_SUSPEND
/**
* cpuidle_find_deepest_state - Find the deepest available idle state.
* @drv: cpuidle driver for the given CPU.
@@ -100,7 +106,7 @@ static int find_deepest_state(struct cpuidle_driver *drv,
int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
struct cpuidle_device *dev)
{
- return find_deepest_state(drv, dev, false);
+ return find_deepest_state(drv, dev, UINT_MAX, 0, false);
}
static void enter_freeze_proper(struct cpuidle_driver *drv,
@@ -139,18 +145,19 @@ int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev)
* that interrupts won't be enabled when it exits and allows the tick to
* be frozen safely.
*/
- index = find_deepest_state(drv, dev, true);
+ index = find_deepest_state(drv, dev, UINT_MAX, 0, true);
if (index >= 0)
enter_freeze_proper(drv, dev, index);
return index;
}
+#endif /* CONFIG_SUSPEND */
/**
* cpuidle_enter_state - enter the state and update stats
* @dev: cpuidle device for this cpu
* @drv: cpuidle driver for this cpu
- * @next_state: index into drv->states of the state to enter
+ * @index: index into the states table in @drv of the state to enter
*/
int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
int index)
@@ -158,9 +165,28 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
int entered_state;
struct cpuidle_state *target_state = &drv->states[index];
+ bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP);
ktime_t time_start, time_end;
s64 diff;
+ /*
+ * Tell the time framework to switch to a broadcast timer because our
+ * local timer will be shut down. If a local timer is used from another
+ * CPU as a broadcast timer, this call may fail if it is not available.
+ */
+ if (broadcast && tick_broadcast_enter()) {
+ index = find_deepest_state(drv, dev, target_state->exit_latency,
+ CPUIDLE_FLAG_TIMER_STOP, false);
+ if (index < 0) {
+ default_idle_call();
+ return -EBUSY;
+ }
+ target_state = &drv->states[index];
+ }
+
+ /* Take note of the planned idle state. */
+ sched_idle_set_state(target_state);
+
trace_cpu_idle_rcuidle(index, dev->cpu);
time_start = ktime_get();
@@ -169,6 +195,16 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
time_end = ktime_get();
trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
+ /* The cpu is no longer idle or about to enter idle. */
+ sched_idle_set_state(NULL);
+
+ if (broadcast) {
+ if (WARN_ON_ONCE(!irqs_disabled()))
+ local_irq_disable();
+
+ tick_broadcast_exit();
+ }
+
if (!cpuidle_state_is_coupled(dev, drv, entered_state))
local_irq_enable();
@@ -233,7 +269,7 @@ int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev,
*/
void cpuidle_reflect(struct cpuidle_device *dev, int index)
{
- if (cpuidle_curr_governor->reflect)
+ if (cpuidle_curr_governor->reflect && index >= 0)
cpuidle_curr_governor->reflect(dev, index);
}
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index b8a5fa15ca24..22e4463d1787 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -367,9 +367,9 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
static void menu_reflect(struct cpuidle_device *dev, int index)
{
struct menu_device *data = this_cpu_ptr(&menu_devices);
+
data->last_state_idx = index;
- if (index >= 0)
- data->needs_update = 1;
+ data->needs_update = 1;
}
/**
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 033c0c86f6ec..4044125fb5d5 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -162,10 +162,10 @@ config CRYPTO_GHASH_S390
config CRYPTO_DEV_MV_CESA
tristate "Marvell's Cryptographic Engine"
depends on PLAT_ORION
- select CRYPTO_ALGAPI
select CRYPTO_AES
- select CRYPTO_BLKCIPHER2
+ select CRYPTO_BLKCIPHER
select CRYPTO_HASH
+ select SRAM
help
This driver allows you to utilize the Cryptographic Engines and
Security Accelerator (CESA) which can be found on the Marvell Orion
@@ -173,10 +173,27 @@ config CRYPTO_DEV_MV_CESA
Currently the driver supports AES in ECB and CBC mode without DMA.
+config CRYPTO_DEV_MARVELL_CESA
+ tristate "New Marvell's Cryptographic Engine driver"
+ depends on PLAT_ORION || ARCH_MVEBU
+ select CRYPTO_AES
+ select CRYPTO_DES
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_HASH
+ select SRAM
+ help
+ This driver allows you to utilize the Cryptographic Engines and
+ Security Accelerator (CESA) which can be found on the Armada 370.
+ This driver supports CPU offload through DMA transfers.
+
+ This driver is aimed at replacing the mv_cesa driver. This will only
+ happen once it has received proper testing.
+
config CRYPTO_DEV_NIAGARA2
tristate "Niagara2 Stream Processing Unit driver"
select CRYPTO_DES
- select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_HASH
depends on SPARC64
help
Each core of a Niagara2 processor contains a Stream
@@ -189,7 +206,6 @@ config CRYPTO_DEV_NIAGARA2
config CRYPTO_DEV_HIFN_795X
tristate "Driver HIFN 795x crypto accelerator chips"
select CRYPTO_DES
- select CRYPTO_ALGAPI
select CRYPTO_BLKCIPHER
select HW_RANDOM if CRYPTO_DEV_HIFN_795X_RNG
depends on PCI
@@ -208,8 +224,10 @@ source drivers/crypto/caam/Kconfig
config CRYPTO_DEV_TALITOS
tristate "Talitos Freescale Security Engine (SEC)"
- select CRYPTO_ALGAPI
+ select CRYPTO_AEAD
select CRYPTO_AUTHENC
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_HASH
select HW_RANDOM
depends on FSL_SOC
help
@@ -222,11 +240,29 @@ config CRYPTO_DEV_TALITOS
To compile this driver as a module, choose M here: the module
will be called talitos.
+config CRYPTO_DEV_TALITOS1
+ bool "SEC1 (SEC 1.0 and SEC Lite 1.2)"
+ depends on CRYPTO_DEV_TALITOS
+ depends on PPC_8xx || PPC_82xx
+ default y
+ help
+ Say 'Y' here to use the Freescale Security Engine (SEC) version 1.0
+ found on MPC82xx or the Freescale Security Engine (SEC Lite)
+ version 1.2 found on MPC8xx
+
+config CRYPTO_DEV_TALITOS2
+ bool "SEC2+ (SEC version 2.0 or upper)"
+ depends on CRYPTO_DEV_TALITOS
+ default y if !PPC_8xx
+ help
+ Say 'Y' here to use the Freescale Security Engine (SEC)
+ version 2 and following as found on MPC83xx, MPC85xx, etc ...
+
config CRYPTO_DEV_IXP4XX
tristate "Driver for IXP4xx crypto hardware acceleration"
depends on ARCH_IXP4XX && IXP4XX_QMGR && IXP4XX_NPE
select CRYPTO_DES
- select CRYPTO_ALGAPI
+ select CRYPTO_AEAD
select CRYPTO_AUTHENC
select CRYPTO_BLKCIPHER
help
@@ -236,7 +272,6 @@ config CRYPTO_DEV_PPC4XX
tristate "Driver AMCC PPC4xx crypto accelerator"
depends on PPC && 4xx
select CRYPTO_HASH
- select CRYPTO_ALGAPI
select CRYPTO_BLKCIPHER
help
This option allows you to have support for AMCC crypto acceleration.
@@ -257,7 +292,7 @@ config CRYPTO_DEV_OMAP_AES
tristate "Support for OMAP AES hw engine"
depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP2PLUS
select CRYPTO_AES
- select CRYPTO_BLKCIPHER2
+ select CRYPTO_BLKCIPHER
help
OMAP processors have AES module accelerator. Select this if you
want to use the OMAP module for AES algorithms.
@@ -266,7 +301,7 @@ config CRYPTO_DEV_OMAP_DES
tristate "Support for OMAP DES3DES hw engine"
depends on ARCH_OMAP2PLUS
select CRYPTO_DES
- select CRYPTO_BLKCIPHER2
+ select CRYPTO_BLKCIPHER
help
OMAP processors have DES/3DES module accelerator. Select this if you
want to use the OMAP module for DES and 3DES algorithms. Currently
@@ -276,9 +311,10 @@ config CRYPTO_DEV_OMAP_DES
config CRYPTO_DEV_PICOXCELL
tristate "Support for picoXcell IPSEC and Layer2 crypto engines"
depends on ARCH_PICOXCELL && HAVE_CLK
+ select CRYPTO_AEAD
select CRYPTO_AES
select CRYPTO_AUTHENC
- select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
select CRYPTO_DES
select CRYPTO_CBC
select CRYPTO_ECB
@@ -304,7 +340,6 @@ config CRYPTO_DEV_S5P
tristate "Support for Samsung S5PV210/Exynos crypto accelerator"
depends on ARCH_S5PV210 || ARCH_EXYNOS
select CRYPTO_AES
- select CRYPTO_ALGAPI
select CRYPTO_BLKCIPHER
help
This option allows you to have support for S5P crypto acceleration.
@@ -312,11 +347,13 @@ config CRYPTO_DEV_S5P
algorithms execution.
config CRYPTO_DEV_NX
- bool "Support for IBM Power7+ in-Nest cryptographic acceleration"
- depends on PPC64 && IBMVIO && !CPU_LITTLE_ENDIAN
- default n
+ bool "Support for IBM PowerPC Nest (NX) cryptographic acceleration"
+ depends on PPC64
help
- Support for Power7+ in-Nest cryptographic acceleration.
+ This enables support for the NX hardware cryptographic accelerator
+ coprocessor that is in IBM PowerPC P7+ or later processors. This
+ does not actually enable any drivers, it only allows you to select
+ which acceleration type (encryption and/or compression) to enable.
if CRYPTO_DEV_NX
source "drivers/crypto/nx/Kconfig"
@@ -325,7 +362,6 @@ endif
config CRYPTO_DEV_UX500
tristate "Driver for ST-Ericsson UX500 crypto hardware acceleration"
depends on ARCH_U8500
- select CRYPTO_ALGAPI
help
Driver for ST-Ericsson UX500 crypto engine.
@@ -343,10 +379,7 @@ config CRYPTO_DEV_BFIN_CRC
config CRYPTO_DEV_ATMEL_AES
tristate "Support for Atmel AES hw accelerator"
depends on ARCH_AT91
- select CRYPTO_CBC
- select CRYPTO_ECB
select CRYPTO_AES
- select CRYPTO_ALGAPI
select CRYPTO_BLKCIPHER
select AT_HDMAC
help
@@ -361,9 +394,6 @@ config CRYPTO_DEV_ATMEL_TDES
tristate "Support for Atmel DES/TDES hw accelerator"
depends on ARCH_AT91
select CRYPTO_DES
- select CRYPTO_CBC
- select CRYPTO_ECB
- select CRYPTO_ALGAPI
select CRYPTO_BLKCIPHER
help
Some Atmel processors have DES/TDES hw accelerator.
@@ -376,10 +406,7 @@ config CRYPTO_DEV_ATMEL_TDES
config CRYPTO_DEV_ATMEL_SHA
tristate "Support for Atmel SHA hw accelerator"
depends on ARCH_AT91
- select CRYPTO_SHA1
- select CRYPTO_SHA256
- select CRYPTO_SHA512
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
Some Atmel processors have SHA1/SHA224/SHA256/SHA384/SHA512
hw accelerator.
@@ -392,7 +419,6 @@ config CRYPTO_DEV_ATMEL_SHA
config CRYPTO_DEV_CCP
bool "Support for AMD Cryptographic Coprocessor"
depends on ((X86 && PCI) || (ARM64 && (OF_ADDRESS || ACPI))) && HAS_IOMEM
- default n
help
The AMD Cryptographic Coprocessor provides hardware support
for encryption, hashing and related operations.
@@ -404,13 +430,11 @@ endif
config CRYPTO_DEV_MXS_DCP
tristate "Support for Freescale MXS DCP"
depends on ARCH_MXS
- select CRYPTO_SHA1
- select CRYPTO_SHA256
select CRYPTO_CBC
select CRYPTO_ECB
select CRYPTO_AES
select CRYPTO_BLKCIPHER
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
The Freescale i.MX23/i.MX28 has SHA1/SHA256 and AES128 CBC/ECB
co-processor on the die.
@@ -429,7 +453,6 @@ config CRYPTO_DEV_QCE
select CRYPTO_CBC
select CRYPTO_XTS
select CRYPTO_CTR
- select CRYPTO_ALGAPI
select CRYPTO_BLKCIPHER
help
This driver supports Qualcomm crypto engine accelerator
@@ -439,7 +462,6 @@ config CRYPTO_DEV_QCE
config CRYPTO_DEV_VMX
bool "Support for VMX cryptographic acceleration instructions"
depends on PPC64
- default n
help
Support for VMX cryptographic acceleration instructions.
@@ -449,7 +471,6 @@ config CRYPTO_DEV_IMGTEC_HASH
tristate "Imagination Technologies hardware hash accelerator"
depends on MIPS || COMPILE_TEST
depends on HAS_DMA
- select CRYPTO_ALGAPI
select CRYPTO_MD5
select CRYPTO_SHA1
select CRYPTO_SHA256
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index fb84be7e6be5..e35c07a8da85 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o
obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o
+obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell/
obj-$(CONFIG_CRYPTO_DEV_MXS_DCP) += mxs-dcp.o
obj-$(CONFIG_CRYPTO_DEV_NIAGARA2) += n2_crypto.o
n2_crypto-y := n2_core.o n2_asm.o
diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig
index e7555ff4cafd..e286e285aa8a 100644
--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -45,7 +45,6 @@ config CRYPTO_DEV_FSL_CAAM_RINGSIZE
config CRYPTO_DEV_FSL_CAAM_INTC
bool "Job Ring interrupt coalescing"
depends on CRYPTO_DEV_FSL_CAAM_JR
- default n
help
Enable the Job Ring's interrupt coalescing feature.
@@ -77,8 +76,9 @@ config CRYPTO_DEV_FSL_CAAM_CRYPTO_API
tristate "Register algorithm implementations with the Crypto API"
depends on CRYPTO_DEV_FSL_CAAM && CRYPTO_DEV_FSL_CAAM_JR
default y
- select CRYPTO_ALGAPI
+ select CRYPTO_AEAD
select CRYPTO_AUTHENC
+ select CRYPTO_BLKCIPHER
help
Selecting this will offload crypto for users of the
scatterlist crypto API (such as the linux native IPSec
@@ -115,7 +115,6 @@ config CRYPTO_DEV_FSL_CAAM_RNG_API
config CRYPTO_DEV_FSL_CAAM_DEBUG
bool "Enable debug output in CAAM driver"
depends on CRYPTO_DEV_FSL_CAAM
- default n
help
Selecting this will enable printing of various debug
information in the CAAM driver.
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 29071a156cbe..daca933a82ec 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -65,6 +65,10 @@
/* max IV is max of AES_BLOCK_SIZE, DES3_EDE_BLOCK_SIZE */
#define CAAM_MAX_IV_LENGTH 16
+#define AEAD_DESC_JOB_IO_LEN (DESC_JOB_IO_LEN + CAAM_CMD_SZ * 2)
+#define GCM_DESC_JOB_IO_LEN (AEAD_DESC_JOB_IO_LEN + \
+ CAAM_CMD_SZ * 4)
+
/* length of descriptors text */
#define DESC_AEAD_BASE (4 * CAAM_CMD_SZ)
#define DESC_AEAD_ENC_LEN (DESC_AEAD_BASE + 15 * CAAM_CMD_SZ)
@@ -79,18 +83,16 @@
#define DESC_AEAD_NULL_DEC_LEN (DESC_AEAD_NULL_BASE + 17 * CAAM_CMD_SZ)
#define DESC_GCM_BASE (3 * CAAM_CMD_SZ)
-#define DESC_GCM_ENC_LEN (DESC_GCM_BASE + 23 * CAAM_CMD_SZ)
-#define DESC_GCM_DEC_LEN (DESC_GCM_BASE + 19 * CAAM_CMD_SZ)
+#define DESC_GCM_ENC_LEN (DESC_GCM_BASE + 16 * CAAM_CMD_SZ)
+#define DESC_GCM_DEC_LEN (DESC_GCM_BASE + 12 * CAAM_CMD_SZ)
#define DESC_RFC4106_BASE (3 * CAAM_CMD_SZ)
-#define DESC_RFC4106_ENC_LEN (DESC_RFC4106_BASE + 15 * CAAM_CMD_SZ)
-#define DESC_RFC4106_DEC_LEN (DESC_RFC4106_BASE + 14 * CAAM_CMD_SZ)
-#define DESC_RFC4106_GIVENC_LEN (DESC_RFC4106_BASE + 21 * CAAM_CMD_SZ)
+#define DESC_RFC4106_ENC_LEN (DESC_RFC4106_BASE + 10 * CAAM_CMD_SZ)
+#define DESC_RFC4106_DEC_LEN (DESC_RFC4106_BASE + 10 * CAAM_CMD_SZ)
#define DESC_RFC4543_BASE (3 * CAAM_CMD_SZ)
-#define DESC_RFC4543_ENC_LEN (DESC_RFC4543_BASE + 25 * CAAM_CMD_SZ)
-#define DESC_RFC4543_DEC_LEN (DESC_RFC4543_BASE + 27 * CAAM_CMD_SZ)
-#define DESC_RFC4543_GIVENC_LEN (DESC_RFC4543_BASE + 30 * CAAM_CMD_SZ)
+#define DESC_RFC4543_ENC_LEN (DESC_RFC4543_BASE + 11 * CAAM_CMD_SZ)
+#define DESC_RFC4543_DEC_LEN (DESC_RFC4543_BASE + 12 * CAAM_CMD_SZ)
#define DESC_ABLKCIPHER_BASE (3 * CAAM_CMD_SZ)
#define DESC_ABLKCIPHER_ENC_LEN (DESC_ABLKCIPHER_BASE + \
@@ -98,8 +100,7 @@
#define DESC_ABLKCIPHER_DEC_LEN (DESC_ABLKCIPHER_BASE + \
15 * CAAM_CMD_SZ)
-#define DESC_MAX_USED_BYTES (DESC_RFC4543_GIVENC_LEN + \
- CAAM_MAX_KEY_SIZE)
+#define DESC_MAX_USED_BYTES (CAAM_DESC_BYTES_MAX - DESC_JOB_IO_LEN)
#define DESC_MAX_USED_LEN (DESC_MAX_USED_BYTES / CAAM_CMD_SZ)
#ifdef DEBUG
@@ -258,7 +259,7 @@ static void init_sh_desc_key_aead(u32 *desc, struct caam_ctx *ctx,
static int aead_null_set_sh_desc(struct crypto_aead *aead)
{
- struct aead_tfm *tfm = &aead->base.crt_aead;
+ unsigned int ivsize = crypto_aead_ivsize(aead);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
bool keys_fit_inline = false;
@@ -273,7 +274,7 @@ static int aead_null_set_sh_desc(struct crypto_aead *aead)
ctx->split_key_pad_len <= CAAM_DESC_BYTES_MAX)
keys_fit_inline = true;
- /* aead_encrypt shared descriptor */
+ /* old_aead_encrypt shared descriptor */
desc = ctx->sh_desc_enc;
init_sh_desc(desc, HDR_SHARE_SERIAL);
@@ -362,7 +363,7 @@ static int aead_null_set_sh_desc(struct crypto_aead *aead)
desc = ctx->sh_desc_dec;
- /* aead_decrypt shared descriptor */
+ /* old_aead_decrypt shared descriptor */
init_sh_desc(desc, HDR_SHARE_SERIAL);
/* Skip if already shared */
@@ -383,7 +384,7 @@ static int aead_null_set_sh_desc(struct crypto_aead *aead)
/* assoclen + cryptlen = seqinlen - ivsize - authsize */
append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
- ctx->authsize + tfm->ivsize);
+ ctx->authsize + ivsize);
/* assoclen = (assoclen + cryptlen) - cryptlen */
append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
@@ -449,7 +450,7 @@ static int aead_null_set_sh_desc(struct crypto_aead *aead)
static int aead_set_sh_desc(struct crypto_aead *aead)
{
- struct aead_tfm *tfm = &aead->base.crt_aead;
+ unsigned int ivsize = crypto_aead_ivsize(aead);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct crypto_tfm *ctfm = crypto_aead_tfm(aead);
const char *alg_name = crypto_tfm_alg_name(ctfm);
@@ -496,7 +497,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
CAAM_DESC_BYTES_MAX)
keys_fit_inline = true;
- /* aead_encrypt shared descriptor */
+ /* old_aead_encrypt shared descriptor */
desc = ctx->sh_desc_enc;
/* Note: Context registers are saved. */
@@ -510,7 +511,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
/* assoclen + cryptlen = seqinlen - ivsize */
- append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, tfm->ivsize);
+ append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, ivsize);
/* assoclen = (assoclen + cryptlen) - cryptlen */
append_math_sub(desc, VARSEQINLEN, REG2, REG3, CAAM_CMD_SZ);
@@ -518,7 +519,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
/* read assoc before reading payload */
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
KEY_VLF);
- aead_append_ld_iv(desc, tfm->ivsize, ctx1_iv_off);
+ aead_append_ld_iv(desc, ivsize, ctx1_iv_off);
/* Load Counter into CONTEXT1 reg */
if (is_rfc3686)
@@ -565,7 +566,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
CAAM_DESC_BYTES_MAX)
keys_fit_inline = true;
- /* aead_decrypt shared descriptor */
+ /* old_aead_decrypt shared descriptor */
desc = ctx->sh_desc_dec;
/* Note: Context registers are saved. */
@@ -577,7 +578,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
/* assoclen + cryptlen = seqinlen - ivsize - authsize */
append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
- ctx->authsize + tfm->ivsize);
+ ctx->authsize + ivsize);
/* assoclen = (assoclen + cryptlen) - cryptlen */
append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
@@ -586,7 +587,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
KEY_VLF);
- aead_append_ld_iv(desc, tfm->ivsize, ctx1_iv_off);
+ aead_append_ld_iv(desc, ivsize, ctx1_iv_off);
/* Load Counter into CONTEXT1 reg */
if (is_rfc3686)
@@ -645,20 +646,20 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
/* Generate IV */
geniv = NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DEST_DECO |
NFIFOENTRY_DTYPE_MSG | NFIFOENTRY_LC1 |
- NFIFOENTRY_PTYPE_RND | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
+ NFIFOENTRY_PTYPE_RND | (ivsize << NFIFOENTRY_DLEN_SHIFT);
append_load_imm_u32(desc, geniv, LDST_CLASS_IND_CCB |
LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
append_move(desc, MOVE_WAITCOMP |
MOVE_SRC_INFIFO | MOVE_DEST_CLASS1CTX |
(ctx1_iv_off << MOVE_OFFSET_SHIFT) |
- (tfm->ivsize << MOVE_LEN_SHIFT));
+ (ivsize << MOVE_LEN_SHIFT));
append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
/* Copy IV to class 1 context */
append_move(desc, MOVE_SRC_CLASS1CTX | MOVE_DEST_OUTFIFO |
(ctx1_iv_off << MOVE_OFFSET_SHIFT) |
- (tfm->ivsize << MOVE_LEN_SHIFT));
+ (ivsize << MOVE_LEN_SHIFT));
/* Return to encryption */
append_operation(desc, ctx->class2_alg_type |
@@ -676,10 +677,10 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
/* Copy iv from outfifo to class 2 fifo */
moveiv = NFIFOENTRY_STYPE_OFIFO | NFIFOENTRY_DEST_CLASS2 |
- NFIFOENTRY_DTYPE_MSG | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
+ NFIFOENTRY_DTYPE_MSG | (ivsize << NFIFOENTRY_DLEN_SHIFT);
append_load_imm_u32(desc, moveiv, LDST_CLASS_IND_CCB |
LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
- append_load_imm_u32(desc, tfm->ivsize, LDST_CLASS_2_CCB |
+ append_load_imm_u32(desc, ivsize, LDST_CLASS_2_CCB |
LDST_SRCDST_WORD_DATASZ_REG | LDST_IMM);
/* Load Counter into CONTEXT1 reg */
@@ -698,7 +699,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
append_math_add(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
/* Not need to reload iv */
- append_seq_fifo_load(desc, tfm->ivsize,
+ append_seq_fifo_load(desc, ivsize,
FIFOLD_CLASS_SKIP);
/* Will read cryptlen */
@@ -738,7 +739,6 @@ static int aead_setauthsize(struct crypto_aead *authenc,
static int gcm_set_sh_desc(struct crypto_aead *aead)
{
- struct aead_tfm *tfm = &aead->base.crt_aead;
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
bool keys_fit_inline = false;
@@ -754,7 +754,7 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
* Job Descriptor and Shared Descriptor
* must fit into the 64-word Descriptor h/w Buffer
*/
- if (DESC_GCM_ENC_LEN + DESC_JOB_IO_LEN +
+ if (DESC_GCM_ENC_LEN + GCM_DESC_JOB_IO_LEN +
ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
keys_fit_inline = true;
@@ -777,34 +777,34 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
append_operation(desc, ctx->class1_alg_type |
OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
- /* cryptlen = seqoutlen - authsize */
- append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
+ /* if assoclen + cryptlen is ZERO, skip to ICV write */
+ append_math_sub(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
+ zero_assoc_jump_cmd2 = append_jump(desc, JUMP_TEST_ALL |
+ JUMP_COND_MATH_Z);
- /* assoclen + cryptlen = seqinlen - ivsize */
- append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, tfm->ivsize);
+ /* if assoclen is ZERO, skip reading the assoc data */
+ append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+ zero_assoc_jump_cmd1 = append_jump(desc, JUMP_TEST_ALL |
+ JUMP_COND_MATH_Z);
- /* assoclen = (assoclen + cryptlen) - cryptlen */
- append_math_sub(desc, REG1, REG2, REG3, CAAM_CMD_SZ);
+ append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
+
+ /* skip assoc data */
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_SKIP | FIFOLDST_VLF);
+
+ /* cryptlen = seqinlen - assoclen */
+ append_math_sub(desc, VARSEQOUTLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
/* if cryptlen is ZERO jump to zero-payload commands */
- append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
zero_payload_jump_cmd = append_jump(desc, JUMP_TEST_ALL |
JUMP_COND_MATH_Z);
- /* read IV */
- append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
- FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
-
- /* if assoclen is ZERO, skip reading the assoc data */
- append_math_add(desc, VARSEQINLEN, ZERO, REG1, CAAM_CMD_SZ);
- zero_assoc_jump_cmd1 = append_jump(desc, JUMP_TEST_ALL |
- JUMP_COND_MATH_Z);
/* read assoc data */
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
set_jump_tgt_here(desc, zero_assoc_jump_cmd1);
- append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+ append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
/* write encrypted data */
append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
@@ -814,31 +814,17 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST1);
/* jump the zero-payload commands */
- append_jump(desc, JUMP_TEST_ALL | 7);
+ append_jump(desc, JUMP_TEST_ALL | 2);
/* zero-payload commands */
set_jump_tgt_here(desc, zero_payload_jump_cmd);
- /* if assoclen is ZERO, jump to IV reading - is the only input data */
- append_math_add(desc, VARSEQINLEN, ZERO, REG1, CAAM_CMD_SZ);
- zero_assoc_jump_cmd2 = append_jump(desc, JUMP_TEST_ALL |
- JUMP_COND_MATH_Z);
- /* read IV */
- append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
- FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
-
/* read assoc data */
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
FIFOLD_TYPE_AAD | FIFOLD_TYPE_LAST1);
- /* jump to ICV writing */
- append_jump(desc, JUMP_TEST_ALL | 2);
-
- /* read IV - is the only input data */
+ /* There is no input data */
set_jump_tgt_here(desc, zero_assoc_jump_cmd2);
- append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
- FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 |
- FIFOLD_TYPE_LAST1);
/* write ICV */
append_seq_store(desc, ctx->authsize, LDST_CLASS_1_CCB |
@@ -862,7 +848,7 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
* must all fit into the 64-word Descriptor h/w Buffer
*/
keys_fit_inline = false;
- if (DESC_GCM_DEC_LEN + DESC_JOB_IO_LEN +
+ if (DESC_GCM_DEC_LEN + GCM_DESC_JOB_IO_LEN +
ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
keys_fit_inline = true;
@@ -886,33 +872,30 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
append_operation(desc, ctx->class1_alg_type |
OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT | OP_ALG_ICV_ON);
- /* assoclen + cryptlen = seqinlen - ivsize - icvsize */
- append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
- ctx->authsize + tfm->ivsize);
-
- /* assoclen = (assoclen + cryptlen) - cryptlen */
- append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
- append_math_sub(desc, REG1, REG3, REG2, CAAM_CMD_SZ);
+ /* if assoclen is ZERO, skip reading the assoc data */
+ append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+ zero_assoc_jump_cmd1 = append_jump(desc, JUMP_TEST_ALL |
+ JUMP_COND_MATH_Z);
- /* read IV */
- append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
- FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+ append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
- /* jump to zero-payload command if cryptlen is zero */
- append_math_add(desc, VARSEQOUTLEN, ZERO, REG2, CAAM_CMD_SZ);
- zero_payload_jump_cmd = append_jump(desc, JUMP_TEST_ALL |
- JUMP_COND_MATH_Z);
+ /* skip assoc data */
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_SKIP | FIFOLDST_VLF);
- append_math_add(desc, VARSEQINLEN, ZERO, REG1, CAAM_CMD_SZ);
- /* if asoclen is ZERO, skip reading assoc data */
- zero_assoc_jump_cmd1 = append_jump(desc, JUMP_TEST_ALL |
- JUMP_COND_MATH_Z);
/* read assoc data */
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
+
set_jump_tgt_here(desc, zero_assoc_jump_cmd1);
- append_math_add(desc, VARSEQINLEN, ZERO, REG2, CAAM_CMD_SZ);
+ /* cryptlen = seqoutlen - assoclen */
+ append_math_sub(desc, VARSEQINLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
+
+ /* jump to zero-payload command if cryptlen is zero */
+ zero_payload_jump_cmd = append_jump(desc, JUMP_TEST_ALL |
+ JUMP_COND_MATH_Z);
+
+ append_math_sub(desc, VARSEQOUTLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
/* store encrypted data */
append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
@@ -921,21 +904,9 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
FIFOLD_TYPE_MSG | FIFOLD_TYPE_FLUSH1);
- /* jump the zero-payload commands */
- append_jump(desc, JUMP_TEST_ALL | 4);
-
/* zero-payload command */
set_jump_tgt_here(desc, zero_payload_jump_cmd);
- /* if assoclen is ZERO, jump to ICV reading */
- append_math_add(desc, VARSEQINLEN, ZERO, REG1, CAAM_CMD_SZ);
- zero_assoc_jump_cmd2 = append_jump(desc, JUMP_TEST_ALL |
- JUMP_COND_MATH_Z);
- /* read assoc data */
- append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
- FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
- set_jump_tgt_here(desc, zero_assoc_jump_cmd2);
-
/* read ICV */
append_seq_fifo_load(desc, ctx->authsize, FIFOLD_CLASS_CLASS1 |
FIFOLD_TYPE_ICV | FIFOLD_TYPE_LAST1);
@@ -968,13 +939,11 @@ static int gcm_setauthsize(struct crypto_aead *authenc, unsigned int authsize)
static int rfc4106_set_sh_desc(struct crypto_aead *aead)
{
- struct aead_tfm *tfm = &aead->base.crt_aead;
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
bool keys_fit_inline = false;
- u32 *key_jump_cmd, *move_cmd, *write_iv_cmd;
+ u32 *key_jump_cmd;
u32 *desc;
- u32 geniv;
if (!ctx->enckeylen || !ctx->authsize)
return 0;
@@ -984,7 +953,7 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
* Job Descriptor and Shared Descriptor
* must fit into the 64-word Descriptor h/w Buffer
*/
- if (DESC_RFC4106_ENC_LEN + DESC_JOB_IO_LEN +
+ if (DESC_RFC4106_ENC_LEN + GCM_DESC_JOB_IO_LEN +
ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
keys_fit_inline = true;
@@ -1007,29 +976,21 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
append_operation(desc, ctx->class1_alg_type |
OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
- /* cryptlen = seqoutlen - authsize */
- append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
+ append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
- /* assoclen + cryptlen = seqinlen - ivsize */
- append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, tfm->ivsize);
-
- /* assoclen = (assoclen + cryptlen) - cryptlen */
- append_math_sub(desc, VARSEQINLEN, REG2, REG3, CAAM_CMD_SZ);
-
- /* Read Salt */
- append_fifo_load_as_imm(desc, (void *)(ctx->key + ctx->enckeylen),
- 4, FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_IV);
- /* Read AES-GCM-ESP IV */
- append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
- FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+ /* Skip assoc data */
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_SKIP | FIFOLDST_VLF);
/* Read assoc data */
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
+ /* cryptlen = seqoutlen - assoclen */
+ append_math_sub(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
+
/* Will read cryptlen bytes */
- append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+ append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
/* Write encrypted data */
append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
@@ -1083,30 +1044,21 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
append_operation(desc, ctx->class1_alg_type |
OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT | OP_ALG_ICV_ON);
- /* assoclen + cryptlen = seqinlen - ivsize - icvsize */
- append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
- ctx->authsize + tfm->ivsize);
-
- /* assoclen = (assoclen + cryptlen) - cryptlen */
- append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
- append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
-
- /* Will write cryptlen bytes */
- append_math_sub(desc, VARSEQOUTLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
+ append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+ append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
- /* Read Salt */
- append_fifo_load_as_imm(desc, (void *)(ctx->key + ctx->enckeylen),
- 4, FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_IV);
- /* Read AES-GCM-ESP IV */
- append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
- FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+ /* Skip assoc data */
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_SKIP | FIFOLDST_VLF);
/* Read assoc data */
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
+ /* Will write cryptlen bytes */
+ append_math_sub(desc, VARSEQOUTLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
+
/* Will read cryptlen bytes */
- append_math_add(desc, VARSEQINLEN, ZERO, REG2, CAAM_CMD_SZ);
+ append_math_sub(desc, VARSEQINLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
/* Store payload data */
append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
@@ -1132,107 +1084,6 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
desc_bytes(desc), 1);
#endif
- /*
- * Job Descriptor and Shared Descriptors
- * must all fit into the 64-word Descriptor h/w Buffer
- */
- keys_fit_inline = false;
- if (DESC_RFC4106_GIVENC_LEN + DESC_JOB_IO_LEN +
- ctx->split_key_pad_len + ctx->enckeylen <=
- CAAM_DESC_BYTES_MAX)
- keys_fit_inline = true;
-
- /* rfc4106_givencrypt shared descriptor */
- desc = ctx->sh_desc_givenc;
-
- init_sh_desc(desc, HDR_SHARE_SERIAL);
-
- /* Skip key loading if it is loaded due to sharing */
- key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
- JUMP_COND_SHRD);
- if (keys_fit_inline)
- append_key_as_imm(desc, (void *)ctx->key, ctx->enckeylen,
- ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
- else
- append_key(desc, ctx->key_dma, ctx->enckeylen,
- CLASS_1 | KEY_DEST_CLASS_REG);
- set_jump_tgt_here(desc, key_jump_cmd);
-
- /* Generate IV */
- geniv = NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DEST_DECO |
- NFIFOENTRY_DTYPE_MSG | NFIFOENTRY_LC1 |
- NFIFOENTRY_PTYPE_RND | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
- append_load_imm_u32(desc, geniv, LDST_CLASS_IND_CCB |
- LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
- append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
- move_cmd = append_move(desc, MOVE_SRC_INFIFO | MOVE_DEST_DESCBUF |
- (tfm->ivsize << MOVE_LEN_SHIFT));
- append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
-
- /* Copy generated IV to OFIFO */
- write_iv_cmd = append_move(desc, MOVE_SRC_DESCBUF | MOVE_DEST_OUTFIFO |
- (tfm->ivsize << MOVE_LEN_SHIFT));
-
- /* Class 1 operation */
- append_operation(desc, ctx->class1_alg_type |
- OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
-
- /* ivsize + cryptlen = seqoutlen - authsize */
- append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
-
- /* assoclen = seqinlen - (ivsize + cryptlen) */
- append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
-
- /* Will write ivsize + cryptlen */
- append_math_add(desc, VARSEQOUTLEN, REG3, REG0, CAAM_CMD_SZ);
-
- /* Read Salt and generated IV */
- append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_IV |
- FIFOLD_TYPE_FLUSH1 | IMMEDIATE | 12);
- /* Append Salt */
- append_data(desc, (void *)(ctx->key + ctx->enckeylen), 4);
- set_move_tgt_here(desc, move_cmd);
- set_move_tgt_here(desc, write_iv_cmd);
- /* Blank commands. Will be overwritten by generated IV. */
- append_cmd(desc, 0x00000000);
- append_cmd(desc, 0x00000000);
- /* End of blank commands */
-
- /* No need to reload iv */
- append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_SKIP);
-
- /* Read assoc data */
- append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
- FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
-
- /* Will read cryptlen */
- append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
-
- /* Store generated IV and encrypted data */
- append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
-
- /* Read payload data */
- append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
- FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST1);
-
- /* Write ICV */
- append_seq_store(desc, ctx->authsize, LDST_CLASS_1_CCB |
- LDST_SRCDST_BYTE_CONTEXT);
-
- ctx->sh_desc_givenc_dma = dma_map_single(jrdev, desc,
- desc_bytes(desc),
- DMA_TO_DEVICE);
- if (dma_mapping_error(jrdev, ctx->sh_desc_givenc_dma)) {
- dev_err(jrdev, "unable to map shared descriptor\n");
- return -ENOMEM;
- }
-#ifdef DEBUG
- print_hex_dump(KERN_ERR,
- "rfc4106 givenc shdesc@"__stringify(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, desc,
- desc_bytes(desc), 1);
-#endif
-
return 0;
}
@@ -1249,14 +1100,12 @@ static int rfc4106_setauthsize(struct crypto_aead *authenc,
static int rfc4543_set_sh_desc(struct crypto_aead *aead)
{
- struct aead_tfm *tfm = &aead->base.crt_aead;
struct caam_ctx *ctx = crypto_aead_ctx(aead);
struct device *jrdev = ctx->jrdev;
bool keys_fit_inline = false;
- u32 *key_jump_cmd, *write_iv_cmd, *write_aad_cmd;
+ u32 *key_jump_cmd;
u32 *read_move_cmd, *write_move_cmd;
u32 *desc;
- u32 geniv;
if (!ctx->enckeylen || !ctx->authsize)
return 0;
@@ -1266,7 +1115,7 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
* Job Descriptor and Shared Descriptor
* must fit into the 64-word Descriptor h/w Buffer
*/
- if (DESC_RFC4543_ENC_LEN + DESC_JOB_IO_LEN +
+ if (DESC_RFC4543_ENC_LEN + GCM_DESC_JOB_IO_LEN +
ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
keys_fit_inline = true;
@@ -1289,48 +1138,8 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
append_operation(desc, ctx->class1_alg_type |
OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
- /* Load AES-GMAC ESP IV into Math1 register */
- append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_WORD_DECO_MATH1 |
- LDST_CLASS_DECO | tfm->ivsize);
-
- /* Wait the DMA transaction to finish */
- append_jump(desc, JUMP_TEST_ALL | JUMP_COND_CALM |
- (1 << JUMP_OFFSET_SHIFT));
-
- /* Overwrite blank immediate AES-GMAC ESP IV data */
- write_iv_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
- (tfm->ivsize << MOVE_LEN_SHIFT));
-
- /* Overwrite blank immediate AAD data */
- write_aad_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
- (tfm->ivsize << MOVE_LEN_SHIFT));
-
- /* cryptlen = seqoutlen - authsize */
- append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
-
- /* assoclen = (seqinlen - ivsize) - cryptlen */
- append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
-
- /* Read Salt and AES-GMAC ESP IV */
- append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
- FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 | (4 + tfm->ivsize));
- /* Append Salt */
- append_data(desc, (void *)(ctx->key + ctx->enckeylen), 4);
- set_move_tgt_here(desc, write_iv_cmd);
- /* Blank commands. Will be overwritten by AES-GMAC ESP IV. */
- append_cmd(desc, 0x00000000);
- append_cmd(desc, 0x00000000);
- /* End of blank commands */
-
- /* Read assoc data */
- append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
- FIFOLD_TYPE_AAD);
-
- /* Will read cryptlen bytes */
- append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
-
- /* Will write cryptlen bytes */
- append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
+ /* assoclen + cryptlen = seqinlen */
+ append_math_sub(desc, REG3, SEQINLEN, REG0, CAAM_CMD_SZ);
/*
* MOVE_LEN opcode is not available in all SEC HW revisions,
@@ -1342,16 +1151,13 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
write_move_cmd = append_move(desc, MOVE_SRC_MATH3 | MOVE_DEST_DESCBUF |
(0x8 << MOVE_LEN_SHIFT));
- /* Authenticate AES-GMAC ESP IV */
- append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
- FIFOLD_TYPE_AAD | tfm->ivsize);
- set_move_tgt_here(desc, write_aad_cmd);
- /* Blank commands. Will be overwritten by AES-GMAC ESP IV. */
- append_cmd(desc, 0x00000000);
- append_cmd(desc, 0x00000000);
- /* End of blank commands */
+ /* Will read assoclen + cryptlen bytes */
+ append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
- /* Read and write cryptlen bytes */
+ /* Will write assoclen + cryptlen bytes */
+ append_math_sub(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
+
+ /* Read and write assoclen + cryptlen bytes */
aead_append_src_dst(desc, FIFOLD_TYPE_AAD);
set_move_tgt_here(desc, read_move_cmd);
@@ -1382,7 +1188,7 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
* must all fit into the 64-word Descriptor h/w Buffer
*/
keys_fit_inline = false;
- if (DESC_RFC4543_DEC_LEN + DESC_JOB_IO_LEN +
+ if (DESC_RFC4543_DEC_LEN + GCM_DESC_JOB_IO_LEN +
ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
keys_fit_inline = true;
@@ -1405,28 +1211,8 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
append_operation(desc, ctx->class1_alg_type |
OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT | OP_ALG_ICV_ON);
- /* Load AES-GMAC ESP IV into Math1 register */
- append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_WORD_DECO_MATH1 |
- LDST_CLASS_DECO | tfm->ivsize);
-
- /* Wait the DMA transaction to finish */
- append_jump(desc, JUMP_TEST_ALL | JUMP_COND_CALM |
- (1 << JUMP_OFFSET_SHIFT));
-
- /* assoclen + cryptlen = (seqinlen - ivsize) - icvsize */
- append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM, ctx->authsize);
-
- /* Overwrite blank immediate AES-GMAC ESP IV data */
- write_iv_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
- (tfm->ivsize << MOVE_LEN_SHIFT));
-
- /* Overwrite blank immediate AAD data */
- write_aad_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
- (tfm->ivsize << MOVE_LEN_SHIFT));
-
- /* assoclen = (assoclen + cryptlen) - cryptlen */
- append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
- append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
+ /* assoclen + cryptlen = seqoutlen */
+ append_math_sub(desc, REG3, SEQOUTLEN, REG0, CAAM_CMD_SZ);
/*
* MOVE_LEN opcode is not available in all SEC HW revisions,
@@ -1438,40 +1224,16 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
write_move_cmd = append_move(desc, MOVE_SRC_MATH3 | MOVE_DEST_DESCBUF |
(0x8 << MOVE_LEN_SHIFT));
- /* Read Salt and AES-GMAC ESP IV */
- append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
- FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 | (4 + tfm->ivsize));
- /* Append Salt */
- append_data(desc, (void *)(ctx->key + ctx->enckeylen), 4);
- set_move_tgt_here(desc, write_iv_cmd);
- /* Blank commands. Will be overwritten by AES-GMAC ESP IV. */
- append_cmd(desc, 0x00000000);
- append_cmd(desc, 0x00000000);
- /* End of blank commands */
-
- /* Read assoc data */
- append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
- FIFOLD_TYPE_AAD);
-
- /* Will read cryptlen bytes */
- append_math_add(desc, VARSEQINLEN, ZERO, REG2, CAAM_CMD_SZ);
+ /* Will read assoclen + cryptlen bytes */
+ append_math_sub(desc, VARSEQINLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
- /* Will write cryptlen bytes */
- append_math_add(desc, VARSEQOUTLEN, ZERO, REG2, CAAM_CMD_SZ);
-
- /* Authenticate AES-GMAC ESP IV */
- append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
- FIFOLD_TYPE_AAD | tfm->ivsize);
- set_move_tgt_here(desc, write_aad_cmd);
- /* Blank commands. Will be overwritten by AES-GMAC ESP IV. */
- append_cmd(desc, 0x00000000);
- append_cmd(desc, 0x00000000);
- /* End of blank commands */
+ /* Will write assoclen + cryptlen bytes */
+ append_math_sub(desc, VARSEQOUTLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
/* Store payload data */
append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
- /* In-snoop cryptlen data */
+ /* In-snoop assoclen + cryptlen data */
append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH | FIFOLDST_VLF |
FIFOLD_TYPE_AAD | FIFOLD_TYPE_LAST2FLUSH1);
@@ -1499,135 +1261,6 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
desc_bytes(desc), 1);
#endif
- /*
- * Job Descriptor and Shared Descriptors
- * must all fit into the 64-word Descriptor h/w Buffer
- */
- keys_fit_inline = false;
- if (DESC_RFC4543_GIVENC_LEN + DESC_JOB_IO_LEN +
- ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
- keys_fit_inline = true;
-
- /* rfc4543_givencrypt shared descriptor */
- desc = ctx->sh_desc_givenc;
-
- init_sh_desc(desc, HDR_SHARE_SERIAL);
-
- /* Skip key loading if it is loaded due to sharing */
- key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
- JUMP_COND_SHRD);
- if (keys_fit_inline)
- append_key_as_imm(desc, (void *)ctx->key, ctx->enckeylen,
- ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
- else
- append_key(desc, ctx->key_dma, ctx->enckeylen,
- CLASS_1 | KEY_DEST_CLASS_REG);
- set_jump_tgt_here(desc, key_jump_cmd);
-
- /* Generate IV */
- geniv = NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DEST_DECO |
- NFIFOENTRY_DTYPE_MSG | NFIFOENTRY_LC1 |
- NFIFOENTRY_PTYPE_RND | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
- append_load_imm_u32(desc, geniv, LDST_CLASS_IND_CCB |
- LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
- append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
- /* Move generated IV to Math1 register */
- append_move(desc, MOVE_SRC_INFIFO | MOVE_DEST_MATH1 |
- (tfm->ivsize << MOVE_LEN_SHIFT));
- append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
-
- /* Overwrite blank immediate AES-GMAC IV data */
- write_iv_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
- (tfm->ivsize << MOVE_LEN_SHIFT));
-
- /* Overwrite blank immediate AAD data */
- write_aad_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
- (tfm->ivsize << MOVE_LEN_SHIFT));
-
- /* Copy generated IV to OFIFO */
- append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_OUTFIFO |
- (tfm->ivsize << MOVE_LEN_SHIFT));
-
- /* Class 1 operation */
- append_operation(desc, ctx->class1_alg_type |
- OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
-
- /* ivsize + cryptlen = seqoutlen - authsize */
- append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
-
- /* assoclen = seqinlen - (ivsize + cryptlen) */
- append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
-
- /* Will write ivsize + cryptlen */
- append_math_add(desc, VARSEQOUTLEN, REG3, REG0, CAAM_CMD_SZ);
-
- /*
- * MOVE_LEN opcode is not available in all SEC HW revisions,
- * thus need to do some magic, i.e. self-patch the descriptor
- * buffer.
- */
- read_move_cmd = append_move(desc, MOVE_SRC_DESCBUF | MOVE_DEST_MATH3 |
- (0x6 << MOVE_LEN_SHIFT));
- write_move_cmd = append_move(desc, MOVE_SRC_MATH3 | MOVE_DEST_DESCBUF |
- (0x8 << MOVE_LEN_SHIFT));
-
- /* Read Salt and AES-GMAC generated IV */
- append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
- FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 | (4 + tfm->ivsize));
- /* Append Salt */
- append_data(desc, (void *)(ctx->key + ctx->enckeylen), 4);
- set_move_tgt_here(desc, write_iv_cmd);
- /* Blank commands. Will be overwritten by AES-GMAC generated IV. */
- append_cmd(desc, 0x00000000);
- append_cmd(desc, 0x00000000);
- /* End of blank commands */
-
- /* No need to reload iv */
- append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_SKIP);
-
- /* Read assoc data */
- append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
- FIFOLD_TYPE_AAD);
-
- /* Will read cryptlen */
- append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
-
- /* Authenticate AES-GMAC IV */
- append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
- FIFOLD_TYPE_AAD | tfm->ivsize);
- set_move_tgt_here(desc, write_aad_cmd);
- /* Blank commands. Will be overwritten by AES-GMAC IV. */
- append_cmd(desc, 0x00000000);
- append_cmd(desc, 0x00000000);
- /* End of blank commands */
-
- /* Read and write cryptlen bytes */
- aead_append_src_dst(desc, FIFOLD_TYPE_AAD);
-
- set_move_tgt_here(desc, read_move_cmd);
- set_move_tgt_here(desc, write_move_cmd);
- append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
- /* Move payload data to OFIFO */
- append_move(desc, MOVE_SRC_INFIFO_CL | MOVE_DEST_OUTFIFO);
-
- /* Write ICV */
- append_seq_store(desc, ctx->authsize, LDST_CLASS_1_CCB |
- LDST_SRCDST_BYTE_CONTEXT);
-
- ctx->sh_desc_givenc_dma = dma_map_single(jrdev, desc,
- desc_bytes(desc),
- DMA_TO_DEVICE);
- if (dma_mapping_error(jrdev, ctx->sh_desc_givenc_dma)) {
- dev_err(jrdev, "unable to map shared descriptor\n");
- return -ENOMEM;
- }
-#ifdef DEBUG
- print_hex_dump(KERN_ERR,
- "rfc4543 givenc shdesc@"__stringify(__LINE__)": ",
- DUMP_PREFIX_ADDRESS, 16, 4, desc,
- desc_bytes(desc), 1);
-#endif
-
return 0;
}
@@ -2100,7 +1733,7 @@ struct aead_edesc {
int sec4_sg_bytes;
dma_addr_t sec4_sg_dma;
struct sec4_sg_entry *sec4_sg;
- u32 hw_desc[0];
+ u32 hw_desc[];
};
/*
@@ -2154,6 +1787,16 @@ static void aead_unmap(struct device *dev,
struct aead_edesc *edesc,
struct aead_request *req)
{
+ caam_unmap(dev, req->src, req->dst,
+ edesc->src_nents, edesc->src_chained, edesc->dst_nents,
+ edesc->dst_chained, 0, 0,
+ edesc->sec4_sg_dma, edesc->sec4_sg_bytes);
+}
+
+static void old_aead_unmap(struct device *dev,
+ struct aead_edesc *edesc,
+ struct aead_request *req)
+{
struct crypto_aead *aead = crypto_aead_reqtfm(req);
int ivsize = crypto_aead_ivsize(aead);
@@ -2184,6 +1827,28 @@ static void aead_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
{
struct aead_request *req = context;
struct aead_edesc *edesc;
+
+#ifdef DEBUG
+ dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+#endif
+
+ edesc = container_of(desc, struct aead_edesc, hw_desc[0]);
+
+ if (err)
+ caam_jr_strstatus(jrdev, err);
+
+ aead_unmap(jrdev, edesc, req);
+
+ kfree(edesc);
+
+ aead_request_complete(req, err);
+}
+
+static void old_aead_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
+ void *context)
+{
+ struct aead_request *req = context;
+ struct aead_edesc *edesc;
#ifdef DEBUG
struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -2198,7 +1863,7 @@ static void aead_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
if (err)
caam_jr_strstatus(jrdev, err);
- aead_unmap(jrdev, edesc, req);
+ old_aead_unmap(jrdev, edesc, req);
#ifdef DEBUG
print_hex_dump(KERN_ERR, "assoc @"__stringify(__LINE__)": ",
@@ -2223,6 +1888,34 @@ static void aead_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
{
struct aead_request *req = context;
struct aead_edesc *edesc;
+
+#ifdef DEBUG
+ dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+#endif
+
+ edesc = container_of(desc, struct aead_edesc, hw_desc[0]);
+
+ if (err)
+ caam_jr_strstatus(jrdev, err);
+
+ aead_unmap(jrdev, edesc, req);
+
+ /*
+ * verify hw auth check passed else return -EBADMSG
+ */
+ if ((err & JRSTA_CCBERR_ERRID_MASK) == JRSTA_CCBERR_ERRID_ICVCHK)
+ err = -EBADMSG;
+
+ kfree(edesc);
+
+ aead_request_complete(req, err);
+}
+
+static void old_aead_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
+ void *context)
+{
+ struct aead_request *req = context;
+ struct aead_edesc *edesc;
#ifdef DEBUG
struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -2246,7 +1939,7 @@ static void aead_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
if (err)
caam_jr_strstatus(jrdev, err);
- aead_unmap(jrdev, edesc, req);
+ old_aead_unmap(jrdev, edesc, req);
/*
* verify hw auth check passed else return -EBADMSG
@@ -2342,10 +2035,10 @@ static void ablkcipher_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
/*
* Fill in aead job descriptor
*/
-static void init_aead_job(u32 *sh_desc, dma_addr_t ptr,
- struct aead_edesc *edesc,
- struct aead_request *req,
- bool all_contig, bool encrypt)
+static void old_init_aead_job(u32 *sh_desc, dma_addr_t ptr,
+ struct aead_edesc *edesc,
+ struct aead_request *req,
+ bool all_contig, bool encrypt)
{
struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -2425,6 +2118,97 @@ static void init_aead_job(u32 *sh_desc, dma_addr_t ptr,
}
/*
+ * Fill in aead job descriptor
+ */
+static void init_aead_job(struct aead_request *req,
+ struct aead_edesc *edesc,
+ bool all_contig, bool encrypt)
+{
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ int authsize = ctx->authsize;
+ u32 *desc = edesc->hw_desc;
+ u32 out_options, in_options;
+ dma_addr_t dst_dma, src_dma;
+ int len, sec4_sg_index = 0;
+ dma_addr_t ptr;
+ u32 *sh_desc;
+
+ sh_desc = encrypt ? ctx->sh_desc_enc : ctx->sh_desc_dec;
+ ptr = encrypt ? ctx->sh_desc_enc_dma : ctx->sh_desc_dec_dma;
+
+ len = desc_len(sh_desc);
+ init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE);
+
+ if (all_contig) {
+ src_dma = sg_dma_address(req->src);
+ in_options = 0;
+ } else {
+ src_dma = edesc->sec4_sg_dma;
+ sec4_sg_index += edesc->src_nents;
+ in_options = LDST_SGF;
+ }
+
+ append_seq_in_ptr(desc, src_dma, req->assoclen + req->cryptlen,
+ in_options);
+
+ dst_dma = src_dma;
+ out_options = in_options;
+
+ if (unlikely(req->src != req->dst)) {
+ if (!edesc->dst_nents) {
+ dst_dma = sg_dma_address(req->dst);
+ } else {
+ dst_dma = edesc->sec4_sg_dma +
+ sec4_sg_index *
+ sizeof(struct sec4_sg_entry);
+ out_options = LDST_SGF;
+ }
+ }
+
+ if (encrypt)
+ append_seq_out_ptr(desc, dst_dma,
+ req->assoclen + req->cryptlen + authsize,
+ out_options);
+ else
+ append_seq_out_ptr(desc, dst_dma,
+ req->assoclen + req->cryptlen - authsize,
+ out_options);
+
+ /* REG3 = assoclen */
+ append_math_add_imm_u32(desc, REG3, ZERO, IMM, req->assoclen);
+}
+
+static void init_gcm_job(struct aead_request *req,
+ struct aead_edesc *edesc,
+ bool all_contig, bool encrypt)
+{
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ unsigned int ivsize = crypto_aead_ivsize(aead);
+ u32 *desc = edesc->hw_desc;
+ bool generic_gcm = (ivsize == 12);
+ unsigned int last;
+
+ init_aead_job(req, edesc, all_contig, encrypt);
+
+ /* BUG This should not be specific to generic GCM. */
+ last = 0;
+ if (encrypt && generic_gcm && !(req->assoclen + req->cryptlen))
+ last = FIFOLD_TYPE_LAST1;
+
+ /* Read GCM IV */
+ append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
+ FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 | 12 | last);
+ /* Append Salt */
+ if (!generic_gcm)
+ append_data(desc, ctx->key + ctx->enckeylen, 4);
+ /* Append IV */
+ append_data(desc, req->iv, ivsize);
+ /* End of blank commands */
+}
+
+/*
* Fill in aead givencrypt job descriptor
*/
static void init_aead_giv_job(u32 *sh_desc, dma_addr_t ptr,
@@ -2608,9 +2392,10 @@ static void init_ablkcipher_giv_job(u32 *sh_desc, dma_addr_t ptr,
/*
* allocate and map the aead extended descriptor
*/
-static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
- int desc_bytes, bool *all_contig_ptr,
- bool encrypt)
+static struct aead_edesc *old_aead_edesc_alloc(struct aead_request *req,
+ int desc_bytes,
+ bool *all_contig_ptr,
+ bool encrypt)
{
struct crypto_aead *aead = crypto_aead_reqtfm(req);
struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -2713,10 +2498,8 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
sec4_sg_index = 0;
if (!all_contig) {
if (!is_gcm) {
- sg_to_sec4_sg(req->assoc,
- assoc_nents,
- edesc->sec4_sg +
- sec4_sg_index, 0);
+ sg_to_sec4_sg_len(req->assoc, req->assoclen,
+ edesc->sec4_sg + sec4_sg_index);
sec4_sg_index += assoc_nents;
}
@@ -2725,10 +2508,8 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
sec4_sg_index += 1;
if (is_gcm) {
- sg_to_sec4_sg(req->assoc,
- assoc_nents,
- edesc->sec4_sg +
- sec4_sg_index, 0);
+ sg_to_sec4_sg_len(req->assoc, req->assoclen,
+ edesc->sec4_sg + sec4_sg_index);
sec4_sg_index += assoc_nents;
}
@@ -2752,7 +2533,124 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
return edesc;
}
-static int aead_encrypt(struct aead_request *req)
+/*
+ * allocate and map the aead extended descriptor
+ */
+static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
+ int desc_bytes, bool *all_contig_ptr,
+ bool encrypt)
+{
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+ int src_nents, dst_nents = 0;
+ struct aead_edesc *edesc;
+ int sgc;
+ bool all_contig = true;
+ bool src_chained = false, dst_chained = false;
+ int sec4_sg_index, sec4_sg_len = 0, sec4_sg_bytes;
+ unsigned int authsize = ctx->authsize;
+
+ if (unlikely(req->dst != req->src)) {
+ src_nents = sg_count(req->src, req->assoclen + req->cryptlen,
+ &src_chained);
+ dst_nents = sg_count(req->dst,
+ req->assoclen + req->cryptlen +
+ (encrypt ? authsize : (-authsize)),
+ &dst_chained);
+ } else {
+ src_nents = sg_count(req->src,
+ req->assoclen + req->cryptlen +
+ (encrypt ? authsize : 0),
+ &src_chained);
+ }
+
+ /* Check if data are contiguous. */
+ all_contig = !src_nents;
+ if (!all_contig) {
+ src_nents = src_nents ? : 1;
+ sec4_sg_len = src_nents;
+ }
+
+ sec4_sg_len += dst_nents;
+
+ sec4_sg_bytes = sec4_sg_len * sizeof(struct sec4_sg_entry);
+
+ /* allocate space for base edesc and hw desc commands, link tables */
+ edesc = kzalloc(sizeof(struct aead_edesc) + desc_bytes +
+ sec4_sg_bytes, GFP_DMA | flags);
+ if (!edesc) {
+ dev_err(jrdev, "could not allocate extended descriptor\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (likely(req->src == req->dst)) {
+ sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
+ DMA_BIDIRECTIONAL, src_chained);
+ if (unlikely(!sgc)) {
+ dev_err(jrdev, "unable to map source\n");
+ kfree(edesc);
+ return ERR_PTR(-ENOMEM);
+ }
+ } else {
+ sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
+ DMA_TO_DEVICE, src_chained);
+ if (unlikely(!sgc)) {
+ dev_err(jrdev, "unable to map source\n");
+ kfree(edesc);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sgc = dma_map_sg_chained(jrdev, req->dst, dst_nents ? : 1,
+ DMA_FROM_DEVICE, dst_chained);
+ if (unlikely(!sgc)) {
+ dev_err(jrdev, "unable to map destination\n");
+ dma_unmap_sg_chained(jrdev, req->src, src_nents ? : 1,
+ DMA_TO_DEVICE, src_chained);
+ kfree(edesc);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ edesc->src_nents = src_nents;
+ edesc->src_chained = src_chained;
+ edesc->dst_nents = dst_nents;
+ edesc->dst_chained = dst_chained;
+ edesc->sec4_sg = (void *)edesc + sizeof(struct aead_edesc) +
+ desc_bytes;
+ *all_contig_ptr = all_contig;
+
+ sec4_sg_index = 0;
+ if (!all_contig) {
+ sg_to_sec4_sg_last(req->src, src_nents,
+ edesc->sec4_sg + sec4_sg_index, 0);
+ sec4_sg_index += src_nents;
+ }
+ if (dst_nents) {
+ sg_to_sec4_sg_last(req->dst, dst_nents,
+ edesc->sec4_sg + sec4_sg_index, 0);
+ }
+
+ if (!sec4_sg_bytes)
+ return edesc;
+
+ edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg,
+ sec4_sg_bytes, DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) {
+ dev_err(jrdev, "unable to map S/G table\n");
+ aead_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ edesc->sec4_sg_bytes = sec4_sg_bytes;
+
+ return edesc;
+}
+
+static int gcm_encrypt(struct aead_request *req)
{
struct aead_edesc *edesc;
struct crypto_aead *aead = crypto_aead_reqtfm(req);
@@ -2763,14 +2661,12 @@ static int aead_encrypt(struct aead_request *req)
int ret = 0;
/* allocate extended descriptor */
- edesc = aead_edesc_alloc(req, DESC_JOB_IO_LEN *
- CAAM_CMD_SZ, &all_contig, true);
+ edesc = aead_edesc_alloc(req, GCM_DESC_JOB_IO_LEN, &all_contig, true);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
/* Create and submit job descriptor */
- init_aead_job(ctx->sh_desc_enc, ctx->sh_desc_enc_dma, edesc, req,
- all_contig, true);
+ init_gcm_job(req, edesc, all_contig, true);
#ifdef DEBUG
print_hex_dump(KERN_ERR, "aead jobdesc@"__stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
@@ -2789,7 +2685,7 @@ static int aead_encrypt(struct aead_request *req)
return ret;
}
-static int aead_decrypt(struct aead_request *req)
+static int old_aead_encrypt(struct aead_request *req)
{
struct aead_edesc *edesc;
struct crypto_aead *aead = crypto_aead_reqtfm(req);
@@ -2800,8 +2696,80 @@ static int aead_decrypt(struct aead_request *req)
int ret = 0;
/* allocate extended descriptor */
- edesc = aead_edesc_alloc(req, DESC_JOB_IO_LEN *
- CAAM_CMD_SZ, &all_contig, false);
+ edesc = old_aead_edesc_alloc(req, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &all_contig, true);
+ if (IS_ERR(edesc))
+ return PTR_ERR(edesc);
+
+ /* Create and submit job descriptor */
+ old_init_aead_job(ctx->sh_desc_enc, ctx->sh_desc_enc_dma, edesc, req,
+ all_contig, true);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead jobdesc@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
+
+ desc = edesc->hw_desc;
+ ret = caam_jr_enqueue(jrdev, desc, old_aead_encrypt_done, req);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ old_aead_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
+
+ return ret;
+}
+
+static int gcm_decrypt(struct aead_request *req)
+{
+ struct aead_edesc *edesc;
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ bool all_contig;
+ u32 *desc;
+ int ret = 0;
+
+ /* allocate extended descriptor */
+ edesc = aead_edesc_alloc(req, GCM_DESC_JOB_IO_LEN, &all_contig, false);
+ if (IS_ERR(edesc))
+ return PTR_ERR(edesc);
+
+ /* Create and submit job descriptor*/
+ init_gcm_job(req, edesc, all_contig, false);
+#ifdef DEBUG
+ print_hex_dump(KERN_ERR, "aead jobdesc@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+ desc_bytes(edesc->hw_desc), 1);
+#endif
+
+ desc = edesc->hw_desc;
+ ret = caam_jr_enqueue(jrdev, desc, aead_decrypt_done, req);
+ if (!ret) {
+ ret = -EINPROGRESS;
+ } else {
+ aead_unmap(jrdev, edesc, req);
+ kfree(edesc);
+ }
+
+ return ret;
+}
+
+static int old_aead_decrypt(struct aead_request *req)
+{
+ struct aead_edesc *edesc;
+ struct crypto_aead *aead = crypto_aead_reqtfm(req);
+ struct caam_ctx *ctx = crypto_aead_ctx(aead);
+ struct device *jrdev = ctx->jrdev;
+ bool all_contig;
+ u32 *desc;
+ int ret = 0;
+
+ /* allocate extended descriptor */
+ edesc = old_aead_edesc_alloc(req, DESC_JOB_IO_LEN *
+ CAAM_CMD_SZ, &all_contig, false);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
@@ -2812,8 +2780,8 @@ static int aead_decrypt(struct aead_request *req)
#endif
/* Create and submit job descriptor*/
- init_aead_job(ctx->sh_desc_dec,
- ctx->sh_desc_dec_dma, edesc, req, all_contig, false);
+ old_init_aead_job(ctx->sh_desc_dec,
+ ctx->sh_desc_dec_dma, edesc, req, all_contig, false);
#ifdef DEBUG
print_hex_dump(KERN_ERR, "aead jobdesc@"__stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
@@ -2821,11 +2789,11 @@ static int aead_decrypt(struct aead_request *req)
#endif
desc = edesc->hw_desc;
- ret = caam_jr_enqueue(jrdev, desc, aead_decrypt_done, req);
+ ret = caam_jr_enqueue(jrdev, desc, old_aead_decrypt_done, req);
if (!ret) {
ret = -EINPROGRESS;
} else {
- aead_unmap(jrdev, edesc, req);
+ old_aead_unmap(jrdev, edesc, req);
kfree(edesc);
}
@@ -2953,8 +2921,8 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request
sec4_sg_index = 0;
if (!(contig & GIV_SRC_CONTIG)) {
if (!is_gcm) {
- sg_to_sec4_sg(req->assoc, assoc_nents,
- edesc->sec4_sg + sec4_sg_index, 0);
+ sg_to_sec4_sg_len(req->assoc, req->assoclen,
+ edesc->sec4_sg + sec4_sg_index);
sec4_sg_index += assoc_nents;
}
@@ -2963,8 +2931,8 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request
sec4_sg_index += 1;
if (is_gcm) {
- sg_to_sec4_sg(req->assoc, assoc_nents,
- edesc->sec4_sg + sec4_sg_index, 0);
+ sg_to_sec4_sg_len(req->assoc, req->assoclen,
+ edesc->sec4_sg + sec4_sg_index);
sec4_sg_index += assoc_nents;
}
@@ -2999,7 +2967,7 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request
return edesc;
}
-static int aead_givencrypt(struct aead_givcrypt_request *areq)
+static int old_aead_givencrypt(struct aead_givcrypt_request *areq)
{
struct aead_request *req = &areq->areq;
struct aead_edesc *edesc;
@@ -3033,11 +3001,11 @@ static int aead_givencrypt(struct aead_givcrypt_request *areq)
#endif
desc = edesc->hw_desc;
- ret = caam_jr_enqueue(jrdev, desc, aead_encrypt_done, req);
+ ret = caam_jr_enqueue(jrdev, desc, old_aead_encrypt_done, req);
if (!ret) {
ret = -EINPROGRESS;
} else {
- aead_unmap(jrdev, edesc, req);
+ old_aead_unmap(jrdev, edesc, req);
kfree(edesc);
}
@@ -3046,7 +3014,7 @@ static int aead_givencrypt(struct aead_givcrypt_request *areq)
static int aead_null_givencrypt(struct aead_givcrypt_request *areq)
{
- return aead_encrypt(&areq->areq);
+ return old_aead_encrypt(&areq->areq);
}
/*
@@ -3379,11 +3347,7 @@ struct caam_alg_template {
u32 type;
union {
struct ablkcipher_alg ablkcipher;
- struct aead_alg aead;
- struct blkcipher_alg blkcipher;
- struct cipher_alg cipher;
- struct compress_alg compress;
- struct rng_alg rng;
+ struct old_aead_alg aead;
} template_u;
u32 class1_alg_type;
u32 class2_alg_type;
@@ -3400,8 +3364,8 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
.givencrypt = aead_null_givencrypt,
.geniv = "<built-in>",
.ivsize = NULL_IV_SIZE,
@@ -3419,8 +3383,8 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
.givencrypt = aead_null_givencrypt,
.geniv = "<built-in>",
.ivsize = NULL_IV_SIZE,
@@ -3438,8 +3402,8 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
.givencrypt = aead_null_givencrypt,
.geniv = "<built-in>",
.ivsize = NULL_IV_SIZE,
@@ -3458,8 +3422,8 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
.givencrypt = aead_null_givencrypt,
.geniv = "<built-in>",
.ivsize = NULL_IV_SIZE,
@@ -3478,8 +3442,8 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
.givencrypt = aead_null_givencrypt,
.geniv = "<built-in>",
.ivsize = NULL_IV_SIZE,
@@ -3498,8 +3462,8 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
.givencrypt = aead_null_givencrypt,
.geniv = "<built-in>",
.ivsize = NULL_IV_SIZE,
@@ -3518,9 +3482,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = MD5_DIGEST_SIZE,
@@ -3537,9 +3501,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA1_DIGEST_SIZE,
@@ -3556,9 +3520,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA224_DIGEST_SIZE,
@@ -3576,9 +3540,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA256_DIGEST_SIZE,
@@ -3596,9 +3560,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA384_DIGEST_SIZE,
@@ -3617,9 +3581,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = AES_BLOCK_SIZE,
.maxauthsize = SHA512_DIGEST_SIZE,
@@ -3637,9 +3601,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = MD5_DIGEST_SIZE,
@@ -3656,9 +3620,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA1_DIGEST_SIZE,
@@ -3675,9 +3639,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA224_DIGEST_SIZE,
@@ -3695,9 +3659,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA256_DIGEST_SIZE,
@@ -3715,9 +3679,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA384_DIGEST_SIZE,
@@ -3735,9 +3699,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES3_EDE_BLOCK_SIZE,
.maxauthsize = SHA512_DIGEST_SIZE,
@@ -3755,9 +3719,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = MD5_DIGEST_SIZE,
@@ -3774,9 +3738,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA1_DIGEST_SIZE,
@@ -3793,9 +3757,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA224_DIGEST_SIZE,
@@ -3813,9 +3777,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA256_DIGEST_SIZE,
@@ -3833,9 +3797,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA384_DIGEST_SIZE,
@@ -3853,9 +3817,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = DES_BLOCK_SIZE,
.maxauthsize = SHA512_DIGEST_SIZE,
@@ -3873,9 +3837,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = CTR_RFC3686_IV_SIZE,
.maxauthsize = MD5_DIGEST_SIZE,
@@ -3892,9 +3856,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = CTR_RFC3686_IV_SIZE,
.maxauthsize = SHA1_DIGEST_SIZE,
@@ -3911,9 +3875,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = CTR_RFC3686_IV_SIZE,
.maxauthsize = SHA224_DIGEST_SIZE,
@@ -3931,9 +3895,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = CTR_RFC3686_IV_SIZE,
.maxauthsize = SHA256_DIGEST_SIZE,
@@ -3951,9 +3915,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = CTR_RFC3686_IV_SIZE,
.maxauthsize = SHA384_DIGEST_SIZE,
@@ -3971,9 +3935,9 @@ static struct caam_alg_template driver_algs[] = {
.template_aead = {
.setkey = aead_setkey,
.setauthsize = aead_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
+ .encrypt = old_aead_encrypt,
+ .decrypt = old_aead_decrypt,
+ .givencrypt = old_aead_givencrypt,
.geniv = "<built-in>",
.ivsize = CTR_RFC3686_IV_SIZE,
.maxauthsize = SHA512_DIGEST_SIZE,
@@ -3983,58 +3947,6 @@ static struct caam_alg_template driver_algs[] = {
OP_ALG_AAI_HMAC_PRECOMP,
.alg_op = OP_ALG_ALGSEL_SHA512 | OP_ALG_AAI_HMAC,
},
- {
- .name = "rfc4106(gcm(aes))",
- .driver_name = "rfc4106-gcm-aes-caam",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = rfc4106_setkey,
- .setauthsize = rfc4106_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
- .geniv = "<built-in>",
- .ivsize = 8,
- .maxauthsize = AES_BLOCK_SIZE,
- },
- .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
- },
- {
- .name = "rfc4543(gcm(aes))",
- .driver_name = "rfc4543-gcm-aes-caam",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = rfc4543_setkey,
- .setauthsize = rfc4543_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = aead_givencrypt,
- .geniv = "<built-in>",
- .ivsize = 8,
- .maxauthsize = AES_BLOCK_SIZE,
- },
- .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
- },
- /* Galois Counter Mode */
- {
- .name = "gcm(aes)",
- .driver_name = "gcm-aes-caam",
- .blocksize = 1,
- .type = CRYPTO_ALG_TYPE_AEAD,
- .template_aead = {
- .setkey = gcm_setkey,
- .setauthsize = gcm_setauthsize,
- .encrypt = aead_encrypt,
- .decrypt = aead_decrypt,
- .givencrypt = NULL,
- .geniv = "<built-in>",
- .ivsize = 12,
- .maxauthsize = AES_BLOCK_SIZE,
- },
- .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
- },
/* ablkcipher descriptor */
{
.name = "cbc(aes)",
@@ -4124,21 +4036,84 @@ static struct caam_alg_template driver_algs[] = {
}
};
-struct caam_crypto_alg {
- struct list_head entry;
+struct caam_alg_entry {
int class1_alg_type;
int class2_alg_type;
int alg_op;
+};
+
+struct caam_aead_alg {
+ struct aead_alg aead;
+ struct caam_alg_entry caam;
+ bool registered;
+};
+
+static struct caam_aead_alg driver_aeads[] = {
+ {
+ .aead = {
+ .base = {
+ .cra_name = "rfc4106(gcm(aes))",
+ .cra_driver_name = "rfc4106-gcm-aes-caam",
+ .cra_blocksize = 1,
+ },
+ .setkey = rfc4106_setkey,
+ .setauthsize = rfc4106_setauthsize,
+ .encrypt = gcm_encrypt,
+ .decrypt = gcm_decrypt,
+ .ivsize = 8,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .caam = {
+ .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
+ },
+ },
+ {
+ .aead = {
+ .base = {
+ .cra_name = "rfc4543(gcm(aes))",
+ .cra_driver_name = "rfc4543-gcm-aes-caam",
+ .cra_blocksize = 1,
+ },
+ .setkey = rfc4543_setkey,
+ .setauthsize = rfc4543_setauthsize,
+ .encrypt = gcm_encrypt,
+ .decrypt = gcm_decrypt,
+ .ivsize = 8,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .caam = {
+ .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
+ },
+ },
+ /* Galois Counter Mode */
+ {
+ .aead = {
+ .base = {
+ .cra_name = "gcm(aes)",
+ .cra_driver_name = "gcm-aes-caam",
+ .cra_blocksize = 1,
+ },
+ .setkey = gcm_setkey,
+ .setauthsize = gcm_setauthsize,
+ .encrypt = gcm_encrypt,
+ .decrypt = gcm_decrypt,
+ .ivsize = 12,
+ .maxauthsize = AES_BLOCK_SIZE,
+ },
+ .caam = {
+ .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
+ },
+ },
+};
+
+struct caam_crypto_alg {
struct crypto_alg crypto_alg;
+ struct list_head entry;
+ struct caam_alg_entry caam;
};
-static int caam_cra_init(struct crypto_tfm *tfm)
+static int caam_init_common(struct caam_ctx *ctx, struct caam_alg_entry *caam)
{
- struct crypto_alg *alg = tfm->__crt_alg;
- struct caam_crypto_alg *caam_alg =
- container_of(alg, struct caam_crypto_alg, crypto_alg);
- struct caam_ctx *ctx = crypto_tfm_ctx(tfm);
-
ctx->jrdev = caam_jr_alloc();
if (IS_ERR(ctx->jrdev)) {
pr_err("Job Ring Device allocation for transform failed\n");
@@ -4146,17 +4121,35 @@ static int caam_cra_init(struct crypto_tfm *tfm)
}
/* copy descriptor header template value */
- ctx->class1_alg_type = OP_TYPE_CLASS1_ALG | caam_alg->class1_alg_type;
- ctx->class2_alg_type = OP_TYPE_CLASS2_ALG | caam_alg->class2_alg_type;
- ctx->alg_op = OP_TYPE_CLASS2_ALG | caam_alg->alg_op;
+ ctx->class1_alg_type = OP_TYPE_CLASS1_ALG | caam->class1_alg_type;
+ ctx->class2_alg_type = OP_TYPE_CLASS2_ALG | caam->class2_alg_type;
+ ctx->alg_op = OP_TYPE_CLASS2_ALG | caam->alg_op;
return 0;
}
-static void caam_cra_exit(struct crypto_tfm *tfm)
+static int caam_cra_init(struct crypto_tfm *tfm)
{
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct caam_crypto_alg *caam_alg =
+ container_of(alg, struct caam_crypto_alg, crypto_alg);
struct caam_ctx *ctx = crypto_tfm_ctx(tfm);
+ return caam_init_common(ctx, &caam_alg->caam);
+}
+
+static int caam_aead_init(struct crypto_aead *tfm)
+{
+ struct aead_alg *alg = crypto_aead_alg(tfm);
+ struct caam_aead_alg *caam_alg =
+ container_of(alg, struct caam_aead_alg, aead);
+ struct caam_ctx *ctx = crypto_aead_ctx(tfm);
+
+ return caam_init_common(ctx, &caam_alg->caam);
+}
+
+static void caam_exit_common(struct caam_ctx *ctx)
+{
if (ctx->sh_desc_enc_dma &&
!dma_mapping_error(ctx->jrdev, ctx->sh_desc_enc_dma))
dma_unmap_single(ctx->jrdev, ctx->sh_desc_enc_dma,
@@ -4179,10 +4172,28 @@ static void caam_cra_exit(struct crypto_tfm *tfm)
caam_jr_free(ctx->jrdev);
}
+static void caam_cra_exit(struct crypto_tfm *tfm)
+{
+ caam_exit_common(crypto_tfm_ctx(tfm));
+}
+
+static void caam_aead_exit(struct crypto_aead *tfm)
+{
+ caam_exit_common(crypto_aead_ctx(tfm));
+}
+
static void __exit caam_algapi_exit(void)
{
struct caam_crypto_alg *t_alg, *n;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(driver_aeads); i++) {
+ struct caam_aead_alg *t_alg = driver_aeads + i;
+
+ if (t_alg->registered)
+ crypto_unregister_aead(&t_alg->aead);
+ }
if (!alg_list.next)
return;
@@ -4235,13 +4246,26 @@ static struct caam_crypto_alg *caam_alg_alloc(struct caam_alg_template
break;
}
- t_alg->class1_alg_type = template->class1_alg_type;
- t_alg->class2_alg_type = template->class2_alg_type;
- t_alg->alg_op = template->alg_op;
+ t_alg->caam.class1_alg_type = template->class1_alg_type;
+ t_alg->caam.class2_alg_type = template->class2_alg_type;
+ t_alg->caam.alg_op = template->alg_op;
return t_alg;
}
+static void caam_aead_alg_init(struct caam_aead_alg *t_alg)
+{
+ struct aead_alg *alg = &t_alg->aead;
+
+ alg->base.cra_module = THIS_MODULE;
+ alg->base.cra_priority = CAAM_CRA_PRIORITY;
+ alg->base.cra_ctxsize = sizeof(struct caam_ctx);
+ alg->base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_KERN_DRIVER_ONLY;
+
+ alg->init = caam_aead_init;
+ alg->exit = caam_aead_exit;
+}
+
static int __init caam_algapi_init(void)
{
struct device_node *dev_node;
@@ -4249,6 +4273,7 @@ static int __init caam_algapi_init(void)
struct device *ctrldev;
void *priv;
int i = 0, err = 0;
+ bool registered = false;
dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
if (!dev_node) {
@@ -4295,10 +4320,30 @@ static int __init caam_algapi_init(void)
pr_warn("%s alg registration failed\n",
t_alg->crypto_alg.cra_driver_name);
kfree(t_alg);
- } else
- list_add_tail(&t_alg->entry, &alg_list);
+ continue;
+ }
+
+ list_add_tail(&t_alg->entry, &alg_list);
+ registered = true;
}
- if (!list_empty(&alg_list))
+
+ for (i = 0; i < ARRAY_SIZE(driver_aeads); i++) {
+ struct caam_aead_alg *t_alg = driver_aeads + i;
+
+ caam_aead_alg_init(t_alg);
+
+ err = crypto_register_aead(&t_alg->aead);
+ if (err) {
+ pr_warn("%s alg registration failed\n",
+ t_alg->aead.base.cra_driver_name);
+ continue;
+ }
+
+ t_alg->registered = true;
+ registered = true;
+ }
+
+ if (registered)
pr_info("caam algorithms registered in /proc/crypto\n");
return err;
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
index ba0532efd3ae..dae1e8099969 100644
--- a/drivers/crypto/caam/caamhash.c
+++ b/drivers/crypto/caam/caamhash.c
@@ -835,17 +835,17 @@ static int ahash_update_ctx(struct ahash_request *req)
src_map_to_sec4_sg(jrdev, req->src, src_nents,
edesc->sec4_sg + sec4_sg_src_index,
chained);
- if (*next_buflen) {
+ if (*next_buflen)
scatterwalk_map_and_copy(next_buf, req->src,
to_hash - *buflen,
*next_buflen, 0);
- state->current_buf = !state->current_buf;
- }
} else {
(edesc->sec4_sg + sec4_sg_src_index - 1)->len |=
SEC4_SG_LEN_FIN;
}
+ state->current_buf = !state->current_buf;
+
sh_len = desc_len(sh_desc);
desc = edesc->hw_desc;
init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER |
@@ -1268,9 +1268,10 @@ static int ahash_update_no_ctx(struct ahash_request *req)
scatterwalk_map_and_copy(next_buf, req->src,
to_hash - *buflen,
*next_buflen, 0);
- state->current_buf = !state->current_buf;
}
+ state->current_buf = !state->current_buf;
+
sh_len = desc_len(sh_desc);
desc = edesc->hw_desc;
init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER |
@@ -1544,6 +1545,8 @@ static int ahash_init(struct ahash_request *req)
state->current_buf = 0;
state->buf_dma = 0;
+ state->buflen_0 = 0;
+ state->buflen_1 = 0;
return 0;
}
diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c
index 26a544b505f1..5095337205b8 100644
--- a/drivers/crypto/caam/caamrng.c
+++ b/drivers/crypto/caam/caamrng.c
@@ -56,7 +56,7 @@
/* Buffer, its dma address and lock */
struct buf_data {
- u8 buf[RN_BUF_SIZE];
+ u8 buf[RN_BUF_SIZE] ____cacheline_aligned;
dma_addr_t addr;
struct completion filled;
u32 hw_desc[DESC_JOB_O_LEN];
diff --git a/drivers/crypto/caam/compat.h b/drivers/crypto/caam/compat.h
index acd7743e2603..f57f395db33f 100644
--- a/drivers/crypto/caam/compat.h
+++ b/drivers/crypto/caam/compat.h
@@ -32,7 +32,7 @@
#include <crypto/des.h>
#include <crypto/sha.h>
#include <crypto/md5.h>
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
#include <crypto/authenc.h>
#include <crypto/scatterwalk.h>
#include <crypto/internal/skcipher.h>
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index efba4ccd4fac..efacab7539ef 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -301,7 +301,7 @@ static int caam_remove(struct platform_device *pdev)
#endif
/* Unmap controller region */
- iounmap(&ctrl);
+ iounmap(ctrl);
return ret;
}
@@ -496,7 +496,7 @@ static int caam_probe(struct platform_device *pdev)
sizeof(struct platform_device *) * rspec,
GFP_KERNEL);
if (ctrlpriv->jrpdev == NULL) {
- iounmap(&ctrl);
+ iounmap(ctrl);
return -ENOMEM;
}
diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h
index 378ddc17f60e..672c97489505 100644
--- a/drivers/crypto/caam/regs.h
+++ b/drivers/crypto/caam/regs.h
@@ -83,35 +83,35 @@
#endif
#endif
+/*
+ * The only users of these wr/rd_reg64 functions is the Job Ring (JR).
+ * The DMA address registers in the JR are a pair of 32-bit registers.
+ * The layout is:
+ *
+ * base + 0x0000 : most-significant 32 bits
+ * base + 0x0004 : least-significant 32 bits
+ *
+ * The 32-bit version of this core therefore has to write to base + 0x0004
+ * to set the 32-bit wide DMA address. This seems to be independent of the
+ * endianness of the written/read data.
+ */
+
#ifndef CONFIG_64BIT
-#ifdef __BIG_ENDIAN
-static inline void wr_reg64(u64 __iomem *reg, u64 data)
-{
- wr_reg32((u32 __iomem *)reg, (data & 0xffffffff00000000ull) >> 32);
- wr_reg32((u32 __iomem *)reg + 1, data & 0x00000000ffffffffull);
-}
+#define REG64_MS32(reg) ((u32 __iomem *)(reg))
+#define REG64_LS32(reg) ((u32 __iomem *)(reg) + 1)
-static inline u64 rd_reg64(u64 __iomem *reg)
-{
- return (((u64)rd_reg32((u32 __iomem *)reg)) << 32) |
- ((u64)rd_reg32((u32 __iomem *)reg + 1));
-}
-#else
-#ifdef __LITTLE_ENDIAN
static inline void wr_reg64(u64 __iomem *reg, u64 data)
{
- wr_reg32((u32 __iomem *)reg + 1, (data & 0xffffffff00000000ull) >> 32);
- wr_reg32((u32 __iomem *)reg, data & 0x00000000ffffffffull);
+ wr_reg32(REG64_MS32(reg), data >> 32);
+ wr_reg32(REG64_LS32(reg), data);
}
static inline u64 rd_reg64(u64 __iomem *reg)
{
- return (((u64)rd_reg32((u32 __iomem *)reg + 1)) << 32) |
- ((u64)rd_reg32((u32 __iomem *)reg));
+ return ((u64)rd_reg32(REG64_MS32(reg)) << 32 |
+ (u64)rd_reg32(REG64_LS32(reg)));
}
#endif
-#endif
-#endif
/*
* jr_outentry
diff --git a/drivers/crypto/caam/sg_sw_sec4.h b/drivers/crypto/caam/sg_sw_sec4.h
index 3b918218aa4c..b68b74cc7b77 100644
--- a/drivers/crypto/caam/sg_sw_sec4.h
+++ b/drivers/crypto/caam/sg_sw_sec4.h
@@ -55,6 +55,21 @@ static inline void sg_to_sec4_sg_last(struct scatterlist *sg, int sg_count,
sec4_sg_ptr->len |= SEC4_SG_LEN_FIN;
}
+static inline struct sec4_sg_entry *sg_to_sec4_sg_len(
+ struct scatterlist *sg, unsigned int total,
+ struct sec4_sg_entry *sec4_sg_ptr)
+{
+ do {
+ unsigned int len = min(sg_dma_len(sg), total);
+
+ dma_to_sec4_sg_one(sec4_sg_ptr, sg_dma_address(sg), len, 0);
+ sec4_sg_ptr++;
+ sg = sg_next(sg);
+ total -= len;
+ } while (total);
+ return sec4_sg_ptr - 1;
+}
+
/* count number of elements in scatterlist */
static inline int __sg_count(struct scatterlist *sg_list, int nbytes,
bool *chained)
@@ -85,34 +100,41 @@ static inline int sg_count(struct scatterlist *sg_list, int nbytes,
return sg_nents;
}
-static int dma_map_sg_chained(struct device *dev, struct scatterlist *sg,
- unsigned int nents, enum dma_data_direction dir,
- bool chained)
+static inline void dma_unmap_sg_chained(
+ struct device *dev, struct scatterlist *sg, unsigned int nents,
+ enum dma_data_direction dir, bool chained)
{
if (unlikely(chained)) {
int i;
for (i = 0; i < nents; i++) {
- dma_map_sg(dev, sg, 1, dir);
+ dma_unmap_sg(dev, sg, 1, dir);
sg = sg_next(sg);
}
- } else {
- dma_map_sg(dev, sg, nents, dir);
+ } else if (nents) {
+ dma_unmap_sg(dev, sg, nents, dir);
}
- return nents;
}
-static int dma_unmap_sg_chained(struct device *dev, struct scatterlist *sg,
- unsigned int nents, enum dma_data_direction dir,
- bool chained)
+static inline int dma_map_sg_chained(
+ struct device *dev, struct scatterlist *sg, unsigned int nents,
+ enum dma_data_direction dir, bool chained)
{
+ struct scatterlist *first = sg;
+
if (unlikely(chained)) {
int i;
for (i = 0; i < nents; i++) {
- dma_unmap_sg(dev, sg, 1, dir);
+ if (!dma_map_sg(dev, sg, 1, dir)) {
+ dma_unmap_sg_chained(dev, first, i, dir,
+ chained);
+ nents = 0;
+ break;
+ }
+
sg = sg_next(sg);
}
- } else {
- dma_unmap_sg(dev, sg, nents, dir);
- }
+ } else
+ nents = dma_map_sg(dev, sg, nents, dir);
+
return nents;
}
diff --git a/drivers/crypto/ccp/Kconfig b/drivers/crypto/ccp/Kconfig
index 7639ffc36c68..ae38f6b6cc10 100644
--- a/drivers/crypto/ccp/Kconfig
+++ b/drivers/crypto/ccp/Kconfig
@@ -13,7 +13,6 @@ config CRYPTO_DEV_CCP_CRYPTO
tristate "Encryption and hashing acceleration support"
depends on CRYPTO_DEV_CCP_DD
default m
- select CRYPTO_ALGAPI
select CRYPTO_HASH
select CRYPTO_BLKCIPHER
select CRYPTO_AUTHENC
diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c
index 71f2e3c89424..d09c6c4af4aa 100644
--- a/drivers/crypto/ccp/ccp-ops.c
+++ b/drivers/crypto/ccp/ccp-ops.c
@@ -52,8 +52,7 @@ struct ccp_dm_workarea {
struct ccp_sg_workarea {
struct scatterlist *sg;
- unsigned int nents;
- unsigned int length;
+ int nents;
struct scatterlist *dma_sg;
struct device *dma_dev;
@@ -496,8 +495,10 @@ static int ccp_init_sg_workarea(struct ccp_sg_workarea *wa, struct device *dev,
if (!sg)
return 0;
- wa->nents = sg_nents(sg);
- wa->length = sg->length;
+ wa->nents = sg_nents_for_len(sg, len);
+ if (wa->nents < 0)
+ return wa->nents;
+
wa->bytes_left = len;
wa->sg_used = 0;
diff --git a/drivers/crypto/ccp/ccp-platform.c b/drivers/crypto/ccp/ccp-platform.c
index b1c20b2b5647..f2e6de361fd1 100644
--- a/drivers/crypto/ccp/ccp-platform.c
+++ b/drivers/crypto/ccp/ccp-platform.c
@@ -90,58 +90,6 @@ static struct resource *ccp_find_mmio_area(struct ccp_device *ccp)
return NULL;
}
-#ifdef CONFIG_ACPI
-static int ccp_acpi_support(struct ccp_device *ccp)
-{
- struct ccp_platform *ccp_platform = ccp->dev_specific;
- struct acpi_device *adev = ACPI_COMPANION(ccp->dev);
- acpi_handle handle;
- acpi_status status;
- unsigned long long data;
- int cca;
-
- /* Retrieve the device cache coherency value */
- handle = adev->handle;
- do {
- status = acpi_evaluate_integer(handle, "_CCA", NULL, &data);
- if (!ACPI_FAILURE(status)) {
- cca = data;
- break;
- }
- } while (!ACPI_FAILURE(status));
-
- if (ACPI_FAILURE(status)) {
- dev_err(ccp->dev, "error obtaining acpi coherency value\n");
- return -EINVAL;
- }
-
- ccp_platform->coherent = !!cca;
-
- return 0;
-}
-#else /* CONFIG_ACPI */
-static int ccp_acpi_support(struct ccp_device *ccp)
-{
- return -EINVAL;
-}
-#endif
-
-#ifdef CONFIG_OF
-static int ccp_of_support(struct ccp_device *ccp)
-{
- struct ccp_platform *ccp_platform = ccp->dev_specific;
-
- ccp_platform->coherent = of_dma_is_coherent(ccp->dev->of_node);
-
- return 0;
-}
-#else
-static int ccp_of_support(struct ccp_device *ccp)
-{
- return -EINVAL;
-}
-#endif
-
static int ccp_platform_probe(struct platform_device *pdev)
{
struct ccp_device *ccp;
@@ -174,21 +122,13 @@ static int ccp_platform_probe(struct platform_device *pdev)
}
ccp->io_regs = ccp->io_map;
- if (!dev->dma_mask)
- dev->dma_mask = &dev->coherent_dma_mask;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
if (ret) {
dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret);
goto e_err;
}
- if (ccp_platform->use_acpi)
- ret = ccp_acpi_support(ccp);
- else
- ret = ccp_of_support(ccp);
- if (ret)
- goto e_err;
-
+ ccp_platform->coherent = device_dma_is_coherent(ccp->dev);
if (ccp_platform->coherent)
ccp->axcache = CACHE_WB_NO_ALLOC;
else
diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c
index 48f453555f1f..7ba495f75370 100644
--- a/drivers/crypto/ixp4xx_crypto.c
+++ b/drivers/crypto/ixp4xx_crypto.c
@@ -25,7 +25,7 @@
#include <crypto/aes.h>
#include <crypto/sha.h>
#include <crypto/algapi.h>
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
#include <crypto/authenc.h>
#include <crypto/scatterwalk.h>
@@ -575,7 +575,8 @@ static int init_tfm_ablk(struct crypto_tfm *tfm)
static int init_tfm_aead(struct crypto_tfm *tfm)
{
- tfm->crt_aead.reqsize = sizeof(struct aead_ctx);
+ crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+ sizeof(struct aead_ctx));
return init_tfm(tfm);
}
@@ -1096,7 +1097,7 @@ static int aead_setup(struct crypto_aead *tfm, unsigned int authsize)
{
struct ixp_ctx *ctx = crypto_aead_ctx(tfm);
u32 *flags = &tfm->base.crt_flags;
- unsigned digest_len = crypto_aead_alg(tfm)->maxauthsize;
+ unsigned digest_len = crypto_aead_maxauthsize(tfm);
int ret;
if (!ctx->enckey_len && !ctx->authkey_len)
@@ -1138,7 +1139,7 @@ out:
static int aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize)
{
- int max = crypto_aead_alg(tfm)->maxauthsize >> 2;
+ int max = crypto_aead_maxauthsize(tfm) >> 2;
if ((authsize>>2) < 1 || (authsize>>2) > max || (authsize & 3))
return -EINVAL;
diff --git a/drivers/crypto/marvell/Makefile b/drivers/crypto/marvell/Makefile
new file mode 100644
index 000000000000..0c12b13574dc
--- /dev/null
+++ b/drivers/crypto/marvell/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell-cesa.o
+marvell-cesa-objs := cesa.o cipher.o hash.o tdma.o
diff --git a/drivers/crypto/marvell/cesa.c b/drivers/crypto/marvell/cesa.c
new file mode 100644
index 000000000000..a432633bced4
--- /dev/null
+++ b/drivers/crypto/marvell/cesa.c
@@ -0,0 +1,548 @@
+/*
+ * Support for Marvell's Cryptographic Engine and Security Accelerator (CESA)
+ * that can be found on the following platform: Orion, Kirkwood, Armada. This
+ * driver supports the TDMA engine on platforms on which it is available.
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Author: Arnaud Ebalard <arno@natisbad.org>
+ *
+ * This work is based on an initial version written by
+ * Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kthread.h>
+#include <linux/mbus.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+
+#include "cesa.h"
+
+static int allhwsupport = !IS_ENABLED(CONFIG_CRYPTO_DEV_MV_CESA);
+module_param_named(allhwsupport, allhwsupport, int, 0444);
+MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if overlaps with the mv_cesa driver)");
+
+struct mv_cesa_dev *cesa_dev;
+
+static void mv_cesa_dequeue_req_unlocked(struct mv_cesa_engine *engine)
+{
+ struct crypto_async_request *req, *backlog;
+ struct mv_cesa_ctx *ctx;
+
+ spin_lock_bh(&cesa_dev->lock);
+ backlog = crypto_get_backlog(&cesa_dev->queue);
+ req = crypto_dequeue_request(&cesa_dev->queue);
+ engine->req = req;
+ spin_unlock_bh(&cesa_dev->lock);
+
+ if (!req)
+ return;
+
+ if (backlog)
+ backlog->complete(backlog, -EINPROGRESS);
+
+ ctx = crypto_tfm_ctx(req->tfm);
+ ctx->ops->prepare(req, engine);
+ ctx->ops->step(req);
+}
+
+static irqreturn_t mv_cesa_int(int irq, void *priv)
+{
+ struct mv_cesa_engine *engine = priv;
+ struct crypto_async_request *req;
+ struct mv_cesa_ctx *ctx;
+ u32 status, mask;
+ irqreturn_t ret = IRQ_NONE;
+
+ while (true) {
+ int res;
+
+ mask = mv_cesa_get_int_mask(engine);
+ status = readl(engine->regs + CESA_SA_INT_STATUS);
+
+ if (!(status & mask))
+ break;
+
+ /*
+ * TODO: avoid clearing the FPGA_INT_STATUS if this not
+ * relevant on some platforms.
+ */
+ writel(~status, engine->regs + CESA_SA_FPGA_INT_STATUS);
+ writel(~status, engine->regs + CESA_SA_INT_STATUS);
+
+ ret = IRQ_HANDLED;
+ spin_lock_bh(&engine->lock);
+ req = engine->req;
+ spin_unlock_bh(&engine->lock);
+ if (req) {
+ ctx = crypto_tfm_ctx(req->tfm);
+ res = ctx->ops->process(req, status & mask);
+ if (res != -EINPROGRESS) {
+ spin_lock_bh(&engine->lock);
+ engine->req = NULL;
+ mv_cesa_dequeue_req_unlocked(engine);
+ spin_unlock_bh(&engine->lock);
+ ctx->ops->cleanup(req);
+ local_bh_disable();
+ req->complete(req, res);
+ local_bh_enable();
+ } else {
+ ctx->ops->step(req);
+ }
+ }
+ }
+
+ return ret;
+}
+
+int mv_cesa_queue_req(struct crypto_async_request *req)
+{
+ int ret;
+ int i;
+
+ spin_lock_bh(&cesa_dev->lock);
+ ret = crypto_enqueue_request(&cesa_dev->queue, req);
+ spin_unlock_bh(&cesa_dev->lock);
+
+ if (ret != -EINPROGRESS)
+ return ret;
+
+ for (i = 0; i < cesa_dev->caps->nengines; i++) {
+ spin_lock_bh(&cesa_dev->engines[i].lock);
+ if (!cesa_dev->engines[i].req)
+ mv_cesa_dequeue_req_unlocked(&cesa_dev->engines[i]);
+ spin_unlock_bh(&cesa_dev->engines[i].lock);
+ }
+
+ return -EINPROGRESS;
+}
+
+static int mv_cesa_add_algs(struct mv_cesa_dev *cesa)
+{
+ int ret;
+ int i, j;
+
+ for (i = 0; i < cesa->caps->ncipher_algs; i++) {
+ ret = crypto_register_alg(cesa->caps->cipher_algs[i]);
+ if (ret)
+ goto err_unregister_crypto;
+ }
+
+ for (i = 0; i < cesa->caps->nahash_algs; i++) {
+ ret = crypto_register_ahash(cesa->caps->ahash_algs[i]);
+ if (ret)
+ goto err_unregister_ahash;
+ }
+
+ return 0;
+
+err_unregister_ahash:
+ for (j = 0; j < i; j++)
+ crypto_unregister_ahash(cesa->caps->ahash_algs[j]);
+ i = cesa->caps->ncipher_algs;
+
+err_unregister_crypto:
+ for (j = 0; j < i; j++)
+ crypto_unregister_alg(cesa->caps->cipher_algs[j]);
+
+ return ret;
+}
+
+static void mv_cesa_remove_algs(struct mv_cesa_dev *cesa)
+{
+ int i;
+
+ for (i = 0; i < cesa->caps->nahash_algs; i++)
+ crypto_unregister_ahash(cesa->caps->ahash_algs[i]);
+
+ for (i = 0; i < cesa->caps->ncipher_algs; i++)
+ crypto_unregister_alg(cesa->caps->cipher_algs[i]);
+}
+
+static struct crypto_alg *orion_cipher_algs[] = {
+ &mv_cesa_ecb_des_alg,
+ &mv_cesa_cbc_des_alg,
+ &mv_cesa_ecb_des3_ede_alg,
+ &mv_cesa_cbc_des3_ede_alg,
+ &mv_cesa_ecb_aes_alg,
+ &mv_cesa_cbc_aes_alg,
+};
+
+static struct ahash_alg *orion_ahash_algs[] = {
+ &mv_md5_alg,
+ &mv_sha1_alg,
+ &mv_ahmac_md5_alg,
+ &mv_ahmac_sha1_alg,
+};
+
+static struct crypto_alg *armada_370_cipher_algs[] = {
+ &mv_cesa_ecb_des_alg,
+ &mv_cesa_cbc_des_alg,
+ &mv_cesa_ecb_des3_ede_alg,
+ &mv_cesa_cbc_des3_ede_alg,
+ &mv_cesa_ecb_aes_alg,
+ &mv_cesa_cbc_aes_alg,
+};
+
+static struct ahash_alg *armada_370_ahash_algs[] = {
+ &mv_md5_alg,
+ &mv_sha1_alg,
+ &mv_sha256_alg,
+ &mv_ahmac_md5_alg,
+ &mv_ahmac_sha1_alg,
+ &mv_ahmac_sha256_alg,
+};
+
+static const struct mv_cesa_caps orion_caps = {
+ .nengines = 1,
+ .cipher_algs = orion_cipher_algs,
+ .ncipher_algs = ARRAY_SIZE(orion_cipher_algs),
+ .ahash_algs = orion_ahash_algs,
+ .nahash_algs = ARRAY_SIZE(orion_ahash_algs),
+ .has_tdma = false,
+};
+
+static const struct mv_cesa_caps kirkwood_caps = {
+ .nengines = 1,
+ .cipher_algs = orion_cipher_algs,
+ .ncipher_algs = ARRAY_SIZE(orion_cipher_algs),
+ .ahash_algs = orion_ahash_algs,
+ .nahash_algs = ARRAY_SIZE(orion_ahash_algs),
+ .has_tdma = true,
+};
+
+static const struct mv_cesa_caps armada_370_caps = {
+ .nengines = 1,
+ .cipher_algs = armada_370_cipher_algs,
+ .ncipher_algs = ARRAY_SIZE(armada_370_cipher_algs),
+ .ahash_algs = armada_370_ahash_algs,
+ .nahash_algs = ARRAY_SIZE(armada_370_ahash_algs),
+ .has_tdma = true,
+};
+
+static const struct mv_cesa_caps armada_xp_caps = {
+ .nengines = 2,
+ .cipher_algs = armada_370_cipher_algs,
+ .ncipher_algs = ARRAY_SIZE(armada_370_cipher_algs),
+ .ahash_algs = armada_370_ahash_algs,
+ .nahash_algs = ARRAY_SIZE(armada_370_ahash_algs),
+ .has_tdma = true,
+};
+
+static const struct of_device_id mv_cesa_of_match_table[] = {
+ { .compatible = "marvell,orion-crypto", .data = &orion_caps },
+ { .compatible = "marvell,kirkwood-crypto", .data = &kirkwood_caps },
+ { .compatible = "marvell,dove-crypto", .data = &kirkwood_caps },
+ { .compatible = "marvell,armada-370-crypto", .data = &armada_370_caps },
+ { .compatible = "marvell,armada-xp-crypto", .data = &armada_xp_caps },
+ { .compatible = "marvell,armada-375-crypto", .data = &armada_xp_caps },
+ { .compatible = "marvell,armada-38x-crypto", .data = &armada_xp_caps },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mv_cesa_of_match_table);
+
+static void
+mv_cesa_conf_mbus_windows(struct mv_cesa_engine *engine,
+ const struct mbus_dram_target_info *dram)
+{
+ void __iomem *iobase = engine->regs;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ writel(0, iobase + CESA_TDMA_WINDOW_CTRL(i));
+ writel(0, iobase + CESA_TDMA_WINDOW_BASE(i));
+ }
+
+ for (i = 0; i < dram->num_cs; i++) {
+ const struct mbus_dram_window *cs = dram->cs + i;
+
+ writel(((cs->size - 1) & 0xffff0000) |
+ (cs->mbus_attr << 8) |
+ (dram->mbus_dram_target_id << 4) | 1,
+ iobase + CESA_TDMA_WINDOW_CTRL(i));
+ writel(cs->base, iobase + CESA_TDMA_WINDOW_BASE(i));
+ }
+}
+
+static int mv_cesa_dev_dma_init(struct mv_cesa_dev *cesa)
+{
+ struct device *dev = cesa->dev;
+ struct mv_cesa_dev_dma *dma;
+
+ if (!cesa->caps->has_tdma)
+ return 0;
+
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+
+ dma->tdma_desc_pool = dmam_pool_create("tdma_desc", dev,
+ sizeof(struct mv_cesa_tdma_desc),
+ 16, 0);
+ if (!dma->tdma_desc_pool)
+ return -ENOMEM;
+
+ dma->op_pool = dmam_pool_create("cesa_op", dev,
+ sizeof(struct mv_cesa_op_ctx), 16, 0);
+ if (!dma->op_pool)
+ return -ENOMEM;
+
+ dma->cache_pool = dmam_pool_create("cesa_cache", dev,
+ CESA_MAX_HASH_BLOCK_SIZE, 1, 0);
+ if (!dma->cache_pool)
+ return -ENOMEM;
+
+ dma->padding_pool = dmam_pool_create("cesa_padding", dev, 72, 1, 0);
+ if (!dma->cache_pool)
+ return -ENOMEM;
+
+ cesa->dma = dma;
+
+ return 0;
+}
+
+static int mv_cesa_get_sram(struct platform_device *pdev, int idx)
+{
+ struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
+ struct mv_cesa_engine *engine = &cesa->engines[idx];
+ const char *res_name = "sram";
+ struct resource *res;
+
+ engine->pool = of_get_named_gen_pool(cesa->dev->of_node,
+ "marvell,crypto-srams",
+ idx);
+ if (engine->pool) {
+ engine->sram = gen_pool_dma_alloc(engine->pool,
+ cesa->sram_size,
+ &engine->sram_dma);
+ if (engine->sram)
+ return 0;
+
+ engine->pool = NULL;
+ return -ENOMEM;
+ }
+
+ if (cesa->caps->nengines > 1) {
+ if (!idx)
+ res_name = "sram0";
+ else
+ res_name = "sram1";
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ res_name);
+ if (!res || resource_size(res) < cesa->sram_size)
+ return -EINVAL;
+
+ engine->sram = devm_ioremap_resource(cesa->dev, res);
+ if (IS_ERR(engine->sram))
+ return PTR_ERR(engine->sram);
+
+ engine->sram_dma = phys_to_dma(cesa->dev,
+ (phys_addr_t)res->start);
+
+ return 0;
+}
+
+static void mv_cesa_put_sram(struct platform_device *pdev, int idx)
+{
+ struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
+ struct mv_cesa_engine *engine = &cesa->engines[idx];
+
+ if (!engine->pool)
+ return;
+
+ gen_pool_free(engine->pool, (unsigned long)engine->sram,
+ cesa->sram_size);
+}
+
+static int mv_cesa_probe(struct platform_device *pdev)
+{
+ const struct mv_cesa_caps *caps = &orion_caps;
+ const struct mbus_dram_target_info *dram;
+ const struct of_device_id *match;
+ struct device *dev = &pdev->dev;
+ struct mv_cesa_dev *cesa;
+ struct mv_cesa_engine *engines;
+ struct resource *res;
+ int irq, ret, i;
+ u32 sram_size;
+
+ if (cesa_dev) {
+ dev_err(&pdev->dev, "Only one CESA device authorized\n");
+ return -EEXIST;
+ }
+
+ if (dev->of_node) {
+ match = of_match_node(mv_cesa_of_match_table, dev->of_node);
+ if (!match || !match->data)
+ return -ENOTSUPP;
+
+ caps = match->data;
+ }
+
+ if ((caps == &orion_caps || caps == &kirkwood_caps) && !allhwsupport)
+ return -ENOTSUPP;
+
+ cesa = devm_kzalloc(dev, sizeof(*cesa), GFP_KERNEL);
+ if (!cesa)
+ return -ENOMEM;
+
+ cesa->caps = caps;
+ cesa->dev = dev;
+
+ sram_size = CESA_SA_DEFAULT_SRAM_SIZE;
+ of_property_read_u32(cesa->dev->of_node, "marvell,crypto-sram-size",
+ &sram_size);
+ if (sram_size < CESA_SA_MIN_SRAM_SIZE)
+ sram_size = CESA_SA_MIN_SRAM_SIZE;
+
+ cesa->sram_size = sram_size;
+ cesa->engines = devm_kzalloc(dev, caps->nengines * sizeof(*engines),
+ GFP_KERNEL);
+ if (!cesa->engines)
+ return -ENOMEM;
+
+ spin_lock_init(&cesa->lock);
+ crypto_init_queue(&cesa->queue, 50);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ cesa->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cesa->regs))
+ return -ENOMEM;
+
+ ret = mv_cesa_dev_dma_init(cesa);
+ if (ret)
+ return ret;
+
+ dram = mv_mbus_dram_info_nooverlap();
+
+ platform_set_drvdata(pdev, cesa);
+
+ for (i = 0; i < caps->nengines; i++) {
+ struct mv_cesa_engine *engine = &cesa->engines[i];
+ char res_name[7];
+
+ engine->id = i;
+ spin_lock_init(&engine->lock);
+
+ ret = mv_cesa_get_sram(pdev, i);
+ if (ret)
+ goto err_cleanup;
+
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0) {
+ ret = irq;
+ goto err_cleanup;
+ }
+
+ /*
+ * Not all platforms can gate the CESA clocks: do not complain
+ * if the clock does not exist.
+ */
+ snprintf(res_name, sizeof(res_name), "cesa%d", i);
+ engine->clk = devm_clk_get(dev, res_name);
+ if (IS_ERR(engine->clk)) {
+ engine->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(engine->clk))
+ engine->clk = NULL;
+ }
+
+ snprintf(res_name, sizeof(res_name), "cesaz%d", i);
+ engine->zclk = devm_clk_get(dev, res_name);
+ if (IS_ERR(engine->zclk))
+ engine->zclk = NULL;
+
+ ret = clk_prepare_enable(engine->clk);
+ if (ret)
+ goto err_cleanup;
+
+ ret = clk_prepare_enable(engine->zclk);
+ if (ret)
+ goto err_cleanup;
+
+ engine->regs = cesa->regs + CESA_ENGINE_OFF(i);
+
+ if (dram && cesa->caps->has_tdma)
+ mv_cesa_conf_mbus_windows(&cesa->engines[i], dram);
+
+ writel(0, cesa->engines[i].regs + CESA_SA_INT_STATUS);
+ writel(CESA_SA_CFG_STOP_DIG_ERR,
+ cesa->engines[i].regs + CESA_SA_CFG);
+ writel(engine->sram_dma & CESA_SA_SRAM_MSK,
+ cesa->engines[i].regs + CESA_SA_DESC_P0);
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, mv_cesa_int,
+ IRQF_ONESHOT,
+ dev_name(&pdev->dev),
+ &cesa->engines[i]);
+ if (ret)
+ goto err_cleanup;
+ }
+
+ cesa_dev = cesa;
+
+ ret = mv_cesa_add_algs(cesa);
+ if (ret) {
+ cesa_dev = NULL;
+ goto err_cleanup;
+ }
+
+ dev_info(dev, "CESA device successfully registered\n");
+
+ return 0;
+
+err_cleanup:
+ for (i = 0; i < caps->nengines; i++) {
+ clk_disable_unprepare(cesa->engines[i].zclk);
+ clk_disable_unprepare(cesa->engines[i].clk);
+ mv_cesa_put_sram(pdev, i);
+ }
+
+ return ret;
+}
+
+static int mv_cesa_remove(struct platform_device *pdev)
+{
+ struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
+ int i;
+
+ mv_cesa_remove_algs(cesa);
+
+ for (i = 0; i < cesa->caps->nengines; i++) {
+ clk_disable_unprepare(cesa->engines[i].zclk);
+ clk_disable_unprepare(cesa->engines[i].clk);
+ mv_cesa_put_sram(pdev, i);
+ }
+
+ return 0;
+}
+
+static struct platform_driver marvell_cesa = {
+ .probe = mv_cesa_probe,
+ .remove = mv_cesa_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "marvell-cesa",
+ .of_match_table = mv_cesa_of_match_table,
+ },
+};
+module_platform_driver(marvell_cesa);
+
+MODULE_ALIAS("platform:mv_crypto");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_AUTHOR("Arnaud Ebalard <arno@natisbad.org>");
+MODULE_DESCRIPTION("Support for Marvell's cryptographic engine");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/crypto/marvell/cesa.h b/drivers/crypto/marvell/cesa.h
new file mode 100644
index 000000000000..b60698b30d30
--- /dev/null
+++ b/drivers/crypto/marvell/cesa.h
@@ -0,0 +1,791 @@
+#ifndef __MARVELL_CESA_H__
+#define __MARVELL_CESA_H__
+
+#include <crypto/algapi.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+
+#include <linux/crypto.h>
+#include <linux/dmapool.h>
+
+#define CESA_ENGINE_OFF(i) (((i) * 0x2000))
+
+#define CESA_TDMA_BYTE_CNT 0x800
+#define CESA_TDMA_SRC_ADDR 0x810
+#define CESA_TDMA_DST_ADDR 0x820
+#define CESA_TDMA_NEXT_ADDR 0x830
+
+#define CESA_TDMA_CONTROL 0x840
+#define CESA_TDMA_DST_BURST GENMASK(2, 0)
+#define CESA_TDMA_DST_BURST_32B 3
+#define CESA_TDMA_DST_BURST_128B 4
+#define CESA_TDMA_OUT_RD_EN BIT(4)
+#define CESA_TDMA_SRC_BURST GENMASK(8, 6)
+#define CESA_TDMA_SRC_BURST_32B (3 << 6)
+#define CESA_TDMA_SRC_BURST_128B (4 << 6)
+#define CESA_TDMA_CHAIN BIT(9)
+#define CESA_TDMA_BYTE_SWAP BIT(11)
+#define CESA_TDMA_NO_BYTE_SWAP BIT(11)
+#define CESA_TDMA_EN BIT(12)
+#define CESA_TDMA_FETCH_ND BIT(13)
+#define CESA_TDMA_ACT BIT(14)
+
+#define CESA_TDMA_CUR 0x870
+#define CESA_TDMA_ERROR_CAUSE 0x8c8
+#define CESA_TDMA_ERROR_MSK 0x8cc
+
+#define CESA_TDMA_WINDOW_BASE(x) (((x) * 0x8) + 0xa00)
+#define CESA_TDMA_WINDOW_CTRL(x) (((x) * 0x8) + 0xa04)
+
+#define CESA_IVDIG(x) (0xdd00 + ((x) * 4) + \
+ (((x) < 5) ? 0 : 0x14))
+
+#define CESA_SA_CMD 0xde00
+#define CESA_SA_CMD_EN_CESA_SA_ACCL0 BIT(0)
+#define CESA_SA_CMD_EN_CESA_SA_ACCL1 BIT(1)
+#define CESA_SA_CMD_DISABLE_SEC BIT(2)
+
+#define CESA_SA_DESC_P0 0xde04
+
+#define CESA_SA_DESC_P1 0xde14
+
+#define CESA_SA_CFG 0xde08
+#define CESA_SA_CFG_STOP_DIG_ERR GENMASK(1, 0)
+#define CESA_SA_CFG_DIG_ERR_CONT 0
+#define CESA_SA_CFG_DIG_ERR_SKIP 1
+#define CESA_SA_CFG_DIG_ERR_STOP 3
+#define CESA_SA_CFG_CH0_W_IDMA BIT(7)
+#define CESA_SA_CFG_CH1_W_IDMA BIT(8)
+#define CESA_SA_CFG_ACT_CH0_IDMA BIT(9)
+#define CESA_SA_CFG_ACT_CH1_IDMA BIT(10)
+#define CESA_SA_CFG_MULTI_PKT BIT(11)
+#define CESA_SA_CFG_PARA_DIS BIT(13)
+
+#define CESA_SA_ACCEL_STATUS 0xde0c
+#define CESA_SA_ST_ACT_0 BIT(0)
+#define CESA_SA_ST_ACT_1 BIT(1)
+
+/*
+ * CESA_SA_FPGA_INT_STATUS looks like a FPGA leftover and is documented only
+ * in Errata 4.12. It looks like that it was part of an IRQ-controller in FPGA
+ * and someone forgot to remove it while switching to the core and moving to
+ * CESA_SA_INT_STATUS.
+ */
+#define CESA_SA_FPGA_INT_STATUS 0xdd68
+#define CESA_SA_INT_STATUS 0xde20
+#define CESA_SA_INT_AUTH_DONE BIT(0)
+#define CESA_SA_INT_DES_E_DONE BIT(1)
+#define CESA_SA_INT_AES_E_DONE BIT(2)
+#define CESA_SA_INT_AES_D_DONE BIT(3)
+#define CESA_SA_INT_ENC_DONE BIT(4)
+#define CESA_SA_INT_ACCEL0_DONE BIT(5)
+#define CESA_SA_INT_ACCEL1_DONE BIT(6)
+#define CESA_SA_INT_ACC0_IDMA_DONE BIT(7)
+#define CESA_SA_INT_ACC1_IDMA_DONE BIT(8)
+#define CESA_SA_INT_IDMA_DONE BIT(9)
+#define CESA_SA_INT_IDMA_OWN_ERR BIT(10)
+
+#define CESA_SA_INT_MSK 0xde24
+
+#define CESA_SA_DESC_CFG_OP_MAC_ONLY 0
+#define CESA_SA_DESC_CFG_OP_CRYPT_ONLY 1
+#define CESA_SA_DESC_CFG_OP_MAC_CRYPT 2
+#define CESA_SA_DESC_CFG_OP_CRYPT_MAC 3
+#define CESA_SA_DESC_CFG_OP_MSK GENMASK(1, 0)
+#define CESA_SA_DESC_CFG_MACM_SHA256 (1 << 4)
+#define CESA_SA_DESC_CFG_MACM_HMAC_SHA256 (3 << 4)
+#define CESA_SA_DESC_CFG_MACM_MD5 (4 << 4)
+#define CESA_SA_DESC_CFG_MACM_SHA1 (5 << 4)
+#define CESA_SA_DESC_CFG_MACM_HMAC_MD5 (6 << 4)
+#define CESA_SA_DESC_CFG_MACM_HMAC_SHA1 (7 << 4)
+#define CESA_SA_DESC_CFG_MACM_MSK GENMASK(6, 4)
+#define CESA_SA_DESC_CFG_CRYPTM_DES (1 << 8)
+#define CESA_SA_DESC_CFG_CRYPTM_3DES (2 << 8)
+#define CESA_SA_DESC_CFG_CRYPTM_AES (3 << 8)
+#define CESA_SA_DESC_CFG_CRYPTM_MSK GENMASK(9, 8)
+#define CESA_SA_DESC_CFG_DIR_ENC (0 << 12)
+#define CESA_SA_DESC_CFG_DIR_DEC (1 << 12)
+#define CESA_SA_DESC_CFG_CRYPTCM_ECB (0 << 16)
+#define CESA_SA_DESC_CFG_CRYPTCM_CBC (1 << 16)
+#define CESA_SA_DESC_CFG_CRYPTCM_MSK BIT(16)
+#define CESA_SA_DESC_CFG_3DES_EEE (0 << 20)
+#define CESA_SA_DESC_CFG_3DES_EDE (1 << 20)
+#define CESA_SA_DESC_CFG_AES_LEN_128 (0 << 24)
+#define CESA_SA_DESC_CFG_AES_LEN_192 (1 << 24)
+#define CESA_SA_DESC_CFG_AES_LEN_256 (2 << 24)
+#define CESA_SA_DESC_CFG_AES_LEN_MSK GENMASK(25, 24)
+#define CESA_SA_DESC_CFG_NOT_FRAG (0 << 30)
+#define CESA_SA_DESC_CFG_FIRST_FRAG (1 << 30)
+#define CESA_SA_DESC_CFG_LAST_FRAG (2 << 30)
+#define CESA_SA_DESC_CFG_MID_FRAG (3 << 30)
+#define CESA_SA_DESC_CFG_FRAG_MSK GENMASK(31, 30)
+
+/*
+ * /-----------\ 0
+ * | ACCEL CFG | 4 * 8
+ * |-----------| 0x20
+ * | CRYPT KEY | 8 * 4
+ * |-----------| 0x40
+ * | IV IN | 4 * 4
+ * |-----------| 0x40 (inplace)
+ * | IV BUF | 4 * 4
+ * |-----------| 0x80
+ * | DATA IN | 16 * x (max ->max_req_size)
+ * |-----------| 0x80 (inplace operation)
+ * | DATA OUT | 16 * x (max ->max_req_size)
+ * \-----------/ SRAM size
+ */
+
+/*
+ * Hashing memory map:
+ * /-----------\ 0
+ * | ACCEL CFG | 4 * 8
+ * |-----------| 0x20
+ * | Inner IV | 8 * 4
+ * |-----------| 0x40
+ * | Outer IV | 8 * 4
+ * |-----------| 0x60
+ * | Output BUF| 8 * 4
+ * |-----------| 0x80
+ * | DATA IN | 64 * x (max ->max_req_size)
+ * \-----------/ SRAM size
+ */
+
+#define CESA_SA_CFG_SRAM_OFFSET 0x00
+#define CESA_SA_DATA_SRAM_OFFSET 0x80
+
+#define CESA_SA_CRYPT_KEY_SRAM_OFFSET 0x20
+#define CESA_SA_CRYPT_IV_SRAM_OFFSET 0x40
+
+#define CESA_SA_MAC_IIV_SRAM_OFFSET 0x20
+#define CESA_SA_MAC_OIV_SRAM_OFFSET 0x40
+#define CESA_SA_MAC_DIG_SRAM_OFFSET 0x60
+
+#define CESA_SA_DESC_CRYPT_DATA(offset) \
+ cpu_to_le32((CESA_SA_DATA_SRAM_OFFSET + (offset)) | \
+ ((CESA_SA_DATA_SRAM_OFFSET + (offset)) << 16))
+
+#define CESA_SA_DESC_CRYPT_IV(offset) \
+ cpu_to_le32((CESA_SA_CRYPT_IV_SRAM_OFFSET + (offset)) | \
+ ((CESA_SA_CRYPT_IV_SRAM_OFFSET + (offset)) << 16))
+
+#define CESA_SA_DESC_CRYPT_KEY(offset) \
+ cpu_to_le32(CESA_SA_CRYPT_KEY_SRAM_OFFSET + (offset))
+
+#define CESA_SA_DESC_MAC_DATA(offset) \
+ cpu_to_le32(CESA_SA_DATA_SRAM_OFFSET + (offset))
+#define CESA_SA_DESC_MAC_DATA_MSK GENMASK(15, 0)
+
+#define CESA_SA_DESC_MAC_TOTAL_LEN(total_len) cpu_to_le32((total_len) << 16)
+#define CESA_SA_DESC_MAC_TOTAL_LEN_MSK GENMASK(31, 16)
+
+#define CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX 0xffff
+
+#define CESA_SA_DESC_MAC_DIGEST(offset) \
+ cpu_to_le32(CESA_SA_MAC_DIG_SRAM_OFFSET + (offset))
+#define CESA_SA_DESC_MAC_DIGEST_MSK GENMASK(15, 0)
+
+#define CESA_SA_DESC_MAC_FRAG_LEN(frag_len) cpu_to_le32((frag_len) << 16)
+#define CESA_SA_DESC_MAC_FRAG_LEN_MSK GENMASK(31, 16)
+
+#define CESA_SA_DESC_MAC_IV(offset) \
+ cpu_to_le32((CESA_SA_MAC_IIV_SRAM_OFFSET + (offset)) | \
+ ((CESA_SA_MAC_OIV_SRAM_OFFSET + (offset)) << 16))
+
+#define CESA_SA_SRAM_SIZE 2048
+#define CESA_SA_SRAM_PAYLOAD_SIZE (cesa_dev->sram_size - \
+ CESA_SA_DATA_SRAM_OFFSET)
+
+#define CESA_SA_DEFAULT_SRAM_SIZE 2048
+#define CESA_SA_MIN_SRAM_SIZE 1024
+
+#define CESA_SA_SRAM_MSK (2048 - 1)
+
+#define CESA_MAX_HASH_BLOCK_SIZE 64
+#define CESA_HASH_BLOCK_SIZE_MSK (CESA_MAX_HASH_BLOCK_SIZE - 1)
+
+/**
+ * struct mv_cesa_sec_accel_desc - security accelerator descriptor
+ * @config: engine config
+ * @enc_p: input and output data pointers for a cipher operation
+ * @enc_len: cipher operation length
+ * @enc_key_p: cipher key pointer
+ * @enc_iv: cipher IV pointers
+ * @mac_src_p: input pointer and total hash length
+ * @mac_digest: digest pointer and hash operation length
+ * @mac_iv: hmac IV pointers
+ *
+ * Structure passed to the CESA engine to describe the crypto operation
+ * to be executed.
+ */
+struct mv_cesa_sec_accel_desc {
+ u32 config;
+ u32 enc_p;
+ u32 enc_len;
+ u32 enc_key_p;
+ u32 enc_iv;
+ u32 mac_src_p;
+ u32 mac_digest;
+ u32 mac_iv;
+};
+
+/**
+ * struct mv_cesa_blkcipher_op_ctx - cipher operation context
+ * @key: cipher key
+ * @iv: cipher IV
+ *
+ * Context associated to a cipher operation.
+ */
+struct mv_cesa_blkcipher_op_ctx {
+ u32 key[8];
+ u32 iv[4];
+};
+
+/**
+ * struct mv_cesa_hash_op_ctx - hash or hmac operation context
+ * @key: cipher key
+ * @iv: cipher IV
+ *
+ * Context associated to an hash or hmac operation.
+ */
+struct mv_cesa_hash_op_ctx {
+ u32 iv[16];
+ u32 hash[8];
+};
+
+/**
+ * struct mv_cesa_op_ctx - crypto operation context
+ * @desc: CESA descriptor
+ * @ctx: context associated to the crypto operation
+ *
+ * Context associated to a crypto operation.
+ */
+struct mv_cesa_op_ctx {
+ struct mv_cesa_sec_accel_desc desc;
+ union {
+ struct mv_cesa_blkcipher_op_ctx blkcipher;
+ struct mv_cesa_hash_op_ctx hash;
+ } ctx;
+};
+
+/* TDMA descriptor flags */
+#define CESA_TDMA_DST_IN_SRAM BIT(31)
+#define CESA_TDMA_SRC_IN_SRAM BIT(30)
+#define CESA_TDMA_TYPE_MSK GENMASK(29, 0)
+#define CESA_TDMA_DUMMY 0
+#define CESA_TDMA_DATA 1
+#define CESA_TDMA_OP 2
+
+/**
+ * struct mv_cesa_tdma_desc - TDMA descriptor
+ * @byte_cnt: number of bytes to transfer
+ * @src: DMA address of the source
+ * @dst: DMA address of the destination
+ * @next_dma: DMA address of the next TDMA descriptor
+ * @cur_dma: DMA address of this TDMA descriptor
+ * @next: pointer to the next TDMA descriptor
+ * @op: CESA operation attached to this TDMA descriptor
+ * @data: raw data attached to this TDMA descriptor
+ * @flags: flags describing the TDMA transfer. See the
+ * "TDMA descriptor flags" section above
+ *
+ * TDMA descriptor used to create a transfer chain describing a crypto
+ * operation.
+ */
+struct mv_cesa_tdma_desc {
+ u32 byte_cnt;
+ u32 src;
+ u32 dst;
+ u32 next_dma;
+ u32 cur_dma;
+ struct mv_cesa_tdma_desc *next;
+ union {
+ struct mv_cesa_op_ctx *op;
+ void *data;
+ };
+ u32 flags;
+};
+
+/**
+ * struct mv_cesa_sg_dma_iter - scatter-gather iterator
+ * @dir: transfer direction
+ * @sg: scatter list
+ * @offset: current position in the scatter list
+ * @op_offset: current position in the crypto operation
+ *
+ * Iterator used to iterate over a scatterlist while creating a TDMA chain for
+ * a crypto operation.
+ */
+struct mv_cesa_sg_dma_iter {
+ enum dma_data_direction dir;
+ struct scatterlist *sg;
+ unsigned int offset;
+ unsigned int op_offset;
+};
+
+/**
+ * struct mv_cesa_dma_iter - crypto operation iterator
+ * @len: the crypto operation length
+ * @offset: current position in the crypto operation
+ * @op_len: sub-operation length (the crypto engine can only act on 2kb
+ * chunks)
+ *
+ * Iterator used to create a TDMA chain for a given crypto operation.
+ */
+struct mv_cesa_dma_iter {
+ unsigned int len;
+ unsigned int offset;
+ unsigned int op_len;
+};
+
+/**
+ * struct mv_cesa_tdma_chain - TDMA chain
+ * @first: first entry in the TDMA chain
+ * @last: last entry in the TDMA chain
+ *
+ * Stores a TDMA chain for a specific crypto operation.
+ */
+struct mv_cesa_tdma_chain {
+ struct mv_cesa_tdma_desc *first;
+ struct mv_cesa_tdma_desc *last;
+};
+
+struct mv_cesa_engine;
+
+/**
+ * struct mv_cesa_caps - CESA device capabilities
+ * @engines: number of engines
+ * @has_tdma: whether this device has a TDMA block
+ * @cipher_algs: supported cipher algorithms
+ * @ncipher_algs: number of supported cipher algorithms
+ * @ahash_algs: supported hash algorithms
+ * @nahash_algs: number of supported hash algorithms
+ *
+ * Structure used to describe CESA device capabilities.
+ */
+struct mv_cesa_caps {
+ int nengines;
+ bool has_tdma;
+ struct crypto_alg **cipher_algs;
+ int ncipher_algs;
+ struct ahash_alg **ahash_algs;
+ int nahash_algs;
+};
+
+/**
+ * struct mv_cesa_dev_dma - DMA pools
+ * @tdma_desc_pool: TDMA desc pool
+ * @op_pool: crypto operation pool
+ * @cache_pool: data cache pool (used by hash implementation when the
+ * hash request is smaller than the hash block size)
+ * @padding_pool: padding pool (used by hash implementation when hardware
+ * padding cannot be used)
+ *
+ * Structure containing the different DMA pools used by this driver.
+ */
+struct mv_cesa_dev_dma {
+ struct dma_pool *tdma_desc_pool;
+ struct dma_pool *op_pool;
+ struct dma_pool *cache_pool;
+ struct dma_pool *padding_pool;
+};
+
+/**
+ * struct mv_cesa_dev - CESA device
+ * @caps: device capabilities
+ * @regs: device registers
+ * @sram_size: usable SRAM size
+ * @lock: device lock
+ * @queue: crypto request queue
+ * @engines: array of engines
+ * @dma: dma pools
+ *
+ * Structure storing CESA device information.
+ */
+struct mv_cesa_dev {
+ const struct mv_cesa_caps *caps;
+ void __iomem *regs;
+ struct device *dev;
+ unsigned int sram_size;
+ spinlock_t lock;
+ struct crypto_queue queue;
+ struct mv_cesa_engine *engines;
+ struct mv_cesa_dev_dma *dma;
+};
+
+/**
+ * struct mv_cesa_engine - CESA engine
+ * @id: engine id
+ * @regs: engine registers
+ * @sram: SRAM memory region
+ * @sram_dma: DMA address of the SRAM memory region
+ * @lock: engine lock
+ * @req: current crypto request
+ * @clk: engine clk
+ * @zclk: engine zclk
+ * @max_req_len: maximum chunk length (useful to create the TDMA chain)
+ * @int_mask: interrupt mask cache
+ * @pool: memory pool pointing to the memory region reserved in
+ * SRAM
+ *
+ * Structure storing CESA engine information.
+ */
+struct mv_cesa_engine {
+ int id;
+ void __iomem *regs;
+ void __iomem *sram;
+ dma_addr_t sram_dma;
+ spinlock_t lock;
+ struct crypto_async_request *req;
+ struct clk *clk;
+ struct clk *zclk;
+ size_t max_req_len;
+ u32 int_mask;
+ struct gen_pool *pool;
+};
+
+/**
+ * struct mv_cesa_req_ops - CESA request operations
+ * @prepare: prepare a request to be executed on the specified engine
+ * @process: process a request chunk result (should return 0 if the
+ * operation, -EINPROGRESS if it needs more steps or an error
+ * code)
+ * @step: launch the crypto operation on the next chunk
+ * @cleanup: cleanup the crypto request (release associated data)
+ */
+struct mv_cesa_req_ops {
+ void (*prepare)(struct crypto_async_request *req,
+ struct mv_cesa_engine *engine);
+ int (*process)(struct crypto_async_request *req, u32 status);
+ void (*step)(struct crypto_async_request *req);
+ void (*cleanup)(struct crypto_async_request *req);
+};
+
+/**
+ * struct mv_cesa_ctx - CESA operation context
+ * @ops: crypto operations
+ *
+ * Base context structure inherited by operation specific ones.
+ */
+struct mv_cesa_ctx {
+ const struct mv_cesa_req_ops *ops;
+};
+
+/**
+ * struct mv_cesa_hash_ctx - CESA hash operation context
+ * @base: base context structure
+ *
+ * Hash context structure.
+ */
+struct mv_cesa_hash_ctx {
+ struct mv_cesa_ctx base;
+};
+
+/**
+ * struct mv_cesa_hash_ctx - CESA hmac operation context
+ * @base: base context structure
+ * @iv: initialization vectors
+ *
+ * HMAC context structure.
+ */
+struct mv_cesa_hmac_ctx {
+ struct mv_cesa_ctx base;
+ u32 iv[16];
+};
+
+/**
+ * enum mv_cesa_req_type - request type definitions
+ * @CESA_STD_REQ: standard request
+ * @CESA_DMA_REQ: DMA request
+ */
+enum mv_cesa_req_type {
+ CESA_STD_REQ,
+ CESA_DMA_REQ,
+};
+
+/**
+ * struct mv_cesa_req - CESA request
+ * @type: request type
+ * @engine: engine associated with this request
+ */
+struct mv_cesa_req {
+ enum mv_cesa_req_type type;
+ struct mv_cesa_engine *engine;
+};
+
+/**
+ * struct mv_cesa_tdma_req - CESA TDMA request
+ * @base: base information
+ * @chain: TDMA chain
+ */
+struct mv_cesa_tdma_req {
+ struct mv_cesa_req base;
+ struct mv_cesa_tdma_chain chain;
+};
+
+/**
+ * struct mv_cesa_sg_std_iter - CESA scatter-gather iterator for standard
+ * requests
+ * @iter: sg mapping iterator
+ * @offset: current offset in the SG entry mapped in memory
+ */
+struct mv_cesa_sg_std_iter {
+ struct sg_mapping_iter iter;
+ unsigned int offset;
+};
+
+/**
+ * struct mv_cesa_ablkcipher_std_req - cipher standard request
+ * @base: base information
+ * @op: operation context
+ * @offset: current operation offset
+ * @size: size of the crypto operation
+ */
+struct mv_cesa_ablkcipher_std_req {
+ struct mv_cesa_req base;
+ struct mv_cesa_op_ctx op;
+ unsigned int offset;
+ unsigned int size;
+ bool skip_ctx;
+};
+
+/**
+ * struct mv_cesa_ablkcipher_req - cipher request
+ * @req: type specific request information
+ * @src_nents: number of entries in the src sg list
+ * @dst_nents: number of entries in the dest sg list
+ */
+struct mv_cesa_ablkcipher_req {
+ union {
+ struct mv_cesa_req base;
+ struct mv_cesa_tdma_req dma;
+ struct mv_cesa_ablkcipher_std_req std;
+ } req;
+ int src_nents;
+ int dst_nents;
+};
+
+/**
+ * struct mv_cesa_ahash_std_req - standard hash request
+ * @base: base information
+ * @offset: current operation offset
+ */
+struct mv_cesa_ahash_std_req {
+ struct mv_cesa_req base;
+ unsigned int offset;
+};
+
+/**
+ * struct mv_cesa_ahash_dma_req - DMA hash request
+ * @base: base information
+ * @padding: padding buffer
+ * @padding_dma: DMA address of the padding buffer
+ * @cache_dma: DMA address of the cache buffer
+ */
+struct mv_cesa_ahash_dma_req {
+ struct mv_cesa_tdma_req base;
+ u8 *padding;
+ dma_addr_t padding_dma;
+ dma_addr_t cache_dma;
+};
+
+/**
+ * struct mv_cesa_ahash_req - hash request
+ * @req: type specific request information
+ * @cache: cache buffer
+ * @cache_ptr: write pointer in the cache buffer
+ * @len: hash total length
+ * @src_nents: number of entries in the scatterlist
+ * @last_req: define whether the current operation is the last one
+ * or not
+ * @state: hash state
+ */
+struct mv_cesa_ahash_req {
+ union {
+ struct mv_cesa_req base;
+ struct mv_cesa_ahash_dma_req dma;
+ struct mv_cesa_ahash_std_req std;
+ } req;
+ struct mv_cesa_op_ctx op_tmpl;
+ u8 *cache;
+ unsigned int cache_ptr;
+ u64 len;
+ int src_nents;
+ bool last_req;
+ __be32 state[8];
+};
+
+/* CESA functions */
+
+extern struct mv_cesa_dev *cesa_dev;
+
+static inline void mv_cesa_update_op_cfg(struct mv_cesa_op_ctx *op,
+ u32 cfg, u32 mask)
+{
+ op->desc.config &= cpu_to_le32(~mask);
+ op->desc.config |= cpu_to_le32(cfg);
+}
+
+static inline u32 mv_cesa_get_op_cfg(struct mv_cesa_op_ctx *op)
+{
+ return le32_to_cpu(op->desc.config);
+}
+
+static inline void mv_cesa_set_op_cfg(struct mv_cesa_op_ctx *op, u32 cfg)
+{
+ op->desc.config = cpu_to_le32(cfg);
+}
+
+static inline void mv_cesa_adjust_op(struct mv_cesa_engine *engine,
+ struct mv_cesa_op_ctx *op)
+{
+ u32 offset = engine->sram_dma & CESA_SA_SRAM_MSK;
+
+ op->desc.enc_p = CESA_SA_DESC_CRYPT_DATA(offset);
+ op->desc.enc_key_p = CESA_SA_DESC_CRYPT_KEY(offset);
+ op->desc.enc_iv = CESA_SA_DESC_CRYPT_IV(offset);
+ op->desc.mac_src_p &= ~CESA_SA_DESC_MAC_DATA_MSK;
+ op->desc.mac_src_p |= CESA_SA_DESC_MAC_DATA(offset);
+ op->desc.mac_digest &= ~CESA_SA_DESC_MAC_DIGEST_MSK;
+ op->desc.mac_digest |= CESA_SA_DESC_MAC_DIGEST(offset);
+ op->desc.mac_iv = CESA_SA_DESC_MAC_IV(offset);
+}
+
+static inline void mv_cesa_set_crypt_op_len(struct mv_cesa_op_ctx *op, int len)
+{
+ op->desc.enc_len = cpu_to_le32(len);
+}
+
+static inline void mv_cesa_set_mac_op_total_len(struct mv_cesa_op_ctx *op,
+ int len)
+{
+ op->desc.mac_src_p &= ~CESA_SA_DESC_MAC_TOTAL_LEN_MSK;
+ op->desc.mac_src_p |= CESA_SA_DESC_MAC_TOTAL_LEN(len);
+}
+
+static inline void mv_cesa_set_mac_op_frag_len(struct mv_cesa_op_ctx *op,
+ int len)
+{
+ op->desc.mac_digest &= ~CESA_SA_DESC_MAC_FRAG_LEN_MSK;
+ op->desc.mac_digest |= CESA_SA_DESC_MAC_FRAG_LEN(len);
+}
+
+static inline void mv_cesa_set_int_mask(struct mv_cesa_engine *engine,
+ u32 int_mask)
+{
+ if (int_mask == engine->int_mask)
+ return;
+
+ writel(int_mask, engine->regs + CESA_SA_INT_MSK);
+ engine->int_mask = int_mask;
+}
+
+static inline u32 mv_cesa_get_int_mask(struct mv_cesa_engine *engine)
+{
+ return engine->int_mask;
+}
+
+int mv_cesa_queue_req(struct crypto_async_request *req);
+
+/* TDMA functions */
+
+static inline void mv_cesa_req_dma_iter_init(struct mv_cesa_dma_iter *iter,
+ unsigned int len)
+{
+ iter->len = len;
+ iter->op_len = min(len, CESA_SA_SRAM_PAYLOAD_SIZE);
+ iter->offset = 0;
+}
+
+static inline void mv_cesa_sg_dma_iter_init(struct mv_cesa_sg_dma_iter *iter,
+ struct scatterlist *sg,
+ enum dma_data_direction dir)
+{
+ iter->op_offset = 0;
+ iter->offset = 0;
+ iter->sg = sg;
+ iter->dir = dir;
+}
+
+static inline unsigned int
+mv_cesa_req_dma_iter_transfer_len(struct mv_cesa_dma_iter *iter,
+ struct mv_cesa_sg_dma_iter *sgiter)
+{
+ return min(iter->op_len - sgiter->op_offset,
+ sg_dma_len(sgiter->sg) - sgiter->offset);
+}
+
+bool mv_cesa_req_dma_iter_next_transfer(struct mv_cesa_dma_iter *chain,
+ struct mv_cesa_sg_dma_iter *sgiter,
+ unsigned int len);
+
+static inline bool mv_cesa_req_dma_iter_next_op(struct mv_cesa_dma_iter *iter)
+{
+ iter->offset += iter->op_len;
+ iter->op_len = min(iter->len - iter->offset,
+ CESA_SA_SRAM_PAYLOAD_SIZE);
+
+ return iter->op_len;
+}
+
+void mv_cesa_dma_step(struct mv_cesa_tdma_req *dreq);
+
+static inline int mv_cesa_dma_process(struct mv_cesa_tdma_req *dreq,
+ u32 status)
+{
+ if (!(status & CESA_SA_INT_ACC0_IDMA_DONE))
+ return -EINPROGRESS;
+
+ if (status & CESA_SA_INT_IDMA_OWN_ERR)
+ return -EINVAL;
+
+ return 0;
+}
+
+void mv_cesa_dma_prepare(struct mv_cesa_tdma_req *dreq,
+ struct mv_cesa_engine *engine);
+
+void mv_cesa_dma_cleanup(struct mv_cesa_tdma_req *dreq);
+
+static inline void
+mv_cesa_tdma_desc_iter_init(struct mv_cesa_tdma_chain *chain)
+{
+ memset(chain, 0, sizeof(*chain));
+}
+
+struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
+ const struct mv_cesa_op_ctx *op_templ,
+ bool skip_ctx,
+ gfp_t flags);
+
+int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
+ dma_addr_t dst, dma_addr_t src, u32 size,
+ u32 flags, gfp_t gfp_flags);
+
+int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain,
+ u32 flags);
+
+int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, u32 flags);
+
+int mv_cesa_dma_add_op_transfers(struct mv_cesa_tdma_chain *chain,
+ struct mv_cesa_dma_iter *dma_iter,
+ struct mv_cesa_sg_dma_iter *sgiter,
+ gfp_t gfp_flags);
+
+/* Algorithm definitions */
+
+extern struct ahash_alg mv_md5_alg;
+extern struct ahash_alg mv_sha1_alg;
+extern struct ahash_alg mv_sha256_alg;
+extern struct ahash_alg mv_ahmac_md5_alg;
+extern struct ahash_alg mv_ahmac_sha1_alg;
+extern struct ahash_alg mv_ahmac_sha256_alg;
+
+extern struct crypto_alg mv_cesa_ecb_des_alg;
+extern struct crypto_alg mv_cesa_cbc_des_alg;
+extern struct crypto_alg mv_cesa_ecb_des3_ede_alg;
+extern struct crypto_alg mv_cesa_cbc_des3_ede_alg;
+extern struct crypto_alg mv_cesa_ecb_aes_alg;
+extern struct crypto_alg mv_cesa_cbc_aes_alg;
+
+#endif /* __MARVELL_CESA_H__ */
diff --git a/drivers/crypto/marvell/cipher.c b/drivers/crypto/marvell/cipher.c
new file mode 100644
index 000000000000..0745cf3b9c0e
--- /dev/null
+++ b/drivers/crypto/marvell/cipher.c
@@ -0,0 +1,797 @@
+/*
+ * Cipher algorithms supported by the CESA: DES, 3DES and AES.
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Author: Arnaud Ebalard <arno@natisbad.org>
+ *
+ * This work is based on an initial version written by
+ * Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
+ *
+ * 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 <crypto/aes.h>
+#include <crypto/des.h>
+
+#include "cesa.h"
+
+struct mv_cesa_des_ctx {
+ struct mv_cesa_ctx base;
+ u8 key[DES_KEY_SIZE];
+};
+
+struct mv_cesa_des3_ctx {
+ struct mv_cesa_ctx base;
+ u8 key[DES3_EDE_KEY_SIZE];
+};
+
+struct mv_cesa_aes_ctx {
+ struct mv_cesa_ctx base;
+ struct crypto_aes_ctx aes;
+};
+
+struct mv_cesa_ablkcipher_dma_iter {
+ struct mv_cesa_dma_iter base;
+ struct mv_cesa_sg_dma_iter src;
+ struct mv_cesa_sg_dma_iter dst;
+};
+
+static inline void
+mv_cesa_ablkcipher_req_iter_init(struct mv_cesa_ablkcipher_dma_iter *iter,
+ struct ablkcipher_request *req)
+{
+ mv_cesa_req_dma_iter_init(&iter->base, req->nbytes);
+ mv_cesa_sg_dma_iter_init(&iter->src, req->src, DMA_TO_DEVICE);
+ mv_cesa_sg_dma_iter_init(&iter->dst, req->dst, DMA_FROM_DEVICE);
+}
+
+static inline bool
+mv_cesa_ablkcipher_req_iter_next_op(struct mv_cesa_ablkcipher_dma_iter *iter)
+{
+ iter->src.op_offset = 0;
+ iter->dst.op_offset = 0;
+
+ return mv_cesa_req_dma_iter_next_op(&iter->base);
+}
+
+static inline void
+mv_cesa_ablkcipher_dma_cleanup(struct ablkcipher_request *req)
+{
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+
+ if (req->dst != req->src) {
+ dma_unmap_sg(cesa_dev->dev, req->dst, creq->dst_nents,
+ DMA_FROM_DEVICE);
+ dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
+ DMA_TO_DEVICE);
+ } else {
+ dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
+ DMA_BIDIRECTIONAL);
+ }
+ mv_cesa_dma_cleanup(&creq->req.dma);
+}
+
+static inline void mv_cesa_ablkcipher_cleanup(struct ablkcipher_request *req)
+{
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ mv_cesa_ablkcipher_dma_cleanup(req);
+}
+
+static void mv_cesa_ablkcipher_std_step(struct ablkcipher_request *req)
+{
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+ struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
+ struct mv_cesa_engine *engine = sreq->base.engine;
+ size_t len = min_t(size_t, req->nbytes - sreq->offset,
+ CESA_SA_SRAM_PAYLOAD_SIZE);
+
+ len = sg_pcopy_to_buffer(req->src, creq->src_nents,
+ engine->sram + CESA_SA_DATA_SRAM_OFFSET,
+ len, sreq->offset);
+
+ sreq->size = len;
+ mv_cesa_set_crypt_op_len(&sreq->op, len);
+
+ /* FIXME: only update enc_len field */
+ if (!sreq->skip_ctx) {
+ memcpy(engine->sram, &sreq->op, sizeof(sreq->op));
+ sreq->skip_ctx = true;
+ } else {
+ memcpy(engine->sram, &sreq->op, sizeof(sreq->op.desc));
+ }
+
+ mv_cesa_set_int_mask(engine, CESA_SA_INT_ACCEL0_DONE);
+ writel(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
+ writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
+}
+
+static int mv_cesa_ablkcipher_std_process(struct ablkcipher_request *req,
+ u32 status)
+{
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+ struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
+ struct mv_cesa_engine *engine = sreq->base.engine;
+ size_t len;
+
+ len = sg_pcopy_from_buffer(req->dst, creq->dst_nents,
+ engine->sram + CESA_SA_DATA_SRAM_OFFSET,
+ sreq->size, sreq->offset);
+
+ sreq->offset += len;
+ if (sreq->offset < req->nbytes)
+ return -EINPROGRESS;
+
+ return 0;
+}
+
+static int mv_cesa_ablkcipher_process(struct crypto_async_request *req,
+ u32 status)
+{
+ struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
+ struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
+ struct mv_cesa_engine *engine = sreq->base.engine;
+ int ret;
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ ret = mv_cesa_dma_process(&creq->req.dma, status);
+ else
+ ret = mv_cesa_ablkcipher_std_process(ablkreq, status);
+
+ if (ret)
+ return ret;
+
+ memcpy(ablkreq->info, engine->sram + CESA_SA_CRYPT_IV_SRAM_OFFSET,
+ crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(ablkreq)));
+
+ return 0;
+}
+
+static void mv_cesa_ablkcipher_step(struct crypto_async_request *req)
+{
+ struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ mv_cesa_dma_step(&creq->req.dma);
+ else
+ mv_cesa_ablkcipher_std_step(ablkreq);
+}
+
+static inline void
+mv_cesa_ablkcipher_dma_prepare(struct ablkcipher_request *req)
+{
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+ struct mv_cesa_tdma_req *dreq = &creq->req.dma;
+
+ mv_cesa_dma_prepare(dreq, dreq->base.engine);
+}
+
+static inline void
+mv_cesa_ablkcipher_std_prepare(struct ablkcipher_request *req)
+{
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+ struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
+ struct mv_cesa_engine *engine = sreq->base.engine;
+
+ sreq->size = 0;
+ sreq->offset = 0;
+ mv_cesa_adjust_op(engine, &sreq->op);
+ memcpy(engine->sram, &sreq->op, sizeof(sreq->op));
+}
+
+static inline void mv_cesa_ablkcipher_prepare(struct crypto_async_request *req,
+ struct mv_cesa_engine *engine)
+{
+ struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
+
+ creq->req.base.engine = engine;
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ mv_cesa_ablkcipher_dma_prepare(ablkreq);
+ else
+ mv_cesa_ablkcipher_std_prepare(ablkreq);
+}
+
+static inline void
+mv_cesa_ablkcipher_req_cleanup(struct crypto_async_request *req)
+{
+ struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
+
+ mv_cesa_ablkcipher_cleanup(ablkreq);
+}
+
+static const struct mv_cesa_req_ops mv_cesa_ablkcipher_req_ops = {
+ .step = mv_cesa_ablkcipher_step,
+ .process = mv_cesa_ablkcipher_process,
+ .prepare = mv_cesa_ablkcipher_prepare,
+ .cleanup = mv_cesa_ablkcipher_req_cleanup,
+};
+
+static int mv_cesa_ablkcipher_cra_init(struct crypto_tfm *tfm)
+{
+ struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->base.ops = &mv_cesa_ablkcipher_req_ops;
+
+ tfm->crt_ablkcipher.reqsize = sizeof(struct mv_cesa_ablkcipher_req);
+
+ return 0;
+}
+
+static int mv_cesa_aes_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
+ unsigned int len)
+{
+ struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+ struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+ int remaining;
+ int offset;
+ int ret;
+ int i;
+
+ ret = crypto_aes_expand_key(&ctx->aes, key, len);
+ if (ret) {
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return ret;
+ }
+
+ remaining = (ctx->aes.key_length - 16) / 4;
+ offset = ctx->aes.key_length + 24 - remaining;
+ for (i = 0; i < remaining; i++)
+ ctx->aes.key_dec[4 + i] =
+ cpu_to_le32(ctx->aes.key_enc[offset + i]);
+
+ return 0;
+}
+
+static int mv_cesa_des_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
+ unsigned int len)
+{
+ struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+ struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(tfm);
+ u32 tmp[DES_EXPKEY_WORDS];
+ int ret;
+
+ if (len != DES_KEY_SIZE) {
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ ret = des_ekey(tmp, key);
+ if (!ret && (tfm->crt_flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+ tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
+ return -EINVAL;
+ }
+
+ memcpy(ctx->key, key, DES_KEY_SIZE);
+
+ return 0;
+}
+
+static int mv_cesa_des3_ede_setkey(struct crypto_ablkcipher *cipher,
+ const u8 *key, unsigned int len)
+{
+ struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+ struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if (len != DES3_EDE_KEY_SIZE) {
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ memcpy(ctx->key, key, DES3_EDE_KEY_SIZE);
+
+ return 0;
+}
+
+static int mv_cesa_ablkcipher_dma_req_init(struct ablkcipher_request *req,
+ const struct mv_cesa_op_ctx *op_templ)
+{
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+ gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC;
+ struct mv_cesa_tdma_req *dreq = &creq->req.dma;
+ struct mv_cesa_ablkcipher_dma_iter iter;
+ struct mv_cesa_tdma_chain chain;
+ bool skip_ctx = false;
+ int ret;
+
+ dreq->base.type = CESA_DMA_REQ;
+ dreq->chain.first = NULL;
+ dreq->chain.last = NULL;
+
+ if (req->src != req->dst) {
+ ret = dma_map_sg(cesa_dev->dev, req->src, creq->src_nents,
+ DMA_TO_DEVICE);
+ if (!ret)
+ return -ENOMEM;
+
+ ret = dma_map_sg(cesa_dev->dev, req->dst, creq->dst_nents,
+ DMA_FROM_DEVICE);
+ if (!ret) {
+ ret = -ENOMEM;
+ goto err_unmap_src;
+ }
+ } else {
+ ret = dma_map_sg(cesa_dev->dev, req->src, creq->src_nents,
+ DMA_BIDIRECTIONAL);
+ if (!ret)
+ return -ENOMEM;
+ }
+
+ mv_cesa_tdma_desc_iter_init(&chain);
+ mv_cesa_ablkcipher_req_iter_init(&iter, req);
+
+ do {
+ struct mv_cesa_op_ctx *op;
+
+ op = mv_cesa_dma_add_op(&chain, op_templ, skip_ctx, flags);
+ if (IS_ERR(op)) {
+ ret = PTR_ERR(op);
+ goto err_free_tdma;
+ }
+ skip_ctx = true;
+
+ mv_cesa_set_crypt_op_len(op, iter.base.op_len);
+
+ /* Add input transfers */
+ ret = mv_cesa_dma_add_op_transfers(&chain, &iter.base,
+ &iter.src, flags);
+ if (ret)
+ goto err_free_tdma;
+
+ /* Add dummy desc to launch the crypto operation */
+ ret = mv_cesa_dma_add_dummy_launch(&chain, flags);
+ if (ret)
+ goto err_free_tdma;
+
+ /* Add output transfers */
+ ret = mv_cesa_dma_add_op_transfers(&chain, &iter.base,
+ &iter.dst, flags);
+ if (ret)
+ goto err_free_tdma;
+
+ } while (mv_cesa_ablkcipher_req_iter_next_op(&iter));
+
+ dreq->chain = chain;
+
+ return 0;
+
+err_free_tdma:
+ mv_cesa_dma_cleanup(dreq);
+ if (req->dst != req->src)
+ dma_unmap_sg(cesa_dev->dev, req->dst, creq->dst_nents,
+ DMA_FROM_DEVICE);
+
+err_unmap_src:
+ dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
+ req->dst != req->src ? DMA_TO_DEVICE : DMA_BIDIRECTIONAL);
+
+ return ret;
+}
+
+static inline int
+mv_cesa_ablkcipher_std_req_init(struct ablkcipher_request *req,
+ const struct mv_cesa_op_ctx *op_templ)
+{
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+ struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
+
+ sreq->base.type = CESA_STD_REQ;
+ sreq->op = *op_templ;
+ sreq->skip_ctx = false;
+
+ return 0;
+}
+
+static int mv_cesa_ablkcipher_req_init(struct ablkcipher_request *req,
+ struct mv_cesa_op_ctx *tmpl)
+{
+ struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ unsigned int blksize = crypto_ablkcipher_blocksize(tfm);
+ int ret;
+
+ if (!IS_ALIGNED(req->nbytes, blksize))
+ return -EINVAL;
+
+ creq->src_nents = sg_nents_for_len(req->src, req->nbytes);
+ creq->dst_nents = sg_nents_for_len(req->dst, req->nbytes);
+
+ mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_OP_CRYPT_ONLY,
+ CESA_SA_DESC_CFG_OP_MSK);
+
+ /* TODO: add a threshold for DMA usage */
+ if (cesa_dev->caps->has_tdma)
+ ret = mv_cesa_ablkcipher_dma_req_init(req, tmpl);
+ else
+ ret = mv_cesa_ablkcipher_std_req_init(req, tmpl);
+
+ return ret;
+}
+
+static int mv_cesa_des_op(struct ablkcipher_request *req,
+ struct mv_cesa_op_ctx *tmpl)
+{
+ struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ int ret;
+
+ mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_DES,
+ CESA_SA_DESC_CFG_CRYPTM_MSK);
+
+ memcpy(tmpl->ctx.blkcipher.key, ctx->key, DES_KEY_SIZE);
+
+ ret = mv_cesa_ablkcipher_req_init(req, tmpl);
+ if (ret)
+ return ret;
+
+ ret = mv_cesa_queue_req(&req->base);
+ if (ret && ret != -EINPROGRESS)
+ mv_cesa_ablkcipher_cleanup(req);
+
+ return ret;
+}
+
+static int mv_cesa_ecb_des_encrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl,
+ CESA_SA_DESC_CFG_CRYPTCM_ECB |
+ CESA_SA_DESC_CFG_DIR_ENC);
+
+ return mv_cesa_des_op(req, &tmpl);
+}
+
+static int mv_cesa_ecb_des_decrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl,
+ CESA_SA_DESC_CFG_CRYPTCM_ECB |
+ CESA_SA_DESC_CFG_DIR_DEC);
+
+ return mv_cesa_des_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_ecb_des_alg = {
+ .cra_name = "ecb(des)",
+ .cra_driver_name = "mv-ecb-des",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_des_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = mv_cesa_ablkcipher_cra_init,
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .setkey = mv_cesa_des_setkey,
+ .encrypt = mv_cesa_ecb_des_encrypt,
+ .decrypt = mv_cesa_ecb_des_decrypt,
+ },
+ },
+};
+
+static int mv_cesa_cbc_des_op(struct ablkcipher_request *req,
+ struct mv_cesa_op_ctx *tmpl)
+{
+ mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTCM_CBC,
+ CESA_SA_DESC_CFG_CRYPTCM_MSK);
+
+ memcpy(tmpl->ctx.blkcipher.iv, req->info, DES_BLOCK_SIZE);
+
+ return mv_cesa_des_op(req, tmpl);
+}
+
+static int mv_cesa_cbc_des_encrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_ENC);
+
+ return mv_cesa_cbc_des_op(req, &tmpl);
+}
+
+static int mv_cesa_cbc_des_decrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_DEC);
+
+ return mv_cesa_cbc_des_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_cbc_des_alg = {
+ .cra_name = "cbc(des)",
+ .cra_driver_name = "mv-cbc-des",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_des_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = mv_cesa_ablkcipher_cra_init,
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ .setkey = mv_cesa_des_setkey,
+ .encrypt = mv_cesa_cbc_des_encrypt,
+ .decrypt = mv_cesa_cbc_des_decrypt,
+ },
+ },
+};
+
+static int mv_cesa_des3_op(struct ablkcipher_request *req,
+ struct mv_cesa_op_ctx *tmpl)
+{
+ struct mv_cesa_des3_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ int ret;
+
+ mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_3DES,
+ CESA_SA_DESC_CFG_CRYPTM_MSK);
+
+ memcpy(tmpl->ctx.blkcipher.key, ctx->key, DES3_EDE_KEY_SIZE);
+
+ ret = mv_cesa_ablkcipher_req_init(req, tmpl);
+ if (ret)
+ return ret;
+
+ ret = mv_cesa_queue_req(&req->base);
+ if (ret && ret != -EINPROGRESS)
+ mv_cesa_ablkcipher_cleanup(req);
+
+ return ret;
+}
+
+static int mv_cesa_ecb_des3_ede_encrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl,
+ CESA_SA_DESC_CFG_CRYPTCM_ECB |
+ CESA_SA_DESC_CFG_3DES_EDE |
+ CESA_SA_DESC_CFG_DIR_ENC);
+
+ return mv_cesa_des3_op(req, &tmpl);
+}
+
+static int mv_cesa_ecb_des3_ede_decrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl,
+ CESA_SA_DESC_CFG_CRYPTCM_ECB |
+ CESA_SA_DESC_CFG_3DES_EDE |
+ CESA_SA_DESC_CFG_DIR_DEC);
+
+ return mv_cesa_des3_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_ecb_des3_ede_alg = {
+ .cra_name = "ecb(des3_ede)",
+ .cra_driver_name = "mv-ecb-des3-ede",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_des3_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = mv_cesa_ablkcipher_cra_init,
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .setkey = mv_cesa_des3_ede_setkey,
+ .encrypt = mv_cesa_ecb_des3_ede_encrypt,
+ .decrypt = mv_cesa_ecb_des3_ede_decrypt,
+ },
+ },
+};
+
+static int mv_cesa_cbc_des3_op(struct ablkcipher_request *req,
+ struct mv_cesa_op_ctx *tmpl)
+{
+ memcpy(tmpl->ctx.blkcipher.iv, req->info, DES3_EDE_BLOCK_SIZE);
+
+ return mv_cesa_des3_op(req, tmpl);
+}
+
+static int mv_cesa_cbc_des3_ede_encrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl,
+ CESA_SA_DESC_CFG_CRYPTCM_CBC |
+ CESA_SA_DESC_CFG_3DES_EDE |
+ CESA_SA_DESC_CFG_DIR_ENC);
+
+ return mv_cesa_cbc_des3_op(req, &tmpl);
+}
+
+static int mv_cesa_cbc_des3_ede_decrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl,
+ CESA_SA_DESC_CFG_CRYPTCM_CBC |
+ CESA_SA_DESC_CFG_3DES_EDE |
+ CESA_SA_DESC_CFG_DIR_DEC);
+
+ return mv_cesa_cbc_des3_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_cbc_des3_ede_alg = {
+ .cra_name = "cbc(des3_ede)",
+ .cra_driver_name = "mv-cbc-des3-ede",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_des3_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = mv_cesa_ablkcipher_cra_init,
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ .setkey = mv_cesa_des3_ede_setkey,
+ .encrypt = mv_cesa_cbc_des3_ede_encrypt,
+ .decrypt = mv_cesa_cbc_des3_ede_decrypt,
+ },
+ },
+};
+
+static int mv_cesa_aes_op(struct ablkcipher_request *req,
+ struct mv_cesa_op_ctx *tmpl)
+{
+ struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ int ret, i;
+ u32 *key;
+ u32 cfg;
+
+ cfg = CESA_SA_DESC_CFG_CRYPTM_AES;
+
+ if (mv_cesa_get_op_cfg(tmpl) & CESA_SA_DESC_CFG_DIR_DEC)
+ key = ctx->aes.key_dec;
+ else
+ key = ctx->aes.key_enc;
+
+ for (i = 0; i < ctx->aes.key_length / sizeof(u32); i++)
+ tmpl->ctx.blkcipher.key[i] = cpu_to_le32(key[i]);
+
+ if (ctx->aes.key_length == 24)
+ cfg |= CESA_SA_DESC_CFG_AES_LEN_192;
+ else if (ctx->aes.key_length == 32)
+ cfg |= CESA_SA_DESC_CFG_AES_LEN_256;
+
+ mv_cesa_update_op_cfg(tmpl, cfg,
+ CESA_SA_DESC_CFG_CRYPTM_MSK |
+ CESA_SA_DESC_CFG_AES_LEN_MSK);
+
+ ret = mv_cesa_ablkcipher_req_init(req, tmpl);
+ if (ret)
+ return ret;
+
+ ret = mv_cesa_queue_req(&req->base);
+ if (ret && ret != -EINPROGRESS)
+ mv_cesa_ablkcipher_cleanup(req);
+
+ return ret;
+}
+
+static int mv_cesa_ecb_aes_encrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl,
+ CESA_SA_DESC_CFG_CRYPTCM_ECB |
+ CESA_SA_DESC_CFG_DIR_ENC);
+
+ return mv_cesa_aes_op(req, &tmpl);
+}
+
+static int mv_cesa_ecb_aes_decrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl,
+ CESA_SA_DESC_CFG_CRYPTCM_ECB |
+ CESA_SA_DESC_CFG_DIR_DEC);
+
+ return mv_cesa_aes_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_ecb_aes_alg = {
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "mv-ecb-aes",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_aes_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = mv_cesa_ablkcipher_cra_init,
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = mv_cesa_aes_setkey,
+ .encrypt = mv_cesa_ecb_aes_encrypt,
+ .decrypt = mv_cesa_ecb_aes_decrypt,
+ },
+ },
+};
+
+static int mv_cesa_cbc_aes_op(struct ablkcipher_request *req,
+ struct mv_cesa_op_ctx *tmpl)
+{
+ mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTCM_CBC,
+ CESA_SA_DESC_CFG_CRYPTCM_MSK);
+ memcpy(tmpl->ctx.blkcipher.iv, req->info, AES_BLOCK_SIZE);
+
+ return mv_cesa_aes_op(req, tmpl);
+}
+
+static int mv_cesa_cbc_aes_encrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_ENC);
+
+ return mv_cesa_cbc_aes_op(req, &tmpl);
+}
+
+static int mv_cesa_cbc_aes_decrypt(struct ablkcipher_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_DEC);
+
+ return mv_cesa_cbc_aes_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_cbc_aes_alg = {
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "mv-cbc-aes",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_aes_ctx),
+ .cra_alignmask = 0,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = mv_cesa_ablkcipher_cra_init,
+ .cra_u = {
+ .ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ .setkey = mv_cesa_aes_setkey,
+ .encrypt = mv_cesa_cbc_aes_encrypt,
+ .decrypt = mv_cesa_cbc_aes_decrypt,
+ },
+ },
+};
diff --git a/drivers/crypto/marvell/hash.c b/drivers/crypto/marvell/hash.c
new file mode 100644
index 000000000000..ae9272eb9c1a
--- /dev/null
+++ b/drivers/crypto/marvell/hash.c
@@ -0,0 +1,1441 @@
+/*
+ * Hash algorithms supported by the CESA: MD5, SHA1 and SHA256.
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Author: Arnaud Ebalard <arno@natisbad.org>
+ *
+ * This work is based on an initial version written by
+ * Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
+ *
+ * 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 <crypto/md5.h>
+#include <crypto/sha.h>
+
+#include "cesa.h"
+
+struct mv_cesa_ahash_dma_iter {
+ struct mv_cesa_dma_iter base;
+ struct mv_cesa_sg_dma_iter src;
+};
+
+static inline void
+mv_cesa_ahash_req_iter_init(struct mv_cesa_ahash_dma_iter *iter,
+ struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ unsigned int len = req->nbytes;
+
+ if (!creq->last_req)
+ len = (len + creq->cache_ptr) & ~CESA_HASH_BLOCK_SIZE_MSK;
+
+ mv_cesa_req_dma_iter_init(&iter->base, len);
+ mv_cesa_sg_dma_iter_init(&iter->src, req->src, DMA_TO_DEVICE);
+ iter->src.op_offset = creq->cache_ptr;
+}
+
+static inline bool
+mv_cesa_ahash_req_iter_next_op(struct mv_cesa_ahash_dma_iter *iter)
+{
+ iter->src.op_offset = 0;
+
+ return mv_cesa_req_dma_iter_next_op(&iter->base);
+}
+
+static inline int mv_cesa_ahash_dma_alloc_cache(struct mv_cesa_ahash_req *creq,
+ gfp_t flags)
+{
+ struct mv_cesa_ahash_dma_req *dreq = &creq->req.dma;
+
+ creq->cache = dma_pool_alloc(cesa_dev->dma->cache_pool, flags,
+ &dreq->cache_dma);
+ if (!creq->cache)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static inline int mv_cesa_ahash_std_alloc_cache(struct mv_cesa_ahash_req *creq,
+ gfp_t flags)
+{
+ creq->cache = kzalloc(CESA_MAX_HASH_BLOCK_SIZE, flags);
+ if (!creq->cache)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int mv_cesa_ahash_alloc_cache(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC;
+ int ret;
+
+ if (creq->cache)
+ return 0;
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ ret = mv_cesa_ahash_dma_alloc_cache(creq, flags);
+ else
+ ret = mv_cesa_ahash_std_alloc_cache(creq, flags);
+
+ return ret;
+}
+
+static inline void mv_cesa_ahash_dma_free_cache(struct mv_cesa_ahash_req *creq)
+{
+ dma_pool_free(cesa_dev->dma->cache_pool, creq->cache,
+ creq->req.dma.cache_dma);
+}
+
+static inline void mv_cesa_ahash_std_free_cache(struct mv_cesa_ahash_req *creq)
+{
+ kfree(creq->cache);
+}
+
+static void mv_cesa_ahash_free_cache(struct mv_cesa_ahash_req *creq)
+{
+ if (!creq->cache)
+ return;
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ mv_cesa_ahash_dma_free_cache(creq);
+ else
+ mv_cesa_ahash_std_free_cache(creq);
+
+ creq->cache = NULL;
+}
+
+static int mv_cesa_ahash_dma_alloc_padding(struct mv_cesa_ahash_dma_req *req,
+ gfp_t flags)
+{
+ if (req->padding)
+ return 0;
+
+ req->padding = dma_pool_alloc(cesa_dev->dma->padding_pool, flags,
+ &req->padding_dma);
+ if (!req->padding)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void mv_cesa_ahash_dma_free_padding(struct mv_cesa_ahash_dma_req *req)
+{
+ if (!req->padding)
+ return;
+
+ dma_pool_free(cesa_dev->dma->padding_pool, req->padding,
+ req->padding_dma);
+ req->padding = NULL;
+}
+
+static inline void mv_cesa_ahash_dma_last_cleanup(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+ mv_cesa_ahash_dma_free_padding(&creq->req.dma);
+}
+
+static inline void mv_cesa_ahash_dma_cleanup(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+ dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents, DMA_TO_DEVICE);
+ mv_cesa_dma_cleanup(&creq->req.dma.base);
+}
+
+static inline void mv_cesa_ahash_cleanup(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ mv_cesa_ahash_dma_cleanup(req);
+}
+
+static void mv_cesa_ahash_last_cleanup(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+ mv_cesa_ahash_free_cache(creq);
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ mv_cesa_ahash_dma_last_cleanup(req);
+}
+
+static int mv_cesa_ahash_pad_len(struct mv_cesa_ahash_req *creq)
+{
+ unsigned int index, padlen;
+
+ index = creq->len & CESA_HASH_BLOCK_SIZE_MSK;
+ padlen = (index < 56) ? (56 - index) : (64 + 56 - index);
+
+ return padlen;
+}
+
+static int mv_cesa_ahash_pad_req(struct mv_cesa_ahash_req *creq, u8 *buf)
+{
+ __be64 bits = cpu_to_be64(creq->len << 3);
+ unsigned int index, padlen;
+
+ buf[0] = 0x80;
+ /* Pad out to 56 mod 64 */
+ index = creq->len & CESA_HASH_BLOCK_SIZE_MSK;
+ padlen = mv_cesa_ahash_pad_len(creq);
+ memset(buf + 1, 0, padlen - 1);
+ memcpy(buf + padlen, &bits, sizeof(bits));
+
+ return padlen + 8;
+}
+
+static void mv_cesa_ahash_std_step(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ struct mv_cesa_ahash_std_req *sreq = &creq->req.std;
+ struct mv_cesa_engine *engine = sreq->base.engine;
+ struct mv_cesa_op_ctx *op;
+ unsigned int new_cache_ptr = 0;
+ u32 frag_mode;
+ size_t len;
+
+ if (creq->cache_ptr)
+ memcpy(engine->sram + CESA_SA_DATA_SRAM_OFFSET, creq->cache,
+ creq->cache_ptr);
+
+ len = min_t(size_t, req->nbytes + creq->cache_ptr - sreq->offset,
+ CESA_SA_SRAM_PAYLOAD_SIZE);
+
+ if (!creq->last_req) {
+ new_cache_ptr = len & CESA_HASH_BLOCK_SIZE_MSK;
+ len &= ~CESA_HASH_BLOCK_SIZE_MSK;
+ }
+
+ if (len - creq->cache_ptr)
+ sreq->offset += sg_pcopy_to_buffer(req->src, creq->src_nents,
+ engine->sram +
+ CESA_SA_DATA_SRAM_OFFSET +
+ creq->cache_ptr,
+ len - creq->cache_ptr,
+ sreq->offset);
+
+ op = &creq->op_tmpl;
+
+ frag_mode = mv_cesa_get_op_cfg(op) & CESA_SA_DESC_CFG_FRAG_MSK;
+
+ if (creq->last_req && sreq->offset == req->nbytes &&
+ creq->len <= CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX) {
+ if (frag_mode == CESA_SA_DESC_CFG_FIRST_FRAG)
+ frag_mode = CESA_SA_DESC_CFG_NOT_FRAG;
+ else if (frag_mode == CESA_SA_DESC_CFG_MID_FRAG)
+ frag_mode = CESA_SA_DESC_CFG_LAST_FRAG;
+ }
+
+ if (frag_mode == CESA_SA_DESC_CFG_NOT_FRAG ||
+ frag_mode == CESA_SA_DESC_CFG_LAST_FRAG) {
+ if (len &&
+ creq->len <= CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX) {
+ mv_cesa_set_mac_op_total_len(op, creq->len);
+ } else {
+ int trailerlen = mv_cesa_ahash_pad_len(creq) + 8;
+
+ if (len + trailerlen > CESA_SA_SRAM_PAYLOAD_SIZE) {
+ len &= CESA_HASH_BLOCK_SIZE_MSK;
+ new_cache_ptr = 64 - trailerlen;
+ memcpy(creq->cache,
+ engine->sram +
+ CESA_SA_DATA_SRAM_OFFSET + len,
+ new_cache_ptr);
+ } else {
+ len += mv_cesa_ahash_pad_req(creq,
+ engine->sram + len +
+ CESA_SA_DATA_SRAM_OFFSET);
+ }
+
+ if (frag_mode == CESA_SA_DESC_CFG_LAST_FRAG)
+ frag_mode = CESA_SA_DESC_CFG_MID_FRAG;
+ else
+ frag_mode = CESA_SA_DESC_CFG_FIRST_FRAG;
+ }
+ }
+
+ mv_cesa_set_mac_op_frag_len(op, len);
+ mv_cesa_update_op_cfg(op, frag_mode, CESA_SA_DESC_CFG_FRAG_MSK);
+
+ /* FIXME: only update enc_len field */
+ memcpy(engine->sram, op, sizeof(*op));
+
+ if (frag_mode == CESA_SA_DESC_CFG_FIRST_FRAG)
+ mv_cesa_update_op_cfg(op, CESA_SA_DESC_CFG_MID_FRAG,
+ CESA_SA_DESC_CFG_FRAG_MSK);
+
+ creq->cache_ptr = new_cache_ptr;
+
+ mv_cesa_set_int_mask(engine, CESA_SA_INT_ACCEL0_DONE);
+ writel(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
+ writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
+}
+
+static int mv_cesa_ahash_std_process(struct ahash_request *req, u32 status)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ struct mv_cesa_ahash_std_req *sreq = &creq->req.std;
+
+ if (sreq->offset < (req->nbytes - creq->cache_ptr))
+ return -EINPROGRESS;
+
+ return 0;
+}
+
+static inline void mv_cesa_ahash_dma_prepare(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ struct mv_cesa_tdma_req *dreq = &creq->req.dma.base;
+
+ mv_cesa_dma_prepare(dreq, dreq->base.engine);
+}
+
+static void mv_cesa_ahash_std_prepare(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ struct mv_cesa_ahash_std_req *sreq = &creq->req.std;
+ struct mv_cesa_engine *engine = sreq->base.engine;
+
+ sreq->offset = 0;
+ mv_cesa_adjust_op(engine, &creq->op_tmpl);
+ memcpy(engine->sram, &creq->op_tmpl, sizeof(creq->op_tmpl));
+}
+
+static void mv_cesa_ahash_step(struct crypto_async_request *req)
+{
+ struct ahash_request *ahashreq = ahash_request_cast(req);
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(ahashreq);
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ mv_cesa_dma_step(&creq->req.dma.base);
+ else
+ mv_cesa_ahash_std_step(ahashreq);
+}
+
+static int mv_cesa_ahash_process(struct crypto_async_request *req, u32 status)
+{
+ struct ahash_request *ahashreq = ahash_request_cast(req);
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(ahashreq);
+ struct mv_cesa_engine *engine = creq->req.base.engine;
+ unsigned int digsize;
+ int ret, i;
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ ret = mv_cesa_dma_process(&creq->req.dma.base, status);
+ else
+ ret = mv_cesa_ahash_std_process(ahashreq, status);
+
+ if (ret == -EINPROGRESS)
+ return ret;
+
+ digsize = crypto_ahash_digestsize(crypto_ahash_reqtfm(ahashreq));
+ for (i = 0; i < digsize / 4; i++)
+ creq->state[i] = readl(engine->regs + CESA_IVDIG(i));
+
+ if (creq->cache_ptr)
+ sg_pcopy_to_buffer(ahashreq->src, creq->src_nents,
+ creq->cache,
+ creq->cache_ptr,
+ ahashreq->nbytes - creq->cache_ptr);
+
+ if (creq->last_req) {
+ for (i = 0; i < digsize / 4; i++) {
+ /*
+ * Hardware provides MD5 digest in a different
+ * endianness than SHA-1 and SHA-256 ones.
+ */
+ if (digsize == MD5_DIGEST_SIZE)
+ creq->state[i] = cpu_to_le32(creq->state[i]);
+ else
+ creq->state[i] = cpu_to_be32(creq->state[i]);
+ }
+
+ memcpy(ahashreq->result, creq->state, digsize);
+ }
+
+ return ret;
+}
+
+static void mv_cesa_ahash_prepare(struct crypto_async_request *req,
+ struct mv_cesa_engine *engine)
+{
+ struct ahash_request *ahashreq = ahash_request_cast(req);
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(ahashreq);
+ unsigned int digsize;
+ int i;
+
+ creq->req.base.engine = engine;
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ mv_cesa_ahash_dma_prepare(ahashreq);
+ else
+ mv_cesa_ahash_std_prepare(ahashreq);
+
+ digsize = crypto_ahash_digestsize(crypto_ahash_reqtfm(ahashreq));
+ for (i = 0; i < digsize / 4; i++)
+ writel(creq->state[i],
+ engine->regs + CESA_IVDIG(i));
+}
+
+static void mv_cesa_ahash_req_cleanup(struct crypto_async_request *req)
+{
+ struct ahash_request *ahashreq = ahash_request_cast(req);
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(ahashreq);
+
+ if (creq->last_req)
+ mv_cesa_ahash_last_cleanup(ahashreq);
+
+ mv_cesa_ahash_cleanup(ahashreq);
+}
+
+static const struct mv_cesa_req_ops mv_cesa_ahash_req_ops = {
+ .step = mv_cesa_ahash_step,
+ .process = mv_cesa_ahash_process,
+ .prepare = mv_cesa_ahash_prepare,
+ .cleanup = mv_cesa_ahash_req_cleanup,
+};
+
+static int mv_cesa_ahash_init(struct ahash_request *req,
+ struct mv_cesa_op_ctx *tmpl)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+ memset(creq, 0, sizeof(*creq));
+ mv_cesa_update_op_cfg(tmpl,
+ CESA_SA_DESC_CFG_OP_MAC_ONLY |
+ CESA_SA_DESC_CFG_FIRST_FRAG,
+ CESA_SA_DESC_CFG_OP_MSK |
+ CESA_SA_DESC_CFG_FRAG_MSK);
+ mv_cesa_set_mac_op_total_len(tmpl, 0);
+ mv_cesa_set_mac_op_frag_len(tmpl, 0);
+ creq->op_tmpl = *tmpl;
+ creq->len = 0;
+
+ return 0;
+}
+
+static inline int mv_cesa_ahash_cra_init(struct crypto_tfm *tfm)
+{
+ struct mv_cesa_hash_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->base.ops = &mv_cesa_ahash_req_ops;
+
+ crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+ sizeof(struct mv_cesa_ahash_req));
+ return 0;
+}
+
+static int mv_cesa_ahash_cache_req(struct ahash_request *req, bool *cached)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ int ret;
+
+ if (((creq->cache_ptr + req->nbytes) & CESA_HASH_BLOCK_SIZE_MSK) &&
+ !creq->last_req) {
+ ret = mv_cesa_ahash_alloc_cache(req);
+ if (ret)
+ return ret;
+ }
+
+ if (creq->cache_ptr + req->nbytes < 64 && !creq->last_req) {
+ *cached = true;
+
+ if (!req->nbytes)
+ return 0;
+
+ sg_pcopy_to_buffer(req->src, creq->src_nents,
+ creq->cache + creq->cache_ptr,
+ req->nbytes, 0);
+
+ creq->cache_ptr += req->nbytes;
+ }
+
+ return 0;
+}
+
+static struct mv_cesa_op_ctx *
+mv_cesa_ahash_dma_add_cache(struct mv_cesa_tdma_chain *chain,
+ struct mv_cesa_ahash_dma_iter *dma_iter,
+ struct mv_cesa_ahash_req *creq,
+ gfp_t flags)
+{
+ struct mv_cesa_ahash_dma_req *ahashdreq = &creq->req.dma;
+ struct mv_cesa_op_ctx *op = NULL;
+ int ret;
+
+ if (!creq->cache_ptr)
+ return NULL;
+
+ ret = mv_cesa_dma_add_data_transfer(chain,
+ CESA_SA_DATA_SRAM_OFFSET,
+ ahashdreq->cache_dma,
+ creq->cache_ptr,
+ CESA_TDMA_DST_IN_SRAM,
+ flags);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (!dma_iter->base.op_len) {
+ op = mv_cesa_dma_add_op(chain, &creq->op_tmpl, false, flags);
+ if (IS_ERR(op))
+ return op;
+
+ mv_cesa_set_mac_op_frag_len(op, creq->cache_ptr);
+
+ /* Add dummy desc to launch crypto operation */
+ ret = mv_cesa_dma_add_dummy_launch(chain, flags);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ return op;
+}
+
+static struct mv_cesa_op_ctx *
+mv_cesa_ahash_dma_add_data(struct mv_cesa_tdma_chain *chain,
+ struct mv_cesa_ahash_dma_iter *dma_iter,
+ struct mv_cesa_ahash_req *creq,
+ gfp_t flags)
+{
+ struct mv_cesa_op_ctx *op;
+ int ret;
+
+ op = mv_cesa_dma_add_op(chain, &creq->op_tmpl, false, flags);
+ if (IS_ERR(op))
+ return op;
+
+ mv_cesa_set_mac_op_frag_len(op, dma_iter->base.op_len);
+
+ if ((mv_cesa_get_op_cfg(&creq->op_tmpl) & CESA_SA_DESC_CFG_FRAG_MSK) ==
+ CESA_SA_DESC_CFG_FIRST_FRAG)
+ mv_cesa_update_op_cfg(&creq->op_tmpl,
+ CESA_SA_DESC_CFG_MID_FRAG,
+ CESA_SA_DESC_CFG_FRAG_MSK);
+
+ /* Add input transfers */
+ ret = mv_cesa_dma_add_op_transfers(chain, &dma_iter->base,
+ &dma_iter->src, flags);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* Add dummy desc to launch crypto operation */
+ ret = mv_cesa_dma_add_dummy_launch(chain, flags);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return op;
+}
+
+static struct mv_cesa_op_ctx *
+mv_cesa_ahash_dma_last_req(struct mv_cesa_tdma_chain *chain,
+ struct mv_cesa_ahash_dma_iter *dma_iter,
+ struct mv_cesa_ahash_req *creq,
+ struct mv_cesa_op_ctx *op,
+ gfp_t flags)
+{
+ struct mv_cesa_ahash_dma_req *ahashdreq = &creq->req.dma;
+ unsigned int len, trailerlen, padoff = 0;
+ int ret;
+
+ if (!creq->last_req)
+ return op;
+
+ if (op && creq->len <= CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX) {
+ u32 frag = CESA_SA_DESC_CFG_NOT_FRAG;
+
+ if ((mv_cesa_get_op_cfg(op) & CESA_SA_DESC_CFG_FRAG_MSK) !=
+ CESA_SA_DESC_CFG_FIRST_FRAG)
+ frag = CESA_SA_DESC_CFG_LAST_FRAG;
+
+ mv_cesa_update_op_cfg(op, frag, CESA_SA_DESC_CFG_FRAG_MSK);
+
+ return op;
+ }
+
+ ret = mv_cesa_ahash_dma_alloc_padding(ahashdreq, flags);
+ if (ret)
+ return ERR_PTR(ret);
+
+ trailerlen = mv_cesa_ahash_pad_req(creq, ahashdreq->padding);
+
+ if (op) {
+ len = min(CESA_SA_SRAM_PAYLOAD_SIZE - dma_iter->base.op_len,
+ trailerlen);
+ if (len) {
+ ret = mv_cesa_dma_add_data_transfer(chain,
+ CESA_SA_DATA_SRAM_OFFSET +
+ dma_iter->base.op_len,
+ ahashdreq->padding_dma,
+ len, CESA_TDMA_DST_IN_SRAM,
+ flags);
+ if (ret)
+ return ERR_PTR(ret);
+
+ mv_cesa_update_op_cfg(op, CESA_SA_DESC_CFG_MID_FRAG,
+ CESA_SA_DESC_CFG_FRAG_MSK);
+ mv_cesa_set_mac_op_frag_len(op,
+ dma_iter->base.op_len + len);
+ padoff += len;
+ }
+ }
+
+ if (padoff >= trailerlen)
+ return op;
+
+ if ((mv_cesa_get_op_cfg(&creq->op_tmpl) & CESA_SA_DESC_CFG_FRAG_MSK) !=
+ CESA_SA_DESC_CFG_FIRST_FRAG)
+ mv_cesa_update_op_cfg(&creq->op_tmpl,
+ CESA_SA_DESC_CFG_MID_FRAG,
+ CESA_SA_DESC_CFG_FRAG_MSK);
+
+ op = mv_cesa_dma_add_op(chain, &creq->op_tmpl, false, flags);
+ if (IS_ERR(op))
+ return op;
+
+ mv_cesa_set_mac_op_frag_len(op, trailerlen - padoff);
+
+ ret = mv_cesa_dma_add_data_transfer(chain,
+ CESA_SA_DATA_SRAM_OFFSET,
+ ahashdreq->padding_dma +
+ padoff,
+ trailerlen - padoff,
+ CESA_TDMA_DST_IN_SRAM,
+ flags);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* Add dummy desc to launch crypto operation */
+ ret = mv_cesa_dma_add_dummy_launch(chain, flags);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return op;
+}
+
+static int mv_cesa_ahash_dma_req_init(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC;
+ struct mv_cesa_ahash_dma_req *ahashdreq = &creq->req.dma;
+ struct mv_cesa_tdma_req *dreq = &ahashdreq->base;
+ struct mv_cesa_tdma_chain chain;
+ struct mv_cesa_ahash_dma_iter iter;
+ struct mv_cesa_op_ctx *op = NULL;
+ int ret;
+
+ dreq->chain.first = NULL;
+ dreq->chain.last = NULL;
+
+ if (creq->src_nents) {
+ ret = dma_map_sg(cesa_dev->dev, req->src, creq->src_nents,
+ DMA_TO_DEVICE);
+ if (!ret) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+
+ mv_cesa_tdma_desc_iter_init(&chain);
+ mv_cesa_ahash_req_iter_init(&iter, req);
+
+ op = mv_cesa_ahash_dma_add_cache(&chain, &iter,
+ creq, flags);
+ if (IS_ERR(op)) {
+ ret = PTR_ERR(op);
+ goto err_free_tdma;
+ }
+
+ do {
+ if (!iter.base.op_len)
+ break;
+
+ op = mv_cesa_ahash_dma_add_data(&chain, &iter,
+ creq, flags);
+ if (IS_ERR(op)) {
+ ret = PTR_ERR(op);
+ goto err_free_tdma;
+ }
+ } while (mv_cesa_ahash_req_iter_next_op(&iter));
+
+ op = mv_cesa_ahash_dma_last_req(&chain, &iter, creq, op, flags);
+ if (IS_ERR(op)) {
+ ret = PTR_ERR(op);
+ goto err_free_tdma;
+ }
+
+ if (op) {
+ /* Add dummy desc to wait for crypto operation end */
+ ret = mv_cesa_dma_add_dummy_end(&chain, flags);
+ if (ret)
+ goto err_free_tdma;
+ }
+
+ if (!creq->last_req)
+ creq->cache_ptr = req->nbytes + creq->cache_ptr -
+ iter.base.len;
+ else
+ creq->cache_ptr = 0;
+
+ dreq->chain = chain;
+
+ return 0;
+
+err_free_tdma:
+ mv_cesa_dma_cleanup(dreq);
+ dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents, DMA_TO_DEVICE);
+
+err:
+ mv_cesa_ahash_last_cleanup(req);
+
+ return ret;
+}
+
+static int mv_cesa_ahash_req_init(struct ahash_request *req, bool *cached)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ int ret;
+
+ if (cesa_dev->caps->has_tdma)
+ creq->req.base.type = CESA_DMA_REQ;
+ else
+ creq->req.base.type = CESA_STD_REQ;
+
+ creq->src_nents = sg_nents_for_len(req->src, req->nbytes);
+
+ ret = mv_cesa_ahash_cache_req(req, cached);
+ if (ret)
+ return ret;
+
+ if (*cached)
+ return 0;
+
+ if (creq->req.base.type == CESA_DMA_REQ)
+ ret = mv_cesa_ahash_dma_req_init(req);
+
+ return ret;
+}
+
+static int mv_cesa_ahash_update(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ bool cached = false;
+ int ret;
+
+ creq->len += req->nbytes;
+ ret = mv_cesa_ahash_req_init(req, &cached);
+ if (ret)
+ return ret;
+
+ if (cached)
+ return 0;
+
+ ret = mv_cesa_queue_req(&req->base);
+ if (ret && ret != -EINPROGRESS) {
+ mv_cesa_ahash_cleanup(req);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mv_cesa_ahash_final(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ struct mv_cesa_op_ctx *tmpl = &creq->op_tmpl;
+ bool cached = false;
+ int ret;
+
+ mv_cesa_set_mac_op_total_len(tmpl, creq->len);
+ creq->last_req = true;
+ req->nbytes = 0;
+
+ ret = mv_cesa_ahash_req_init(req, &cached);
+ if (ret)
+ return ret;
+
+ if (cached)
+ return 0;
+
+ ret = mv_cesa_queue_req(&req->base);
+ if (ret && ret != -EINPROGRESS)
+ mv_cesa_ahash_cleanup(req);
+
+ return ret;
+}
+
+static int mv_cesa_ahash_finup(struct ahash_request *req)
+{
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ struct mv_cesa_op_ctx *tmpl = &creq->op_tmpl;
+ bool cached = false;
+ int ret;
+
+ creq->len += req->nbytes;
+ mv_cesa_set_mac_op_total_len(tmpl, creq->len);
+ creq->last_req = true;
+
+ ret = mv_cesa_ahash_req_init(req, &cached);
+ if (ret)
+ return ret;
+
+ if (cached)
+ return 0;
+
+ ret = mv_cesa_queue_req(&req->base);
+ if (ret && ret != -EINPROGRESS)
+ mv_cesa_ahash_cleanup(req);
+
+ return ret;
+}
+
+static int mv_cesa_md5_init(struct ahash_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_MD5);
+
+ mv_cesa_ahash_init(req, &tmpl);
+
+ return 0;
+}
+
+static int mv_cesa_md5_export(struct ahash_request *req, void *out)
+{
+ struct md5_state *out_state = out;
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ unsigned int digsize = crypto_ahash_digestsize(ahash);
+
+ out_state->byte_count = creq->len;
+ memcpy(out_state->hash, creq->state, digsize);
+ memset(out_state->block, 0, sizeof(out_state->block));
+ if (creq->cache)
+ memcpy(out_state->block, creq->cache, creq->cache_ptr);
+
+ return 0;
+}
+
+static int mv_cesa_md5_import(struct ahash_request *req, const void *in)
+{
+ const struct md5_state *in_state = in;
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ unsigned int digsize = crypto_ahash_digestsize(ahash);
+ unsigned int cache_ptr;
+ int ret;
+
+ creq->len = in_state->byte_count;
+ memcpy(creq->state, in_state->hash, digsize);
+ creq->cache_ptr = 0;
+
+ cache_ptr = creq->len % sizeof(in_state->block);
+ if (!cache_ptr)
+ return 0;
+
+ ret = mv_cesa_ahash_alloc_cache(req);
+ if (ret)
+ return ret;
+
+ memcpy(creq->cache, in_state->block, cache_ptr);
+ creq->cache_ptr = cache_ptr;
+
+ return 0;
+}
+
+static int mv_cesa_md5_digest(struct ahash_request *req)
+{
+ int ret;
+
+ ret = mv_cesa_md5_init(req);
+ if (ret)
+ return ret;
+
+ return mv_cesa_ahash_finup(req);
+}
+
+struct ahash_alg mv_md5_alg = {
+ .init = mv_cesa_md5_init,
+ .update = mv_cesa_ahash_update,
+ .final = mv_cesa_ahash_final,
+ .finup = mv_cesa_ahash_finup,
+ .digest = mv_cesa_md5_digest,
+ .export = mv_cesa_md5_export,
+ .import = mv_cesa_md5_import,
+ .halg = {
+ .digestsize = MD5_DIGEST_SIZE,
+ .base = {
+ .cra_name = "md5",
+ .cra_driver_name = "mv-md5",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_KERN_DRIVER_ONLY,
+ .cra_blocksize = MD5_HMAC_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_hash_ctx),
+ .cra_init = mv_cesa_ahash_cra_init,
+ .cra_module = THIS_MODULE,
+ }
+ }
+};
+
+static int mv_cesa_sha1_init(struct ahash_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_SHA1);
+
+ mv_cesa_ahash_init(req, &tmpl);
+
+ return 0;
+}
+
+static int mv_cesa_sha1_export(struct ahash_request *req, void *out)
+{
+ struct sha1_state *out_state = out;
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ unsigned int digsize = crypto_ahash_digestsize(ahash);
+
+ out_state->count = creq->len;
+ memcpy(out_state->state, creq->state, digsize);
+ memset(out_state->buffer, 0, sizeof(out_state->buffer));
+ if (creq->cache)
+ memcpy(out_state->buffer, creq->cache, creq->cache_ptr);
+
+ return 0;
+}
+
+static int mv_cesa_sha1_import(struct ahash_request *req, const void *in)
+{
+ const struct sha1_state *in_state = in;
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ unsigned int digsize = crypto_ahash_digestsize(ahash);
+ unsigned int cache_ptr;
+ int ret;
+
+ creq->len = in_state->count;
+ memcpy(creq->state, in_state->state, digsize);
+ creq->cache_ptr = 0;
+
+ cache_ptr = creq->len % SHA1_BLOCK_SIZE;
+ if (!cache_ptr)
+ return 0;
+
+ ret = mv_cesa_ahash_alloc_cache(req);
+ if (ret)
+ return ret;
+
+ memcpy(creq->cache, in_state->buffer, cache_ptr);
+ creq->cache_ptr = cache_ptr;
+
+ return 0;
+}
+
+static int mv_cesa_sha1_digest(struct ahash_request *req)
+{
+ int ret;
+
+ ret = mv_cesa_sha1_init(req);
+ if (ret)
+ return ret;
+
+ return mv_cesa_ahash_finup(req);
+}
+
+struct ahash_alg mv_sha1_alg = {
+ .init = mv_cesa_sha1_init,
+ .update = mv_cesa_ahash_update,
+ .final = mv_cesa_ahash_final,
+ .finup = mv_cesa_ahash_finup,
+ .digest = mv_cesa_sha1_digest,
+ .export = mv_cesa_sha1_export,
+ .import = mv_cesa_sha1_import,
+ .halg = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .base = {
+ .cra_name = "sha1",
+ .cra_driver_name = "mv-sha1",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_KERN_DRIVER_ONLY,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_hash_ctx),
+ .cra_init = mv_cesa_ahash_cra_init,
+ .cra_module = THIS_MODULE,
+ }
+ }
+};
+
+static int mv_cesa_sha256_init(struct ahash_request *req)
+{
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_SHA256);
+
+ mv_cesa_ahash_init(req, &tmpl);
+
+ return 0;
+}
+
+static int mv_cesa_sha256_digest(struct ahash_request *req)
+{
+ int ret;
+
+ ret = mv_cesa_sha256_init(req);
+ if (ret)
+ return ret;
+
+ return mv_cesa_ahash_finup(req);
+}
+
+static int mv_cesa_sha256_export(struct ahash_request *req, void *out)
+{
+ struct sha256_state *out_state = out;
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ unsigned int ds = crypto_ahash_digestsize(ahash);
+
+ out_state->count = creq->len;
+ memcpy(out_state->state, creq->state, ds);
+ memset(out_state->buf, 0, sizeof(out_state->buf));
+ if (creq->cache)
+ memcpy(out_state->buf, creq->cache, creq->cache_ptr);
+
+ return 0;
+}
+
+static int mv_cesa_sha256_import(struct ahash_request *req, const void *in)
+{
+ const struct sha256_state *in_state = in;
+ struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+ struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+ unsigned int digsize = crypto_ahash_digestsize(ahash);
+ unsigned int cache_ptr;
+ int ret;
+
+ creq->len = in_state->count;
+ memcpy(creq->state, in_state->state, digsize);
+ creq->cache_ptr = 0;
+
+ cache_ptr = creq->len % SHA256_BLOCK_SIZE;
+ if (!cache_ptr)
+ return 0;
+
+ ret = mv_cesa_ahash_alloc_cache(req);
+ if (ret)
+ return ret;
+
+ memcpy(creq->cache, in_state->buf, cache_ptr);
+ creq->cache_ptr = cache_ptr;
+
+ return 0;
+}
+
+struct ahash_alg mv_sha256_alg = {
+ .init = mv_cesa_sha256_init,
+ .update = mv_cesa_ahash_update,
+ .final = mv_cesa_ahash_final,
+ .finup = mv_cesa_ahash_finup,
+ .digest = mv_cesa_sha256_digest,
+ .export = mv_cesa_sha256_export,
+ .import = mv_cesa_sha256_import,
+ .halg = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .base = {
+ .cra_name = "sha256",
+ .cra_driver_name = "mv-sha256",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_KERN_DRIVER_ONLY,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_hash_ctx),
+ .cra_init = mv_cesa_ahash_cra_init,
+ .cra_module = THIS_MODULE,
+ }
+ }
+};
+
+struct mv_cesa_ahash_result {
+ struct completion completion;
+ int error;
+};
+
+static void mv_cesa_hmac_ahash_complete(struct crypto_async_request *req,
+ int error)
+{
+ struct mv_cesa_ahash_result *result = req->data;
+
+ if (error == -EINPROGRESS)
+ return;
+
+ result->error = error;
+ complete(&result->completion);
+}
+
+static int mv_cesa_ahmac_iv_state_init(struct ahash_request *req, u8 *pad,
+ void *state, unsigned int blocksize)
+{
+ struct mv_cesa_ahash_result result;
+ struct scatterlist sg;
+ int ret;
+
+ ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ mv_cesa_hmac_ahash_complete, &result);
+ sg_init_one(&sg, pad, blocksize);
+ ahash_request_set_crypt(req, &sg, pad, blocksize);
+ init_completion(&result.completion);
+
+ ret = crypto_ahash_init(req);
+ if (ret)
+ return ret;
+
+ ret = crypto_ahash_update(req);
+ if (ret && ret != -EINPROGRESS)
+ return ret;
+
+ wait_for_completion_interruptible(&result.completion);
+ if (result.error)
+ return result.error;
+
+ ret = crypto_ahash_export(req, state);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mv_cesa_ahmac_pad_init(struct ahash_request *req,
+ const u8 *key, unsigned int keylen,
+ u8 *ipad, u8 *opad,
+ unsigned int blocksize)
+{
+ struct mv_cesa_ahash_result result;
+ struct scatterlist sg;
+ int ret;
+ int i;
+
+ if (keylen <= blocksize) {
+ memcpy(ipad, key, keylen);
+ } else {
+ u8 *keydup = kmemdup(key, keylen, GFP_KERNEL);
+
+ if (!keydup)
+ return -ENOMEM;
+
+ ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ mv_cesa_hmac_ahash_complete,
+ &result);
+ sg_init_one(&sg, keydup, keylen);
+ ahash_request_set_crypt(req, &sg, ipad, keylen);
+ init_completion(&result.completion);
+
+ ret = crypto_ahash_digest(req);
+ if (ret == -EINPROGRESS) {
+ wait_for_completion_interruptible(&result.completion);
+ ret = result.error;
+ }
+
+ /* Set the memory region to 0 to avoid any leak. */
+ memset(keydup, 0, keylen);
+ kfree(keydup);
+
+ if (ret)
+ return ret;
+
+ keylen = crypto_ahash_digestsize(crypto_ahash_reqtfm(req));
+ }
+
+ memset(ipad + keylen, 0, blocksize - keylen);
+ memcpy(opad, ipad, blocksize);
+
+ for (i = 0; i < blocksize; i++) {
+ ipad[i] ^= 0x36;
+ opad[i] ^= 0x5c;
+ }
+
+ return 0;
+}
+
+static int mv_cesa_ahmac_setkey(const char *hash_alg_name,
+ const u8 *key, unsigned int keylen,
+ void *istate, void *ostate)
+{
+ struct ahash_request *req;
+ struct crypto_ahash *tfm;
+ unsigned int blocksize;
+ u8 *ipad = NULL;
+ u8 *opad;
+ int ret;
+
+ tfm = crypto_alloc_ahash(hash_alg_name, CRYPTO_ALG_TYPE_AHASH,
+ CRYPTO_ALG_TYPE_AHASH_MASK);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ req = ahash_request_alloc(tfm, GFP_KERNEL);
+ if (!req) {
+ ret = -ENOMEM;
+ goto free_ahash;
+ }
+
+ crypto_ahash_clear_flags(tfm, ~0);
+
+ blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
+
+ ipad = kzalloc(2 * blocksize, GFP_KERNEL);
+ if (!ipad) {
+ ret = -ENOMEM;
+ goto free_req;
+ }
+
+ opad = ipad + blocksize;
+
+ ret = mv_cesa_ahmac_pad_init(req, key, keylen, ipad, opad, blocksize);
+ if (ret)
+ goto free_ipad;
+
+ ret = mv_cesa_ahmac_iv_state_init(req, ipad, istate, blocksize);
+ if (ret)
+ goto free_ipad;
+
+ ret = mv_cesa_ahmac_iv_state_init(req, opad, ostate, blocksize);
+
+free_ipad:
+ kfree(ipad);
+free_req:
+ ahash_request_free(req);
+free_ahash:
+ crypto_free_ahash(tfm);
+
+ return ret;
+}
+
+static int mv_cesa_ahmac_cra_init(struct crypto_tfm *tfm)
+{
+ struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->base.ops = &mv_cesa_ahash_req_ops;
+
+ crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+ sizeof(struct mv_cesa_ahash_req));
+ return 0;
+}
+
+static int mv_cesa_ahmac_md5_init(struct ahash_request *req)
+{
+ struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_HMAC_MD5);
+ memcpy(tmpl.ctx.hash.iv, ctx->iv, sizeof(ctx->iv));
+
+ mv_cesa_ahash_init(req, &tmpl);
+
+ return 0;
+}
+
+static int mv_cesa_ahmac_md5_setkey(struct crypto_ahash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
+ struct md5_state istate, ostate;
+ int ret, i;
+
+ ret = mv_cesa_ahmac_setkey("mv-md5", key, keylen, &istate, &ostate);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(istate.hash); i++)
+ ctx->iv[i] = be32_to_cpu(istate.hash[i]);
+
+ for (i = 0; i < ARRAY_SIZE(ostate.hash); i++)
+ ctx->iv[i + 8] = be32_to_cpu(ostate.hash[i]);
+
+ return 0;
+}
+
+static int mv_cesa_ahmac_md5_digest(struct ahash_request *req)
+{
+ int ret;
+
+ ret = mv_cesa_ahmac_md5_init(req);
+ if (ret)
+ return ret;
+
+ return mv_cesa_ahash_finup(req);
+}
+
+struct ahash_alg mv_ahmac_md5_alg = {
+ .init = mv_cesa_ahmac_md5_init,
+ .update = mv_cesa_ahash_update,
+ .final = mv_cesa_ahash_final,
+ .finup = mv_cesa_ahash_finup,
+ .digest = mv_cesa_ahmac_md5_digest,
+ .setkey = mv_cesa_ahmac_md5_setkey,
+ .export = mv_cesa_md5_export,
+ .import = mv_cesa_md5_import,
+ .halg = {
+ .digestsize = MD5_DIGEST_SIZE,
+ .statesize = sizeof(struct md5_state),
+ .base = {
+ .cra_name = "hmac(md5)",
+ .cra_driver_name = "mv-hmac-md5",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_KERN_DRIVER_ONLY,
+ .cra_blocksize = MD5_HMAC_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_hmac_ctx),
+ .cra_init = mv_cesa_ahmac_cra_init,
+ .cra_module = THIS_MODULE,
+ }
+ }
+};
+
+static int mv_cesa_ahmac_sha1_init(struct ahash_request *req)
+{
+ struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_HMAC_SHA1);
+ memcpy(tmpl.ctx.hash.iv, ctx->iv, sizeof(ctx->iv));
+
+ mv_cesa_ahash_init(req, &tmpl);
+
+ return 0;
+}
+
+static int mv_cesa_ahmac_sha1_setkey(struct crypto_ahash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
+ struct sha1_state istate, ostate;
+ int ret, i;
+
+ ret = mv_cesa_ahmac_setkey("mv-sha1", key, keylen, &istate, &ostate);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(istate.state); i++)
+ ctx->iv[i] = be32_to_cpu(istate.state[i]);
+
+ for (i = 0; i < ARRAY_SIZE(ostate.state); i++)
+ ctx->iv[i + 8] = be32_to_cpu(ostate.state[i]);
+
+ return 0;
+}
+
+static int mv_cesa_ahmac_sha1_digest(struct ahash_request *req)
+{
+ int ret;
+
+ ret = mv_cesa_ahmac_sha1_init(req);
+ if (ret)
+ return ret;
+
+ return mv_cesa_ahash_finup(req);
+}
+
+struct ahash_alg mv_ahmac_sha1_alg = {
+ .init = mv_cesa_ahmac_sha1_init,
+ .update = mv_cesa_ahash_update,
+ .final = mv_cesa_ahash_final,
+ .finup = mv_cesa_ahash_finup,
+ .digest = mv_cesa_ahmac_sha1_digest,
+ .setkey = mv_cesa_ahmac_sha1_setkey,
+ .export = mv_cesa_sha1_export,
+ .import = mv_cesa_sha1_import,
+ .halg = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .statesize = sizeof(struct sha1_state),
+ .base = {
+ .cra_name = "hmac(sha1)",
+ .cra_driver_name = "mv-hmac-sha1",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_KERN_DRIVER_ONLY,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_hmac_ctx),
+ .cra_init = mv_cesa_ahmac_cra_init,
+ .cra_module = THIS_MODULE,
+ }
+ }
+};
+
+static int mv_cesa_ahmac_sha256_setkey(struct crypto_ahash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
+ struct sha256_state istate, ostate;
+ int ret, i;
+
+ ret = mv_cesa_ahmac_setkey("mv-sha256", key, keylen, &istate, &ostate);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(istate.state); i++)
+ ctx->iv[i] = be32_to_cpu(istate.state[i]);
+
+ for (i = 0; i < ARRAY_SIZE(ostate.state); i++)
+ ctx->iv[i + 8] = be32_to_cpu(ostate.state[i]);
+
+ return 0;
+}
+
+static int mv_cesa_ahmac_sha256_init(struct ahash_request *req)
+{
+ struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct mv_cesa_op_ctx tmpl;
+
+ mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_HMAC_SHA256);
+ memcpy(tmpl.ctx.hash.iv, ctx->iv, sizeof(ctx->iv));
+
+ mv_cesa_ahash_init(req, &tmpl);
+
+ return 0;
+}
+
+static int mv_cesa_ahmac_sha256_digest(struct ahash_request *req)
+{
+ int ret;
+
+ ret = mv_cesa_ahmac_sha256_init(req);
+ if (ret)
+ return ret;
+
+ return mv_cesa_ahash_finup(req);
+}
+
+struct ahash_alg mv_ahmac_sha256_alg = {
+ .init = mv_cesa_ahmac_sha256_init,
+ .update = mv_cesa_ahash_update,
+ .final = mv_cesa_ahash_final,
+ .finup = mv_cesa_ahash_finup,
+ .digest = mv_cesa_ahmac_sha256_digest,
+ .setkey = mv_cesa_ahmac_sha256_setkey,
+ .export = mv_cesa_sha256_export,
+ .import = mv_cesa_sha256_import,
+ .halg = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .statesize = sizeof(struct sha256_state),
+ .base = {
+ .cra_name = "hmac(sha256)",
+ .cra_driver_name = "mv-hmac-sha256",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_KERN_DRIVER_ONLY,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mv_cesa_hmac_ctx),
+ .cra_init = mv_cesa_ahmac_cra_init,
+ .cra_module = THIS_MODULE,
+ }
+ }
+};
diff --git a/drivers/crypto/marvell/tdma.c b/drivers/crypto/marvell/tdma.c
new file mode 100644
index 000000000000..64a366c50174
--- /dev/null
+++ b/drivers/crypto/marvell/tdma.c
@@ -0,0 +1,224 @@
+/*
+ * Provide TDMA helper functions used by cipher and hash algorithm
+ * implementations.
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Author: Arnaud Ebalard <arno@natisbad.org>
+ *
+ * This work is based on an initial version written by
+ * Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
+ *
+ * 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 "cesa.h"
+
+bool mv_cesa_req_dma_iter_next_transfer(struct mv_cesa_dma_iter *iter,
+ struct mv_cesa_sg_dma_iter *sgiter,
+ unsigned int len)
+{
+ if (!sgiter->sg)
+ return false;
+
+ sgiter->op_offset += len;
+ sgiter->offset += len;
+ if (sgiter->offset == sg_dma_len(sgiter->sg)) {
+ if (sg_is_last(sgiter->sg))
+ return false;
+ sgiter->offset = 0;
+ sgiter->sg = sg_next(sgiter->sg);
+ }
+
+ if (sgiter->op_offset == iter->op_len)
+ return false;
+
+ return true;
+}
+
+void mv_cesa_dma_step(struct mv_cesa_tdma_req *dreq)
+{
+ struct mv_cesa_engine *engine = dreq->base.engine;
+
+ writel(0, engine->regs + CESA_SA_CFG);
+
+ mv_cesa_set_int_mask(engine, CESA_SA_INT_ACC0_IDMA_DONE);
+ writel(CESA_TDMA_DST_BURST_128B | CESA_TDMA_SRC_BURST_128B |
+ CESA_TDMA_NO_BYTE_SWAP | CESA_TDMA_EN,
+ engine->regs + CESA_TDMA_CONTROL);
+
+ writel(CESA_SA_CFG_ACT_CH0_IDMA | CESA_SA_CFG_MULTI_PKT |
+ CESA_SA_CFG_CH0_W_IDMA | CESA_SA_CFG_PARA_DIS,
+ engine->regs + CESA_SA_CFG);
+ writel(dreq->chain.first->cur_dma,
+ engine->regs + CESA_TDMA_NEXT_ADDR);
+ writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
+}
+
+void mv_cesa_dma_cleanup(struct mv_cesa_tdma_req *dreq)
+{
+ struct mv_cesa_tdma_desc *tdma;
+
+ for (tdma = dreq->chain.first; tdma;) {
+ struct mv_cesa_tdma_desc *old_tdma = tdma;
+
+ if (tdma->flags & CESA_TDMA_OP)
+ dma_pool_free(cesa_dev->dma->op_pool, tdma->op,
+ le32_to_cpu(tdma->src));
+
+ tdma = tdma->next;
+ dma_pool_free(cesa_dev->dma->tdma_desc_pool, old_tdma,
+ le32_to_cpu(old_tdma->cur_dma));
+ }
+
+ dreq->chain.first = NULL;
+ dreq->chain.last = NULL;
+}
+
+void mv_cesa_dma_prepare(struct mv_cesa_tdma_req *dreq,
+ struct mv_cesa_engine *engine)
+{
+ struct mv_cesa_tdma_desc *tdma;
+
+ for (tdma = dreq->chain.first; tdma; tdma = tdma->next) {
+ if (tdma->flags & CESA_TDMA_DST_IN_SRAM)
+ tdma->dst = cpu_to_le32(tdma->dst + engine->sram_dma);
+
+ if (tdma->flags & CESA_TDMA_SRC_IN_SRAM)
+ tdma->src = cpu_to_le32(tdma->src + engine->sram_dma);
+
+ if (tdma->flags & CESA_TDMA_OP)
+ mv_cesa_adjust_op(engine, tdma->op);
+ }
+}
+
+static struct mv_cesa_tdma_desc *
+mv_cesa_dma_add_desc(struct mv_cesa_tdma_chain *chain, gfp_t flags)
+{
+ struct mv_cesa_tdma_desc *new_tdma = NULL;
+ dma_addr_t dma_handle;
+
+ new_tdma = dma_pool_alloc(cesa_dev->dma->tdma_desc_pool, flags,
+ &dma_handle);
+ if (!new_tdma)
+ return ERR_PTR(-ENOMEM);
+
+ memset(new_tdma, 0, sizeof(*new_tdma));
+ new_tdma->cur_dma = cpu_to_le32(dma_handle);
+ if (chain->last) {
+ chain->last->next_dma = new_tdma->cur_dma;
+ chain->last->next = new_tdma;
+ } else {
+ chain->first = new_tdma;
+ }
+
+ chain->last = new_tdma;
+
+ return new_tdma;
+}
+
+struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
+ const struct mv_cesa_op_ctx *op_templ,
+ bool skip_ctx,
+ gfp_t flags)
+{
+ struct mv_cesa_tdma_desc *tdma;
+ struct mv_cesa_op_ctx *op;
+ dma_addr_t dma_handle;
+
+ tdma = mv_cesa_dma_add_desc(chain, flags);
+ if (IS_ERR(tdma))
+ return ERR_CAST(tdma);
+
+ op = dma_pool_alloc(cesa_dev->dma->op_pool, flags, &dma_handle);
+ if (!op)
+ return ERR_PTR(-ENOMEM);
+
+ *op = *op_templ;
+
+ tdma = chain->last;
+ tdma->op = op;
+ tdma->byte_cnt = (skip_ctx ? sizeof(op->desc) : sizeof(*op)) | BIT(31);
+ tdma->src = dma_handle;
+ tdma->flags = CESA_TDMA_DST_IN_SRAM | CESA_TDMA_OP;
+
+ return op;
+}
+
+int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
+ dma_addr_t dst, dma_addr_t src, u32 size,
+ u32 flags, gfp_t gfp_flags)
+{
+ struct mv_cesa_tdma_desc *tdma;
+
+ tdma = mv_cesa_dma_add_desc(chain, gfp_flags);
+ if (IS_ERR(tdma))
+ return PTR_ERR(tdma);
+
+ tdma->byte_cnt = size | BIT(31);
+ tdma->src = src;
+ tdma->dst = dst;
+
+ flags &= (CESA_TDMA_DST_IN_SRAM | CESA_TDMA_SRC_IN_SRAM);
+ tdma->flags = flags | CESA_TDMA_DATA;
+
+ return 0;
+}
+
+int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain,
+ u32 flags)
+{
+ struct mv_cesa_tdma_desc *tdma;
+
+ tdma = mv_cesa_dma_add_desc(chain, flags);
+ if (IS_ERR(tdma))
+ return PTR_ERR(tdma);
+
+ return 0;
+}
+
+int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, u32 flags)
+{
+ struct mv_cesa_tdma_desc *tdma;
+
+ tdma = mv_cesa_dma_add_desc(chain, flags);
+ if (IS_ERR(tdma))
+ return PTR_ERR(tdma);
+
+ tdma->byte_cnt = BIT(31);
+
+ return 0;
+}
+
+int mv_cesa_dma_add_op_transfers(struct mv_cesa_tdma_chain *chain,
+ struct mv_cesa_dma_iter *dma_iter,
+ struct mv_cesa_sg_dma_iter *sgiter,
+ gfp_t gfp_flags)
+{
+ u32 flags = sgiter->dir == DMA_TO_DEVICE ?
+ CESA_TDMA_DST_IN_SRAM : CESA_TDMA_SRC_IN_SRAM;
+ unsigned int len;
+
+ do {
+ dma_addr_t dst, src;
+ int ret;
+
+ len = mv_cesa_req_dma_iter_transfer_len(dma_iter, sgiter);
+ if (sgiter->dir == DMA_TO_DEVICE) {
+ dst = CESA_SA_DATA_SRAM_OFFSET + sgiter->op_offset;
+ src = sg_dma_address(sgiter->sg) + sgiter->offset;
+ } else {
+ dst = sg_dma_address(sgiter->sg) + sgiter->offset;
+ src = CESA_SA_DATA_SRAM_OFFSET + sgiter->op_offset;
+ }
+
+ ret = mv_cesa_dma_add_data_transfer(chain, dst, src, len,
+ flags, gfp_flags);
+ if (ret)
+ return ret;
+
+ } while (mv_cesa_req_dma_iter_next_transfer(dma_iter, sgiter, len));
+
+ return 0;
+}
diff --git a/drivers/crypto/mv_cesa.c b/drivers/crypto/mv_cesa.c
index f91f15ddee92..5bcd575fa96f 100644
--- a/drivers/crypto/mv_cesa.c
+++ b/drivers/crypto/mv_cesa.c
@@ -9,6 +9,7 @@
#include <crypto/aes.h>
#include <crypto/algapi.h>
#include <linux/crypto.h>
+#include <linux/genalloc.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kthread.h>
@@ -29,6 +30,8 @@
#define MAX_HW_HASH_SIZE 0xFFFF
#define MV_CESA_EXPIRE 500 /* msec */
+#define MV_CESA_DEFAULT_SRAM_SIZE 2048
+
/*
* STM:
* /---------------------------------------\
@@ -83,6 +86,8 @@ struct req_progress {
struct crypto_priv {
void __iomem *reg;
void __iomem *sram;
+ struct gen_pool *sram_pool;
+ dma_addr_t sram_dma;
int irq;
struct clk *clk;
struct task_struct *queue_th;
@@ -595,7 +600,7 @@ static int queue_manag(void *data)
cpg->eng_st = ENGINE_IDLE;
do {
struct crypto_async_request *async_req = NULL;
- struct crypto_async_request *backlog;
+ struct crypto_async_request *backlog = NULL;
__set_current_state(TASK_INTERRUPTIBLE);
@@ -1019,6 +1024,39 @@ static struct ahash_alg mv_hmac_sha1_alg = {
}
};
+static int mv_cesa_get_sram(struct platform_device *pdev,
+ struct crypto_priv *cp)
+{
+ struct resource *res;
+ u32 sram_size = MV_CESA_DEFAULT_SRAM_SIZE;
+
+ of_property_read_u32(pdev->dev.of_node, "marvell,crypto-sram-size",
+ &sram_size);
+
+ cp->sram_size = sram_size;
+ cp->sram_pool = of_get_named_gen_pool(pdev->dev.of_node,
+ "marvell,crypto-srams", 0);
+ if (cp->sram_pool) {
+ cp->sram = gen_pool_dma_alloc(cp->sram_pool, sram_size,
+ &cp->sram_dma);
+ if (cp->sram)
+ return 0;
+
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "sram");
+ if (!res || resource_size(res) < cp->sram_size)
+ return -EINVAL;
+
+ cp->sram = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(cp->sram))
+ return PTR_ERR(cp->sram);
+
+ return 0;
+}
+
static int mv_probe(struct platform_device *pdev)
{
struct crypto_priv *cp;
@@ -1041,24 +1079,17 @@ static int mv_probe(struct platform_device *pdev)
spin_lock_init(&cp->lock);
crypto_init_queue(&cp->queue, 50);
- cp->reg = ioremap(res->start, resource_size(res));
- if (!cp->reg) {
- ret = -ENOMEM;
+ cp->reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(cp->reg)) {
+ ret = PTR_ERR(cp->reg);
goto err;
}
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
- if (!res) {
- ret = -ENXIO;
- goto err_unmap_reg;
- }
- cp->sram_size = resource_size(res);
+ ret = mv_cesa_get_sram(pdev, cp);
+ if (ret)
+ goto err;
+
cp->max_req_size = cp->sram_size - SRAM_CFG_SPACE;
- cp->sram = ioremap(res->start, cp->sram_size);
- if (!cp->sram) {
- ret = -ENOMEM;
- goto err_unmap_reg;
- }
if (pdev->dev.of_node)
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
@@ -1066,7 +1097,7 @@ static int mv_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0 || irq == NO_IRQ) {
ret = irq;
- goto err_unmap_sram;
+ goto err;
}
cp->irq = irq;
@@ -1076,7 +1107,7 @@ static int mv_probe(struct platform_device *pdev)
cp->queue_th = kthread_run(queue_manag, cp, "mv_crypto");
if (IS_ERR(cp->queue_th)) {
ret = PTR_ERR(cp->queue_th);
- goto err_unmap_sram;
+ goto err;
}
ret = request_irq(irq, crypto_int, 0, dev_name(&pdev->dev),
@@ -1134,10 +1165,6 @@ err_irq:
}
err_thread:
kthread_stop(cp->queue_th);
-err_unmap_sram:
- iounmap(cp->sram);
-err_unmap_reg:
- iounmap(cp->reg);
err:
kfree(cp);
cpg = NULL;
@@ -1157,8 +1184,6 @@ static int mv_remove(struct platform_device *pdev)
kthread_stop(cp->queue_th);
free_irq(cp->irq, cp);
memset(cp->sram, 0, cp->sram_size);
- iounmap(cp->sram);
- iounmap(cp->reg);
if (!IS_ERR(cp->clk)) {
clk_disable_unprepare(cp->clk);
@@ -1172,6 +1197,8 @@ static int mv_remove(struct platform_device *pdev)
static const struct of_device_id mv_cesa_of_match_table[] = {
{ .compatible = "marvell,orion-crypto", },
+ { .compatible = "marvell,kirkwood-crypto", },
+ { .compatible = "marvell,dove-crypto", },
{}
};
MODULE_DEVICE_TABLE(of, mv_cesa_of_match_table);
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index 10a9aeff1666..2e8dab9d4263 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1281,10 +1281,10 @@ static const char md5_zero[MD5_DIGEST_SIZE] = {
0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e,
};
static const u32 md5_init[MD5_HASH_WORDS] = {
- cpu_to_le32(0x67452301),
- cpu_to_le32(0xefcdab89),
- cpu_to_le32(0x98badcfe),
- cpu_to_le32(0x10325476),
+ cpu_to_le32(MD5_H0),
+ cpu_to_le32(MD5_H1),
+ cpu_to_le32(MD5_H2),
+ cpu_to_le32(MD5_H3),
};
static const char sha1_zero[SHA1_DIGEST_SIZE] = {
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32,
diff --git a/drivers/crypto/nx/Kconfig b/drivers/crypto/nx/Kconfig
index f82616621ae1..e421c96c763a 100644
--- a/drivers/crypto/nx/Kconfig
+++ b/drivers/crypto/nx/Kconfig
@@ -1,26 +1,55 @@
+
config CRYPTO_DEV_NX_ENCRYPT
- tristate "Encryption acceleration support"
- depends on PPC64 && IBMVIO
+ tristate "Encryption acceleration support on pSeries platform"
+ depends on PPC_PSERIES && IBMVIO && !CPU_LITTLE_ENDIAN
default y
select CRYPTO_AES
- select CRYPTO_CBC
- select CRYPTO_ECB
select CRYPTO_CCM
- select CRYPTO_GCM
- select CRYPTO_AUTHENC
- select CRYPTO_XCBC
- select CRYPTO_SHA256
- select CRYPTO_SHA512
help
- Support for Power7+ in-Nest encryption acceleration. This
- module supports acceleration for AES and SHA2 algorithms. If you
- choose 'M' here, this module will be called nx_crypto.
+ Support for PowerPC Nest (NX) encryption acceleration. This
+ module supports acceleration for AES and SHA2 algorithms on
+ the pSeries platform. If you choose 'M' here, this module
+ will be called nx_crypto.
config CRYPTO_DEV_NX_COMPRESS
tristate "Compression acceleration support"
- depends on PPC64 && IBMVIO
default y
help
- Support for Power7+ in-Nest compression acceleration. This
- module supports acceleration for AES and SHA2 algorithms. If you
- choose 'M' here, this module will be called nx_compress.
+ Support for PowerPC Nest (NX) compression acceleration. This
+ module supports acceleration for compressing memory with the 842
+ algorithm. One of the platform drivers must be selected also.
+ If you choose 'M' here, this module will be called nx_compress.
+
+if CRYPTO_DEV_NX_COMPRESS
+
+config CRYPTO_DEV_NX_COMPRESS_PSERIES
+ tristate "Compression acceleration support on pSeries platform"
+ depends on PPC_PSERIES && IBMVIO
+ default y
+ help
+ Support for PowerPC Nest (NX) compression acceleration. This
+ module supports acceleration for compressing memory with the 842
+ algorithm. This supports NX hardware on the pSeries platform.
+ If you choose 'M' here, this module will be called nx_compress_pseries.
+
+config CRYPTO_DEV_NX_COMPRESS_POWERNV
+ tristate "Compression acceleration support on PowerNV platform"
+ depends on PPC_POWERNV
+ default y
+ help
+ Support for PowerPC Nest (NX) compression acceleration. This
+ module supports acceleration for compressing memory with the 842
+ algorithm. This supports NX hardware on the PowerNV platform.
+ If you choose 'M' here, this module will be called nx_compress_powernv.
+
+config CRYPTO_DEV_NX_COMPRESS_CRYPTO
+ tristate "Compression acceleration cryptographic interface"
+ select CRYPTO_ALGAPI
+ select 842_DECOMPRESS
+ default y
+ help
+ Support for PowerPC Nest (NX) accelerators using the cryptographic
+ API. If you choose 'M' here, this module will be called
+ nx_compress_crypto.
+
+endif
diff --git a/drivers/crypto/nx/Makefile b/drivers/crypto/nx/Makefile
index bb770ea45ce9..e1684f5adb11 100644
--- a/drivers/crypto/nx/Makefile
+++ b/drivers/crypto/nx/Makefile
@@ -10,5 +10,12 @@ nx-crypto-objs := nx.o \
nx-sha256.o \
nx-sha512.o
-obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS) += nx-compress.o
+obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS) += nx-compress.o nx-compress-platform.o
+obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_PSERIES) += nx-compress-pseries.o
+obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_POWERNV) += nx-compress-powernv.o
+obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_CRYPTO) += nx-compress-crypto.o
nx-compress-objs := nx-842.o
+nx-compress-platform-objs := nx-842-platform.o
+nx-compress-pseries-objs := nx-842-pseries.o
+nx-compress-powernv-objs := nx-842-powernv.o
+nx-compress-crypto-objs := nx-842-crypto.o
diff --git a/drivers/crypto/nx/nx-842-crypto.c b/drivers/crypto/nx/nx-842-crypto.c
new file mode 100644
index 000000000000..d53a1dcd7b4e
--- /dev/null
+++ b/drivers/crypto/nx/nx-842-crypto.c
@@ -0,0 +1,580 @@
+/*
+ * Cryptographic API for the NX-842 hardware compression.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) IBM Corporation, 2011-2015
+ *
+ * Original Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
+ * Seth Jennings <sjenning@linux.vnet.ibm.com>
+ *
+ * Rewrite: Dan Streetman <ddstreet@ieee.org>
+ *
+ * This is an interface to the NX-842 compression hardware in PowerPC
+ * processors. Most of the complexity of this drvier is due to the fact that
+ * the NX-842 compression hardware requires the input and output data buffers
+ * to be specifically aligned, to be a specific multiple in length, and within
+ * specific minimum and maximum lengths. Those restrictions, provided by the
+ * nx-842 driver via nx842_constraints, mean this driver must use bounce
+ * buffers and headers to correct misaligned in or out buffers, and to split
+ * input buffers that are too large.
+ *
+ * This driver will fall back to software decompression if the hardware
+ * decompression fails, so this driver's decompression should never fail as
+ * long as the provided compressed buffer is valid. Any compressed buffer
+ * created by this driver will have a header (except ones where the input
+ * perfectly matches the constraints); so users of this driver cannot simply
+ * pass a compressed buffer created by this driver over to the 842 software
+ * decompression library. Instead, users must use this driver to decompress;
+ * if the hardware fails or is unavailable, the compressed buffer will be
+ * parsed and the header removed, and the raw 842 buffer(s) passed to the 842
+ * software decompression library.
+ *
+ * This does not fall back to software compression, however, since the caller
+ * of this function is specifically requesting hardware compression; if the
+ * hardware compression fails, the caller can fall back to software
+ * compression, and the raw 842 compressed buffer that the software compressor
+ * creates can be passed to this driver for hardware decompression; any
+ * buffer without our specific header magic is assumed to be a raw 842 buffer
+ * and passed directly to the hardware. Note that the software compression
+ * library will produce a compressed buffer that is incompatible with the
+ * hardware decompressor if the original input buffer length is not a multiple
+ * of 8; if such a compressed buffer is passed to this driver for
+ * decompression, the hardware will reject it and this driver will then pass
+ * it over to the software library for decompression.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/vmalloc.h>
+#include <linux/sw842.h>
+#include <linux/ratelimit.h>
+
+#include "nx-842.h"
+
+/* The first 5 bits of this magic are 0x1f, which is an invalid 842 5-bit
+ * template (see lib/842/842.h), so this magic number will never appear at
+ * the start of a raw 842 compressed buffer. That is important, as any buffer
+ * passed to us without this magic is assumed to be a raw 842 compressed
+ * buffer, and passed directly to the hardware to decompress.
+ */
+#define NX842_CRYPTO_MAGIC (0xf842)
+#define NX842_CRYPTO_GROUP_MAX (0x20)
+#define NX842_CRYPTO_HEADER_SIZE(g) \
+ (sizeof(struct nx842_crypto_header) + \
+ sizeof(struct nx842_crypto_header_group) * (g))
+#define NX842_CRYPTO_HEADER_MAX_SIZE \
+ NX842_CRYPTO_HEADER_SIZE(NX842_CRYPTO_GROUP_MAX)
+
+/* bounce buffer size */
+#define BOUNCE_BUFFER_ORDER (2)
+#define BOUNCE_BUFFER_SIZE \
+ ((unsigned int)(PAGE_SIZE << BOUNCE_BUFFER_ORDER))
+
+/* try longer on comp because we can fallback to sw decomp if hw is busy */
+#define COMP_BUSY_TIMEOUT (250) /* ms */
+#define DECOMP_BUSY_TIMEOUT (50) /* ms */
+
+struct nx842_crypto_header_group {
+ __be16 padding; /* unused bytes at start of group */
+ __be32 compressed_length; /* compressed bytes in group */
+ __be32 uncompressed_length; /* bytes after decompression */
+} __packed;
+
+struct nx842_crypto_header {
+ __be16 magic; /* NX842_CRYPTO_MAGIC */
+ __be16 ignore; /* decompressed end bytes to ignore */
+ u8 groups; /* total groups in this header */
+ struct nx842_crypto_header_group group[];
+} __packed;
+
+struct nx842_crypto_param {
+ u8 *in;
+ unsigned int iremain;
+ u8 *out;
+ unsigned int oremain;
+ unsigned int ototal;
+};
+
+static int update_param(struct nx842_crypto_param *p,
+ unsigned int slen, unsigned int dlen)
+{
+ if (p->iremain < slen)
+ return -EOVERFLOW;
+ if (p->oremain < dlen)
+ return -ENOSPC;
+
+ p->in += slen;
+ p->iremain -= slen;
+ p->out += dlen;
+ p->oremain -= dlen;
+ p->ototal += dlen;
+
+ return 0;
+}
+
+struct nx842_crypto_ctx {
+ u8 *wmem;
+ u8 *sbounce, *dbounce;
+
+ struct nx842_crypto_header header;
+ struct nx842_crypto_header_group group[NX842_CRYPTO_GROUP_MAX];
+};
+
+static int nx842_crypto_init(struct crypto_tfm *tfm)
+{
+ struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ ctx->wmem = kmalloc(nx842_workmem_size(), GFP_KERNEL);
+ ctx->sbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
+ ctx->dbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
+ if (!ctx->wmem || !ctx->sbounce || !ctx->dbounce) {
+ kfree(ctx->wmem);
+ free_page((unsigned long)ctx->sbounce);
+ free_page((unsigned long)ctx->dbounce);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void nx842_crypto_exit(struct crypto_tfm *tfm)
+{
+ struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ kfree(ctx->wmem);
+ free_page((unsigned long)ctx->sbounce);
+ free_page((unsigned long)ctx->dbounce);
+}
+
+static int read_constraints(struct nx842_constraints *c)
+{
+ int ret;
+
+ ret = nx842_constraints(c);
+ if (ret) {
+ pr_err_ratelimited("could not get nx842 constraints : %d\n",
+ ret);
+ return ret;
+ }
+
+ /* limit maximum, to always have enough bounce buffer to decompress */
+ if (c->maximum > BOUNCE_BUFFER_SIZE) {
+ c->maximum = BOUNCE_BUFFER_SIZE;
+ pr_info_once("limiting nx842 maximum to %x\n", c->maximum);
+ }
+
+ return 0;
+}
+
+static int nx842_crypto_add_header(struct nx842_crypto_header *hdr, u8 *buf)
+{
+ int s = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
+
+ /* compress should have added space for header */
+ if (s > be16_to_cpu(hdr->group[0].padding)) {
+ pr_err("Internal error: no space for header\n");
+ return -EINVAL;
+ }
+
+ memcpy(buf, hdr, s);
+
+ print_hex_dump_debug("header ", DUMP_PREFIX_OFFSET, 16, 1, buf, s, 0);
+
+ return 0;
+}
+
+static int compress(struct nx842_crypto_ctx *ctx,
+ struct nx842_crypto_param *p,
+ struct nx842_crypto_header_group *g,
+ struct nx842_constraints *c,
+ u16 *ignore,
+ unsigned int hdrsize)
+{
+ unsigned int slen = p->iremain, dlen = p->oremain, tmplen;
+ unsigned int adj_slen = slen;
+ u8 *src = p->in, *dst = p->out;
+ int ret, dskip = 0;
+ ktime_t timeout;
+
+ if (p->iremain == 0)
+ return -EOVERFLOW;
+
+ if (p->oremain == 0 || hdrsize + c->minimum > dlen)
+ return -ENOSPC;
+
+ if (slen % c->multiple)
+ adj_slen = round_up(slen, c->multiple);
+ if (slen < c->minimum)
+ adj_slen = c->minimum;
+ if (slen > c->maximum)
+ adj_slen = slen = c->maximum;
+ if (adj_slen > slen || (u64)src % c->alignment) {
+ adj_slen = min(adj_slen, BOUNCE_BUFFER_SIZE);
+ slen = min(slen, BOUNCE_BUFFER_SIZE);
+ if (adj_slen > slen)
+ memset(ctx->sbounce + slen, 0, adj_slen - slen);
+ memcpy(ctx->sbounce, src, slen);
+ src = ctx->sbounce;
+ slen = adj_slen;
+ pr_debug("using comp sbounce buffer, len %x\n", slen);
+ }
+
+ dst += hdrsize;
+ dlen -= hdrsize;
+
+ if ((u64)dst % c->alignment) {
+ dskip = (int)(PTR_ALIGN(dst, c->alignment) - dst);
+ dst += dskip;
+ dlen -= dskip;
+ }
+ if (dlen % c->multiple)
+ dlen = round_down(dlen, c->multiple);
+ if (dlen < c->minimum) {
+nospc:
+ dst = ctx->dbounce;
+ dlen = min(p->oremain, BOUNCE_BUFFER_SIZE);
+ dlen = round_down(dlen, c->multiple);
+ dskip = 0;
+ pr_debug("using comp dbounce buffer, len %x\n", dlen);
+ }
+ if (dlen > c->maximum)
+ dlen = c->maximum;
+
+ tmplen = dlen;
+ timeout = ktime_add_ms(ktime_get(), COMP_BUSY_TIMEOUT);
+ do {
+ dlen = tmplen; /* reset dlen, if we're retrying */
+ ret = nx842_compress(src, slen, dst, &dlen, ctx->wmem);
+ /* possibly we should reduce the slen here, instead of
+ * retrying with the dbounce buffer?
+ */
+ if (ret == -ENOSPC && dst != ctx->dbounce)
+ goto nospc;
+ } while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
+ if (ret)
+ return ret;
+
+ dskip += hdrsize;
+
+ if (dst == ctx->dbounce)
+ memcpy(p->out + dskip, dst, dlen);
+
+ g->padding = cpu_to_be16(dskip);
+ g->compressed_length = cpu_to_be32(dlen);
+ g->uncompressed_length = cpu_to_be32(slen);
+
+ if (p->iremain < slen) {
+ *ignore = slen - p->iremain;
+ slen = p->iremain;
+ }
+
+ pr_debug("compress slen %x ignore %x dlen %x padding %x\n",
+ slen, *ignore, dlen, dskip);
+
+ return update_param(p, slen, dskip + dlen);
+}
+
+static int nx842_crypto_compress(struct crypto_tfm *tfm,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen)
+{
+ struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct nx842_crypto_header *hdr = &ctx->header;
+ struct nx842_crypto_param p;
+ struct nx842_constraints c;
+ unsigned int groups, hdrsize, h;
+ int ret, n;
+ bool add_header;
+ u16 ignore = 0;
+
+ p.in = (u8 *)src;
+ p.iremain = slen;
+ p.out = dst;
+ p.oremain = *dlen;
+ p.ototal = 0;
+
+ *dlen = 0;
+
+ ret = read_constraints(&c);
+ if (ret)
+ return ret;
+
+ groups = min_t(unsigned int, NX842_CRYPTO_GROUP_MAX,
+ DIV_ROUND_UP(p.iremain, c.maximum));
+ hdrsize = NX842_CRYPTO_HEADER_SIZE(groups);
+
+ /* skip adding header if the buffers meet all constraints */
+ add_header = (p.iremain % c.multiple ||
+ p.iremain < c.minimum ||
+ p.iremain > c.maximum ||
+ (u64)p.in % c.alignment ||
+ p.oremain % c.multiple ||
+ p.oremain < c.minimum ||
+ p.oremain > c.maximum ||
+ (u64)p.out % c.alignment);
+
+ hdr->magic = cpu_to_be16(NX842_CRYPTO_MAGIC);
+ hdr->groups = 0;
+ hdr->ignore = 0;
+
+ while (p.iremain > 0) {
+ n = hdr->groups++;
+ if (hdr->groups > NX842_CRYPTO_GROUP_MAX)
+ return -ENOSPC;
+
+ /* header goes before first group */
+ h = !n && add_header ? hdrsize : 0;
+
+ if (ignore)
+ pr_warn("interal error, ignore is set %x\n", ignore);
+
+ ret = compress(ctx, &p, &hdr->group[n], &c, &ignore, h);
+ if (ret)
+ return ret;
+ }
+
+ if (!add_header && hdr->groups > 1) {
+ pr_err("Internal error: No header but multiple groups\n");
+ return -EINVAL;
+ }
+
+ /* ignore indicates the input stream needed to be padded */
+ hdr->ignore = cpu_to_be16(ignore);
+ if (ignore)
+ pr_debug("marked %d bytes as ignore\n", ignore);
+
+ if (add_header)
+ ret = nx842_crypto_add_header(hdr, dst);
+ if (ret)
+ return ret;
+
+ *dlen = p.ototal;
+
+ pr_debug("compress total slen %x dlen %x\n", slen, *dlen);
+
+ return 0;
+}
+
+static int decompress(struct nx842_crypto_ctx *ctx,
+ struct nx842_crypto_param *p,
+ struct nx842_crypto_header_group *g,
+ struct nx842_constraints *c,
+ u16 ignore,
+ bool usehw)
+{
+ unsigned int slen = be32_to_cpu(g->compressed_length);
+ unsigned int required_len = be32_to_cpu(g->uncompressed_length);
+ unsigned int dlen = p->oremain, tmplen;
+ unsigned int adj_slen = slen;
+ u8 *src = p->in, *dst = p->out;
+ u16 padding = be16_to_cpu(g->padding);
+ int ret, spadding = 0, dpadding = 0;
+ ktime_t timeout;
+
+ if (!slen || !required_len)
+ return -EINVAL;
+
+ if (p->iremain <= 0 || padding + slen > p->iremain)
+ return -EOVERFLOW;
+
+ if (p->oremain <= 0 || required_len - ignore > p->oremain)
+ return -ENOSPC;
+
+ src += padding;
+
+ if (!usehw)
+ goto usesw;
+
+ if (slen % c->multiple)
+ adj_slen = round_up(slen, c->multiple);
+ if (slen < c->minimum)
+ adj_slen = c->minimum;
+ if (slen > c->maximum)
+ goto usesw;
+ if (slen < adj_slen || (u64)src % c->alignment) {
+ /* we can append padding bytes because the 842 format defines
+ * an "end" template (see lib/842/842_decompress.c) and will
+ * ignore any bytes following it.
+ */
+ if (slen < adj_slen)
+ memset(ctx->sbounce + slen, 0, adj_slen - slen);
+ memcpy(ctx->sbounce, src, slen);
+ src = ctx->sbounce;
+ spadding = adj_slen - slen;
+ slen = adj_slen;
+ pr_debug("using decomp sbounce buffer, len %x\n", slen);
+ }
+
+ if (dlen % c->multiple)
+ dlen = round_down(dlen, c->multiple);
+ if (dlen < required_len || (u64)dst % c->alignment) {
+ dst = ctx->dbounce;
+ dlen = min(required_len, BOUNCE_BUFFER_SIZE);
+ pr_debug("using decomp dbounce buffer, len %x\n", dlen);
+ }
+ if (dlen < c->minimum)
+ goto usesw;
+ if (dlen > c->maximum)
+ dlen = c->maximum;
+
+ tmplen = dlen;
+ timeout = ktime_add_ms(ktime_get(), DECOMP_BUSY_TIMEOUT);
+ do {
+ dlen = tmplen; /* reset dlen, if we're retrying */
+ ret = nx842_decompress(src, slen, dst, &dlen, ctx->wmem);
+ } while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
+ if (ret) {
+usesw:
+ /* reset everything, sw doesn't have constraints */
+ src = p->in + padding;
+ slen = be32_to_cpu(g->compressed_length);
+ spadding = 0;
+ dst = p->out;
+ dlen = p->oremain;
+ dpadding = 0;
+ if (dlen < required_len) { /* have ignore bytes */
+ dst = ctx->dbounce;
+ dlen = BOUNCE_BUFFER_SIZE;
+ }
+ pr_info_ratelimited("using software 842 decompression\n");
+ ret = sw842_decompress(src, slen, dst, &dlen);
+ }
+ if (ret)
+ return ret;
+
+ slen -= spadding;
+
+ dlen -= ignore;
+ if (ignore)
+ pr_debug("ignoring last %x bytes\n", ignore);
+
+ if (dst == ctx->dbounce)
+ memcpy(p->out, dst, dlen);
+
+ pr_debug("decompress slen %x padding %x dlen %x ignore %x\n",
+ slen, padding, dlen, ignore);
+
+ return update_param(p, slen + padding, dlen);
+}
+
+static int nx842_crypto_decompress(struct crypto_tfm *tfm,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen)
+{
+ struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct nx842_crypto_header *hdr;
+ struct nx842_crypto_param p;
+ struct nx842_constraints c;
+ int n, ret, hdr_len;
+ u16 ignore = 0;
+ bool usehw = true;
+
+ p.in = (u8 *)src;
+ p.iremain = slen;
+ p.out = dst;
+ p.oremain = *dlen;
+ p.ototal = 0;
+
+ *dlen = 0;
+
+ if (read_constraints(&c))
+ usehw = false;
+
+ hdr = (struct nx842_crypto_header *)src;
+
+ /* If it doesn't start with our header magic number, assume it's a raw
+ * 842 compressed buffer and pass it directly to the hardware driver
+ */
+ if (be16_to_cpu(hdr->magic) != NX842_CRYPTO_MAGIC) {
+ struct nx842_crypto_header_group g = {
+ .padding = 0,
+ .compressed_length = cpu_to_be32(p.iremain),
+ .uncompressed_length = cpu_to_be32(p.oremain),
+ };
+
+ ret = decompress(ctx, &p, &g, &c, 0, usehw);
+ if (ret)
+ return ret;
+
+ *dlen = p.ototal;
+
+ return 0;
+ }
+
+ if (!hdr->groups) {
+ pr_err("header has no groups\n");
+ return -EINVAL;
+ }
+ if (hdr->groups > NX842_CRYPTO_GROUP_MAX) {
+ pr_err("header has too many groups %x, max %x\n",
+ hdr->groups, NX842_CRYPTO_GROUP_MAX);
+ return -EINVAL;
+ }
+
+ hdr_len = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
+ if (hdr_len > slen)
+ return -EOVERFLOW;
+
+ memcpy(&ctx->header, src, hdr_len);
+ hdr = &ctx->header;
+
+ for (n = 0; n < hdr->groups; n++) {
+ /* ignore applies to last group */
+ if (n + 1 == hdr->groups)
+ ignore = be16_to_cpu(hdr->ignore);
+
+ ret = decompress(ctx, &p, &hdr->group[n], &c, ignore, usehw);
+ if (ret)
+ return ret;
+ }
+
+ *dlen = p.ototal;
+
+ pr_debug("decompress total slen %x dlen %x\n", slen, *dlen);
+
+ return 0;
+}
+
+static struct crypto_alg alg = {
+ .cra_name = "842",
+ .cra_driver_name = "842-nx",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
+ .cra_ctxsize = sizeof(struct nx842_crypto_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_init = nx842_crypto_init,
+ .cra_exit = nx842_crypto_exit,
+ .cra_u = { .compress = {
+ .coa_compress = nx842_crypto_compress,
+ .coa_decompress = nx842_crypto_decompress } }
+};
+
+static int __init nx842_crypto_mod_init(void)
+{
+ return crypto_register_alg(&alg);
+}
+module_init(nx842_crypto_mod_init);
+
+static void __exit nx842_crypto_mod_exit(void)
+{
+ crypto_unregister_alg(&alg);
+}
+module_exit(nx842_crypto_mod_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IBM PowerPC Nest (NX) 842 Hardware Compression Interface");
+MODULE_ALIAS_CRYPTO("842");
+MODULE_ALIAS_CRYPTO("842-nx");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
diff --git a/drivers/crypto/nx/nx-842-platform.c b/drivers/crypto/nx/nx-842-platform.c
new file mode 100644
index 000000000000..664f13dd06ed
--- /dev/null
+++ b/drivers/crypto/nx/nx-842-platform.c
@@ -0,0 +1,84 @@
+
+#include "nx-842.h"
+
+/* this is needed, separate from the main nx-842.c driver, because that main
+ * driver loads the platform drivers during its init(), and it expects one
+ * (or none) of the platform drivers to set this pointer to its driver.
+ * That means this pointer can't be in the main nx-842 driver, because it
+ * wouldn't be accessible until after the main driver loaded, which wouldn't
+ * be possible as it's waiting for the platform driver to load. So place it
+ * here.
+ */
+static struct nx842_driver *driver;
+static DEFINE_SPINLOCK(driver_lock);
+
+struct nx842_driver *nx842_platform_driver(void)
+{
+ return driver;
+}
+EXPORT_SYMBOL_GPL(nx842_platform_driver);
+
+bool nx842_platform_driver_set(struct nx842_driver *_driver)
+{
+ bool ret = false;
+
+ spin_lock(&driver_lock);
+
+ if (!driver) {
+ driver = _driver;
+ ret = true;
+ } else
+ WARN(1, "can't set platform driver, already set to %s\n",
+ driver->name);
+
+ spin_unlock(&driver_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nx842_platform_driver_set);
+
+/* only call this from the platform driver exit function */
+void nx842_platform_driver_unset(struct nx842_driver *_driver)
+{
+ spin_lock(&driver_lock);
+
+ if (driver == _driver)
+ driver = NULL;
+ else if (driver)
+ WARN(1, "can't unset platform driver %s, currently set to %s\n",
+ _driver->name, driver->name);
+ else
+ WARN(1, "can't unset platform driver, already unset\n");
+
+ spin_unlock(&driver_lock);
+}
+EXPORT_SYMBOL_GPL(nx842_platform_driver_unset);
+
+bool nx842_platform_driver_get(void)
+{
+ bool ret = false;
+
+ spin_lock(&driver_lock);
+
+ if (driver)
+ ret = try_module_get(driver->owner);
+
+ spin_unlock(&driver_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nx842_platform_driver_get);
+
+void nx842_platform_driver_put(void)
+{
+ spin_lock(&driver_lock);
+
+ if (driver)
+ module_put(driver->owner);
+
+ spin_unlock(&driver_lock);
+}
+EXPORT_SYMBOL_GPL(nx842_platform_driver_put);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
+MODULE_DESCRIPTION("842 H/W Compression platform driver");
diff --git a/drivers/crypto/nx/nx-842-powernv.c b/drivers/crypto/nx/nx-842-powernv.c
new file mode 100644
index 000000000000..33b3b0abf4ae
--- /dev/null
+++ b/drivers/crypto/nx/nx-842-powernv.c
@@ -0,0 +1,637 @@
+/*
+ * Driver for IBM PowerNV 842 compression accelerator
+ *
+ * Copyright (C) 2015 Dan Streetman, IBM Corp
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "nx-842.h"
+
+#include <linux/timer.h>
+
+#include <asm/prom.h>
+#include <asm/icswx.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
+MODULE_DESCRIPTION("842 H/W Compression driver for IBM PowerNV processors");
+
+#define WORKMEM_ALIGN (CRB_ALIGN)
+#define CSB_WAIT_MAX (5000) /* ms */
+
+struct nx842_workmem {
+ /* Below fields must be properly aligned */
+ struct coprocessor_request_block crb; /* CRB_ALIGN align */
+ struct data_descriptor_entry ddl_in[DDL_LEN_MAX]; /* DDE_ALIGN align */
+ struct data_descriptor_entry ddl_out[DDL_LEN_MAX]; /* DDE_ALIGN align */
+ /* Above fields must be properly aligned */
+
+ ktime_t start;
+
+ char padding[WORKMEM_ALIGN]; /* unused, to allow alignment */
+} __packed __aligned(WORKMEM_ALIGN);
+
+struct nx842_coproc {
+ unsigned int chip_id;
+ unsigned int ct;
+ unsigned int ci;
+ struct list_head list;
+};
+
+/* no cpu hotplug on powernv, so this list never changes after init */
+static LIST_HEAD(nx842_coprocs);
+static unsigned int nx842_ct;
+
+/**
+ * setup_indirect_dde - Setup an indirect DDE
+ *
+ * The DDE is setup with the the DDE count, byte count, and address of
+ * first direct DDE in the list.
+ */
+static void setup_indirect_dde(struct data_descriptor_entry *dde,
+ struct data_descriptor_entry *ddl,
+ unsigned int dde_count, unsigned int byte_count)
+{
+ dde->flags = 0;
+ dde->count = dde_count;
+ dde->index = 0;
+ dde->length = cpu_to_be32(byte_count);
+ dde->address = cpu_to_be64(nx842_get_pa(ddl));
+}
+
+/**
+ * setup_direct_dde - Setup single DDE from buffer
+ *
+ * The DDE is setup with the buffer and length. The buffer must be properly
+ * aligned. The used length is returned.
+ * Returns:
+ * N Successfully set up DDE with N bytes
+ */
+static unsigned int setup_direct_dde(struct data_descriptor_entry *dde,
+ unsigned long pa, unsigned int len)
+{
+ unsigned int l = min_t(unsigned int, len, LEN_ON_PAGE(pa));
+
+ dde->flags = 0;
+ dde->count = 0;
+ dde->index = 0;
+ dde->length = cpu_to_be32(l);
+ dde->address = cpu_to_be64(pa);
+
+ return l;
+}
+
+/**
+ * setup_ddl - Setup DDL from buffer
+ *
+ * Returns:
+ * 0 Successfully set up DDL
+ */
+static int setup_ddl(struct data_descriptor_entry *dde,
+ struct data_descriptor_entry *ddl,
+ unsigned char *buf, unsigned int len,
+ bool in)
+{
+ unsigned long pa = nx842_get_pa(buf);
+ int i, ret, total_len = len;
+
+ if (!IS_ALIGNED(pa, DDE_BUFFER_ALIGN)) {
+ pr_debug("%s buffer pa 0x%lx not 0x%x-byte aligned\n",
+ in ? "input" : "output", pa, DDE_BUFFER_ALIGN);
+ return -EINVAL;
+ }
+
+ /* only need to check last mult; since buffer must be
+ * DDE_BUFFER_ALIGN aligned, and that is a multiple of
+ * DDE_BUFFER_SIZE_MULT, and pre-last page DDE buffers
+ * are guaranteed a multiple of DDE_BUFFER_SIZE_MULT.
+ */
+ if (len % DDE_BUFFER_LAST_MULT) {
+ pr_debug("%s buffer len 0x%x not a multiple of 0x%x\n",
+ in ? "input" : "output", len, DDE_BUFFER_LAST_MULT);
+ if (in)
+ return -EINVAL;
+ len = round_down(len, DDE_BUFFER_LAST_MULT);
+ }
+
+ /* use a single direct DDE */
+ if (len <= LEN_ON_PAGE(pa)) {
+ ret = setup_direct_dde(dde, pa, len);
+ WARN_ON(ret < len);
+ return 0;
+ }
+
+ /* use the DDL */
+ for (i = 0; i < DDL_LEN_MAX && len > 0; i++) {
+ ret = setup_direct_dde(&ddl[i], pa, len);
+ buf += ret;
+ len -= ret;
+ pa = nx842_get_pa(buf);
+ }
+
+ if (len > 0) {
+ pr_debug("0x%x total %s bytes 0x%x too many for DDL.\n",
+ total_len, in ? "input" : "output", len);
+ if (in)
+ return -EMSGSIZE;
+ total_len -= len;
+ }
+ setup_indirect_dde(dde, ddl, i, total_len);
+
+ return 0;
+}
+
+#define CSB_ERR(csb, msg, ...) \
+ pr_err("ERROR: " msg " : %02x %02x %02x %02x %08x\n", \
+ ##__VA_ARGS__, (csb)->flags, \
+ (csb)->cs, (csb)->cc, (csb)->ce, \
+ be32_to_cpu((csb)->count))
+
+#define CSB_ERR_ADDR(csb, msg, ...) \
+ CSB_ERR(csb, msg " at %lx", ##__VA_ARGS__, \
+ (unsigned long)be64_to_cpu((csb)->address))
+
+/**
+ * wait_for_csb
+ */
+static int wait_for_csb(struct nx842_workmem *wmem,
+ struct coprocessor_status_block *csb)
+{
+ ktime_t start = wmem->start, now = ktime_get();
+ ktime_t timeout = ktime_add_ms(start, CSB_WAIT_MAX);
+
+ while (!(ACCESS_ONCE(csb->flags) & CSB_V)) {
+ cpu_relax();
+ now = ktime_get();
+ if (ktime_after(now, timeout))
+ break;
+ }
+
+ /* hw has updated csb and output buffer */
+ barrier();
+
+ /* check CSB flags */
+ if (!(csb->flags & CSB_V)) {
+ CSB_ERR(csb, "CSB still not valid after %ld us, giving up",
+ (long)ktime_us_delta(now, start));
+ return -ETIMEDOUT;
+ }
+ if (csb->flags & CSB_F) {
+ CSB_ERR(csb, "Invalid CSB format");
+ return -EPROTO;
+ }
+ if (csb->flags & CSB_CH) {
+ CSB_ERR(csb, "Invalid CSB chaining state");
+ return -EPROTO;
+ }
+
+ /* verify CSB completion sequence is 0 */
+ if (csb->cs) {
+ CSB_ERR(csb, "Invalid CSB completion sequence");
+ return -EPROTO;
+ }
+
+ /* check CSB Completion Code */
+ switch (csb->cc) {
+ /* no error */
+ case CSB_CC_SUCCESS:
+ break;
+ case CSB_CC_TPBC_GT_SPBC:
+ /* not an error, but the compressed data is
+ * larger than the uncompressed data :(
+ */
+ break;
+
+ /* input data errors */
+ case CSB_CC_OPERAND_OVERLAP:
+ /* input and output buffers overlap */
+ CSB_ERR(csb, "Operand Overlap error");
+ return -EINVAL;
+ case CSB_CC_INVALID_OPERAND:
+ CSB_ERR(csb, "Invalid operand");
+ return -EINVAL;
+ case CSB_CC_NOSPC:
+ /* output buffer too small */
+ return -ENOSPC;
+ case CSB_CC_ABORT:
+ CSB_ERR(csb, "Function aborted");
+ return -EINTR;
+ case CSB_CC_CRC_MISMATCH:
+ CSB_ERR(csb, "CRC mismatch");
+ return -EINVAL;
+ case CSB_CC_TEMPL_INVALID:
+ CSB_ERR(csb, "Compressed data template invalid");
+ return -EINVAL;
+ case CSB_CC_TEMPL_OVERFLOW:
+ CSB_ERR(csb, "Compressed data template shows data past end");
+ return -EINVAL;
+
+ /* these should not happen */
+ case CSB_CC_INVALID_ALIGN:
+ /* setup_ddl should have detected this */
+ CSB_ERR_ADDR(csb, "Invalid alignment");
+ return -EINVAL;
+ case CSB_CC_DATA_LENGTH:
+ /* setup_ddl should have detected this */
+ CSB_ERR(csb, "Invalid data length");
+ return -EINVAL;
+ case CSB_CC_WR_TRANSLATION:
+ case CSB_CC_TRANSLATION:
+ case CSB_CC_TRANSLATION_DUP1:
+ case CSB_CC_TRANSLATION_DUP2:
+ case CSB_CC_TRANSLATION_DUP3:
+ case CSB_CC_TRANSLATION_DUP4:
+ case CSB_CC_TRANSLATION_DUP5:
+ case CSB_CC_TRANSLATION_DUP6:
+ /* should not happen, we use physical addrs */
+ CSB_ERR_ADDR(csb, "Translation error");
+ return -EPROTO;
+ case CSB_CC_WR_PROTECTION:
+ case CSB_CC_PROTECTION:
+ case CSB_CC_PROTECTION_DUP1:
+ case CSB_CC_PROTECTION_DUP2:
+ case CSB_CC_PROTECTION_DUP3:
+ case CSB_CC_PROTECTION_DUP4:
+ case CSB_CC_PROTECTION_DUP5:
+ case CSB_CC_PROTECTION_DUP6:
+ /* should not happen, we use physical addrs */
+ CSB_ERR_ADDR(csb, "Protection error");
+ return -EPROTO;
+ case CSB_CC_PRIVILEGE:
+ /* shouldn't happen, we're in HYP mode */
+ CSB_ERR(csb, "Insufficient Privilege error");
+ return -EPROTO;
+ case CSB_CC_EXCESSIVE_DDE:
+ /* shouldn't happen, setup_ddl doesn't use many dde's */
+ CSB_ERR(csb, "Too many DDEs in DDL");
+ return -EINVAL;
+ case CSB_CC_TRANSPORT:
+ /* shouldn't happen, we setup CRB correctly */
+ CSB_ERR(csb, "Invalid CRB");
+ return -EINVAL;
+ case CSB_CC_SEGMENTED_DDL:
+ /* shouldn't happen, setup_ddl creates DDL right */
+ CSB_ERR(csb, "Segmented DDL error");
+ return -EINVAL;
+ case CSB_CC_DDE_OVERFLOW:
+ /* shouldn't happen, setup_ddl creates DDL right */
+ CSB_ERR(csb, "DDE overflow error");
+ return -EINVAL;
+ case CSB_CC_SESSION:
+ /* should not happen with ICSWX */
+ CSB_ERR(csb, "Session violation error");
+ return -EPROTO;
+ case CSB_CC_CHAIN:
+ /* should not happen, we don't use chained CRBs */
+ CSB_ERR(csb, "Chained CRB error");
+ return -EPROTO;
+ case CSB_CC_SEQUENCE:
+ /* should not happen, we don't use chained CRBs */
+ CSB_ERR(csb, "CRB seqeunce number error");
+ return -EPROTO;
+ case CSB_CC_UNKNOWN_CODE:
+ CSB_ERR(csb, "Unknown subfunction code");
+ return -EPROTO;
+
+ /* hardware errors */
+ case CSB_CC_RD_EXTERNAL:
+ case CSB_CC_RD_EXTERNAL_DUP1:
+ case CSB_CC_RD_EXTERNAL_DUP2:
+ case CSB_CC_RD_EXTERNAL_DUP3:
+ CSB_ERR_ADDR(csb, "Read error outside coprocessor");
+ return -EPROTO;
+ case CSB_CC_WR_EXTERNAL:
+ CSB_ERR_ADDR(csb, "Write error outside coprocessor");
+ return -EPROTO;
+ case CSB_CC_INTERNAL:
+ CSB_ERR(csb, "Internal error in coprocessor");
+ return -EPROTO;
+ case CSB_CC_PROVISION:
+ CSB_ERR(csb, "Storage provision error");
+ return -EPROTO;
+ case CSB_CC_HW:
+ CSB_ERR(csb, "Correctable hardware error");
+ return -EPROTO;
+
+ default:
+ CSB_ERR(csb, "Invalid CC %d", csb->cc);
+ return -EPROTO;
+ }
+
+ /* check Completion Extension state */
+ if (csb->ce & CSB_CE_TERMINATION) {
+ CSB_ERR(csb, "CSB request was terminated");
+ return -EPROTO;
+ }
+ if (csb->ce & CSB_CE_INCOMPLETE) {
+ CSB_ERR(csb, "CSB request not complete");
+ return -EPROTO;
+ }
+ if (!(csb->ce & CSB_CE_TPBC)) {
+ CSB_ERR(csb, "TPBC not provided, unknown target length");
+ return -EPROTO;
+ }
+
+ /* successful completion */
+ pr_debug_ratelimited("Processed %u bytes in %lu us\n", csb->count,
+ (unsigned long)ktime_us_delta(now, start));
+
+ return 0;
+}
+
+/**
+ * nx842_powernv_function - compress/decompress data using the 842 algorithm
+ *
+ * (De)compression provided by the NX842 coprocessor on IBM PowerNV systems.
+ * This compresses or decompresses the provided input buffer into the provided
+ * output buffer.
+ *
+ * Upon return from this function @outlen contains the length of the
+ * output data. If there is an error then @outlen will be 0 and an
+ * error will be specified by the return code from this function.
+ *
+ * The @workmem buffer should only be used by one function call at a time.
+ *
+ * @in: input buffer pointer
+ * @inlen: input buffer size
+ * @out: output buffer pointer
+ * @outlenp: output buffer size pointer
+ * @workmem: working memory buffer pointer, size determined by
+ * nx842_powernv_driver.workmem_size
+ * @fc: function code, see CCW Function Codes in nx-842.h
+ *
+ * Returns:
+ * 0 Success, output of length @outlenp stored in the buffer at @out
+ * -ENODEV Hardware unavailable
+ * -ENOSPC Output buffer is to small
+ * -EMSGSIZE Input buffer too large
+ * -EINVAL buffer constraints do not fix nx842_constraints
+ * -EPROTO hardware error during operation
+ * -ETIMEDOUT hardware did not complete operation in reasonable time
+ * -EINTR operation was aborted
+ */
+static int nx842_powernv_function(const unsigned char *in, unsigned int inlen,
+ unsigned char *out, unsigned int *outlenp,
+ void *workmem, int fc)
+{
+ struct coprocessor_request_block *crb;
+ struct coprocessor_status_block *csb;
+ struct nx842_workmem *wmem;
+ int ret;
+ u64 csb_addr;
+ u32 ccw;
+ unsigned int outlen = *outlenp;
+
+ wmem = PTR_ALIGN(workmem, WORKMEM_ALIGN);
+
+ *outlenp = 0;
+
+ /* shoudn't happen, we don't load without a coproc */
+ if (!nx842_ct) {
+ pr_err_ratelimited("coprocessor CT is 0");
+ return -ENODEV;
+ }
+
+ crb = &wmem->crb;
+ csb = &crb->csb;
+
+ /* Clear any previous values */
+ memset(crb, 0, sizeof(*crb));
+
+ /* set up DDLs */
+ ret = setup_ddl(&crb->source, wmem->ddl_in,
+ (unsigned char *)in, inlen, true);
+ if (ret)
+ return ret;
+ ret = setup_ddl(&crb->target, wmem->ddl_out,
+ out, outlen, false);
+ if (ret)
+ return ret;
+
+ /* set up CCW */
+ ccw = 0;
+ ccw = SET_FIELD(ccw, CCW_CT, nx842_ct);
+ ccw = SET_FIELD(ccw, CCW_CI_842, 0); /* use 0 for hw auto-selection */
+ ccw = SET_FIELD(ccw, CCW_FC_842, fc);
+
+ /* set up CRB's CSB addr */
+ csb_addr = nx842_get_pa(csb) & CRB_CSB_ADDRESS;
+ csb_addr |= CRB_CSB_AT; /* Addrs are phys */
+ crb->csb_addr = cpu_to_be64(csb_addr);
+
+ wmem->start = ktime_get();
+
+ /* do ICSWX */
+ ret = icswx(cpu_to_be32(ccw), crb);
+
+ pr_debug_ratelimited("icswx CR %x ccw %x crb->ccw %x\n", ret,
+ (unsigned int)ccw,
+ (unsigned int)be32_to_cpu(crb->ccw));
+
+ switch (ret) {
+ case ICSWX_INITIATED:
+ ret = wait_for_csb(wmem, csb);
+ break;
+ case ICSWX_BUSY:
+ pr_debug_ratelimited("842 Coprocessor busy\n");
+ ret = -EBUSY;
+ break;
+ case ICSWX_REJECTED:
+ pr_err_ratelimited("ICSWX rejected\n");
+ ret = -EPROTO;
+ break;
+ default:
+ pr_err_ratelimited("Invalid ICSWX return code %x\n", ret);
+ ret = -EPROTO;
+ break;
+ }
+
+ if (!ret)
+ *outlenp = be32_to_cpu(csb->count);
+
+ return ret;
+}
+
+/**
+ * nx842_powernv_compress - Compress data using the 842 algorithm
+ *
+ * Compression provided by the NX842 coprocessor on IBM PowerNV systems.
+ * The input buffer is compressed and the result is stored in the
+ * provided output buffer.
+ *
+ * Upon return from this function @outlen contains the length of the
+ * compressed data. If there is an error then @outlen will be 0 and an
+ * error will be specified by the return code from this function.
+ *
+ * @in: input buffer pointer
+ * @inlen: input buffer size
+ * @out: output buffer pointer
+ * @outlenp: output buffer size pointer
+ * @workmem: working memory buffer pointer, size determined by
+ * nx842_powernv_driver.workmem_size
+ *
+ * Returns: see @nx842_powernv_function()
+ */
+static int nx842_powernv_compress(const unsigned char *in, unsigned int inlen,
+ unsigned char *out, unsigned int *outlenp,
+ void *wmem)
+{
+ return nx842_powernv_function(in, inlen, out, outlenp,
+ wmem, CCW_FC_842_COMP_NOCRC);
+}
+
+/**
+ * nx842_powernv_decompress - Decompress data using the 842 algorithm
+ *
+ * Decompression provided by the NX842 coprocessor on IBM PowerNV systems.
+ * The input buffer is decompressed and the result is stored in the
+ * provided output buffer.
+ *
+ * Upon return from this function @outlen contains the length of the
+ * decompressed data. If there is an error then @outlen will be 0 and an
+ * error will be specified by the return code from this function.
+ *
+ * @in: input buffer pointer
+ * @inlen: input buffer size
+ * @out: output buffer pointer
+ * @outlenp: output buffer size pointer
+ * @workmem: working memory buffer pointer, size determined by
+ * nx842_powernv_driver.workmem_size
+ *
+ * Returns: see @nx842_powernv_function()
+ */
+static int nx842_powernv_decompress(const unsigned char *in, unsigned int inlen,
+ unsigned char *out, unsigned int *outlenp,
+ void *wmem)
+{
+ return nx842_powernv_function(in, inlen, out, outlenp,
+ wmem, CCW_FC_842_DECOMP_NOCRC);
+}
+
+static int __init nx842_powernv_probe(struct device_node *dn)
+{
+ struct nx842_coproc *coproc;
+ struct property *ct_prop, *ci_prop;
+ unsigned int ct, ci;
+ int chip_id;
+
+ chip_id = of_get_ibm_chip_id(dn);
+ if (chip_id < 0) {
+ pr_err("ibm,chip-id missing\n");
+ return -EINVAL;
+ }
+ ct_prop = of_find_property(dn, "ibm,842-coprocessor-type", NULL);
+ if (!ct_prop) {
+ pr_err("ibm,842-coprocessor-type missing\n");
+ return -EINVAL;
+ }
+ ct = be32_to_cpu(*(unsigned int *)ct_prop->value);
+ ci_prop = of_find_property(dn, "ibm,842-coprocessor-instance", NULL);
+ if (!ci_prop) {
+ pr_err("ibm,842-coprocessor-instance missing\n");
+ return -EINVAL;
+ }
+ ci = be32_to_cpu(*(unsigned int *)ci_prop->value);
+
+ coproc = kmalloc(sizeof(*coproc), GFP_KERNEL);
+ if (!coproc)
+ return -ENOMEM;
+
+ coproc->chip_id = chip_id;
+ coproc->ct = ct;
+ coproc->ci = ci;
+ INIT_LIST_HEAD(&coproc->list);
+ list_add(&coproc->list, &nx842_coprocs);
+
+ pr_info("coprocessor found on chip %d, CT %d CI %d\n", chip_id, ct, ci);
+
+ if (!nx842_ct)
+ nx842_ct = ct;
+ else if (nx842_ct != ct)
+ pr_err("NX842 chip %d, CT %d != first found CT %d\n",
+ chip_id, ct, nx842_ct);
+
+ return 0;
+}
+
+static struct nx842_constraints nx842_powernv_constraints = {
+ .alignment = DDE_BUFFER_ALIGN,
+ .multiple = DDE_BUFFER_LAST_MULT,
+ .minimum = DDE_BUFFER_LAST_MULT,
+ .maximum = (DDL_LEN_MAX - 1) * PAGE_SIZE,
+};
+
+static struct nx842_driver nx842_powernv_driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .workmem_size = sizeof(struct nx842_workmem),
+ .constraints = &nx842_powernv_constraints,
+ .compress = nx842_powernv_compress,
+ .decompress = nx842_powernv_decompress,
+};
+
+static __init int nx842_powernv_init(void)
+{
+ struct device_node *dn;
+
+ /* verify workmem size/align restrictions */
+ BUILD_BUG_ON(WORKMEM_ALIGN % CRB_ALIGN);
+ BUILD_BUG_ON(CRB_ALIGN % DDE_ALIGN);
+ BUILD_BUG_ON(CRB_SIZE % DDE_ALIGN);
+ /* verify buffer size/align restrictions */
+ BUILD_BUG_ON(PAGE_SIZE % DDE_BUFFER_ALIGN);
+ BUILD_BUG_ON(DDE_BUFFER_ALIGN % DDE_BUFFER_SIZE_MULT);
+ BUILD_BUG_ON(DDE_BUFFER_SIZE_MULT % DDE_BUFFER_LAST_MULT);
+
+ pr_info("loading\n");
+
+ for_each_compatible_node(dn, NULL, "ibm,power-nx")
+ nx842_powernv_probe(dn);
+
+ if (!nx842_ct) {
+ pr_err("no coprocessors found\n");
+ return -ENODEV;
+ }
+
+ if (!nx842_platform_driver_set(&nx842_powernv_driver)) {
+ struct nx842_coproc *coproc, *n;
+
+ list_for_each_entry_safe(coproc, n, &nx842_coprocs, list) {
+ list_del(&coproc->list);
+ kfree(coproc);
+ }
+
+ return -EEXIST;
+ }
+
+ pr_info("loaded\n");
+
+ return 0;
+}
+module_init(nx842_powernv_init);
+
+static void __exit nx842_powernv_exit(void)
+{
+ struct nx842_coproc *coproc, *n;
+
+ nx842_platform_driver_unset(&nx842_powernv_driver);
+
+ list_for_each_entry_safe(coproc, n, &nx842_coprocs, list) {
+ list_del(&coproc->list);
+ kfree(coproc);
+ }
+
+ pr_info("unloaded\n");
+}
+module_exit(nx842_powernv_exit);
diff --git a/drivers/crypto/nx/nx-842-pseries.c b/drivers/crypto/nx/nx-842-pseries.c
new file mode 100644
index 000000000000..3040a6091bf2
--- /dev/null
+++ b/drivers/crypto/nx/nx-842-pseries.c
@@ -0,0 +1,1140 @@
+/*
+ * Driver for IBM Power 842 compression accelerator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (C) IBM Corporation, 2012
+ *
+ * Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
+ * Seth Jennings <sjenning@linux.vnet.ibm.com>
+ */
+
+#include <asm/vio.h>
+
+#include "nx-842.h"
+#include "nx_csbcpb.h" /* struct nx_csbcpb */
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robert Jennings <rcj@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("842 H/W Compression driver for IBM Power processors");
+
+static struct nx842_constraints nx842_pseries_constraints = {
+ .alignment = DDE_BUFFER_ALIGN,
+ .multiple = DDE_BUFFER_LAST_MULT,
+ .minimum = DDE_BUFFER_LAST_MULT,
+ .maximum = PAGE_SIZE, /* dynamic, max_sync_size */
+};
+
+static int check_constraints(unsigned long buf, unsigned int *len, bool in)
+{
+ if (!IS_ALIGNED(buf, nx842_pseries_constraints.alignment)) {
+ pr_debug("%s buffer 0x%lx not aligned to 0x%x\n",
+ in ? "input" : "output", buf,
+ nx842_pseries_constraints.alignment);
+ return -EINVAL;
+ }
+ if (*len % nx842_pseries_constraints.multiple) {
+ pr_debug("%s buffer len 0x%x not multiple of 0x%x\n",
+ in ? "input" : "output", *len,
+ nx842_pseries_constraints.multiple);
+ if (in)
+ return -EINVAL;
+ *len = round_down(*len, nx842_pseries_constraints.multiple);
+ }
+ if (*len < nx842_pseries_constraints.minimum) {
+ pr_debug("%s buffer len 0x%x under minimum 0x%x\n",
+ in ? "input" : "output", *len,
+ nx842_pseries_constraints.minimum);
+ return -EINVAL;
+ }
+ if (*len > nx842_pseries_constraints.maximum) {
+ pr_debug("%s buffer len 0x%x over maximum 0x%x\n",
+ in ? "input" : "output", *len,
+ nx842_pseries_constraints.maximum);
+ if (in)
+ return -EINVAL;
+ *len = nx842_pseries_constraints.maximum;
+ }
+ return 0;
+}
+
+/* I assume we need to align the CSB? */
+#define WORKMEM_ALIGN (256)
+
+struct nx842_workmem {
+ /* scatterlist */
+ char slin[4096];
+ char slout[4096];
+ /* coprocessor status/parameter block */
+ struct nx_csbcpb csbcpb;
+
+ char padding[WORKMEM_ALIGN];
+} __aligned(WORKMEM_ALIGN);
+
+/* Macros for fields within nx_csbcpb */
+/* Check the valid bit within the csbcpb valid field */
+#define NX842_CSBCBP_VALID_CHK(x) (x & BIT_MASK(7))
+
+/* CE macros operate on the completion_extension field bits in the csbcpb.
+ * CE0 0=full completion, 1=partial completion
+ * CE1 0=CE0 indicates completion, 1=termination (output may be modified)
+ * CE2 0=processed_bytes is source bytes, 1=processed_bytes is target bytes */
+#define NX842_CSBCPB_CE0(x) (x & BIT_MASK(7))
+#define NX842_CSBCPB_CE1(x) (x & BIT_MASK(6))
+#define NX842_CSBCPB_CE2(x) (x & BIT_MASK(5))
+
+/* The NX unit accepts data only on 4K page boundaries */
+#define NX842_HW_PAGE_SIZE (4096)
+#define NX842_HW_PAGE_MASK (~(NX842_HW_PAGE_SIZE-1))
+
+enum nx842_status {
+ UNAVAILABLE,
+ AVAILABLE
+};
+
+struct ibm_nx842_counters {
+ atomic64_t comp_complete;
+ atomic64_t comp_failed;
+ atomic64_t decomp_complete;
+ atomic64_t decomp_failed;
+ atomic64_t swdecomp;
+ atomic64_t comp_times[32];
+ atomic64_t decomp_times[32];
+};
+
+static struct nx842_devdata {
+ struct vio_dev *vdev;
+ struct device *dev;
+ struct ibm_nx842_counters *counters;
+ unsigned int max_sg_len;
+ unsigned int max_sync_size;
+ unsigned int max_sync_sg;
+ enum nx842_status status;
+} __rcu *devdata;
+static DEFINE_SPINLOCK(devdata_mutex);
+
+#define NX842_COUNTER_INC(_x) \
+static inline void nx842_inc_##_x( \
+ const struct nx842_devdata *dev) { \
+ if (dev) \
+ atomic64_inc(&dev->counters->_x); \
+}
+NX842_COUNTER_INC(comp_complete);
+NX842_COUNTER_INC(comp_failed);
+NX842_COUNTER_INC(decomp_complete);
+NX842_COUNTER_INC(decomp_failed);
+NX842_COUNTER_INC(swdecomp);
+
+#define NX842_HIST_SLOTS 16
+
+static void ibm_nx842_incr_hist(atomic64_t *times, unsigned int time)
+{
+ int bucket = fls(time);
+
+ if (bucket)
+ bucket = min((NX842_HIST_SLOTS - 1), bucket - 1);
+
+ atomic64_inc(&times[bucket]);
+}
+
+/* NX unit operation flags */
+#define NX842_OP_COMPRESS 0x0
+#define NX842_OP_CRC 0x1
+#define NX842_OP_DECOMPRESS 0x2
+#define NX842_OP_COMPRESS_CRC (NX842_OP_COMPRESS | NX842_OP_CRC)
+#define NX842_OP_DECOMPRESS_CRC (NX842_OP_DECOMPRESS | NX842_OP_CRC)
+#define NX842_OP_ASYNC (1<<23)
+#define NX842_OP_NOTIFY (1<<22)
+#define NX842_OP_NOTIFY_INT(x) ((x & 0xff)<<8)
+
+static unsigned long nx842_get_desired_dma(struct vio_dev *viodev)
+{
+ /* No use of DMA mappings within the driver. */
+ return 0;
+}
+
+struct nx842_slentry {
+ __be64 ptr; /* Real address (use __pa()) */
+ __be64 len;
+};
+
+/* pHyp scatterlist entry */
+struct nx842_scatterlist {
+ int entry_nr; /* number of slentries */
+ struct nx842_slentry *entries; /* ptr to array of slentries */
+};
+
+/* Does not include sizeof(entry_nr) in the size */
+static inline unsigned long nx842_get_scatterlist_size(
+ struct nx842_scatterlist *sl)
+{
+ return sl->entry_nr * sizeof(struct nx842_slentry);
+}
+
+static int nx842_build_scatterlist(unsigned long buf, int len,
+ struct nx842_scatterlist *sl)
+{
+ unsigned long entrylen;
+ struct nx842_slentry *entry;
+
+ sl->entry_nr = 0;
+
+ entry = sl->entries;
+ while (len) {
+ entry->ptr = cpu_to_be64(nx842_get_pa((void *)buf));
+ entrylen = min_t(int, len,
+ LEN_ON_SIZE(buf, NX842_HW_PAGE_SIZE));
+ entry->len = cpu_to_be64(entrylen);
+
+ len -= entrylen;
+ buf += entrylen;
+
+ sl->entry_nr++;
+ entry++;
+ }
+
+ return 0;
+}
+
+static int nx842_validate_result(struct device *dev,
+ struct cop_status_block *csb)
+{
+ /* The csb must be valid after returning from vio_h_cop_sync */
+ if (!NX842_CSBCBP_VALID_CHK(csb->valid)) {
+ dev_err(dev, "%s: cspcbp not valid upon completion.\n",
+ __func__);
+ dev_dbg(dev, "valid:0x%02x cs:0x%02x cc:0x%02x ce:0x%02x\n",
+ csb->valid,
+ csb->crb_seq_number,
+ csb->completion_code,
+ csb->completion_extension);
+ dev_dbg(dev, "processed_bytes:%d address:0x%016lx\n",
+ be32_to_cpu(csb->processed_byte_count),
+ (unsigned long)be64_to_cpu(csb->address));
+ return -EIO;
+ }
+
+ /* Check return values from the hardware in the CSB */
+ switch (csb->completion_code) {
+ case 0: /* Completed without error */
+ break;
+ case 64: /* Target bytes > Source bytes during compression */
+ case 13: /* Output buffer too small */
+ dev_dbg(dev, "%s: Compression output larger than input\n",
+ __func__);
+ return -ENOSPC;
+ case 66: /* Input data contains an illegal template field */
+ case 67: /* Template indicates data past the end of the input stream */
+ dev_dbg(dev, "%s: Bad data for decompression (code:%d)\n",
+ __func__, csb->completion_code);
+ return -EINVAL;
+ default:
+ dev_dbg(dev, "%s: Unspecified error (code:%d)\n",
+ __func__, csb->completion_code);
+ return -EIO;
+ }
+
+ /* Hardware sanity check */
+ if (!NX842_CSBCPB_CE2(csb->completion_extension)) {
+ dev_err(dev, "%s: No error returned by hardware, but "
+ "data returned is unusable, contact support.\n"
+ "(Additional info: csbcbp->processed bytes "
+ "does not specify processed bytes for the "
+ "target buffer.)\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * nx842_pseries_compress - Compress data using the 842 algorithm
+ *
+ * Compression provide by the NX842 coprocessor on IBM Power systems.
+ * The input buffer is compressed and the result is stored in the
+ * provided output buffer.
+ *
+ * Upon return from this function @outlen contains the length of the
+ * compressed data. If there is an error then @outlen will be 0 and an
+ * error will be specified by the return code from this function.
+ *
+ * @in: Pointer to input buffer
+ * @inlen: Length of input buffer
+ * @out: Pointer to output buffer
+ * @outlen: Length of output buffer
+ * @wrkmem: ptr to buffer for working memory, size determined by
+ * nx842_pseries_driver.workmem_size
+ *
+ * Returns:
+ * 0 Success, output of length @outlen stored in the buffer at @out
+ * -ENOMEM Unable to allocate internal buffers
+ * -ENOSPC Output buffer is to small
+ * -EIO Internal error
+ * -ENODEV Hardware unavailable
+ */
+static int nx842_pseries_compress(const unsigned char *in, unsigned int inlen,
+ unsigned char *out, unsigned int *outlen,
+ void *wmem)
+{
+ struct nx842_devdata *local_devdata;
+ struct device *dev = NULL;
+ struct nx842_workmem *workmem;
+ struct nx842_scatterlist slin, slout;
+ struct nx_csbcpb *csbcpb;
+ int ret = 0, max_sync_size;
+ unsigned long inbuf, outbuf;
+ struct vio_pfo_op op = {
+ .done = NULL,
+ .handle = 0,
+ .timeout = 0,
+ };
+ unsigned long start = get_tb();
+
+ inbuf = (unsigned long)in;
+ if (check_constraints(inbuf, &inlen, true))
+ return -EINVAL;
+
+ outbuf = (unsigned long)out;
+ if (check_constraints(outbuf, outlen, false))
+ return -EINVAL;
+
+ rcu_read_lock();
+ local_devdata = rcu_dereference(devdata);
+ if (!local_devdata || !local_devdata->dev) {
+ rcu_read_unlock();
+ return -ENODEV;
+ }
+ max_sync_size = local_devdata->max_sync_size;
+ dev = local_devdata->dev;
+
+ /* Init scatterlist */
+ workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN);
+ slin.entries = (struct nx842_slentry *)workmem->slin;
+ slout.entries = (struct nx842_slentry *)workmem->slout;
+
+ /* Init operation */
+ op.flags = NX842_OP_COMPRESS;
+ csbcpb = &workmem->csbcpb;
+ memset(csbcpb, 0, sizeof(*csbcpb));
+ op.csbcpb = nx842_get_pa(csbcpb);
+
+ if ((inbuf & NX842_HW_PAGE_MASK) ==
+ ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) {
+ /* Create direct DDE */
+ op.in = nx842_get_pa((void *)inbuf);
+ op.inlen = inlen;
+ } else {
+ /* Create indirect DDE (scatterlist) */
+ nx842_build_scatterlist(inbuf, inlen, &slin);
+ op.in = nx842_get_pa(slin.entries);
+ op.inlen = -nx842_get_scatterlist_size(&slin);
+ }
+
+ if ((outbuf & NX842_HW_PAGE_MASK) ==
+ ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) {
+ /* Create direct DDE */
+ op.out = nx842_get_pa((void *)outbuf);
+ op.outlen = *outlen;
+ } else {
+ /* Create indirect DDE (scatterlist) */
+ nx842_build_scatterlist(outbuf, *outlen, &slout);
+ op.out = nx842_get_pa(slout.entries);
+ op.outlen = -nx842_get_scatterlist_size(&slout);
+ }
+
+ dev_dbg(dev, "%s: op.in %lx op.inlen %ld op.out %lx op.outlen %ld\n",
+ __func__, (unsigned long)op.in, (long)op.inlen,
+ (unsigned long)op.out, (long)op.outlen);
+
+ /* Send request to pHyp */
+ ret = vio_h_cop_sync(local_devdata->vdev, &op);
+
+ /* Check for pHyp error */
+ if (ret) {
+ dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
+ __func__, ret, op.hcall_err);
+ ret = -EIO;
+ goto unlock;
+ }
+
+ /* Check for hardware error */
+ ret = nx842_validate_result(dev, &csbcpb->csb);
+ if (ret)
+ goto unlock;
+
+ *outlen = be32_to_cpu(csbcpb->csb.processed_byte_count);
+ dev_dbg(dev, "%s: processed_bytes=%d\n", __func__, *outlen);
+
+unlock:
+ if (ret)
+ nx842_inc_comp_failed(local_devdata);
+ else {
+ nx842_inc_comp_complete(local_devdata);
+ ibm_nx842_incr_hist(local_devdata->counters->comp_times,
+ (get_tb() - start) / tb_ticks_per_usec);
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+/**
+ * nx842_pseries_decompress - Decompress data using the 842 algorithm
+ *
+ * Decompression provide by the NX842 coprocessor on IBM Power systems.
+ * The input buffer is decompressed and the result is stored in the
+ * provided output buffer. The size allocated to the output buffer is
+ * provided by the caller of this function in @outlen. Upon return from
+ * this function @outlen contains the length of the decompressed data.
+ * If there is an error then @outlen will be 0 and an error will be
+ * specified by the return code from this function.
+ *
+ * @in: Pointer to input buffer
+ * @inlen: Length of input buffer
+ * @out: Pointer to output buffer
+ * @outlen: Length of output buffer
+ * @wrkmem: ptr to buffer for working memory, size determined by
+ * nx842_pseries_driver.workmem_size
+ *
+ * Returns:
+ * 0 Success, output of length @outlen stored in the buffer at @out
+ * -ENODEV Hardware decompression device is unavailable
+ * -ENOMEM Unable to allocate internal buffers
+ * -ENOSPC Output buffer is to small
+ * -EINVAL Bad input data encountered when attempting decompress
+ * -EIO Internal error
+ */
+static int nx842_pseries_decompress(const unsigned char *in, unsigned int inlen,
+ unsigned char *out, unsigned int *outlen,
+ void *wmem)
+{
+ struct nx842_devdata *local_devdata;
+ struct device *dev = NULL;
+ struct nx842_workmem *workmem;
+ struct nx842_scatterlist slin, slout;
+ struct nx_csbcpb *csbcpb;
+ int ret = 0, max_sync_size;
+ unsigned long inbuf, outbuf;
+ struct vio_pfo_op op = {
+ .done = NULL,
+ .handle = 0,
+ .timeout = 0,
+ };
+ unsigned long start = get_tb();
+
+ /* Ensure page alignment and size */
+ inbuf = (unsigned long)in;
+ if (check_constraints(inbuf, &inlen, true))
+ return -EINVAL;
+
+ outbuf = (unsigned long)out;
+ if (check_constraints(outbuf, outlen, false))
+ return -EINVAL;
+
+ rcu_read_lock();
+ local_devdata = rcu_dereference(devdata);
+ if (!local_devdata || !local_devdata->dev) {
+ rcu_read_unlock();
+ return -ENODEV;
+ }
+ max_sync_size = local_devdata->max_sync_size;
+ dev = local_devdata->dev;
+
+ workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN);
+
+ /* Init scatterlist */
+ slin.entries = (struct nx842_slentry *)workmem->slin;
+ slout.entries = (struct nx842_slentry *)workmem->slout;
+
+ /* Init operation */
+ op.flags = NX842_OP_DECOMPRESS;
+ csbcpb = &workmem->csbcpb;
+ memset(csbcpb, 0, sizeof(*csbcpb));
+ op.csbcpb = nx842_get_pa(csbcpb);
+
+ if ((inbuf & NX842_HW_PAGE_MASK) ==
+ ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) {
+ /* Create direct DDE */
+ op.in = nx842_get_pa((void *)inbuf);
+ op.inlen = inlen;
+ } else {
+ /* Create indirect DDE (scatterlist) */
+ nx842_build_scatterlist(inbuf, inlen, &slin);
+ op.in = nx842_get_pa(slin.entries);
+ op.inlen = -nx842_get_scatterlist_size(&slin);
+ }
+
+ if ((outbuf & NX842_HW_PAGE_MASK) ==
+ ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) {
+ /* Create direct DDE */
+ op.out = nx842_get_pa((void *)outbuf);
+ op.outlen = *outlen;
+ } else {
+ /* Create indirect DDE (scatterlist) */
+ nx842_build_scatterlist(outbuf, *outlen, &slout);
+ op.out = nx842_get_pa(slout.entries);
+ op.outlen = -nx842_get_scatterlist_size(&slout);
+ }
+
+ dev_dbg(dev, "%s: op.in %lx op.inlen %ld op.out %lx op.outlen %ld\n",
+ __func__, (unsigned long)op.in, (long)op.inlen,
+ (unsigned long)op.out, (long)op.outlen);
+
+ /* Send request to pHyp */
+ ret = vio_h_cop_sync(local_devdata->vdev, &op);
+
+ /* Check for pHyp error */
+ if (ret) {
+ dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
+ __func__, ret, op.hcall_err);
+ goto unlock;
+ }
+
+ /* Check for hardware error */
+ ret = nx842_validate_result(dev, &csbcpb->csb);
+ if (ret)
+ goto unlock;
+
+ *outlen = be32_to_cpu(csbcpb->csb.processed_byte_count);
+
+unlock:
+ if (ret)
+ /* decompress fail */
+ nx842_inc_decomp_failed(local_devdata);
+ else {
+ nx842_inc_decomp_complete(local_devdata);
+ ibm_nx842_incr_hist(local_devdata->counters->decomp_times,
+ (get_tb() - start) / tb_ticks_per_usec);
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/**
+ * nx842_OF_set_defaults -- Set default (disabled) values for devdata
+ *
+ * @devdata - struct nx842_devdata to update
+ *
+ * Returns:
+ * 0 on success
+ * -ENOENT if @devdata ptr is NULL
+ */
+static int nx842_OF_set_defaults(struct nx842_devdata *devdata)
+{
+ if (devdata) {
+ devdata->max_sync_size = 0;
+ devdata->max_sync_sg = 0;
+ devdata->max_sg_len = 0;
+ devdata->status = UNAVAILABLE;
+ return 0;
+ } else
+ return -ENOENT;
+}
+
+/**
+ * nx842_OF_upd_status -- Update the device info from OF status prop
+ *
+ * The status property indicates if the accelerator is enabled. If the
+ * device is in the OF tree it indicates that the hardware is present.
+ * The status field indicates if the device is enabled when the status
+ * is 'okay'. Otherwise the device driver will be disabled.
+ *
+ * @devdata - struct nx842_devdata to update
+ * @prop - struct property point containing the maxsyncop for the update
+ *
+ * Returns:
+ * 0 - Device is available
+ * -EINVAL - Device is not available
+ */
+static int nx842_OF_upd_status(struct nx842_devdata *devdata,
+ struct property *prop) {
+ int ret = 0;
+ const char *status = (const char *)prop->value;
+
+ if (!strncmp(status, "okay", (size_t)prop->length)) {
+ devdata->status = AVAILABLE;
+ } else {
+ dev_info(devdata->dev, "%s: status '%s' is not 'okay'\n",
+ __func__, status);
+ devdata->status = UNAVAILABLE;
+ }
+
+ return ret;
+}
+
+/**
+ * nx842_OF_upd_maxsglen -- Update the device info from OF maxsglen prop
+ *
+ * Definition of the 'ibm,max-sg-len' OF property:
+ * This field indicates the maximum byte length of a scatter list
+ * for the platform facility. It is a single cell encoded as with encode-int.
+ *
+ * Example:
+ * # od -x ibm,max-sg-len
+ * 0000000 0000 0ff0
+ *
+ * In this example, the maximum byte length of a scatter list is
+ * 0x0ff0 (4,080).
+ *
+ * @devdata - struct nx842_devdata to update
+ * @prop - struct property point containing the maxsyncop for the update
+ *
+ * Returns:
+ * 0 on success
+ * -EINVAL on failure
+ */
+static int nx842_OF_upd_maxsglen(struct nx842_devdata *devdata,
+ struct property *prop) {
+ int ret = 0;
+ const unsigned int maxsglen = of_read_number(prop->value, 1);
+
+ if (prop->length != sizeof(maxsglen)) {
+ dev_err(devdata->dev, "%s: unexpected format for ibm,max-sg-len property\n", __func__);
+ dev_dbg(devdata->dev, "%s: ibm,max-sg-len is %d bytes long, expected %lu bytes\n", __func__,
+ prop->length, sizeof(maxsglen));
+ ret = -EINVAL;
+ } else {
+ devdata->max_sg_len = min_t(unsigned int,
+ maxsglen, NX842_HW_PAGE_SIZE);
+ }
+
+ return ret;
+}
+
+/**
+ * nx842_OF_upd_maxsyncop -- Update the device info from OF maxsyncop prop
+ *
+ * Definition of the 'ibm,max-sync-cop' OF property:
+ * Two series of cells. The first series of cells represents the maximums
+ * that can be synchronously compressed. The second series of cells
+ * represents the maximums that can be synchronously decompressed.
+ * 1. The first cell in each series contains the count of the number of
+ * data length, scatter list elements pairs that follow – each being
+ * of the form
+ * a. One cell data byte length
+ * b. One cell total number of scatter list elements
+ *
+ * Example:
+ * # od -x ibm,max-sync-cop
+ * 0000000 0000 0001 0000 1000 0000 01fe 0000 0001
+ * 0000020 0000 1000 0000 01fe
+ *
+ * In this example, compression supports 0x1000 (4,096) data byte length
+ * and 0x1fe (510) total scatter list elements. Decompression supports
+ * 0x1000 (4,096) data byte length and 0x1f3 (510) total scatter list
+ * elements.
+ *
+ * @devdata - struct nx842_devdata to update
+ * @prop - struct property point containing the maxsyncop for the update
+ *
+ * Returns:
+ * 0 on success
+ * -EINVAL on failure
+ */
+static int nx842_OF_upd_maxsyncop(struct nx842_devdata *devdata,
+ struct property *prop) {
+ int ret = 0;
+ unsigned int comp_data_limit, decomp_data_limit;
+ unsigned int comp_sg_limit, decomp_sg_limit;
+ const struct maxsynccop_t {
+ __be32 comp_elements;
+ __be32 comp_data_limit;
+ __be32 comp_sg_limit;
+ __be32 decomp_elements;
+ __be32 decomp_data_limit;
+ __be32 decomp_sg_limit;
+ } *maxsynccop;
+
+ if (prop->length != sizeof(*maxsynccop)) {
+ dev_err(devdata->dev, "%s: unexpected format for ibm,max-sync-cop property\n", __func__);
+ dev_dbg(devdata->dev, "%s: ibm,max-sync-cop is %d bytes long, expected %lu bytes\n", __func__, prop->length,
+ sizeof(*maxsynccop));
+ ret = -EINVAL;
+ goto out;
+ }
+
+ maxsynccop = (const struct maxsynccop_t *)prop->value;
+ comp_data_limit = be32_to_cpu(maxsynccop->comp_data_limit);
+ comp_sg_limit = be32_to_cpu(maxsynccop->comp_sg_limit);
+ decomp_data_limit = be32_to_cpu(maxsynccop->decomp_data_limit);
+ decomp_sg_limit = be32_to_cpu(maxsynccop->decomp_sg_limit);
+
+ /* Use one limit rather than separate limits for compression and
+ * decompression. Set a maximum for this so as not to exceed the
+ * size that the header can support and round the value down to
+ * the hardware page size (4K) */
+ devdata->max_sync_size = min(comp_data_limit, decomp_data_limit);
+
+ devdata->max_sync_size = min_t(unsigned int, devdata->max_sync_size,
+ 65536);
+
+ if (devdata->max_sync_size < 4096) {
+ dev_err(devdata->dev, "%s: hardware max data size (%u) is "
+ "less than the driver minimum, unable to use "
+ "the hardware device\n",
+ __func__, devdata->max_sync_size);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ nx842_pseries_constraints.maximum = devdata->max_sync_size;
+
+ devdata->max_sync_sg = min(comp_sg_limit, decomp_sg_limit);
+ if (devdata->max_sync_sg < 1) {
+ dev_err(devdata->dev, "%s: hardware max sg size (%u) is "
+ "less than the driver minimum, unable to use "
+ "the hardware device\n",
+ __func__, devdata->max_sync_sg);
+ ret = -EINVAL;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ *
+ * nx842_OF_upd -- Handle OF properties updates for the device.
+ *
+ * Set all properties from the OF tree. Optionally, a new property
+ * can be provided by the @new_prop pointer to overwrite an existing value.
+ * The device will remain disabled until all values are valid, this function
+ * will return an error for updates unless all values are valid.
+ *
+ * @new_prop: If not NULL, this property is being updated. If NULL, update
+ * all properties from the current values in the OF tree.
+ *
+ * Returns:
+ * 0 - Success
+ * -ENOMEM - Could not allocate memory for new devdata structure
+ * -EINVAL - property value not found, new_prop is not a recognized
+ * property for the device or property value is not valid.
+ * -ENODEV - Device is not available
+ */
+static int nx842_OF_upd(struct property *new_prop)
+{
+ struct nx842_devdata *old_devdata = NULL;
+ struct nx842_devdata *new_devdata = NULL;
+ struct device_node *of_node = NULL;
+ struct property *status = NULL;
+ struct property *maxsglen = NULL;
+ struct property *maxsyncop = NULL;
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devdata_mutex, flags);
+ old_devdata = rcu_dereference_check(devdata,
+ lockdep_is_held(&devdata_mutex));
+ if (old_devdata)
+ of_node = old_devdata->dev->of_node;
+
+ if (!old_devdata || !of_node) {
+ pr_err("%s: device is not available\n", __func__);
+ spin_unlock_irqrestore(&devdata_mutex, flags);
+ return -ENODEV;
+ }
+
+ new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS);
+ if (!new_devdata) {
+ dev_err(old_devdata->dev, "%s: Could not allocate memory for device data\n", __func__);
+ ret = -ENOMEM;
+ goto error_out;
+ }
+
+ memcpy(new_devdata, old_devdata, sizeof(*old_devdata));
+ new_devdata->counters = old_devdata->counters;
+
+ /* Set ptrs for existing properties */
+ status = of_find_property(of_node, "status", NULL);
+ maxsglen = of_find_property(of_node, "ibm,max-sg-len", NULL);
+ maxsyncop = of_find_property(of_node, "ibm,max-sync-cop", NULL);
+ if (!status || !maxsglen || !maxsyncop) {
+ dev_err(old_devdata->dev, "%s: Could not locate device properties\n", __func__);
+ ret = -EINVAL;
+ goto error_out;
+ }
+
+ /*
+ * If this is a property update, there are only certain properties that
+ * we care about. Bail if it isn't in the below list
+ */
+ if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) ||
+ strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) ||
+ strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length)))
+ goto out;
+
+ /* Perform property updates */
+ ret = nx842_OF_upd_status(new_devdata, status);
+ if (ret)
+ goto error_out;
+
+ ret = nx842_OF_upd_maxsglen(new_devdata, maxsglen);
+ if (ret)
+ goto error_out;
+
+ ret = nx842_OF_upd_maxsyncop(new_devdata, maxsyncop);
+ if (ret)
+ goto error_out;
+
+out:
+ dev_info(old_devdata->dev, "%s: max_sync_size new:%u old:%u\n",
+ __func__, new_devdata->max_sync_size,
+ old_devdata->max_sync_size);
+ dev_info(old_devdata->dev, "%s: max_sync_sg new:%u old:%u\n",
+ __func__, new_devdata->max_sync_sg,
+ old_devdata->max_sync_sg);
+ dev_info(old_devdata->dev, "%s: max_sg_len new:%u old:%u\n",
+ __func__, new_devdata->max_sg_len,
+ old_devdata->max_sg_len);
+
+ rcu_assign_pointer(devdata, new_devdata);
+ spin_unlock_irqrestore(&devdata_mutex, flags);
+ synchronize_rcu();
+ dev_set_drvdata(new_devdata->dev, new_devdata);
+ kfree(old_devdata);
+ return 0;
+
+error_out:
+ if (new_devdata) {
+ dev_info(old_devdata->dev, "%s: device disabled\n", __func__);
+ nx842_OF_set_defaults(new_devdata);
+ rcu_assign_pointer(devdata, new_devdata);
+ spin_unlock_irqrestore(&devdata_mutex, flags);
+ synchronize_rcu();
+ dev_set_drvdata(new_devdata->dev, new_devdata);
+ kfree(old_devdata);
+ } else {
+ dev_err(old_devdata->dev, "%s: could not update driver from hardware\n", __func__);
+ spin_unlock_irqrestore(&devdata_mutex, flags);
+ }
+
+ if (!ret)
+ ret = -EINVAL;
+ return ret;
+}
+
+/**
+ * nx842_OF_notifier - Process updates to OF properties for the device
+ *
+ * @np: notifier block
+ * @action: notifier action
+ * @update: struct pSeries_reconfig_prop_update pointer if action is
+ * PSERIES_UPDATE_PROPERTY
+ *
+ * Returns:
+ * NOTIFY_OK on success
+ * NOTIFY_BAD encoded with error number on failure, use
+ * notifier_to_errno() to decode this value
+ */
+static int nx842_OF_notifier(struct notifier_block *np, unsigned long action,
+ void *data)
+{
+ struct of_reconfig_data *upd = data;
+ struct nx842_devdata *local_devdata;
+ struct device_node *node = NULL;
+
+ rcu_read_lock();
+ local_devdata = rcu_dereference(devdata);
+ if (local_devdata)
+ node = local_devdata->dev->of_node;
+
+ if (local_devdata &&
+ action == OF_RECONFIG_UPDATE_PROPERTY &&
+ !strcmp(upd->dn->name, node->name)) {
+ rcu_read_unlock();
+ nx842_OF_upd(upd->prop);
+ } else
+ rcu_read_unlock();
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block nx842_of_nb = {
+ .notifier_call = nx842_OF_notifier,
+};
+
+#define nx842_counter_read(_name) \
+static ssize_t nx842_##_name##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) { \
+ struct nx842_devdata *local_devdata; \
+ int p = 0; \
+ rcu_read_lock(); \
+ local_devdata = rcu_dereference(devdata); \
+ if (local_devdata) \
+ p = snprintf(buf, PAGE_SIZE, "%ld\n", \
+ atomic64_read(&local_devdata->counters->_name)); \
+ rcu_read_unlock(); \
+ return p; \
+}
+
+#define NX842DEV_COUNTER_ATTR_RO(_name) \
+ nx842_counter_read(_name); \
+ static struct device_attribute dev_attr_##_name = __ATTR(_name, \
+ 0444, \
+ nx842_##_name##_show,\
+ NULL);
+
+NX842DEV_COUNTER_ATTR_RO(comp_complete);
+NX842DEV_COUNTER_ATTR_RO(comp_failed);
+NX842DEV_COUNTER_ATTR_RO(decomp_complete);
+NX842DEV_COUNTER_ATTR_RO(decomp_failed);
+NX842DEV_COUNTER_ATTR_RO(swdecomp);
+
+static ssize_t nx842_timehist_show(struct device *,
+ struct device_attribute *, char *);
+
+static struct device_attribute dev_attr_comp_times = __ATTR(comp_times, 0444,
+ nx842_timehist_show, NULL);
+static struct device_attribute dev_attr_decomp_times = __ATTR(decomp_times,
+ 0444, nx842_timehist_show, NULL);
+
+static ssize_t nx842_timehist_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ char *p = buf;
+ struct nx842_devdata *local_devdata;
+ atomic64_t *times;
+ int bytes_remain = PAGE_SIZE;
+ int bytes;
+ int i;
+
+ rcu_read_lock();
+ local_devdata = rcu_dereference(devdata);
+ if (!local_devdata) {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ if (attr == &dev_attr_comp_times)
+ times = local_devdata->counters->comp_times;
+ else if (attr == &dev_attr_decomp_times)
+ times = local_devdata->counters->decomp_times;
+ else {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ for (i = 0; i < (NX842_HIST_SLOTS - 2); i++) {
+ bytes = snprintf(p, bytes_remain, "%u-%uus:\t%ld\n",
+ i ? (2<<(i-1)) : 0, (2<<i)-1,
+ atomic64_read(&times[i]));
+ bytes_remain -= bytes;
+ p += bytes;
+ }
+ /* The last bucket holds everything over
+ * 2<<(NX842_HIST_SLOTS - 2) us */
+ bytes = snprintf(p, bytes_remain, "%uus - :\t%ld\n",
+ 2<<(NX842_HIST_SLOTS - 2),
+ atomic64_read(&times[(NX842_HIST_SLOTS - 1)]));
+ p += bytes;
+
+ rcu_read_unlock();
+ return p - buf;
+}
+
+static struct attribute *nx842_sysfs_entries[] = {
+ &dev_attr_comp_complete.attr,
+ &dev_attr_comp_failed.attr,
+ &dev_attr_decomp_complete.attr,
+ &dev_attr_decomp_failed.attr,
+ &dev_attr_swdecomp.attr,
+ &dev_attr_comp_times.attr,
+ &dev_attr_decomp_times.attr,
+ NULL,
+};
+
+static struct attribute_group nx842_attribute_group = {
+ .name = NULL, /* put in device directory */
+ .attrs = nx842_sysfs_entries,
+};
+
+static struct nx842_driver nx842_pseries_driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .workmem_size = sizeof(struct nx842_workmem),
+ .constraints = &nx842_pseries_constraints,
+ .compress = nx842_pseries_compress,
+ .decompress = nx842_pseries_decompress,
+};
+
+static int __init nx842_probe(struct vio_dev *viodev,
+ const struct vio_device_id *id)
+{
+ struct nx842_devdata *old_devdata, *new_devdata = NULL;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&devdata_mutex, flags);
+ old_devdata = rcu_dereference_check(devdata,
+ lockdep_is_held(&devdata_mutex));
+
+ if (old_devdata && old_devdata->vdev != NULL) {
+ dev_err(&viodev->dev, "%s: Attempt to register more than one instance of the hardware\n", __func__);
+ ret = -1;
+ goto error_unlock;
+ }
+
+ dev_set_drvdata(&viodev->dev, NULL);
+
+ new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS);
+ if (!new_devdata) {
+ dev_err(&viodev->dev, "%s: Could not allocate memory for device data\n", __func__);
+ ret = -ENOMEM;
+ goto error_unlock;
+ }
+
+ new_devdata->counters = kzalloc(sizeof(*new_devdata->counters),
+ GFP_NOFS);
+ if (!new_devdata->counters) {
+ dev_err(&viodev->dev, "%s: Could not allocate memory for performance counters\n", __func__);
+ ret = -ENOMEM;
+ goto error_unlock;
+ }
+
+ new_devdata->vdev = viodev;
+ new_devdata->dev = &viodev->dev;
+ nx842_OF_set_defaults(new_devdata);
+
+ rcu_assign_pointer(devdata, new_devdata);
+ spin_unlock_irqrestore(&devdata_mutex, flags);
+ synchronize_rcu();
+ kfree(old_devdata);
+
+ of_reconfig_notifier_register(&nx842_of_nb);
+
+ ret = nx842_OF_upd(NULL);
+ if (ret && ret != -ENODEV) {
+ dev_err(&viodev->dev, "could not parse device tree. %d\n", ret);
+ ret = -1;
+ goto error;
+ }
+
+ rcu_read_lock();
+ dev_set_drvdata(&viodev->dev, rcu_dereference(devdata));
+ rcu_read_unlock();
+
+ if (sysfs_create_group(&viodev->dev.kobj, &nx842_attribute_group)) {
+ dev_err(&viodev->dev, "could not create sysfs device attributes\n");
+ ret = -1;
+ goto error;
+ }
+
+ return 0;
+
+error_unlock:
+ spin_unlock_irqrestore(&devdata_mutex, flags);
+ if (new_devdata)
+ kfree(new_devdata->counters);
+ kfree(new_devdata);
+error:
+ return ret;
+}
+
+static int __exit nx842_remove(struct vio_dev *viodev)
+{
+ struct nx842_devdata *old_devdata;
+ unsigned long flags;
+
+ pr_info("Removing IBM Power 842 compression device\n");
+ sysfs_remove_group(&viodev->dev.kobj, &nx842_attribute_group);
+
+ spin_lock_irqsave(&devdata_mutex, flags);
+ old_devdata = rcu_dereference_check(devdata,
+ lockdep_is_held(&devdata_mutex));
+ of_reconfig_notifier_unregister(&nx842_of_nb);
+ RCU_INIT_POINTER(devdata, NULL);
+ spin_unlock_irqrestore(&devdata_mutex, flags);
+ synchronize_rcu();
+ dev_set_drvdata(&viodev->dev, NULL);
+ if (old_devdata)
+ kfree(old_devdata->counters);
+ kfree(old_devdata);
+
+ return 0;
+}
+
+static struct vio_device_id nx842_vio_driver_ids[] = {
+ {"ibm,compression-v1", "ibm,compression"},
+ {"", ""},
+};
+
+static struct vio_driver nx842_vio_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = nx842_probe,
+ .remove = __exit_p(nx842_remove),
+ .get_desired_dma = nx842_get_desired_dma,
+ .id_table = nx842_vio_driver_ids,
+};
+
+static int __init nx842_init(void)
+{
+ struct nx842_devdata *new_devdata;
+ int ret;
+
+ pr_info("Registering IBM Power 842 compression driver\n");
+
+ if (!of_find_compatible_node(NULL, NULL, "ibm,compression"))
+ return -ENODEV;
+
+ RCU_INIT_POINTER(devdata, NULL);
+ new_devdata = kzalloc(sizeof(*new_devdata), GFP_KERNEL);
+ if (!new_devdata) {
+ pr_err("Could not allocate memory for device data\n");
+ return -ENOMEM;
+ }
+ new_devdata->status = UNAVAILABLE;
+ RCU_INIT_POINTER(devdata, new_devdata);
+
+ ret = vio_register_driver(&nx842_vio_driver);
+ if (ret) {
+ pr_err("Could not register VIO driver %d\n", ret);
+
+ kfree(new_devdata);
+ return ret;
+ }
+
+ if (!nx842_platform_driver_set(&nx842_pseries_driver)) {
+ vio_unregister_driver(&nx842_vio_driver);
+ kfree(new_devdata);
+ return -EEXIST;
+ }
+
+ return 0;
+}
+
+module_init(nx842_init);
+
+static void __exit nx842_exit(void)
+{
+ struct nx842_devdata *old_devdata;
+ unsigned long flags;
+
+ pr_info("Exiting IBM Power 842 compression driver\n");
+ nx842_platform_driver_unset(&nx842_pseries_driver);
+ spin_lock_irqsave(&devdata_mutex, flags);
+ old_devdata = rcu_dereference_check(devdata,
+ lockdep_is_held(&devdata_mutex));
+ RCU_INIT_POINTER(devdata, NULL);
+ spin_unlock_irqrestore(&devdata_mutex, flags);
+ synchronize_rcu();
+ if (old_devdata && old_devdata->dev)
+ dev_set_drvdata(old_devdata->dev, NULL);
+ kfree(old_devdata);
+ vio_unregister_driver(&nx842_vio_driver);
+}
+
+module_exit(nx842_exit);
+
diff --git a/drivers/crypto/nx/nx-842.c b/drivers/crypto/nx/nx-842.c
index 887196e9b50c..6e5e0d60d0c8 100644
--- a/drivers/crypto/nx/nx-842.c
+++ b/drivers/crypto/nx/nx-842.c
@@ -1,5 +1,10 @@
/*
- * Driver for IBM Power 842 compression accelerator
+ * Driver frontend for IBM Power 842 compression accelerator
+ *
+ * Copyright (C) 2015 Dan Streetman, IBM Corp
+ *
+ * Designer of the Power data compression engine:
+ * Bulent Abali <abali@us.ibm.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
@@ -10,1594 +15,89 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright (C) IBM Corporation, 2012
- *
- * Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
- * Seth Jennings <sjenning@linux.vnet.ibm.com>
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/nx842.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-
-#include <asm/page.h>
-#include <asm/vio.h>
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include "nx_csbcpb.h" /* struct nx_csbcpb */
+#include "nx-842.h"
-#define MODULE_NAME "nx-compress"
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Robert Jennings <rcj@linux.vnet.ibm.com>");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
MODULE_DESCRIPTION("842 H/W Compression driver for IBM Power processors");
-#define SHIFT_4K 12
-#define SHIFT_64K 16
-#define SIZE_4K (1UL << SHIFT_4K)
-#define SIZE_64K (1UL << SHIFT_64K)
-
-/* IO buffer must be 128 byte aligned */
-#define IO_BUFFER_ALIGN 128
-
-struct nx842_header {
- int blocks_nr; /* number of compressed blocks */
- int offset; /* offset of the first block (from beginning of header) */
- int sizes[0]; /* size of compressed blocks */
-};
-
-static inline int nx842_header_size(const struct nx842_header *hdr)
-{
- return sizeof(struct nx842_header) +
- hdr->blocks_nr * sizeof(hdr->sizes[0]);
-}
-
-/* Macros for fields within nx_csbcpb */
-/* Check the valid bit within the csbcpb valid field */
-#define NX842_CSBCBP_VALID_CHK(x) (x & BIT_MASK(7))
-
-/* CE macros operate on the completion_extension field bits in the csbcpb.
- * CE0 0=full completion, 1=partial completion
- * CE1 0=CE0 indicates completion, 1=termination (output may be modified)
- * CE2 0=processed_bytes is source bytes, 1=processed_bytes is target bytes */
-#define NX842_CSBCPB_CE0(x) (x & BIT_MASK(7))
-#define NX842_CSBCPB_CE1(x) (x & BIT_MASK(6))
-#define NX842_CSBCPB_CE2(x) (x & BIT_MASK(5))
-
-/* The NX unit accepts data only on 4K page boundaries */
-#define NX842_HW_PAGE_SHIFT SHIFT_4K
-#define NX842_HW_PAGE_SIZE (ASM_CONST(1) << NX842_HW_PAGE_SHIFT)
-#define NX842_HW_PAGE_MASK (~(NX842_HW_PAGE_SIZE-1))
-
-enum nx842_status {
- UNAVAILABLE,
- AVAILABLE
-};
-
-struct ibm_nx842_counters {
- atomic64_t comp_complete;
- atomic64_t comp_failed;
- atomic64_t decomp_complete;
- atomic64_t decomp_failed;
- atomic64_t swdecomp;
- atomic64_t comp_times[32];
- atomic64_t decomp_times[32];
-};
-
-static struct nx842_devdata {
- struct vio_dev *vdev;
- struct device *dev;
- struct ibm_nx842_counters *counters;
- unsigned int max_sg_len;
- unsigned int max_sync_size;
- unsigned int max_sync_sg;
- enum nx842_status status;
-} __rcu *devdata;
-static DEFINE_SPINLOCK(devdata_mutex);
-
-#define NX842_COUNTER_INC(_x) \
-static inline void nx842_inc_##_x( \
- const struct nx842_devdata *dev) { \
- if (dev) \
- atomic64_inc(&dev->counters->_x); \
-}
-NX842_COUNTER_INC(comp_complete);
-NX842_COUNTER_INC(comp_failed);
-NX842_COUNTER_INC(decomp_complete);
-NX842_COUNTER_INC(decomp_failed);
-NX842_COUNTER_INC(swdecomp);
-
-#define NX842_HIST_SLOTS 16
-
-static void ibm_nx842_incr_hist(atomic64_t *times, unsigned int time)
-{
- int bucket = fls(time);
-
- if (bucket)
- bucket = min((NX842_HIST_SLOTS - 1), bucket - 1);
-
- atomic64_inc(&times[bucket]);
-}
-
-/* NX unit operation flags */
-#define NX842_OP_COMPRESS 0x0
-#define NX842_OP_CRC 0x1
-#define NX842_OP_DECOMPRESS 0x2
-#define NX842_OP_COMPRESS_CRC (NX842_OP_COMPRESS | NX842_OP_CRC)
-#define NX842_OP_DECOMPRESS_CRC (NX842_OP_DECOMPRESS | NX842_OP_CRC)
-#define NX842_OP_ASYNC (1<<23)
-#define NX842_OP_NOTIFY (1<<22)
-#define NX842_OP_NOTIFY_INT(x) ((x & 0xff)<<8)
-
-static unsigned long nx842_get_desired_dma(struct vio_dev *viodev)
-{
- /* No use of DMA mappings within the driver. */
- return 0;
-}
-
-struct nx842_slentry {
- unsigned long ptr; /* Real address (use __pa()) */
- unsigned long len;
-};
-
-/* pHyp scatterlist entry */
-struct nx842_scatterlist {
- int entry_nr; /* number of slentries */
- struct nx842_slentry *entries; /* ptr to array of slentries */
-};
-
-/* Does not include sizeof(entry_nr) in the size */
-static inline unsigned long nx842_get_scatterlist_size(
- struct nx842_scatterlist *sl)
-{
- return sl->entry_nr * sizeof(struct nx842_slentry);
-}
-
-static inline unsigned long nx842_get_pa(void *addr)
-{
- if (is_vmalloc_addr(addr))
- return page_to_phys(vmalloc_to_page(addr))
- + offset_in_page(addr);
- else
- return __pa(addr);
-}
-
-static int nx842_build_scatterlist(unsigned long buf, int len,
- struct nx842_scatterlist *sl)
-{
- unsigned long nextpage;
- struct nx842_slentry *entry;
-
- sl->entry_nr = 0;
-
- entry = sl->entries;
- while (len) {
- entry->ptr = nx842_get_pa((void *)buf);
- nextpage = ALIGN(buf + 1, NX842_HW_PAGE_SIZE);
- if (nextpage < buf + len) {
- /* we aren't at the end yet */
- if (IS_ALIGNED(buf, NX842_HW_PAGE_SIZE))
- /* we are in the middle (or beginning) */
- entry->len = NX842_HW_PAGE_SIZE;
- else
- /* we are at the beginning */
- entry->len = nextpage - buf;
- } else {
- /* at the end */
- entry->len = len;
- }
-
- len -= entry->len;
- buf += entry->len;
- sl->entry_nr++;
- entry++;
- }
-
- return 0;
-}
-
-/*
- * Working memory for software decompression
- */
-struct sw842_fifo {
- union {
- char f8[256][8];
- char f4[512][4];
- };
- char f2[256][2];
- unsigned char f84_full;
- unsigned char f2_full;
- unsigned char f8_count;
- unsigned char f2_count;
- unsigned int f4_count;
-};
-
-/*
- * Working memory for crypto API
+/**
+ * nx842_constraints
+ *
+ * This provides the driver's constraints. Different nx842 implementations
+ * may have varying requirements. The constraints are:
+ * @alignment: All buffers should be aligned to this
+ * @multiple: All buffer lengths should be a multiple of this
+ * @minimum: Buffer lengths must not be less than this amount
+ * @maximum: Buffer lengths must not be more than this amount
+ *
+ * The constraints apply to all buffers and lengths, both input and output,
+ * for both compression and decompression, except for the minimum which
+ * only applies to compression input and decompression output; the
+ * compressed data can be less than the minimum constraint. It can be
+ * assumed that compressed data will always adhere to the multiple
+ * constraint.
+ *
+ * The driver may succeed even if these constraints are violated;
+ * however the driver can return failure or suffer reduced performance
+ * if any constraint is not met.
*/
-struct nx842_workmem {
- char bounce[PAGE_SIZE]; /* bounce buffer for decompression input */
- union {
- /* hardware working memory */
- struct {
- /* scatterlist */
- char slin[SIZE_4K];
- char slout[SIZE_4K];
- /* coprocessor status/parameter block */
- struct nx_csbcpb csbcpb;
- };
- /* software working memory */
- struct sw842_fifo swfifo; /* software decompression fifo */
- };
-};
-
-int nx842_get_workmem_size(void)
-{
- return sizeof(struct nx842_workmem) + NX842_HW_PAGE_SIZE;
-}
-EXPORT_SYMBOL_GPL(nx842_get_workmem_size);
-
-int nx842_get_workmem_size_aligned(void)
-{
- return sizeof(struct nx842_workmem);
-}
-EXPORT_SYMBOL_GPL(nx842_get_workmem_size_aligned);
-
-static int nx842_validate_result(struct device *dev,
- struct cop_status_block *csb)
+int nx842_constraints(struct nx842_constraints *c)
{
- /* The csb must be valid after returning from vio_h_cop_sync */
- if (!NX842_CSBCBP_VALID_CHK(csb->valid)) {
- dev_err(dev, "%s: cspcbp not valid upon completion.\n",
- __func__);
- dev_dbg(dev, "valid:0x%02x cs:0x%02x cc:0x%02x ce:0x%02x\n",
- csb->valid,
- csb->crb_seq_number,
- csb->completion_code,
- csb->completion_extension);
- dev_dbg(dev, "processed_bytes:%d address:0x%016lx\n",
- csb->processed_byte_count,
- (unsigned long)csb->address);
- return -EIO;
- }
-
- /* Check return values from the hardware in the CSB */
- switch (csb->completion_code) {
- case 0: /* Completed without error */
- break;
- case 64: /* Target bytes > Source bytes during compression */
- case 13: /* Output buffer too small */
- dev_dbg(dev, "%s: Compression output larger than input\n",
- __func__);
- return -ENOSPC;
- case 66: /* Input data contains an illegal template field */
- case 67: /* Template indicates data past the end of the input stream */
- dev_dbg(dev, "%s: Bad data for decompression (code:%d)\n",
- __func__, csb->completion_code);
- return -EINVAL;
- default:
- dev_dbg(dev, "%s: Unspecified error (code:%d)\n",
- __func__, csb->completion_code);
- return -EIO;
- }
-
- /* Hardware sanity check */
- if (!NX842_CSBCPB_CE2(csb->completion_extension)) {
- dev_err(dev, "%s: No error returned by hardware, but "
- "data returned is unusable, contact support.\n"
- "(Additional info: csbcbp->processed bytes "
- "does not specify processed bytes for the "
- "target buffer.)\n", __func__);
- return -EIO;
- }
-
+ memcpy(c, nx842_platform_driver()->constraints, sizeof(*c));
return 0;
}
+EXPORT_SYMBOL_GPL(nx842_constraints);
/**
- * nx842_compress - Compress data using the 842 algorithm
- *
- * Compression provide by the NX842 coprocessor on IBM Power systems.
- * The input buffer is compressed and the result is stored in the
- * provided output buffer.
- *
- * Upon return from this function @outlen contains the length of the
- * compressed data. If there is an error then @outlen will be 0 and an
- * error will be specified by the return code from this function.
- *
- * @in: Pointer to input buffer, must be page aligned
- * @inlen: Length of input buffer, must be PAGE_SIZE
- * @out: Pointer to output buffer
- * @outlen: Length of output buffer
- * @wrkmem: ptr to buffer for working memory, size determined by
- * nx842_get_workmem_size()
+ * nx842_workmem_size
*
- * Returns:
- * 0 Success, output of length @outlen stored in the buffer at @out
- * -ENOMEM Unable to allocate internal buffers
- * -ENOSPC Output buffer is to small
- * -EMSGSIZE XXX Difficult to describe this limitation
- * -EIO Internal error
- * -ENODEV Hardware unavailable
+ * Get the amount of working memory the driver requires.
*/
-int nx842_compress(const unsigned char *in, unsigned int inlen,
- unsigned char *out, unsigned int *outlen, void *wmem)
+size_t nx842_workmem_size(void)
{
- struct nx842_header *hdr;
- struct nx842_devdata *local_devdata;
- struct device *dev = NULL;
- struct nx842_workmem *workmem;
- struct nx842_scatterlist slin, slout;
- struct nx_csbcpb *csbcpb;
- int ret = 0, max_sync_size, i, bytesleft, size, hdrsize;
- unsigned long inbuf, outbuf, padding;
- struct vio_pfo_op op = {
- .done = NULL,
- .handle = 0,
- .timeout = 0,
- };
- unsigned long start_time = get_tb();
-
- /*
- * Make sure input buffer is 64k page aligned. This is assumed since
- * this driver is designed for page compression only (for now). This
- * is very nice since we can now use direct DDE(s) for the input and
- * the alignment is guaranteed.
- */
- inbuf = (unsigned long)in;
- if (!IS_ALIGNED(inbuf, PAGE_SIZE) || inlen != PAGE_SIZE)
- return -EINVAL;
-
- rcu_read_lock();
- local_devdata = rcu_dereference(devdata);
- if (!local_devdata || !local_devdata->dev) {
- rcu_read_unlock();
- return -ENODEV;
- }
- max_sync_size = local_devdata->max_sync_size;
- dev = local_devdata->dev;
-
- /* Create the header */
- hdr = (struct nx842_header *)out;
- hdr->blocks_nr = PAGE_SIZE / max_sync_size;
- hdrsize = nx842_header_size(hdr);
- outbuf = (unsigned long)out + hdrsize;
- bytesleft = *outlen - hdrsize;
-
- /* Init scatterlist */
- workmem = (struct nx842_workmem *)ALIGN((unsigned long)wmem,
- NX842_HW_PAGE_SIZE);
- slin.entries = (struct nx842_slentry *)workmem->slin;
- slout.entries = (struct nx842_slentry *)workmem->slout;
-
- /* Init operation */
- op.flags = NX842_OP_COMPRESS;
- csbcpb = &workmem->csbcpb;
- memset(csbcpb, 0, sizeof(*csbcpb));
- op.csbcpb = nx842_get_pa(csbcpb);
- op.out = nx842_get_pa(slout.entries);
-
- for (i = 0; i < hdr->blocks_nr; i++) {
- /*
- * Aligning the output blocks to 128 bytes does waste space,
- * but it prevents the need for bounce buffers and memory
- * copies. It also simplifies the code a lot. In the worst
- * case (64k page, 4k max_sync_size), you lose up to
- * (128*16)/64k = ~3% the compression factor. For 64k
- * max_sync_size, the loss would be at most 128/64k = ~0.2%.
- */
- padding = ALIGN(outbuf, IO_BUFFER_ALIGN) - outbuf;
- outbuf += padding;
- bytesleft -= padding;
- if (i == 0)
- /* save offset into first block in header */
- hdr->offset = padding + hdrsize;
-
- if (bytesleft <= 0) {
- ret = -ENOSPC;
- goto unlock;
- }
-
- /*
- * NOTE: If the default max_sync_size is changed from 4k
- * to 64k, remove the "likely" case below, since a
- * scatterlist will always be needed.
- */
- if (likely(max_sync_size == NX842_HW_PAGE_SIZE)) {
- /* Create direct DDE */
- op.in = nx842_get_pa((void *)inbuf);
- op.inlen = max_sync_size;
-
- } else {
- /* Create indirect DDE (scatterlist) */
- nx842_build_scatterlist(inbuf, max_sync_size, &slin);
- op.in = nx842_get_pa(slin.entries);
- op.inlen = -nx842_get_scatterlist_size(&slin);
- }
-
- /*
- * If max_sync_size != NX842_HW_PAGE_SIZE, an indirect
- * DDE is required for the outbuf.
- * If max_sync_size == NX842_HW_PAGE_SIZE, outbuf must
- * also be page aligned (1 in 128/4k=32 chance) in order
- * to use a direct DDE.
- * This is unlikely, just use an indirect DDE always.
- */
- nx842_build_scatterlist(outbuf,
- min(bytesleft, max_sync_size), &slout);
- /* op.out set before loop */
- op.outlen = -nx842_get_scatterlist_size(&slout);
-
- /* Send request to pHyp */
- ret = vio_h_cop_sync(local_devdata->vdev, &op);
-
- /* Check for pHyp error */
- if (ret) {
- dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
- __func__, ret, op.hcall_err);
- ret = -EIO;
- goto unlock;
- }
-
- /* Check for hardware error */
- ret = nx842_validate_result(dev, &csbcpb->csb);
- if (ret && ret != -ENOSPC)
- goto unlock;
-
- /* Handle incompressible data */
- if (unlikely(ret == -ENOSPC)) {
- if (bytesleft < max_sync_size) {
- /*
- * Not enough space left in the output buffer
- * to store uncompressed block
- */
- goto unlock;
- } else {
- /* Store incompressible block */
- memcpy((void *)outbuf, (void *)inbuf,
- max_sync_size);
- hdr->sizes[i] = -max_sync_size;
- outbuf += max_sync_size;
- bytesleft -= max_sync_size;
- /* Reset ret, incompressible data handled */
- ret = 0;
- }
- } else {
- /* Normal case, compression was successful */
- size = csbcpb->csb.processed_byte_count;
- dev_dbg(dev, "%s: processed_bytes=%d\n",
- __func__, size);
- hdr->sizes[i] = size;
- outbuf += size;
- bytesleft -= size;
- }
-
- inbuf += max_sync_size;
- }
-
- *outlen = (unsigned int)(outbuf - (unsigned long)out);
-
-unlock:
- if (ret)
- nx842_inc_comp_failed(local_devdata);
- else {
- nx842_inc_comp_complete(local_devdata);
- ibm_nx842_incr_hist(local_devdata->counters->comp_times,
- (get_tb() - start_time) / tb_ticks_per_usec);
- }
- rcu_read_unlock();
- return ret;
+ return nx842_platform_driver()->workmem_size;
}
-EXPORT_SYMBOL_GPL(nx842_compress);
-
-static int sw842_decompress(const unsigned char *, int, unsigned char *, int *,
- const void *);
+EXPORT_SYMBOL_GPL(nx842_workmem_size);
-/**
- * nx842_decompress - Decompress data using the 842 algorithm
- *
- * Decompression provide by the NX842 coprocessor on IBM Power systems.
- * The input buffer is decompressed and the result is stored in the
- * provided output buffer. The size allocated to the output buffer is
- * provided by the caller of this function in @outlen. Upon return from
- * this function @outlen contains the length of the decompressed data.
- * If there is an error then @outlen will be 0 and an error will be
- * specified by the return code from this function.
- *
- * @in: Pointer to input buffer, will use bounce buffer if not 128 byte
- * aligned
- * @inlen: Length of input buffer
- * @out: Pointer to output buffer, must be page aligned
- * @outlen: Length of output buffer, must be PAGE_SIZE
- * @wrkmem: ptr to buffer for working memory, size determined by
- * nx842_get_workmem_size()
- *
- * Returns:
- * 0 Success, output of length @outlen stored in the buffer at @out
- * -ENODEV Hardware decompression device is unavailable
- * -ENOMEM Unable to allocate internal buffers
- * -ENOSPC Output buffer is to small
- * -EINVAL Bad input data encountered when attempting decompress
- * -EIO Internal error
- */
-int nx842_decompress(const unsigned char *in, unsigned int inlen,
- unsigned char *out, unsigned int *outlen, void *wmem)
+int nx842_compress(const unsigned char *in, unsigned int ilen,
+ unsigned char *out, unsigned int *olen, void *wmem)
{
- struct nx842_header *hdr;
- struct nx842_devdata *local_devdata;
- struct device *dev = NULL;
- struct nx842_workmem *workmem;
- struct nx842_scatterlist slin, slout;
- struct nx_csbcpb *csbcpb;
- int ret = 0, i, size, max_sync_size;
- unsigned long inbuf, outbuf;
- struct vio_pfo_op op = {
- .done = NULL,
- .handle = 0,
- .timeout = 0,
- };
- unsigned long start_time = get_tb();
-
- /* Ensure page alignment and size */
- outbuf = (unsigned long)out;
- if (!IS_ALIGNED(outbuf, PAGE_SIZE) || *outlen != PAGE_SIZE)
- return -EINVAL;
-
- rcu_read_lock();
- local_devdata = rcu_dereference(devdata);
- if (local_devdata)
- dev = local_devdata->dev;
-
- /* Get header */
- hdr = (struct nx842_header *)in;
-
- workmem = (struct nx842_workmem *)ALIGN((unsigned long)wmem,
- NX842_HW_PAGE_SIZE);
-
- inbuf = (unsigned long)in + hdr->offset;
- if (likely(!IS_ALIGNED(inbuf, IO_BUFFER_ALIGN))) {
- /* Copy block(s) into bounce buffer for alignment */
- memcpy(workmem->bounce, in + hdr->offset, inlen - hdr->offset);
- inbuf = (unsigned long)workmem->bounce;
- }
-
- /* Init scatterlist */
- slin.entries = (struct nx842_slentry *)workmem->slin;
- slout.entries = (struct nx842_slentry *)workmem->slout;
-
- /* Init operation */
- op.flags = NX842_OP_DECOMPRESS;
- csbcpb = &workmem->csbcpb;
- memset(csbcpb, 0, sizeof(*csbcpb));
- op.csbcpb = nx842_get_pa(csbcpb);
-
- /*
- * max_sync_size may have changed since compression,
- * so we can't read it from the device info. We need
- * to derive it from hdr->blocks_nr.
- */
- max_sync_size = PAGE_SIZE / hdr->blocks_nr;
-
- for (i = 0; i < hdr->blocks_nr; i++) {
- /* Skip padding */
- inbuf = ALIGN(inbuf, IO_BUFFER_ALIGN);
-
- if (hdr->sizes[i] < 0) {
- /* Negative sizes indicate uncompressed data blocks */
- size = abs(hdr->sizes[i]);
- memcpy((void *)outbuf, (void *)inbuf, size);
- outbuf += size;
- inbuf += size;
- continue;
- }
-
- if (!dev)
- goto sw;
-
- /*
- * The better the compression, the more likely the "likely"
- * case becomes.
- */
- if (likely((inbuf & NX842_HW_PAGE_MASK) ==
- ((inbuf + hdr->sizes[i] - 1) & NX842_HW_PAGE_MASK))) {
- /* Create direct DDE */
- op.in = nx842_get_pa((void *)inbuf);
- op.inlen = hdr->sizes[i];
- } else {
- /* Create indirect DDE (scatterlist) */
- nx842_build_scatterlist(inbuf, hdr->sizes[i] , &slin);
- op.in = nx842_get_pa(slin.entries);
- op.inlen = -nx842_get_scatterlist_size(&slin);
- }
-
- /*
- * NOTE: If the default max_sync_size is changed from 4k
- * to 64k, remove the "likely" case below, since a
- * scatterlist will always be needed.
- */
- if (likely(max_sync_size == NX842_HW_PAGE_SIZE)) {
- /* Create direct DDE */
- op.out = nx842_get_pa((void *)outbuf);
- op.outlen = max_sync_size;
- } else {
- /* Create indirect DDE (scatterlist) */
- nx842_build_scatterlist(outbuf, max_sync_size, &slout);
- op.out = nx842_get_pa(slout.entries);
- op.outlen = -nx842_get_scatterlist_size(&slout);
- }
-
- /* Send request to pHyp */
- ret = vio_h_cop_sync(local_devdata->vdev, &op);
-
- /* Check for pHyp error */
- if (ret) {
- dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
- __func__, ret, op.hcall_err);
- dev = NULL;
- goto sw;
- }
-
- /* Check for hardware error */
- ret = nx842_validate_result(dev, &csbcpb->csb);
- if (ret) {
- dev = NULL;
- goto sw;
- }
-
- /* HW decompression success */
- inbuf += hdr->sizes[i];
- outbuf += csbcpb->csb.processed_byte_count;
- continue;
-
-sw:
- /* software decompression */
- size = max_sync_size;
- ret = sw842_decompress(
- (unsigned char *)inbuf, hdr->sizes[i],
- (unsigned char *)outbuf, &size, wmem);
- if (ret)
- pr_debug("%s: sw842_decompress failed with %d\n",
- __func__, ret);
-
- if (ret) {
- if (ret != -ENOSPC && ret != -EINVAL &&
- ret != -EMSGSIZE)
- ret = -EIO;
- goto unlock;
- }
-
- /* SW decompression success */
- inbuf += hdr->sizes[i];
- outbuf += size;
- }
-
- *outlen = (unsigned int)(outbuf - (unsigned long)out);
-
-unlock:
- if (ret)
- /* decompress fail */
- nx842_inc_decomp_failed(local_devdata);
- else {
- if (!dev)
- /* software decompress */
- nx842_inc_swdecomp(local_devdata);
- nx842_inc_decomp_complete(local_devdata);
- ibm_nx842_incr_hist(local_devdata->counters->decomp_times,
- (get_tb() - start_time) / tb_ticks_per_usec);
- }
-
- rcu_read_unlock();
- return ret;
+ return nx842_platform_driver()->compress(in, ilen, out, olen, wmem);
}
-EXPORT_SYMBOL_GPL(nx842_decompress);
+EXPORT_SYMBOL_GPL(nx842_compress);
-/**
- * nx842_OF_set_defaults -- Set default (disabled) values for devdata
- *
- * @devdata - struct nx842_devdata to update
- *
- * Returns:
- * 0 on success
- * -ENOENT if @devdata ptr is NULL
- */
-static int nx842_OF_set_defaults(struct nx842_devdata *devdata)
+int nx842_decompress(const unsigned char *in, unsigned int ilen,
+ unsigned char *out, unsigned int *olen, void *wmem)
{
- if (devdata) {
- devdata->max_sync_size = 0;
- devdata->max_sync_sg = 0;
- devdata->max_sg_len = 0;
- devdata->status = UNAVAILABLE;
- return 0;
- } else
- return -ENOENT;
-}
-
-/**
- * nx842_OF_upd_status -- Update the device info from OF status prop
- *
- * The status property indicates if the accelerator is enabled. If the
- * device is in the OF tree it indicates that the hardware is present.
- * The status field indicates if the device is enabled when the status
- * is 'okay'. Otherwise the device driver will be disabled.
- *
- * @devdata - struct nx842_devdata to update
- * @prop - struct property point containing the maxsyncop for the update
- *
- * Returns:
- * 0 - Device is available
- * -EINVAL - Device is not available
- */
-static int nx842_OF_upd_status(struct nx842_devdata *devdata,
- struct property *prop) {
- int ret = 0;
- const char *status = (const char *)prop->value;
-
- if (!strncmp(status, "okay", (size_t)prop->length)) {
- devdata->status = AVAILABLE;
- } else {
- dev_info(devdata->dev, "%s: status '%s' is not 'okay'\n",
- __func__, status);
- devdata->status = UNAVAILABLE;
- }
-
- return ret;
-}
-
-/**
- * nx842_OF_upd_maxsglen -- Update the device info from OF maxsglen prop
- *
- * Definition of the 'ibm,max-sg-len' OF property:
- * This field indicates the maximum byte length of a scatter list
- * for the platform facility. It is a single cell encoded as with encode-int.
- *
- * Example:
- * # od -x ibm,max-sg-len
- * 0000000 0000 0ff0
- *
- * In this example, the maximum byte length of a scatter list is
- * 0x0ff0 (4,080).
- *
- * @devdata - struct nx842_devdata to update
- * @prop - struct property point containing the maxsyncop for the update
- *
- * Returns:
- * 0 on success
- * -EINVAL on failure
- */
-static int nx842_OF_upd_maxsglen(struct nx842_devdata *devdata,
- struct property *prop) {
- int ret = 0;
- const int *maxsglen = prop->value;
-
- if (prop->length != sizeof(*maxsglen)) {
- dev_err(devdata->dev, "%s: unexpected format for ibm,max-sg-len property\n", __func__);
- dev_dbg(devdata->dev, "%s: ibm,max-sg-len is %d bytes long, expected %lu bytes\n", __func__,
- prop->length, sizeof(*maxsglen));
- ret = -EINVAL;
- } else {
- devdata->max_sg_len = (unsigned int)min(*maxsglen,
- (int)NX842_HW_PAGE_SIZE);
- }
-
- return ret;
-}
-
-/**
- * nx842_OF_upd_maxsyncop -- Update the device info from OF maxsyncop prop
- *
- * Definition of the 'ibm,max-sync-cop' OF property:
- * Two series of cells. The first series of cells represents the maximums
- * that can be synchronously compressed. The second series of cells
- * represents the maximums that can be synchronously decompressed.
- * 1. The first cell in each series contains the count of the number of
- * data length, scatter list elements pairs that follow – each being
- * of the form
- * a. One cell data byte length
- * b. One cell total number of scatter list elements
- *
- * Example:
- * # od -x ibm,max-sync-cop
- * 0000000 0000 0001 0000 1000 0000 01fe 0000 0001
- * 0000020 0000 1000 0000 01fe
- *
- * In this example, compression supports 0x1000 (4,096) data byte length
- * and 0x1fe (510) total scatter list elements. Decompression supports
- * 0x1000 (4,096) data byte length and 0x1f3 (510) total scatter list
- * elements.
- *
- * @devdata - struct nx842_devdata to update
- * @prop - struct property point containing the maxsyncop for the update
- *
- * Returns:
- * 0 on success
- * -EINVAL on failure
- */
-static int nx842_OF_upd_maxsyncop(struct nx842_devdata *devdata,
- struct property *prop) {
- int ret = 0;
- const struct maxsynccop_t {
- int comp_elements;
- int comp_data_limit;
- int comp_sg_limit;
- int decomp_elements;
- int decomp_data_limit;
- int decomp_sg_limit;
- } *maxsynccop;
-
- if (prop->length != sizeof(*maxsynccop)) {
- dev_err(devdata->dev, "%s: unexpected format for ibm,max-sync-cop property\n", __func__);
- dev_dbg(devdata->dev, "%s: ibm,max-sync-cop is %d bytes long, expected %lu bytes\n", __func__, prop->length,
- sizeof(*maxsynccop));
- ret = -EINVAL;
- goto out;
- }
-
- maxsynccop = (const struct maxsynccop_t *)prop->value;
-
- /* Use one limit rather than separate limits for compression and
- * decompression. Set a maximum for this so as not to exceed the
- * size that the header can support and round the value down to
- * the hardware page size (4K) */
- devdata->max_sync_size =
- (unsigned int)min(maxsynccop->comp_data_limit,
- maxsynccop->decomp_data_limit);
-
- devdata->max_sync_size = min_t(unsigned int, devdata->max_sync_size,
- SIZE_64K);
-
- if (devdata->max_sync_size < SIZE_4K) {
- dev_err(devdata->dev, "%s: hardware max data size (%u) is "
- "less than the driver minimum, unable to use "
- "the hardware device\n",
- __func__, devdata->max_sync_size);
- ret = -EINVAL;
- goto out;
- }
-
- devdata->max_sync_sg = (unsigned int)min(maxsynccop->comp_sg_limit,
- maxsynccop->decomp_sg_limit);
- if (devdata->max_sync_sg < 1) {
- dev_err(devdata->dev, "%s: hardware max sg size (%u) is "
- "less than the driver minimum, unable to use "
- "the hardware device\n",
- __func__, devdata->max_sync_sg);
- ret = -EINVAL;
- goto out;
- }
-
-out:
- return ret;
+ return nx842_platform_driver()->decompress(in, ilen, out, olen, wmem);
}
+EXPORT_SYMBOL_GPL(nx842_decompress);
-/**
- *
- * nx842_OF_upd -- Handle OF properties updates for the device.
- *
- * Set all properties from the OF tree. Optionally, a new property
- * can be provided by the @new_prop pointer to overwrite an existing value.
- * The device will remain disabled until all values are valid, this function
- * will return an error for updates unless all values are valid.
- *
- * @new_prop: If not NULL, this property is being updated. If NULL, update
- * all properties from the current values in the OF tree.
- *
- * Returns:
- * 0 - Success
- * -ENOMEM - Could not allocate memory for new devdata structure
- * -EINVAL - property value not found, new_prop is not a recognized
- * property for the device or property value is not valid.
- * -ENODEV - Device is not available
- */
-static int nx842_OF_upd(struct property *new_prop)
+static __init int nx842_init(void)
{
- struct nx842_devdata *old_devdata = NULL;
- struct nx842_devdata *new_devdata = NULL;
- struct device_node *of_node = NULL;
- struct property *status = NULL;
- struct property *maxsglen = NULL;
- struct property *maxsyncop = NULL;
- int ret = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&devdata_mutex, flags);
- old_devdata = rcu_dereference_check(devdata,
- lockdep_is_held(&devdata_mutex));
- if (old_devdata)
- of_node = old_devdata->dev->of_node;
+ request_module("nx-compress-powernv");
+ request_module("nx-compress-pseries");
- if (!old_devdata || !of_node) {
- pr_err("%s: device is not available\n", __func__);
- spin_unlock_irqrestore(&devdata_mutex, flags);
- return -ENODEV;
- }
-
- new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS);
- if (!new_devdata) {
- dev_err(old_devdata->dev, "%s: Could not allocate memory for device data\n", __func__);
- ret = -ENOMEM;
- goto error_out;
- }
-
- memcpy(new_devdata, old_devdata, sizeof(*old_devdata));
- new_devdata->counters = old_devdata->counters;
-
- /* Set ptrs for existing properties */
- status = of_find_property(of_node, "status", NULL);
- maxsglen = of_find_property(of_node, "ibm,max-sg-len", NULL);
- maxsyncop = of_find_property(of_node, "ibm,max-sync-cop", NULL);
- if (!status || !maxsglen || !maxsyncop) {
- dev_err(old_devdata->dev, "%s: Could not locate device properties\n", __func__);
- ret = -EINVAL;
- goto error_out;
- }
-
- /*
- * If this is a property update, there are only certain properties that
- * we care about. Bail if it isn't in the below list
+ /* we prevent loading if there's no platform driver, and we get the
+ * module that set it so it won't unload, so we don't need to check
+ * if it's set in any of the above functions
*/
- if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) ||
- strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) ||
- strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length)))
- goto out;
-
- /* Perform property updates */
- ret = nx842_OF_upd_status(new_devdata, status);
- if (ret)
- goto error_out;
-
- ret = nx842_OF_upd_maxsglen(new_devdata, maxsglen);
- if (ret)
- goto error_out;
-
- ret = nx842_OF_upd_maxsyncop(new_devdata, maxsyncop);
- if (ret)
- goto error_out;
-
-out:
- dev_info(old_devdata->dev, "%s: max_sync_size new:%u old:%u\n",
- __func__, new_devdata->max_sync_size,
- old_devdata->max_sync_size);
- dev_info(old_devdata->dev, "%s: max_sync_sg new:%u old:%u\n",
- __func__, new_devdata->max_sync_sg,
- old_devdata->max_sync_sg);
- dev_info(old_devdata->dev, "%s: max_sg_len new:%u old:%u\n",
- __func__, new_devdata->max_sg_len,
- old_devdata->max_sg_len);
-
- rcu_assign_pointer(devdata, new_devdata);
- spin_unlock_irqrestore(&devdata_mutex, flags);
- synchronize_rcu();
- dev_set_drvdata(new_devdata->dev, new_devdata);
- kfree(old_devdata);
- return 0;
-
-error_out:
- if (new_devdata) {
- dev_info(old_devdata->dev, "%s: device disabled\n", __func__);
- nx842_OF_set_defaults(new_devdata);
- rcu_assign_pointer(devdata, new_devdata);
- spin_unlock_irqrestore(&devdata_mutex, flags);
- synchronize_rcu();
- dev_set_drvdata(new_devdata->dev, new_devdata);
- kfree(old_devdata);
- } else {
- dev_err(old_devdata->dev, "%s: could not update driver from hardware\n", __func__);
- spin_unlock_irqrestore(&devdata_mutex, flags);
- }
-
- if (!ret)
- ret = -EINVAL;
- return ret;
-}
-
-/**
- * nx842_OF_notifier - Process updates to OF properties for the device
- *
- * @np: notifier block
- * @action: notifier action
- * @update: struct pSeries_reconfig_prop_update pointer if action is
- * PSERIES_UPDATE_PROPERTY
- *
- * Returns:
- * NOTIFY_OK on success
- * NOTIFY_BAD encoded with error number on failure, use
- * notifier_to_errno() to decode this value
- */
-static int nx842_OF_notifier(struct notifier_block *np, unsigned long action,
- void *data)
-{
- struct of_reconfig_data *upd = data;
- struct nx842_devdata *local_devdata;
- struct device_node *node = NULL;
-
- rcu_read_lock();
- local_devdata = rcu_dereference(devdata);
- if (local_devdata)
- node = local_devdata->dev->of_node;
-
- if (local_devdata &&
- action == OF_RECONFIG_UPDATE_PROPERTY &&
- !strcmp(upd->dn->name, node->name)) {
- rcu_read_unlock();
- nx842_OF_upd(upd->prop);
- } else
- rcu_read_unlock();
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block nx842_of_nb = {
- .notifier_call = nx842_OF_notifier,
-};
-
-#define nx842_counter_read(_name) \
-static ssize_t nx842_##_name##_show(struct device *dev, \
- struct device_attribute *attr, \
- char *buf) { \
- struct nx842_devdata *local_devdata; \
- int p = 0; \
- rcu_read_lock(); \
- local_devdata = rcu_dereference(devdata); \
- if (local_devdata) \
- p = snprintf(buf, PAGE_SIZE, "%ld\n", \
- atomic64_read(&local_devdata->counters->_name)); \
- rcu_read_unlock(); \
- return p; \
-}
-
-#define NX842DEV_COUNTER_ATTR_RO(_name) \
- nx842_counter_read(_name); \
- static struct device_attribute dev_attr_##_name = __ATTR(_name, \
- 0444, \
- nx842_##_name##_show,\
- NULL);
-
-NX842DEV_COUNTER_ATTR_RO(comp_complete);
-NX842DEV_COUNTER_ATTR_RO(comp_failed);
-NX842DEV_COUNTER_ATTR_RO(decomp_complete);
-NX842DEV_COUNTER_ATTR_RO(decomp_failed);
-NX842DEV_COUNTER_ATTR_RO(swdecomp);
-
-static ssize_t nx842_timehist_show(struct device *,
- struct device_attribute *, char *);
-
-static struct device_attribute dev_attr_comp_times = __ATTR(comp_times, 0444,
- nx842_timehist_show, NULL);
-static struct device_attribute dev_attr_decomp_times = __ATTR(decomp_times,
- 0444, nx842_timehist_show, NULL);
-
-static ssize_t nx842_timehist_show(struct device *dev,
- struct device_attribute *attr, char *buf) {
- char *p = buf;
- struct nx842_devdata *local_devdata;
- atomic64_t *times;
- int bytes_remain = PAGE_SIZE;
- int bytes;
- int i;
-
- rcu_read_lock();
- local_devdata = rcu_dereference(devdata);
- if (!local_devdata) {
- rcu_read_unlock();
- return 0;
- }
-
- if (attr == &dev_attr_comp_times)
- times = local_devdata->counters->comp_times;
- else if (attr == &dev_attr_decomp_times)
- times = local_devdata->counters->decomp_times;
- else {
- rcu_read_unlock();
- return 0;
- }
-
- for (i = 0; i < (NX842_HIST_SLOTS - 2); i++) {
- bytes = snprintf(p, bytes_remain, "%u-%uus:\t%ld\n",
- i ? (2<<(i-1)) : 0, (2<<i)-1,
- atomic64_read(&times[i]));
- bytes_remain -= bytes;
- p += bytes;
- }
- /* The last bucket holds everything over
- * 2<<(NX842_HIST_SLOTS - 2) us */
- bytes = snprintf(p, bytes_remain, "%uus - :\t%ld\n",
- 2<<(NX842_HIST_SLOTS - 2),
- atomic64_read(&times[(NX842_HIST_SLOTS - 1)]));
- p += bytes;
-
- rcu_read_unlock();
- return p - buf;
-}
-
-static struct attribute *nx842_sysfs_entries[] = {
- &dev_attr_comp_complete.attr,
- &dev_attr_comp_failed.attr,
- &dev_attr_decomp_complete.attr,
- &dev_attr_decomp_failed.attr,
- &dev_attr_swdecomp.attr,
- &dev_attr_comp_times.attr,
- &dev_attr_decomp_times.attr,
- NULL,
-};
-
-static struct attribute_group nx842_attribute_group = {
- .name = NULL, /* put in device directory */
- .attrs = nx842_sysfs_entries,
-};
-
-static int __init nx842_probe(struct vio_dev *viodev,
- const struct vio_device_id *id)
-{
- struct nx842_devdata *old_devdata, *new_devdata = NULL;
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&devdata_mutex, flags);
- old_devdata = rcu_dereference_check(devdata,
- lockdep_is_held(&devdata_mutex));
-
- if (old_devdata && old_devdata->vdev != NULL) {
- dev_err(&viodev->dev, "%s: Attempt to register more than one instance of the hardware\n", __func__);
- ret = -1;
- goto error_unlock;
- }
-
- dev_set_drvdata(&viodev->dev, NULL);
-
- new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS);
- if (!new_devdata) {
- dev_err(&viodev->dev, "%s: Could not allocate memory for device data\n", __func__);
- ret = -ENOMEM;
- goto error_unlock;
- }
-
- new_devdata->counters = kzalloc(sizeof(*new_devdata->counters),
- GFP_NOFS);
- if (!new_devdata->counters) {
- dev_err(&viodev->dev, "%s: Could not allocate memory for performance counters\n", __func__);
- ret = -ENOMEM;
- goto error_unlock;
- }
-
- new_devdata->vdev = viodev;
- new_devdata->dev = &viodev->dev;
- nx842_OF_set_defaults(new_devdata);
-
- rcu_assign_pointer(devdata, new_devdata);
- spin_unlock_irqrestore(&devdata_mutex, flags);
- synchronize_rcu();
- kfree(old_devdata);
-
- of_reconfig_notifier_register(&nx842_of_nb);
-
- ret = nx842_OF_upd(NULL);
- if (ret && ret != -ENODEV) {
- dev_err(&viodev->dev, "could not parse device tree. %d\n", ret);
- ret = -1;
- goto error;
- }
-
- rcu_read_lock();
- dev_set_drvdata(&viodev->dev, rcu_dereference(devdata));
- rcu_read_unlock();
-
- if (sysfs_create_group(&viodev->dev.kobj, &nx842_attribute_group)) {
- dev_err(&viodev->dev, "could not create sysfs device attributes\n");
- ret = -1;
- goto error;
+ if (!nx842_platform_driver_get()) {
+ pr_err("no nx842 driver found.\n");
+ return -ENODEV;
}
return 0;
-
-error_unlock:
- spin_unlock_irqrestore(&devdata_mutex, flags);
- if (new_devdata)
- kfree(new_devdata->counters);
- kfree(new_devdata);
-error:
- return ret;
-}
-
-static int __exit nx842_remove(struct vio_dev *viodev)
-{
- struct nx842_devdata *old_devdata;
- unsigned long flags;
-
- pr_info("Removing IBM Power 842 compression device\n");
- sysfs_remove_group(&viodev->dev.kobj, &nx842_attribute_group);
-
- spin_lock_irqsave(&devdata_mutex, flags);
- old_devdata = rcu_dereference_check(devdata,
- lockdep_is_held(&devdata_mutex));
- of_reconfig_notifier_unregister(&nx842_of_nb);
- RCU_INIT_POINTER(devdata, NULL);
- spin_unlock_irqrestore(&devdata_mutex, flags);
- synchronize_rcu();
- dev_set_drvdata(&viodev->dev, NULL);
- if (old_devdata)
- kfree(old_devdata->counters);
- kfree(old_devdata);
- return 0;
-}
-
-static struct vio_device_id nx842_driver_ids[] = {
- {"ibm,compression-v1", "ibm,compression"},
- {"", ""},
-};
-
-static struct vio_driver nx842_driver = {
- .name = MODULE_NAME,
- .probe = nx842_probe,
- .remove = __exit_p(nx842_remove),
- .get_desired_dma = nx842_get_desired_dma,
- .id_table = nx842_driver_ids,
-};
-
-static int __init nx842_init(void)
-{
- struct nx842_devdata *new_devdata;
- pr_info("Registering IBM Power 842 compression driver\n");
-
- RCU_INIT_POINTER(devdata, NULL);
- new_devdata = kzalloc(sizeof(*new_devdata), GFP_KERNEL);
- if (!new_devdata) {
- pr_err("Could not allocate memory for device data\n");
- return -ENOMEM;
- }
- new_devdata->status = UNAVAILABLE;
- RCU_INIT_POINTER(devdata, new_devdata);
-
- return vio_register_driver(&nx842_driver);
}
-
module_init(nx842_init);
static void __exit nx842_exit(void)
{
- struct nx842_devdata *old_devdata;
- unsigned long flags;
-
- pr_info("Exiting IBM Power 842 compression driver\n");
- spin_lock_irqsave(&devdata_mutex, flags);
- old_devdata = rcu_dereference_check(devdata,
- lockdep_is_held(&devdata_mutex));
- RCU_INIT_POINTER(devdata, NULL);
- spin_unlock_irqrestore(&devdata_mutex, flags);
- synchronize_rcu();
- if (old_devdata)
- dev_set_drvdata(old_devdata->dev, NULL);
- kfree(old_devdata);
- vio_unregister_driver(&nx842_driver);
+ nx842_platform_driver_put();
}
-
module_exit(nx842_exit);
-
-/*********************************
- * 842 software decompressor
-*********************************/
-typedef int (*sw842_template_op)(const char **, int *, unsigned char **,
- struct sw842_fifo *);
-
-static int sw842_data8(const char **, int *, unsigned char **,
- struct sw842_fifo *);
-static int sw842_data4(const char **, int *, unsigned char **,
- struct sw842_fifo *);
-static int sw842_data2(const char **, int *, unsigned char **,
- struct sw842_fifo *);
-static int sw842_ptr8(const char **, int *, unsigned char **,
- struct sw842_fifo *);
-static int sw842_ptr4(const char **, int *, unsigned char **,
- struct sw842_fifo *);
-static int sw842_ptr2(const char **, int *, unsigned char **,
- struct sw842_fifo *);
-
-/* special templates */
-#define SW842_TMPL_REPEAT 0x1B
-#define SW842_TMPL_ZEROS 0x1C
-#define SW842_TMPL_EOF 0x1E
-
-static sw842_template_op sw842_tmpl_ops[26][4] = {
- { sw842_data8, NULL}, /* 0 (00000) */
- { sw842_data4, sw842_data2, sw842_ptr2, NULL},
- { sw842_data4, sw842_ptr2, sw842_data2, NULL},
- { sw842_data4, sw842_ptr2, sw842_ptr2, NULL},
- { sw842_data4, sw842_ptr4, NULL},
- { sw842_data2, sw842_ptr2, sw842_data4, NULL},
- { sw842_data2, sw842_ptr2, sw842_data2, sw842_ptr2},
- { sw842_data2, sw842_ptr2, sw842_ptr2, sw842_data2},
- { sw842_data2, sw842_ptr2, sw842_ptr2, sw842_ptr2,},
- { sw842_data2, sw842_ptr2, sw842_ptr4, NULL},
- { sw842_ptr2, sw842_data2, sw842_data4, NULL}, /* 10 (01010) */
- { sw842_ptr2, sw842_data4, sw842_ptr2, NULL},
- { sw842_ptr2, sw842_data2, sw842_ptr2, sw842_data2},
- { sw842_ptr2, sw842_data2, sw842_ptr2, sw842_ptr2},
- { sw842_ptr2, sw842_data2, sw842_ptr4, NULL},
- { sw842_ptr2, sw842_ptr2, sw842_data4, NULL},
- { sw842_ptr2, sw842_ptr2, sw842_data2, sw842_ptr2},
- { sw842_ptr2, sw842_ptr2, sw842_ptr2, sw842_data2},
- { sw842_ptr2, sw842_ptr2, sw842_ptr2, sw842_ptr2},
- { sw842_ptr2, sw842_ptr2, sw842_ptr4, NULL},
- { sw842_ptr4, sw842_data4, NULL}, /* 20 (10100) */
- { sw842_ptr4, sw842_data2, sw842_ptr2, NULL},
- { sw842_ptr4, sw842_ptr2, sw842_data2, NULL},
- { sw842_ptr4, sw842_ptr2, sw842_ptr2, NULL},
- { sw842_ptr4, sw842_ptr4, NULL},
- { sw842_ptr8, NULL}
-};
-
-/* Software decompress helpers */
-
-static uint8_t sw842_get_byte(const char *buf, int bit)
-{
- uint8_t tmpl;
- uint16_t tmp;
- tmp = htons(*(uint16_t *)(buf));
- tmp = (uint16_t)(tmp << bit);
- tmp = ntohs(tmp);
- memcpy(&tmpl, &tmp, 1);
- return tmpl;
-}
-
-static uint8_t sw842_get_template(const char **buf, int *bit)
-{
- uint8_t byte;
- byte = sw842_get_byte(*buf, *bit);
- byte = byte >> 3;
- byte &= 0x1F;
- *buf += (*bit + 5) / 8;
- *bit = (*bit + 5) % 8;
- return byte;
-}
-
-/* repeat_count happens to be 5-bit too (like the template) */
-static uint8_t sw842_get_repeat_count(const char **buf, int *bit)
-{
- uint8_t byte;
- byte = sw842_get_byte(*buf, *bit);
- byte = byte >> 2;
- byte &= 0x3F;
- *buf += (*bit + 6) / 8;
- *bit = (*bit + 6) % 8;
- return byte;
-}
-
-static uint8_t sw842_get_ptr2(const char **buf, int *bit)
-{
- uint8_t ptr;
- ptr = sw842_get_byte(*buf, *bit);
- (*buf)++;
- return ptr;
-}
-
-static uint16_t sw842_get_ptr4(const char **buf, int *bit,
- struct sw842_fifo *fifo)
-{
- uint16_t ptr;
- ptr = htons(*(uint16_t *)(*buf));
- ptr = (uint16_t)(ptr << *bit);
- ptr = ptr >> 7;
- ptr &= 0x01FF;
- *buf += (*bit + 9) / 8;
- *bit = (*bit + 9) % 8;
- return ptr;
-}
-
-static uint8_t sw842_get_ptr8(const char **buf, int *bit,
- struct sw842_fifo *fifo)
-{
- return sw842_get_ptr2(buf, bit);
-}
-
-/* Software decompress template ops */
-
-static int sw842_data8(const char **inbuf, int *inbit,
- unsigned char **outbuf, struct sw842_fifo *fifo)
-{
- int ret;
-
- ret = sw842_data4(inbuf, inbit, outbuf, fifo);
- if (ret)
- return ret;
- ret = sw842_data4(inbuf, inbit, outbuf, fifo);
- return ret;
-}
-
-static int sw842_data4(const char **inbuf, int *inbit,
- unsigned char **outbuf, struct sw842_fifo *fifo)
-{
- int ret;
-
- ret = sw842_data2(inbuf, inbit, outbuf, fifo);
- if (ret)
- return ret;
- ret = sw842_data2(inbuf, inbit, outbuf, fifo);
- return ret;
-}
-
-static int sw842_data2(const char **inbuf, int *inbit,
- unsigned char **outbuf, struct sw842_fifo *fifo)
-{
- **outbuf = sw842_get_byte(*inbuf, *inbit);
- (*inbuf)++;
- (*outbuf)++;
- **outbuf = sw842_get_byte(*inbuf, *inbit);
- (*inbuf)++;
- (*outbuf)++;
- return 0;
-}
-
-static int sw842_ptr8(const char **inbuf, int *inbit,
- unsigned char **outbuf, struct sw842_fifo *fifo)
-{
- uint8_t ptr;
- ptr = sw842_get_ptr8(inbuf, inbit, fifo);
- if (!fifo->f84_full && (ptr >= fifo->f8_count))
- return 1;
- memcpy(*outbuf, fifo->f8[ptr], 8);
- *outbuf += 8;
- return 0;
-}
-
-static int sw842_ptr4(const char **inbuf, int *inbit,
- unsigned char **outbuf, struct sw842_fifo *fifo)
-{
- uint16_t ptr;
- ptr = sw842_get_ptr4(inbuf, inbit, fifo);
- if (!fifo->f84_full && (ptr >= fifo->f4_count))
- return 1;
- memcpy(*outbuf, fifo->f4[ptr], 4);
- *outbuf += 4;
- return 0;
-}
-
-static int sw842_ptr2(const char **inbuf, int *inbit,
- unsigned char **outbuf, struct sw842_fifo *fifo)
-{
- uint8_t ptr;
- ptr = sw842_get_ptr2(inbuf, inbit);
- if (!fifo->f2_full && (ptr >= fifo->f2_count))
- return 1;
- memcpy(*outbuf, fifo->f2[ptr], 2);
- *outbuf += 2;
- return 0;
-}
-
-static void sw842_copy_to_fifo(const char *buf, struct sw842_fifo *fifo)
-{
- unsigned char initial_f2count = fifo->f2_count;
-
- memcpy(fifo->f8[fifo->f8_count], buf, 8);
- fifo->f4_count += 2;
- fifo->f8_count += 1;
-
- if (!fifo->f84_full && fifo->f4_count >= 512) {
- fifo->f84_full = 1;
- fifo->f4_count /= 512;
- }
-
- memcpy(fifo->f2[fifo->f2_count++], buf, 2);
- memcpy(fifo->f2[fifo->f2_count++], buf + 2, 2);
- memcpy(fifo->f2[fifo->f2_count++], buf + 4, 2);
- memcpy(fifo->f2[fifo->f2_count++], buf + 6, 2);
- if (fifo->f2_count < initial_f2count)
- fifo->f2_full = 1;
-}
-
-static int sw842_decompress(const unsigned char *src, int srclen,
- unsigned char *dst, int *destlen,
- const void *wrkmem)
-{
- uint8_t tmpl;
- const char *inbuf;
- int inbit = 0;
- unsigned char *outbuf, *outbuf_end, *origbuf, *prevbuf;
- const char *inbuf_end;
- sw842_template_op op;
- int opindex;
- int i, repeat_count;
- struct sw842_fifo *fifo;
- int ret = 0;
-
- fifo = &((struct nx842_workmem *)(wrkmem))->swfifo;
- memset(fifo, 0, sizeof(*fifo));
-
- origbuf = NULL;
- inbuf = src;
- inbuf_end = src + srclen;
- outbuf = dst;
- outbuf_end = dst + *destlen;
-
- while ((tmpl = sw842_get_template(&inbuf, &inbit)) != SW842_TMPL_EOF) {
- if (inbuf >= inbuf_end) {
- ret = -EINVAL;
- goto out;
- }
-
- opindex = 0;
- prevbuf = origbuf;
- origbuf = outbuf;
- switch (tmpl) {
- case SW842_TMPL_REPEAT:
- if (prevbuf == NULL) {
- ret = -EINVAL;
- goto out;
- }
-
- repeat_count = sw842_get_repeat_count(&inbuf,
- &inbit) + 1;
-
- /* Did the repeat count advance past the end of input */
- if (inbuf > inbuf_end) {
- ret = -EINVAL;
- goto out;
- }
-
- for (i = 0; i < repeat_count; i++) {
- /* Would this overflow the output buffer */
- if ((outbuf + 8) > outbuf_end) {
- ret = -ENOSPC;
- goto out;
- }
-
- memcpy(outbuf, prevbuf, 8);
- sw842_copy_to_fifo(outbuf, fifo);
- outbuf += 8;
- }
- break;
-
- case SW842_TMPL_ZEROS:
- /* Would this overflow the output buffer */
- if ((outbuf + 8) > outbuf_end) {
- ret = -ENOSPC;
- goto out;
- }
-
- memset(outbuf, 0, 8);
- sw842_copy_to_fifo(outbuf, fifo);
- outbuf += 8;
- break;
-
- default:
- if (tmpl > 25) {
- ret = -EINVAL;
- goto out;
- }
-
- /* Does this go past the end of the input buffer */
- if ((inbuf + 2) > inbuf_end) {
- ret = -EINVAL;
- goto out;
- }
-
- /* Would this overflow the output buffer */
- if ((outbuf + 8) > outbuf_end) {
- ret = -ENOSPC;
- goto out;
- }
-
- while (opindex < 4 &&
- (op = sw842_tmpl_ops[tmpl][opindex++])
- != NULL) {
- ret = (*op)(&inbuf, &inbit, &outbuf, fifo);
- if (ret) {
- ret = -EINVAL;
- goto out;
- }
- sw842_copy_to_fifo(origbuf, fifo);
- }
- }
- }
-
-out:
- if (!ret)
- *destlen = (unsigned int)(outbuf - dst);
- else
- *destlen = 0;
-
- return ret;
-}
diff --git a/drivers/crypto/nx/nx-842.h b/drivers/crypto/nx/nx-842.h
new file mode 100644
index 000000000000..ac0ea79d0f8b
--- /dev/null
+++ b/drivers/crypto/nx/nx-842.h
@@ -0,0 +1,144 @@
+
+#ifndef __NX_842_H__
+#define __NX_842_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sw842.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/ratelimit.h>
+
+/* Restrictions on Data Descriptor List (DDL) and Entry (DDE) buffers
+ *
+ * From NX P8 workbook, sec 4.9.1 "842 details"
+ * Each DDE buffer is 128 byte aligned
+ * Each DDE buffer size is a multiple of 32 bytes (except the last)
+ * The last DDE buffer size is a multiple of 8 bytes
+ */
+#define DDE_BUFFER_ALIGN (128)
+#define DDE_BUFFER_SIZE_MULT (32)
+#define DDE_BUFFER_LAST_MULT (8)
+
+/* Arbitrary DDL length limit
+ * Allows max buffer size of MAX-1 to MAX pages
+ * (depending on alignment)
+ */
+#define DDL_LEN_MAX (17)
+
+/* CCW 842 CI/FC masks
+ * NX P8 workbook, section 4.3.1, figure 4-6
+ * "CI/FC Boundary by NX CT type"
+ */
+#define CCW_CI_842 (0x00003ff8)
+#define CCW_FC_842 (0x00000007)
+
+/* CCW Function Codes (FC) for 842
+ * NX P8 workbook, section 4.9, table 4-28
+ * "Function Code Definitions for 842 Memory Compression"
+ */
+#define CCW_FC_842_COMP_NOCRC (0)
+#define CCW_FC_842_COMP_CRC (1)
+#define CCW_FC_842_DECOMP_NOCRC (2)
+#define CCW_FC_842_DECOMP_CRC (3)
+#define CCW_FC_842_MOVE (4)
+
+/* CSB CC Error Types for 842
+ * NX P8 workbook, section 4.10.3, table 4-30
+ * "Reported Error Types Summary Table"
+ */
+/* These are all duplicates of existing codes defined in icswx.h. */
+#define CSB_CC_TRANSLATION_DUP1 (80)
+#define CSB_CC_TRANSLATION_DUP2 (82)
+#define CSB_CC_TRANSLATION_DUP3 (84)
+#define CSB_CC_TRANSLATION_DUP4 (86)
+#define CSB_CC_TRANSLATION_DUP5 (92)
+#define CSB_CC_TRANSLATION_DUP6 (94)
+#define CSB_CC_PROTECTION_DUP1 (81)
+#define CSB_CC_PROTECTION_DUP2 (83)
+#define CSB_CC_PROTECTION_DUP3 (85)
+#define CSB_CC_PROTECTION_DUP4 (87)
+#define CSB_CC_PROTECTION_DUP5 (93)
+#define CSB_CC_PROTECTION_DUP6 (95)
+#define CSB_CC_RD_EXTERNAL_DUP1 (89)
+#define CSB_CC_RD_EXTERNAL_DUP2 (90)
+#define CSB_CC_RD_EXTERNAL_DUP3 (91)
+/* These are specific to NX */
+/* 842 codes */
+#define CSB_CC_TPBC_GT_SPBC (64) /* no error, but >1 comp ratio */
+#define CSB_CC_CRC_MISMATCH (65) /* decomp crc mismatch */
+#define CSB_CC_TEMPL_INVALID (66) /* decomp invalid template value */
+#define CSB_CC_TEMPL_OVERFLOW (67) /* decomp template shows data after end */
+/* sym crypt codes */
+#define CSB_CC_DECRYPT_OVERFLOW (64)
+/* asym crypt codes */
+#define CSB_CC_MINV_OVERFLOW (128)
+/* These are reserved for hypervisor use */
+#define CSB_CC_HYP_RESERVE_START (240)
+#define CSB_CC_HYP_RESERVE_END (253)
+#define CSB_CC_HYP_NO_HW (254)
+#define CSB_CC_HYP_HANG_ABORTED (255)
+
+/* CCB Completion Modes (CM) for 842
+ * NX P8 workbook, section 4.3, figure 4-5
+ * "CRB Details - Normal Cop_Req (CL=00, C=1)"
+ */
+#define CCB_CM_EXTRA_WRITE (CCB_CM0_ALL_COMPLETIONS & CCB_CM12_STORE)
+#define CCB_CM_INTERRUPT (CCB_CM0_ALL_COMPLETIONS & CCB_CM12_INTERRUPT)
+
+#define LEN_ON_SIZE(pa, size) ((size) - ((pa) & ((size) - 1)))
+#define LEN_ON_PAGE(pa) LEN_ON_SIZE(pa, PAGE_SIZE)
+
+static inline unsigned long nx842_get_pa(void *addr)
+{
+ if (!is_vmalloc_addr(addr))
+ return __pa(addr);
+
+ return page_to_phys(vmalloc_to_page(addr)) + offset_in_page(addr);
+}
+
+/* Get/Set bit fields */
+#define MASK_LSH(m) (__builtin_ffsl(m) - 1)
+#define GET_FIELD(v, m) (((v) & (m)) >> MASK_LSH(m))
+#define SET_FIELD(v, m, val) (((v) & ~(m)) | (((val) << MASK_LSH(m)) & (m)))
+
+struct nx842_constraints {
+ int alignment;
+ int multiple;
+ int minimum;
+ int maximum;
+};
+
+struct nx842_driver {
+ char *name;
+ struct module *owner;
+ size_t workmem_size;
+
+ struct nx842_constraints *constraints;
+
+ int (*compress)(const unsigned char *in, unsigned int in_len,
+ unsigned char *out, unsigned int *out_len,
+ void *wrkmem);
+ int (*decompress)(const unsigned char *in, unsigned int in_len,
+ unsigned char *out, unsigned int *out_len,
+ void *wrkmem);
+};
+
+struct nx842_driver *nx842_platform_driver(void);
+bool nx842_platform_driver_set(struct nx842_driver *driver);
+void nx842_platform_driver_unset(struct nx842_driver *driver);
+bool nx842_platform_driver_get(void);
+void nx842_platform_driver_put(void);
+
+size_t nx842_workmem_size(void);
+
+int nx842_constraints(struct nx842_constraints *constraints);
+
+int nx842_compress(const unsigned char *in, unsigned int in_len,
+ unsigned char *out, unsigned int *out_len, void *wrkmem);
+int nx842_decompress(const unsigned char *in, unsigned int in_len,
+ unsigned char *out, unsigned int *out_len, void *wrkmem);
+
+#endif /* __NX_842_H__ */
diff --git a/drivers/crypto/nx/nx-aes-gcm.c b/drivers/crypto/nx/nx-aes-gcm.c
index 88c562434bc0..08ac6d48688c 100644
--- a/drivers/crypto/nx/nx-aes-gcm.c
+++ b/drivers/crypto/nx/nx-aes-gcm.c
@@ -93,17 +93,6 @@ out:
return rc;
}
-static int gcm_aes_nx_setauthsize(struct crypto_aead *tfm,
- unsigned int authsize)
-{
- if (authsize > crypto_aead_alg(tfm)->maxauthsize)
- return -EINVAL;
-
- crypto_aead_crt(tfm)->authsize = authsize;
-
- return 0;
-}
-
static int gcm4106_aes_nx_setauthsize(struct crypto_aead *tfm,
unsigned int authsize)
{
@@ -116,8 +105,6 @@ static int gcm4106_aes_nx_setauthsize(struct crypto_aead *tfm,
return -EINVAL;
}
- crypto_aead_crt(tfm)->authsize = authsize;
-
return 0;
}
@@ -134,7 +121,7 @@ static int nx_gca(struct nx_crypto_ctx *nx_ctx,
unsigned int max_sg_len;
if (nbytes <= AES_BLOCK_SIZE) {
- scatterwalk_start(&walk, req->assoc);
+ scatterwalk_start(&walk, req->src);
scatterwalk_copychunks(out, &walk, nbytes, SCATTERWALK_FROM_SG);
scatterwalk_done(&walk, SCATTERWALK_FROM_SG, 0);
return 0;
@@ -159,7 +146,7 @@ static int nx_gca(struct nx_crypto_ctx *nx_ctx,
NX_PAGE_SIZE * (max_sg_len - 1));
nx_sg = nx_walk_and_build(nx_ctx->in_sg, max_sg_len,
- req->assoc, processed, &to_process);
+ req->src, processed, &to_process);
if ((to_process + processed) < nbytes)
NX_CPB_FDM(csbcpb_aead) |= NX_FDM_INTERMEDIATE;
@@ -225,7 +212,7 @@ static int gmac(struct aead_request *req, struct blkcipher_desc *desc)
NX_PAGE_SIZE * (max_sg_len - 1));
nx_sg = nx_walk_and_build(nx_ctx->in_sg, max_sg_len,
- req->assoc, processed, &to_process);
+ req->src, processed, &to_process);
if ((to_process + processed) < nbytes)
NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
@@ -377,7 +364,8 @@ static int gcm_aes_nx_crypt(struct aead_request *req, int enc)
csbcpb->cpb.aes_gcm.bit_length_data = nbytes * 8;
desc.tfm = (struct crypto_blkcipher *) req->base.tfm;
rc = nx_build_sg_lists(nx_ctx, &desc, req->dst,
- req->src, &to_process, processed,
+ req->src, &to_process,
+ processed + req->assoclen,
csbcpb->cpb.aes_gcm.iv_or_cnt);
if (rc)
@@ -412,17 +400,19 @@ static int gcm_aes_nx_crypt(struct aead_request *req, int enc)
mac:
if (enc) {
/* copy out the auth tag */
- scatterwalk_map_and_copy(csbcpb->cpb.aes_gcm.out_pat_or_mac,
- req->dst, nbytes,
- crypto_aead_authsize(crypto_aead_reqtfm(req)),
- SCATTERWALK_TO_SG);
+ scatterwalk_map_and_copy(
+ csbcpb->cpb.aes_gcm.out_pat_or_mac,
+ req->dst, req->assoclen + nbytes,
+ crypto_aead_authsize(crypto_aead_reqtfm(req)),
+ SCATTERWALK_TO_SG);
} else {
u8 *itag = nx_ctx->priv.gcm.iauth_tag;
u8 *otag = csbcpb->cpb.aes_gcm.out_pat_or_mac;
- scatterwalk_map_and_copy(itag, req->src, nbytes,
- crypto_aead_authsize(crypto_aead_reqtfm(req)),
- SCATTERWALK_FROM_SG);
+ scatterwalk_map_and_copy(
+ itag, req->src, req->assoclen + nbytes,
+ crypto_aead_authsize(crypto_aead_reqtfm(req)),
+ SCATTERWALK_FROM_SG);
rc = memcmp(itag, otag,
crypto_aead_authsize(crypto_aead_reqtfm(req))) ?
-EBADMSG : 0;
@@ -481,45 +471,39 @@ static int gcm4106_aes_nx_decrypt(struct aead_request *req)
* during encrypt/decrypt doesn't solve this problem, because it calls
* blkcipher_walk_done under the covers, which doesn't use walk->blocksize,
* but instead uses this tfm->blocksize. */
-struct crypto_alg nx_gcm_aes_alg = {
- .cra_name = "gcm(aes)",
- .cra_driver_name = "gcm-aes-nx",
- .cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AEAD,
- .cra_blocksize = 1,
- .cra_ctxsize = sizeof(struct nx_crypto_ctx),
- .cra_type = &crypto_aead_type,
- .cra_module = THIS_MODULE,
- .cra_init = nx_crypto_ctx_aes_gcm_init,
- .cra_exit = nx_crypto_ctx_exit,
- .cra_aead = {
- .ivsize = AES_BLOCK_SIZE,
- .maxauthsize = AES_BLOCK_SIZE,
- .setkey = gcm_aes_nx_set_key,
- .setauthsize = gcm_aes_nx_setauthsize,
- .encrypt = gcm_aes_nx_encrypt,
- .decrypt = gcm_aes_nx_decrypt,
- }
+struct aead_alg nx_gcm_aes_alg = {
+ .base = {
+ .cra_name = "gcm(aes)",
+ .cra_driver_name = "gcm-aes-nx",
+ .cra_priority = 300,
+ .cra_blocksize = 1,
+ .cra_ctxsize = sizeof(struct nx_crypto_ctx),
+ .cra_module = THIS_MODULE,
+ },
+ .init = nx_crypto_ctx_aes_gcm_init,
+ .exit = nx_crypto_ctx_aead_exit,
+ .ivsize = 12,
+ .maxauthsize = AES_BLOCK_SIZE,
+ .setkey = gcm_aes_nx_set_key,
+ .encrypt = gcm_aes_nx_encrypt,
+ .decrypt = gcm_aes_nx_decrypt,
};
-struct crypto_alg nx_gcm4106_aes_alg = {
- .cra_name = "rfc4106(gcm(aes))",
- .cra_driver_name = "rfc4106-gcm-aes-nx",
- .cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AEAD,
- .cra_blocksize = 1,
- .cra_ctxsize = sizeof(struct nx_crypto_ctx),
- .cra_type = &crypto_nivaead_type,
- .cra_module = THIS_MODULE,
- .cra_init = nx_crypto_ctx_aes_gcm_init,
- .cra_exit = nx_crypto_ctx_exit,
- .cra_aead = {
- .ivsize = 8,
- .maxauthsize = AES_BLOCK_SIZE,
- .geniv = "seqiv",
- .setkey = gcm4106_aes_nx_set_key,
- .setauthsize = gcm4106_aes_nx_setauthsize,
- .encrypt = gcm4106_aes_nx_encrypt,
- .decrypt = gcm4106_aes_nx_decrypt,
- }
+struct aead_alg nx_gcm4106_aes_alg = {
+ .base = {
+ .cra_name = "rfc4106(gcm(aes))",
+ .cra_driver_name = "rfc4106-gcm-aes-nx",
+ .cra_priority = 300,
+ .cra_blocksize = 1,
+ .cra_ctxsize = sizeof(struct nx_crypto_ctx),
+ .cra_module = THIS_MODULE,
+ },
+ .init = nx_crypto_ctx_aes_gcm_init,
+ .exit = nx_crypto_ctx_aead_exit,
+ .ivsize = 8,
+ .maxauthsize = AES_BLOCK_SIZE,
+ .setkey = gcm4106_aes_nx_set_key,
+ .setauthsize = gcm4106_aes_nx_setauthsize,
+ .encrypt = gcm4106_aes_nx_encrypt,
+ .decrypt = gcm4106_aes_nx_decrypt,
};
diff --git a/drivers/crypto/nx/nx-sha256.c b/drivers/crypto/nx/nx-sha256.c
index 23621da624c3..4e91bdb83c59 100644
--- a/drivers/crypto/nx/nx-sha256.c
+++ b/drivers/crypto/nx/nx-sha256.c
@@ -33,8 +33,9 @@ static int nx_sha256_init(struct shash_desc *desc)
{
struct sha256_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+ struct nx_sg *out_sg;
int len;
- int rc;
+ u32 max_sg_len;
nx_ctx_init(nx_ctx, HCOP_FC_SHA);
@@ -44,15 +45,18 @@ static int nx_sha256_init(struct shash_desc *desc)
NX_CPB_SET_DIGEST_SIZE(nx_ctx->csbcpb, NX_DS_SHA256);
+ max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+ nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+ max_sg_len = min_t(u64, max_sg_len,
+ nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
len = SHA256_DIGEST_SIZE;
- rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->out_sg,
- &nx_ctx->op.outlen,
- &len,
- (u8 *) sctx->state,
- NX_DS_SHA256);
+ out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state,
+ &len, max_sg_len);
+ nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
- if (rc)
- goto out;
+ if (len != SHA256_DIGEST_SIZE)
+ return -EINVAL;
sctx->state[0] = __cpu_to_be32(SHA256_H0);
sctx->state[1] = __cpu_to_be32(SHA256_H1);
@@ -64,7 +68,6 @@ static int nx_sha256_init(struct shash_desc *desc)
sctx->state[7] = __cpu_to_be32(SHA256_H7);
sctx->count = 0;
-out:
return 0;
}
@@ -74,10 +77,12 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data,
struct sha256_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+ struct nx_sg *in_sg;
u64 to_process = 0, leftover, total;
unsigned long irq_flags;
int rc = 0;
int data_len;
+ u32 max_sg_len;
u64 buf_len = (sctx->count % SHA256_BLOCK_SIZE);
spin_lock_irqsave(&nx_ctx->lock, irq_flags);
@@ -97,6 +102,12 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data,
NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
+ in_sg = nx_ctx->in_sg;
+ max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+ nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+ max_sg_len = min_t(u64, max_sg_len,
+ nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
do {
/*
* to_process: the SHA256_BLOCK_SIZE data chunk to process in
@@ -108,25 +119,22 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data,
if (buf_len) {
data_len = buf_len;
- rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
- &nx_ctx->op.inlen,
- &data_len,
- (u8 *) sctx->buf,
- NX_DS_SHA256);
+ in_sg = nx_build_sg_list(nx_ctx->in_sg,
+ (u8 *) sctx->buf,
+ &data_len,
+ max_sg_len);
- if (rc || data_len != buf_len)
+ if (data_len != buf_len) {
+ rc = -EINVAL;
goto out;
+ }
}
data_len = to_process - buf_len;
- rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
- &nx_ctx->op.inlen,
- &data_len,
- (u8 *) data,
- NX_DS_SHA256);
+ in_sg = nx_build_sg_list(in_sg, (u8 *) data,
+ &data_len, max_sg_len);
- if (rc)
- goto out;
+ nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
to_process = (data_len + buf_len);
leftover = total - to_process;
@@ -173,12 +181,19 @@ static int nx_sha256_final(struct shash_desc *desc, u8 *out)
struct sha256_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+ struct nx_sg *in_sg, *out_sg;
unsigned long irq_flags;
- int rc;
+ u32 max_sg_len;
+ int rc = 0;
int len;
spin_lock_irqsave(&nx_ctx->lock, irq_flags);
+ max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+ nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+ max_sg_len = min_t(u64, max_sg_len,
+ nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
/* final is represented by continuing the operation and indicating that
* this is not an intermediate operation */
if (sctx->count >= SHA256_BLOCK_SIZE) {
@@ -195,25 +210,24 @@ static int nx_sha256_final(struct shash_desc *desc, u8 *out)
csbcpb->cpb.sha256.message_bit_length = (u64) (sctx->count * 8);
len = sctx->count & (SHA256_BLOCK_SIZE - 1);
- rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
- &nx_ctx->op.inlen,
- &len,
- (u8 *) sctx->buf,
- NX_DS_SHA256);
+ in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *) sctx->buf,
+ &len, max_sg_len);
- if (rc || len != (sctx->count & (SHA256_BLOCK_SIZE - 1)))
+ if (len != (sctx->count & (SHA256_BLOCK_SIZE - 1))) {
+ rc = -EINVAL;
goto out;
+ }
len = SHA256_DIGEST_SIZE;
- rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->out_sg,
- &nx_ctx->op.outlen,
- &len,
- out,
- NX_DS_SHA256);
+ out_sg = nx_build_sg_list(nx_ctx->out_sg, out, &len, max_sg_len);
- if (rc || len != SHA256_DIGEST_SIZE)
+ if (len != SHA256_DIGEST_SIZE) {
+ rc = -EINVAL;
goto out;
+ }
+ nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
+ nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
if (!nx_ctx->op.outlen) {
rc = -EINVAL;
goto out;
diff --git a/drivers/crypto/nx/nx-sha512.c b/drivers/crypto/nx/nx-sha512.c
index b3adf1022673..e6a58d2ee628 100644
--- a/drivers/crypto/nx/nx-sha512.c
+++ b/drivers/crypto/nx/nx-sha512.c
@@ -32,8 +32,9 @@ static int nx_sha512_init(struct shash_desc *desc)
{
struct sha512_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+ struct nx_sg *out_sg;
int len;
- int rc;
+ u32 max_sg_len;
nx_ctx_init(nx_ctx, HCOP_FC_SHA);
@@ -43,15 +44,18 @@ static int nx_sha512_init(struct shash_desc *desc)
NX_CPB_SET_DIGEST_SIZE(nx_ctx->csbcpb, NX_DS_SHA512);
+ max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+ nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+ max_sg_len = min_t(u64, max_sg_len,
+ nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
len = SHA512_DIGEST_SIZE;
- rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->out_sg,
- &nx_ctx->op.outlen,
- &len,
- (u8 *)sctx->state,
- NX_DS_SHA512);
+ out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state,
+ &len, max_sg_len);
+ nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
- if (rc || len != SHA512_DIGEST_SIZE)
- goto out;
+ if (len != SHA512_DIGEST_SIZE)
+ return -EINVAL;
sctx->state[0] = __cpu_to_be64(SHA512_H0);
sctx->state[1] = __cpu_to_be64(SHA512_H1);
@@ -63,7 +67,6 @@ static int nx_sha512_init(struct shash_desc *desc)
sctx->state[7] = __cpu_to_be64(SHA512_H7);
sctx->count[0] = 0;
-out:
return 0;
}
@@ -73,10 +76,12 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data,
struct sha512_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+ struct nx_sg *in_sg;
u64 to_process, leftover = 0, total;
unsigned long irq_flags;
int rc = 0;
int data_len;
+ u32 max_sg_len;
u64 buf_len = (sctx->count[0] % SHA512_BLOCK_SIZE);
spin_lock_irqsave(&nx_ctx->lock, irq_flags);
@@ -96,6 +101,12 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data,
NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
+ in_sg = nx_ctx->in_sg;
+ max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+ nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+ max_sg_len = min_t(u64, max_sg_len,
+ nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
do {
/*
* to_process: the SHA512_BLOCK_SIZE data chunk to process in
@@ -108,25 +119,26 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data,
if (buf_len) {
data_len = buf_len;
- rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
- &nx_ctx->op.inlen,
- &data_len,
- (u8 *) sctx->buf,
- NX_DS_SHA512);
+ in_sg = nx_build_sg_list(nx_ctx->in_sg,
+ (u8 *) sctx->buf,
+ &data_len, max_sg_len);
- if (rc || data_len != buf_len)
+ if (data_len != buf_len) {
+ rc = -EINVAL;
goto out;
+ }
}
data_len = to_process - buf_len;
- rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
- &nx_ctx->op.inlen,
- &data_len,
- (u8 *) data,
- NX_DS_SHA512);
+ in_sg = nx_build_sg_list(in_sg, (u8 *) data,
+ &data_len, max_sg_len);
- if (rc || data_len != (to_process - buf_len))
+ nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
+
+ if (data_len != (to_process - buf_len)) {
+ rc = -EINVAL;
goto out;
+ }
to_process = (data_len + buf_len);
leftover = total - to_process;
@@ -172,13 +184,20 @@ static int nx_sha512_final(struct shash_desc *desc, u8 *out)
struct sha512_state *sctx = shash_desc_ctx(desc);
struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+ struct nx_sg *in_sg, *out_sg;
+ u32 max_sg_len;
u64 count0;
unsigned long irq_flags;
- int rc;
+ int rc = 0;
int len;
spin_lock_irqsave(&nx_ctx->lock, irq_flags);
+ max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+ nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+ max_sg_len = min_t(u64, max_sg_len,
+ nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
/* final is represented by continuing the operation and indicating that
* this is not an intermediate operation */
if (sctx->count[0] >= SHA512_BLOCK_SIZE) {
@@ -200,24 +219,20 @@ static int nx_sha512_final(struct shash_desc *desc, u8 *out)
csbcpb->cpb.sha512.message_bit_length_lo = count0;
len = sctx->count[0] & (SHA512_BLOCK_SIZE - 1);
- rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
- &nx_ctx->op.inlen,
- &len,
- (u8 *)sctx->buf,
- NX_DS_SHA512);
+ in_sg = nx_build_sg_list(nx_ctx->in_sg, sctx->buf, &len,
+ max_sg_len);
- if (rc || len != (sctx->count[0] & (SHA512_BLOCK_SIZE - 1)))
+ if (len != (sctx->count[0] & (SHA512_BLOCK_SIZE - 1))) {
+ rc = -EINVAL;
goto out;
+ }
len = SHA512_DIGEST_SIZE;
- rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->out_sg,
- &nx_ctx->op.outlen,
- &len,
- out,
- NX_DS_SHA512);
+ out_sg = nx_build_sg_list(nx_ctx->out_sg, out, &len,
+ max_sg_len);
- if (rc)
- goto out;
+ nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
+ nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
if (!nx_ctx->op.outlen) {
rc = -EINVAL;
diff --git a/drivers/crypto/nx/nx.c b/drivers/crypto/nx/nx.c
index 1da6dc59d0dd..f6198f29a4a8 100644
--- a/drivers/crypto/nx/nx.c
+++ b/drivers/crypto/nx/nx.c
@@ -19,8 +19,8 @@
* Author: Kent Yoder <yoder1@us.ibm.com>
*/
+#include <crypto/internal/aead.h>
#include <crypto/internal/hash.h>
-#include <crypto/hash.h>
#include <crypto/aes.h>
#include <crypto/sha.h>
#include <crypto/algapi.h>
@@ -29,10 +29,10 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/mm.h>
-#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <linux/device.h>
#include <linux/of.h>
+#include <linux/types.h>
#include <asm/hvcall.h>
#include <asm/vio.h>
@@ -215,8 +215,15 @@ struct nx_sg *nx_walk_and_build(struct nx_sg *nx_dst,
* @delta: is the amount we need to crop in order to bound the list.
*
*/
-static long int trim_sg_list(struct nx_sg *sg, struct nx_sg *end, unsigned int delta)
+static long int trim_sg_list(struct nx_sg *sg,
+ struct nx_sg *end,
+ unsigned int delta,
+ unsigned int *nbytes)
{
+ long int oplen;
+ long int data_back;
+ unsigned int is_delta = delta;
+
while (delta && end > sg) {
struct nx_sg *last = end - 1;
@@ -228,54 +235,20 @@ static long int trim_sg_list(struct nx_sg *sg, struct nx_sg *end, unsigned int d
delta -= last->len;
}
}
- return (sg - end) * sizeof(struct nx_sg);
-}
-/**
- * nx_sha_build_sg_list - walk and build sg list to sha modes
- * using right bounds and limits.
- * @nx_ctx: NX crypto context for the lists we're building
- * @nx_sg: current sg list in or out list
- * @op_len: current op_len to be used in order to build a sg list
- * @nbytes: number or bytes to be processed
- * @offset: buf offset
- * @mode: SHA256 or SHA512
- */
-int nx_sha_build_sg_list(struct nx_crypto_ctx *nx_ctx,
- struct nx_sg *nx_in_outsg,
- s64 *op_len,
- unsigned int *nbytes,
- u8 *offset,
- u32 mode)
-{
- unsigned int delta = 0;
- unsigned int total = *nbytes;
- struct nx_sg *nx_insg = nx_in_outsg;
- unsigned int max_sg_len;
-
- max_sg_len = min_t(u64, nx_ctx->ap->sglen,
- nx_driver.of.max_sg_len/sizeof(struct nx_sg));
- max_sg_len = min_t(u64, max_sg_len,
- nx_ctx->ap->databytelen/NX_PAGE_SIZE);
-
- *nbytes = min_t(u64, *nbytes, nx_ctx->ap->databytelen);
- nx_insg = nx_build_sg_list(nx_insg, offset, nbytes, max_sg_len);
-
- switch (mode) {
- case NX_DS_SHA256:
- if (*nbytes < total)
- delta = *nbytes - (*nbytes & ~(SHA256_BLOCK_SIZE - 1));
- break;
- case NX_DS_SHA512:
- if (*nbytes < total)
- delta = *nbytes - (*nbytes & ~(SHA512_BLOCK_SIZE - 1));
- break;
- default:
- return -EINVAL;
+ /* There are cases where we need to crop list in order to make it
+ * a block size multiple, but we also need to align data. In order to
+ * that we need to calculate how much we need to put back to be
+ * processed
+ */
+ oplen = (sg - end) * sizeof(struct nx_sg);
+ if (is_delta) {
+ data_back = (abs(oplen) / AES_BLOCK_SIZE) * sg->len;
+ data_back = *nbytes - (data_back & ~(AES_BLOCK_SIZE - 1));
+ *nbytes -= data_back;
}
- *op_len = trim_sg_list(nx_in_outsg, nx_insg, delta);
- return 0;
+ return oplen;
}
/**
@@ -330,8 +303,8 @@ int nx_build_sg_lists(struct nx_crypto_ctx *nx_ctx,
/* these lengths should be negative, which will indicate to phyp that
* the input and output parameters are scatterlists, not linear
* buffers */
- nx_ctx->op.inlen = trim_sg_list(nx_ctx->in_sg, nx_insg, delta);
- nx_ctx->op.outlen = trim_sg_list(nx_ctx->out_sg, nx_outsg, delta);
+ nx_ctx->op.inlen = trim_sg_list(nx_ctx->in_sg, nx_insg, delta, nbytes);
+ nx_ctx->op.outlen = trim_sg_list(nx_ctx->out_sg, nx_outsg, delta, nbytes);
return 0;
}
@@ -426,6 +399,13 @@ static void nx_of_update_msc(struct device *dev,
goto next_loop;
}
+ if (!trip->sglen || trip->databytelen < NX_PAGE_SIZE) {
+ dev_warn(dev, "bogus sglen/databytelen: "
+ "%u/%u (ignored)\n", trip->sglen,
+ trip->databytelen);
+ goto next_loop;
+ }
+
switch (trip->keybitlen) {
case 128:
case 160:
@@ -518,6 +498,72 @@ static void nx_of_init(struct device *dev, struct nx_of *props)
nx_of_update_msc(dev, p, props);
}
+static bool nx_check_prop(struct device *dev, u32 fc, u32 mode, int slot)
+{
+ struct alg_props *props = &nx_driver.of.ap[fc][mode][slot];
+
+ if (!props->sglen || props->databytelen < NX_PAGE_SIZE) {
+ if (dev)
+ dev_warn(dev, "bogus sglen/databytelen for %u/%u/%u: "
+ "%u/%u (ignored)\n", fc, mode, slot,
+ props->sglen, props->databytelen);
+ return false;
+ }
+
+ return true;
+}
+
+static bool nx_check_props(struct device *dev, u32 fc, u32 mode)
+{
+ int i;
+
+ for (i = 0; i < 3; i++)
+ if (!nx_check_prop(dev, fc, mode, i))
+ return false;
+
+ return true;
+}
+
+static int nx_register_alg(struct crypto_alg *alg, u32 fc, u32 mode)
+{
+ return nx_check_props(&nx_driver.viodev->dev, fc, mode) ?
+ crypto_register_alg(alg) : 0;
+}
+
+static int nx_register_aead(struct aead_alg *alg, u32 fc, u32 mode)
+{
+ return nx_check_props(&nx_driver.viodev->dev, fc, mode) ?
+ crypto_register_aead(alg) : 0;
+}
+
+static int nx_register_shash(struct shash_alg *alg, u32 fc, u32 mode, int slot)
+{
+ return (slot >= 0 ? nx_check_prop(&nx_driver.viodev->dev,
+ fc, mode, slot) :
+ nx_check_props(&nx_driver.viodev->dev, fc, mode)) ?
+ crypto_register_shash(alg) : 0;
+}
+
+static void nx_unregister_alg(struct crypto_alg *alg, u32 fc, u32 mode)
+{
+ if (nx_check_props(NULL, fc, mode))
+ crypto_unregister_alg(alg);
+}
+
+static void nx_unregister_aead(struct aead_alg *alg, u32 fc, u32 mode)
+{
+ if (nx_check_props(NULL, fc, mode))
+ crypto_unregister_aead(alg);
+}
+
+static void nx_unregister_shash(struct shash_alg *alg, u32 fc, u32 mode,
+ int slot)
+{
+ if (slot >= 0 ? nx_check_prop(NULL, fc, mode, slot) :
+ nx_check_props(NULL, fc, mode))
+ crypto_unregister_shash(alg);
+}
+
/**
* nx_register_algs - register algorithms with the crypto API
*
@@ -542,72 +588,77 @@ static int nx_register_algs(void)
nx_driver.of.status = NX_OKAY;
- rc = crypto_register_alg(&nx_ecb_aes_alg);
+ rc = nx_register_alg(&nx_ecb_aes_alg, NX_FC_AES, NX_MODE_AES_ECB);
if (rc)
goto out;
- rc = crypto_register_alg(&nx_cbc_aes_alg);
+ rc = nx_register_alg(&nx_cbc_aes_alg, NX_FC_AES, NX_MODE_AES_CBC);
if (rc)
goto out_unreg_ecb;
- rc = crypto_register_alg(&nx_ctr_aes_alg);
+ rc = nx_register_alg(&nx_ctr_aes_alg, NX_FC_AES, NX_MODE_AES_CTR);
if (rc)
goto out_unreg_cbc;
- rc = crypto_register_alg(&nx_ctr3686_aes_alg);
+ rc = nx_register_alg(&nx_ctr3686_aes_alg, NX_FC_AES, NX_MODE_AES_CTR);
if (rc)
goto out_unreg_ctr;
- rc = crypto_register_alg(&nx_gcm_aes_alg);
+ rc = nx_register_aead(&nx_gcm_aes_alg, NX_FC_AES, NX_MODE_AES_GCM);
if (rc)
goto out_unreg_ctr3686;
- rc = crypto_register_alg(&nx_gcm4106_aes_alg);
+ rc = nx_register_aead(&nx_gcm4106_aes_alg, NX_FC_AES, NX_MODE_AES_GCM);
if (rc)
goto out_unreg_gcm;
- rc = crypto_register_alg(&nx_ccm_aes_alg);
+ rc = nx_register_alg(&nx_ccm_aes_alg, NX_FC_AES, NX_MODE_AES_CCM);
if (rc)
goto out_unreg_gcm4106;
- rc = crypto_register_alg(&nx_ccm4309_aes_alg);
+ rc = nx_register_alg(&nx_ccm4309_aes_alg, NX_FC_AES, NX_MODE_AES_CCM);
if (rc)
goto out_unreg_ccm;
- rc = crypto_register_shash(&nx_shash_sha256_alg);
+ rc = nx_register_shash(&nx_shash_sha256_alg, NX_FC_SHA, NX_MODE_SHA,
+ NX_PROPS_SHA256);
if (rc)
goto out_unreg_ccm4309;
- rc = crypto_register_shash(&nx_shash_sha512_alg);
+ rc = nx_register_shash(&nx_shash_sha512_alg, NX_FC_SHA, NX_MODE_SHA,
+ NX_PROPS_SHA512);
if (rc)
goto out_unreg_s256;
- rc = crypto_register_shash(&nx_shash_aes_xcbc_alg);
+ rc = nx_register_shash(&nx_shash_aes_xcbc_alg,
+ NX_FC_AES, NX_MODE_AES_XCBC_MAC, -1);
if (rc)
goto out_unreg_s512;
goto out;
out_unreg_s512:
- crypto_unregister_shash(&nx_shash_sha512_alg);
+ nx_unregister_shash(&nx_shash_sha512_alg, NX_FC_SHA, NX_MODE_SHA,
+ NX_PROPS_SHA512);
out_unreg_s256:
- crypto_unregister_shash(&nx_shash_sha256_alg);
+ nx_unregister_shash(&nx_shash_sha256_alg, NX_FC_SHA, NX_MODE_SHA,
+ NX_PROPS_SHA256);
out_unreg_ccm4309:
- crypto_unregister_alg(&nx_ccm4309_aes_alg);
+ nx_unregister_alg(&nx_ccm4309_aes_alg, NX_FC_AES, NX_MODE_AES_CCM);
out_unreg_ccm:
- crypto_unregister_alg(&nx_ccm_aes_alg);
+ nx_unregister_alg(&nx_ccm_aes_alg, NX_FC_AES, NX_MODE_AES_CCM);
out_unreg_gcm4106:
- crypto_unregister_alg(&nx_gcm4106_aes_alg);
+ nx_unregister_aead(&nx_gcm4106_aes_alg, NX_FC_AES, NX_MODE_AES_GCM);
out_unreg_gcm:
- crypto_unregister_alg(&nx_gcm_aes_alg);
+ nx_unregister_aead(&nx_gcm_aes_alg, NX_FC_AES, NX_MODE_AES_GCM);
out_unreg_ctr3686:
- crypto_unregister_alg(&nx_ctr3686_aes_alg);
+ nx_unregister_alg(&nx_ctr3686_aes_alg, NX_FC_AES, NX_MODE_AES_CTR);
out_unreg_ctr:
- crypto_unregister_alg(&nx_ctr_aes_alg);
+ nx_unregister_alg(&nx_ctr_aes_alg, NX_FC_AES, NX_MODE_AES_CTR);
out_unreg_cbc:
- crypto_unregister_alg(&nx_cbc_aes_alg);
+ nx_unregister_alg(&nx_cbc_aes_alg, NX_FC_AES, NX_MODE_AES_CBC);
out_unreg_ecb:
- crypto_unregister_alg(&nx_ecb_aes_alg);
+ nx_unregister_alg(&nx_ecb_aes_alg, NX_FC_AES, NX_MODE_AES_ECB);
out:
return rc;
}
@@ -666,9 +717,9 @@ int nx_crypto_ctx_aes_ccm_init(struct crypto_tfm *tfm)
NX_MODE_AES_CCM);
}
-int nx_crypto_ctx_aes_gcm_init(struct crypto_tfm *tfm)
+int nx_crypto_ctx_aes_gcm_init(struct crypto_aead *tfm)
{
- return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES,
+ return nx_crypto_ctx_init(crypto_aead_ctx(tfm), NX_FC_AES,
NX_MODE_AES_GCM);
}
@@ -720,6 +771,13 @@ void nx_crypto_ctx_exit(struct crypto_tfm *tfm)
nx_ctx->out_sg = NULL;
}
+void nx_crypto_ctx_aead_exit(struct crypto_aead *tfm)
+{
+ struct nx_crypto_ctx *nx_ctx = crypto_aead_ctx(tfm);
+
+ kzfree(nx_ctx->kmem);
+}
+
static int nx_probe(struct vio_dev *viodev, const struct vio_device_id *id)
{
dev_dbg(&viodev->dev, "driver probed: %s resource id: 0x%x\n",
@@ -746,17 +804,24 @@ static int nx_remove(struct vio_dev *viodev)
if (nx_driver.of.status == NX_OKAY) {
NX_DEBUGFS_FINI(&nx_driver);
- crypto_unregister_alg(&nx_ccm_aes_alg);
- crypto_unregister_alg(&nx_ccm4309_aes_alg);
- crypto_unregister_alg(&nx_gcm_aes_alg);
- crypto_unregister_alg(&nx_gcm4106_aes_alg);
- crypto_unregister_alg(&nx_ctr_aes_alg);
- crypto_unregister_alg(&nx_ctr3686_aes_alg);
- crypto_unregister_alg(&nx_cbc_aes_alg);
- crypto_unregister_alg(&nx_ecb_aes_alg);
- crypto_unregister_shash(&nx_shash_sha256_alg);
- crypto_unregister_shash(&nx_shash_sha512_alg);
- crypto_unregister_shash(&nx_shash_aes_xcbc_alg);
+ nx_unregister_shash(&nx_shash_aes_xcbc_alg,
+ NX_FC_AES, NX_MODE_AES_XCBC_MAC, -1);
+ nx_unregister_shash(&nx_shash_sha512_alg,
+ NX_FC_SHA, NX_MODE_SHA, NX_PROPS_SHA256);
+ nx_unregister_shash(&nx_shash_sha256_alg,
+ NX_FC_SHA, NX_MODE_SHA, NX_PROPS_SHA512);
+ nx_unregister_alg(&nx_ccm4309_aes_alg,
+ NX_FC_AES, NX_MODE_AES_CCM);
+ nx_unregister_alg(&nx_ccm_aes_alg, NX_FC_AES, NX_MODE_AES_CCM);
+ nx_unregister_aead(&nx_gcm4106_aes_alg,
+ NX_FC_AES, NX_MODE_AES_GCM);
+ nx_unregister_aead(&nx_gcm_aes_alg,
+ NX_FC_AES, NX_MODE_AES_GCM);
+ nx_unregister_alg(&nx_ctr3686_aes_alg,
+ NX_FC_AES, NX_MODE_AES_CTR);
+ nx_unregister_alg(&nx_ctr_aes_alg, NX_FC_AES, NX_MODE_AES_CTR);
+ nx_unregister_alg(&nx_cbc_aes_alg, NX_FC_AES, NX_MODE_AES_CBC);
+ nx_unregister_alg(&nx_ecb_aes_alg, NX_FC_AES, NX_MODE_AES_ECB);
}
return 0;
diff --git a/drivers/crypto/nx/nx.h b/drivers/crypto/nx/nx.h
index 6c9ecaaead52..de3ea8738146 100644
--- a/drivers/crypto/nx/nx.h
+++ b/drivers/crypto/nx/nx.h
@@ -143,18 +143,17 @@ struct nx_crypto_ctx {
/* prototypes */
int nx_crypto_ctx_aes_ccm_init(struct crypto_tfm *tfm);
-int nx_crypto_ctx_aes_gcm_init(struct crypto_tfm *tfm);
+int nx_crypto_ctx_aes_gcm_init(struct crypto_aead *tfm);
int nx_crypto_ctx_aes_xcbc_init(struct crypto_tfm *tfm);
int nx_crypto_ctx_aes_ctr_init(struct crypto_tfm *tfm);
int nx_crypto_ctx_aes_cbc_init(struct crypto_tfm *tfm);
int nx_crypto_ctx_aes_ecb_init(struct crypto_tfm *tfm);
int nx_crypto_ctx_sha_init(struct crypto_tfm *tfm);
void nx_crypto_ctx_exit(struct crypto_tfm *tfm);
+void nx_crypto_ctx_aead_exit(struct crypto_aead *tfm);
void nx_ctx_init(struct nx_crypto_ctx *nx_ctx, unsigned int function);
int nx_hcall_sync(struct nx_crypto_ctx *ctx, struct vio_pfo_op *op,
u32 may_sleep);
-int nx_sha_build_sg_list(struct nx_crypto_ctx *, struct nx_sg *,
- s64 *, unsigned int *, u8 *, u32);
struct nx_sg *nx_build_sg_list(struct nx_sg *, u8 *, unsigned int *, u32);
int nx_build_sg_lists(struct nx_crypto_ctx *, struct blkcipher_desc *,
struct scatterlist *, struct scatterlist *, unsigned int *,
@@ -178,8 +177,8 @@ void nx_debugfs_fini(struct nx_crypto_driver *);
extern struct crypto_alg nx_cbc_aes_alg;
extern struct crypto_alg nx_ecb_aes_alg;
-extern struct crypto_alg nx_gcm_aes_alg;
-extern struct crypto_alg nx_gcm4106_aes_alg;
+extern struct aead_alg nx_gcm_aes_alg;
+extern struct aead_alg nx_gcm4106_aes_alg;
extern struct crypto_alg nx_ctr_aes_alg;
extern struct crypto_alg nx_ctr3686_aes_alg;
extern struct crypto_alg nx_ccm_aes_alg;
diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c
index 4d63e0d4da9a..b2024c95a3cf 100644
--- a/drivers/crypto/omap-sham.c
+++ b/drivers/crypto/omap-sham.c
@@ -362,7 +362,13 @@ static void omap_sham_copy_ready_hash(struct ahash_request *req)
static int omap_sham_hw_init(struct omap_sham_dev *dd)
{
- pm_runtime_get_sync(dd->dev);
+ int err;
+
+ err = pm_runtime_get_sync(dd->dev);
+ if (err < 0) {
+ dev_err(dd->dev, "failed to get sync: %d\n", err);
+ return err;
+ }
if (!test_bit(FLAGS_INIT, &dd->flags)) {
set_bit(FLAGS_INIT, &dd->flags);
@@ -1793,6 +1799,10 @@ static const struct of_device_id omap_sham_of_match[] = {
.data = &omap_sham_pdata_omap2,
},
{
+ .compatible = "ti,omap3-sham",
+ .data = &omap_sham_pdata_omap2,
+ },
+ {
.compatible = "ti,omap4-sham",
.data = &omap_sham_pdata_omap4,
},
@@ -1947,7 +1957,13 @@ static int omap_sham_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
pm_runtime_irq_safe(dev);
- pm_runtime_get_sync(dev);
+
+ err = pm_runtime_get_sync(dev);
+ if (err < 0) {
+ dev_err(dev, "failed to get sync: %d\n", err);
+ goto err_pm;
+ }
+
rev = omap_sham_read(dd, SHA_REG_REV(dd));
pm_runtime_put_sync(&pdev->dev);
@@ -1977,6 +1993,7 @@ err_algs:
for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--)
crypto_unregister_ahash(
&dd->pdata->algs_info[i].algs_list[j]);
+err_pm:
pm_runtime_disable(dev);
if (dd->dma_lch)
dma_release_channel(dd->dma_lch);
@@ -2019,7 +2036,11 @@ static int omap_sham_suspend(struct device *dev)
static int omap_sham_resume(struct device *dev)
{
- pm_runtime_get_sync(dev);
+ int err = pm_runtime_get_sync(dev);
+ if (err < 0) {
+ dev_err(dev, "failed to get sync: %d\n", err);
+ return err;
+ }
return 0;
}
#endif
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index c178ed8c3908..da2d6777bd09 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -22,7 +22,7 @@
#include <asm/cpu_device_id.h>
#include <asm/byteorder.h>
#include <asm/processor.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
/*
* Number of data blocks actually fetched for each xcrypt insn.
diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c
index 95f7d27ce491..4e154c9b9206 100644
--- a/drivers/crypto/padlock-sha.c
+++ b/drivers/crypto/padlock-sha.c
@@ -23,7 +23,7 @@
#include <linux/kernel.h>
#include <linux/scatterlist.h>
#include <asm/cpu_device_id.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
struct padlock_sha_desc {
struct shash_desc fallback;
diff --git a/drivers/crypto/picoxcell_crypto.c b/drivers/crypto/picoxcell_crypto.c
index 5da5b98b8f29..4f56f3681abd 100644
--- a/drivers/crypto/picoxcell_crypto.c
+++ b/drivers/crypto/picoxcell_crypto.c
@@ -15,7 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
#include <crypto/aes.h>
#include <crypto/algapi.h>
#include <crypto/authenc.h>
@@ -40,6 +40,7 @@
#include <linux/rtnetlink.h>
#include <linux/scatterlist.h>
#include <linux/sched.h>
+#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/timer.h>
@@ -261,18 +262,9 @@ static unsigned spacc_load_ctx(struct spacc_generic_ctx *ctx,
}
/* Count the number of scatterlist entries in a scatterlist. */
-static int sg_count(struct scatterlist *sg_list, int nbytes)
+static inline int sg_count(struct scatterlist *sg_list, int nbytes)
{
- struct scatterlist *sg = sg_list;
- int sg_nents = 0;
-
- while (nbytes > 0) {
- ++sg_nents;
- nbytes -= sg->length;
- sg = sg_next(sg);
- }
-
- return sg_nents;
+ return sg_nents_for_len(sg_list, nbytes);
}
static inline void ddt_set(struct spacc_ddt *ddt, dma_addr_t phys, size_t len)
@@ -326,6 +318,7 @@ static int spacc_aead_make_ddts(struct spacc_req *req, u8 *giv)
struct spacc_ddt *src_ddt, *dst_ddt;
unsigned ivsize = crypto_aead_ivsize(crypto_aead_reqtfm(areq));
unsigned nents = sg_count(areq->src, areq->cryptlen);
+ unsigned total;
dma_addr_t iv_addr;
struct scatterlist *cur;
int i, dst_ents, src_ents, assoc_ents;
@@ -369,11 +362,18 @@ static int spacc_aead_make_ddts(struct spacc_req *req, u8 *giv)
* Map the associated data. For decryption we don't copy the
* associated data.
*/
+ total = areq->assoclen;
for_each_sg(areq->assoc, cur, assoc_ents, i) {
- ddt_set(src_ddt++, sg_dma_address(cur), sg_dma_len(cur));
+ unsigned len = sg_dma_len(cur);
+
+ if (len > total)
+ len = total;
+
+ total -= len;
+
+ ddt_set(src_ddt++, sg_dma_address(cur), len);
if (req->is_encrypt)
- ddt_set(dst_ddt++, sg_dma_address(cur),
- sg_dma_len(cur));
+ ddt_set(dst_ddt++, sg_dma_address(cur), len);
}
ddt_set(src_ddt++, iv_addr, ivsize);
@@ -790,7 +790,8 @@ static int spacc_aead_cra_init(struct crypto_tfm *tfm)
get_random_bytes(ctx->salt, sizeof(ctx->salt));
- tfm->crt_aead.reqsize = sizeof(struct spacc_req);
+ crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+ sizeof(struct spacc_req));
return 0;
}
@@ -1754,15 +1755,15 @@ static int spacc_probe(struct platform_device *pdev)
return PTR_ERR(engine->clk);
}
- if (clk_enable(engine->clk)) {
- dev_info(&pdev->dev, "unable to enable clk\n");
+ if (clk_prepare_enable(engine->clk)) {
+ dev_info(&pdev->dev, "unable to prepare/enable clk\n");
clk_put(engine->clk);
return -EIO;
}
err = device_create_file(&pdev->dev, &dev_attr_stat_irq_thresh);
if (err) {
- clk_disable(engine->clk);
+ clk_disable_unprepare(engine->clk);
clk_put(engine->clk);
return err;
}
@@ -1830,7 +1831,7 @@ static int spacc_remove(struct platform_device *pdev)
crypto_unregister_alg(&alg->alg);
}
- clk_disable(engine->clk);
+ clk_disable_unprepare(engine->clk);
clk_put(engine->clk);
return 0;
diff --git a/drivers/crypto/qat/Kconfig b/drivers/crypto/qat/Kconfig
index 49bede2a9f77..6fdb9e8b22a7 100644
--- a/drivers/crypto/qat/Kconfig
+++ b/drivers/crypto/qat/Kconfig
@@ -2,9 +2,8 @@ config CRYPTO_DEV_QAT
tristate
select CRYPTO_AEAD
select CRYPTO_AUTHENC
- select CRYPTO_ALGAPI
- select CRYPTO_AES
- select CRYPTO_CBC
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_HMAC
select CRYPTO_SHA1
select CRYPTO_SHA256
select CRYPTO_SHA512
@@ -13,7 +12,6 @@ config CRYPTO_DEV_QAT
config CRYPTO_DEV_QAT_DH895xCC
tristate "Support for Intel(R) DH895xCC"
depends on X86 && PCI
- default n
select CRYPTO_DEV_QAT
help
Support for Intel(R) DH895xcc with Intel(R) QuickAssist Technology
diff --git a/drivers/crypto/qat/qat_common/adf_accel_devices.h b/drivers/crypto/qat/qat_common/adf_accel_devices.h
index f22ce7169fa5..5fe902967620 100644
--- a/drivers/crypto/qat/qat_common/adf_accel_devices.h
+++ b/drivers/crypto/qat/qat_common/adf_accel_devices.h
@@ -48,7 +48,6 @@
#define ADF_ACCEL_DEVICES_H_
#include <linux/module.h>
#include <linux/list.h>
-#include <linux/proc_fs.h>
#include <linux/io.h>
#include "adf_cfg_common.h"
diff --git a/drivers/crypto/qat/qat_common/adf_cfg_user.h b/drivers/crypto/qat/qat_common/adf_cfg_user.h
index 0c38a155a865..ef5988afd4c6 100644
--- a/drivers/crypto/qat/qat_common/adf_cfg_user.h
+++ b/drivers/crypto/qat/qat_common/adf_cfg_user.h
@@ -54,14 +54,6 @@ struct adf_user_cfg_key_val {
char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
char val[ADF_CFG_MAX_VAL_LEN_IN_BYTES];
union {
- char *user_val_ptr;
- uint64_t padding1;
- };
- union {
- struct adf_user_cfg_key_val *prev;
- uint64_t padding2;
- };
- union {
struct adf_user_cfg_key_val *next;
uint64_t padding3;
};
@@ -75,10 +67,6 @@ struct adf_user_cfg_section {
uint64_t padding1;
};
union {
- struct adf_user_cfg_section *prev;
- uint64_t padding2;
- };
- union {
struct adf_user_cfg_section *next;
uint64_t padding3;
};
diff --git a/drivers/crypto/qat/qat_common/adf_common_drv.h b/drivers/crypto/qat/qat_common/adf_common_drv.h
index 0666ee6a3360..27e16c09230b 100644
--- a/drivers/crypto/qat/qat_common/adf_common_drv.h
+++ b/drivers/crypto/qat/qat_common/adf_common_drv.h
@@ -53,6 +53,13 @@
#include "icp_qat_fw_loader_handle.h"
#include "icp_qat_hal.h"
+#define ADF_MAJOR_VERSION 0
+#define ADF_MINOR_VERSION 1
+#define ADF_BUILD_VERSION 3
+#define ADF_DRV_VERSION __stringify(ADF_MAJOR_VERSION) "." \
+ __stringify(ADF_MINOR_VERSION) "." \
+ __stringify(ADF_BUILD_VERSION)
+
#define ADF_STATUS_RESTARTING 0
#define ADF_STATUS_STARTING 1
#define ADF_STATUS_CONFIGURED 2
diff --git a/drivers/crypto/qat/qat_common/adf_ctl_drv.c b/drivers/crypto/qat/qat_common/adf_ctl_drv.c
index cb5f066e93a6..e056b9e9bf8a 100644
--- a/drivers/crypto/qat/qat_common/adf_ctl_drv.c
+++ b/drivers/crypto/qat/qat_common/adf_ctl_drv.c
@@ -504,3 +504,4 @@ MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Intel");
MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
MODULE_ALIAS_CRYPTO("intel_qat");
+MODULE_VERSION(ADF_DRV_VERSION);
diff --git a/drivers/crypto/qat/qat_common/qat_algs.c b/drivers/crypto/qat/qat_common/qat_algs.c
index 1dc5b0a17cf7..067402c7c2a9 100644
--- a/drivers/crypto/qat/qat_common/qat_algs.c
+++ b/drivers/crypto/qat/qat_common/qat_algs.c
@@ -47,7 +47,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/crypto.h>
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
#include <crypto/aes.h>
#include <crypto/sha.h>
#include <crypto/hash.h>
@@ -653,7 +653,7 @@ static void qat_alg_free_bufl(struct qat_crypto_instance *inst,
}
static int qat_alg_sgl_to_bufl(struct qat_crypto_instance *inst,
- struct scatterlist *assoc,
+ struct scatterlist *assoc, int assoclen,
struct scatterlist *sgl,
struct scatterlist *sglout, uint8_t *iv,
uint8_t ivlen,
@@ -685,15 +685,21 @@ static int qat_alg_sgl_to_bufl(struct qat_crypto_instance *inst,
for_each_sg(assoc, sg, assoc_n, i) {
if (!sg->length)
continue;
- bufl->bufers[bufs].addr = dma_map_single(dev,
- sg_virt(sg),
- sg->length,
- DMA_BIDIRECTIONAL);
- bufl->bufers[bufs].len = sg->length;
+
+ if (!(assoclen > 0))
+ break;
+
+ bufl->bufers[bufs].addr =
+ dma_map_single(dev, sg_virt(sg),
+ min_t(int, assoclen, sg->length),
+ DMA_BIDIRECTIONAL);
+ bufl->bufers[bufs].len = min_t(int, assoclen, sg->length);
if (unlikely(dma_mapping_error(dev, bufl->bufers[bufs].addr)))
goto err;
bufs++;
+ assoclen -= sg->length;
}
+
if (ivlen) {
bufl->bufers[bufs].addr = dma_map_single(dev, iv, ivlen,
DMA_BIDIRECTIONAL);
@@ -845,8 +851,9 @@ static int qat_alg_aead_dec(struct aead_request *areq)
int digst_size = crypto_aead_crt(aead_tfm)->authsize;
int ret, ctr = 0;
- ret = qat_alg_sgl_to_bufl(ctx->inst, areq->assoc, areq->src, areq->dst,
- areq->iv, AES_BLOCK_SIZE, qat_req);
+ ret = qat_alg_sgl_to_bufl(ctx->inst, areq->assoc, areq->assoclen,
+ areq->src, areq->dst, areq->iv,
+ AES_BLOCK_SIZE, qat_req);
if (unlikely(ret))
return ret;
@@ -889,8 +896,9 @@ static int qat_alg_aead_enc_internal(struct aead_request *areq, uint8_t *iv,
struct icp_qat_fw_la_bulk_req *msg;
int ret, ctr = 0;
- ret = qat_alg_sgl_to_bufl(ctx->inst, areq->assoc, areq->src, areq->dst,
- iv, AES_BLOCK_SIZE, qat_req);
+ ret = qat_alg_sgl_to_bufl(ctx->inst, areq->assoc, areq->assoclen,
+ areq->src, areq->dst, iv, AES_BLOCK_SIZE,
+ qat_req);
if (unlikely(ret))
return ret;
@@ -1017,7 +1025,7 @@ static int qat_alg_ablkcipher_encrypt(struct ablkcipher_request *req)
struct icp_qat_fw_la_bulk_req *msg;
int ret, ctr = 0;
- ret = qat_alg_sgl_to_bufl(ctx->inst, NULL, req->src, req->dst,
+ ret = qat_alg_sgl_to_bufl(ctx->inst, NULL, 0, req->src, req->dst,
NULL, 0, qat_req);
if (unlikely(ret))
return ret;
@@ -1055,7 +1063,7 @@ static int qat_alg_ablkcipher_decrypt(struct ablkcipher_request *req)
struct icp_qat_fw_la_bulk_req *msg;
int ret, ctr = 0;
- ret = qat_alg_sgl_to_bufl(ctx->inst, NULL, req->src, req->dst,
+ ret = qat_alg_sgl_to_bufl(ctx->inst, NULL, 0, req->src, req->dst,
NULL, 0, qat_req);
if (unlikely(ret))
return ret;
@@ -1094,8 +1102,9 @@ static int qat_alg_aead_init(struct crypto_tfm *tfm,
return -EFAULT;
spin_lock_init(&ctx->lock);
ctx->qat_hash_alg = hash;
- tfm->crt_aead.reqsize = sizeof(struct aead_request) +
- sizeof(struct qat_crypto_request);
+ crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+ sizeof(struct aead_request) +
+ sizeof(struct qat_crypto_request));
ctx->tfm = tfm;
return 0;
}
diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_drv.c b/drivers/crypto/qat/qat_dh895xcc/adf_drv.c
index 9decea2779c6..1bde45b7a3c5 100644
--- a/drivers/crypto/qat/qat_dh895xcc/adf_drv.c
+++ b/drivers/crypto/qat/qat_dh895xcc/adf_drv.c
@@ -300,6 +300,8 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
goto out_err;
+ pcie_set_readrq(pdev, 1024);
+
/* enable PCI device */
if (pci_enable_device(pdev)) {
ret = -EFAULT;
@@ -417,5 +419,6 @@ module_exit(adfdrv_release);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Intel");
-MODULE_FIRMWARE("qat_895xcc.bin");
+MODULE_FIRMWARE(ADF_DH895XCC_FW);
MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
+MODULE_VERSION(ADF_DRV_VERSION);
diff --git a/drivers/crypto/sahara.c b/drivers/crypto/sahara.c
index 6be377f6b9e7..397a500b3d8a 100644
--- a/drivers/crypto/sahara.c
+++ b/drivers/crypto/sahara.c
@@ -1578,8 +1578,12 @@ static int sahara_probe(struct platform_device *pdev)
init_completion(&dev->dma_completion);
- clk_prepare_enable(dev->clk_ipg);
- clk_prepare_enable(dev->clk_ahb);
+ err = clk_prepare_enable(dev->clk_ipg);
+ if (err)
+ goto err_link;
+ err = clk_prepare_enable(dev->clk_ahb);
+ if (err)
+ goto clk_ipg_disable;
version = sahara_read(dev, SAHARA_REG_VERSION);
if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx27-sahara")) {
@@ -1619,10 +1623,11 @@ err_algs:
dma_free_coherent(&pdev->dev,
SAHARA_MAX_HW_LINK * sizeof(struct sahara_hw_link),
dev->hw_link[0], dev->hw_phys_link[0]);
- clk_disable_unprepare(dev->clk_ipg);
- clk_disable_unprepare(dev->clk_ahb);
kthread_stop(dev->kthread);
dev_ptr = NULL;
+ clk_disable_unprepare(dev->clk_ahb);
+clk_ipg_disable:
+ clk_disable_unprepare(dev->clk_ipg);
err_link:
dma_free_coherent(&pdev->dev,
2 * AES_KEYSIZE_128,
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 857414afa29a..83aca95a95bc 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -46,7 +46,7 @@
#include <crypto/des.h>
#include <crypto/sha.h>
#include <crypto/md5.h>
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
#include <crypto/authenc.h>
#include <crypto/skcipher.h>
#include <crypto/hash.h>
@@ -55,49 +55,92 @@
#include "talitos.h"
-static void to_talitos_ptr(struct talitos_ptr *talitos_ptr, dma_addr_t dma_addr)
+static void to_talitos_ptr(struct talitos_ptr *ptr, dma_addr_t dma_addr,
+ bool is_sec1)
{
- talitos_ptr->ptr = cpu_to_be32(lower_32_bits(dma_addr));
- talitos_ptr->eptr = upper_32_bits(dma_addr);
+ ptr->ptr = cpu_to_be32(lower_32_bits(dma_addr));
+ if (!is_sec1)
+ ptr->eptr = upper_32_bits(dma_addr);
+}
+
+static void to_talitos_ptr_len(struct talitos_ptr *ptr, unsigned int len,
+ bool is_sec1)
+{
+ if (is_sec1) {
+ ptr->res = 0;
+ ptr->len1 = cpu_to_be16(len);
+ } else {
+ ptr->len = cpu_to_be16(len);
+ }
+}
+
+static unsigned short from_talitos_ptr_len(struct talitos_ptr *ptr,
+ bool is_sec1)
+{
+ if (is_sec1)
+ return be16_to_cpu(ptr->len1);
+ else
+ return be16_to_cpu(ptr->len);
+}
+
+static void to_talitos_ptr_extent_clear(struct talitos_ptr *ptr, bool is_sec1)
+{
+ if (!is_sec1)
+ ptr->j_extent = 0;
}
/*
* map virtual single (contiguous) pointer to h/w descriptor pointer
*/
static void map_single_talitos_ptr(struct device *dev,
- struct talitos_ptr *talitos_ptr,
- unsigned short len, void *data,
- unsigned char extent,
+ struct talitos_ptr *ptr,
+ unsigned int len, void *data,
enum dma_data_direction dir)
{
dma_addr_t dma_addr = dma_map_single(dev, data, len, dir);
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
- talitos_ptr->len = cpu_to_be16(len);
- to_talitos_ptr(talitos_ptr, dma_addr);
- talitos_ptr->j_extent = extent;
+ to_talitos_ptr_len(ptr, len, is_sec1);
+ to_talitos_ptr(ptr, dma_addr, is_sec1);
+ to_talitos_ptr_extent_clear(ptr, is_sec1);
}
/*
* unmap bus single (contiguous) h/w descriptor pointer
*/
static void unmap_single_talitos_ptr(struct device *dev,
- struct talitos_ptr *talitos_ptr,
+ struct talitos_ptr *ptr,
enum dma_data_direction dir)
{
- dma_unmap_single(dev, be32_to_cpu(talitos_ptr->ptr),
- be16_to_cpu(talitos_ptr->len), dir);
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
+
+ dma_unmap_single(dev, be32_to_cpu(ptr->ptr),
+ from_talitos_ptr_len(ptr, is_sec1), dir);
}
static int reset_channel(struct device *dev, int ch)
{
struct talitos_private *priv = dev_get_drvdata(dev);
unsigned int timeout = TALITOS_TIMEOUT;
+ bool is_sec1 = has_ftr_sec1(priv);
- setbits32(priv->chan[ch].reg + TALITOS_CCCR, TALITOS_CCCR_RESET);
+ if (is_sec1) {
+ setbits32(priv->chan[ch].reg + TALITOS_CCCR_LO,
+ TALITOS1_CCCR_LO_RESET);
- while ((in_be32(priv->chan[ch].reg + TALITOS_CCCR) & TALITOS_CCCR_RESET)
- && --timeout)
- cpu_relax();
+ while ((in_be32(priv->chan[ch].reg + TALITOS_CCCR_LO) &
+ TALITOS1_CCCR_LO_RESET) && --timeout)
+ cpu_relax();
+ } else {
+ setbits32(priv->chan[ch].reg + TALITOS_CCCR,
+ TALITOS2_CCCR_RESET);
+
+ while ((in_be32(priv->chan[ch].reg + TALITOS_CCCR) &
+ TALITOS2_CCCR_RESET) && --timeout)
+ cpu_relax();
+ }
if (timeout == 0) {
dev_err(dev, "failed to reset channel %d\n", ch);
@@ -120,11 +163,12 @@ static int reset_device(struct device *dev)
{
struct talitos_private *priv = dev_get_drvdata(dev);
unsigned int timeout = TALITOS_TIMEOUT;
- u32 mcr = TALITOS_MCR_SWR;
+ bool is_sec1 = has_ftr_sec1(priv);
+ u32 mcr = is_sec1 ? TALITOS1_MCR_SWR : TALITOS2_MCR_SWR;
setbits32(priv->reg + TALITOS_MCR, mcr);
- while ((in_be32(priv->reg + TALITOS_MCR) & TALITOS_MCR_SWR)
+ while ((in_be32(priv->reg + TALITOS_MCR) & mcr)
&& --timeout)
cpu_relax();
@@ -148,6 +192,7 @@ static int init_device(struct device *dev)
{
struct talitos_private *priv = dev_get_drvdata(dev);
int ch, err;
+ bool is_sec1 = has_ftr_sec1(priv);
/*
* Master reset
@@ -171,12 +216,19 @@ static int init_device(struct device *dev)
}
/* enable channel done and error interrupts */
- setbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_INIT);
- setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT);
+ if (is_sec1) {
+ clrbits32(priv->reg + TALITOS_IMR, TALITOS1_IMR_INIT);
+ clrbits32(priv->reg + TALITOS_IMR_LO, TALITOS1_IMR_LO_INIT);
+ /* disable parity error check in DEU (erroneous? test vect.) */
+ setbits32(priv->reg_deu + TALITOS_EUICR, TALITOS1_DEUICR_KPE);
+ } else {
+ setbits32(priv->reg + TALITOS_IMR, TALITOS2_IMR_INIT);
+ setbits32(priv->reg + TALITOS_IMR_LO, TALITOS2_IMR_LO_INIT);
+ }
/* disable integrity check error interrupts (use writeback instead) */
if (priv->features & TALITOS_FTR_HW_AUTH_CHECK)
- setbits32(priv->reg + TALITOS_MDEUICR_LO,
+ setbits32(priv->reg_mdeu + TALITOS_EUICR_LO,
TALITOS_MDEUICR_LO_ICE);
return 0;
@@ -204,6 +256,7 @@ int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
struct talitos_request *request;
unsigned long flags;
int head;
+ bool is_sec1 = has_ftr_sec1(priv);
spin_lock_irqsave(&priv->chan[ch].head_lock, flags);
@@ -217,8 +270,17 @@ int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
request = &priv->chan[ch].fifo[head];
/* map descriptor and save caller data */
- request->dma_desc = dma_map_single(dev, desc, sizeof(*desc),
- DMA_BIDIRECTIONAL);
+ if (is_sec1) {
+ desc->hdr1 = desc->hdr;
+ desc->next_desc = 0;
+ request->dma_desc = dma_map_single(dev, &desc->hdr1,
+ TALITOS_DESC_SIZE,
+ DMA_BIDIRECTIONAL);
+ } else {
+ request->dma_desc = dma_map_single(dev, desc,
+ TALITOS_DESC_SIZE,
+ DMA_BIDIRECTIONAL);
+ }
request->callback = callback;
request->context = context;
@@ -250,16 +312,21 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
struct talitos_request *request, saved_req;
unsigned long flags;
int tail, status;
+ bool is_sec1 = has_ftr_sec1(priv);
spin_lock_irqsave(&priv->chan[ch].tail_lock, flags);
tail = priv->chan[ch].tail;
while (priv->chan[ch].fifo[tail].desc) {
+ __be32 hdr;
+
request = &priv->chan[ch].fifo[tail];
/* descriptors with their done bits set don't get the error */
rmb();
- if ((request->desc->hdr & DESC_HDR_DONE) == DESC_HDR_DONE)
+ hdr = is_sec1 ? request->desc->hdr1 : request->desc->hdr;
+
+ if ((hdr & DESC_HDR_DONE) == DESC_HDR_DONE)
status = 0;
else
if (!error)
@@ -268,7 +335,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
status = error;
dma_unmap_single(dev, request->dma_desc,
- sizeof(struct talitos_desc),
+ TALITOS_DESC_SIZE,
DMA_BIDIRECTIONAL);
/* copy entries so we can call callback outside lock */
@@ -302,8 +369,37 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
/*
* process completed requests for channels that have done status
*/
-#define DEF_TALITOS_DONE(name, ch_done_mask) \
-static void talitos_done_##name(unsigned long data) \
+#define DEF_TALITOS1_DONE(name, ch_done_mask) \
+static void talitos1_done_##name(unsigned long data) \
+{ \
+ struct device *dev = (struct device *)data; \
+ struct talitos_private *priv = dev_get_drvdata(dev); \
+ unsigned long flags; \
+ \
+ if (ch_done_mask & 0x10000000) \
+ flush_channel(dev, 0, 0, 0); \
+ if (priv->num_channels == 1) \
+ goto out; \
+ if (ch_done_mask & 0x40000000) \
+ flush_channel(dev, 1, 0, 0); \
+ if (ch_done_mask & 0x00010000) \
+ flush_channel(dev, 2, 0, 0); \
+ if (ch_done_mask & 0x00040000) \
+ flush_channel(dev, 3, 0, 0); \
+ \
+out: \
+ /* At this point, all completed channels have been processed */ \
+ /* Unmask done interrupts for channels completed later on. */ \
+ spin_lock_irqsave(&priv->reg_lock, flags); \
+ clrbits32(priv->reg + TALITOS_IMR, ch_done_mask); \
+ clrbits32(priv->reg + TALITOS_IMR_LO, TALITOS1_IMR_LO_INIT); \
+ spin_unlock_irqrestore(&priv->reg_lock, flags); \
+}
+
+DEF_TALITOS1_DONE(4ch, TALITOS1_ISR_4CHDONE)
+
+#define DEF_TALITOS2_DONE(name, ch_done_mask) \
+static void talitos2_done_##name(unsigned long data) \
{ \
struct device *dev = (struct device *)data; \
struct talitos_private *priv = dev_get_drvdata(dev); \
@@ -325,12 +421,13 @@ out: \
/* Unmask done interrupts for channels completed later on. */ \
spin_lock_irqsave(&priv->reg_lock, flags); \
setbits32(priv->reg + TALITOS_IMR, ch_done_mask); \
- setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT); \
+ setbits32(priv->reg + TALITOS_IMR_LO, TALITOS2_IMR_LO_INIT); \
spin_unlock_irqrestore(&priv->reg_lock, flags); \
}
-DEF_TALITOS_DONE(4ch, TALITOS_ISR_4CHDONE)
-DEF_TALITOS_DONE(ch0_2, TALITOS_ISR_CH_0_2_DONE)
-DEF_TALITOS_DONE(ch1_3, TALITOS_ISR_CH_1_3_DONE)
+
+DEF_TALITOS2_DONE(4ch, TALITOS2_ISR_4CHDONE)
+DEF_TALITOS2_DONE(ch0_2, TALITOS2_ISR_CH_0_2_DONE)
+DEF_TALITOS2_DONE(ch1_3, TALITOS2_ISR_CH_1_3_DONE)
/*
* locate current (offending) descriptor
@@ -377,44 +474,44 @@ static void report_eu_error(struct device *dev, int ch, u32 desc_hdr)
switch (desc_hdr & DESC_HDR_SEL0_MASK) {
case DESC_HDR_SEL0_AFEU:
dev_err(dev, "AFEUISR 0x%08x_%08x\n",
- in_be32(priv->reg + TALITOS_AFEUISR),
- in_be32(priv->reg + TALITOS_AFEUISR_LO));
+ in_be32(priv->reg_afeu + TALITOS_EUISR),
+ in_be32(priv->reg_afeu + TALITOS_EUISR_LO));
break;
case DESC_HDR_SEL0_DEU:
dev_err(dev, "DEUISR 0x%08x_%08x\n",
- in_be32(priv->reg + TALITOS_DEUISR),
- in_be32(priv->reg + TALITOS_DEUISR_LO));
+ in_be32(priv->reg_deu + TALITOS_EUISR),
+ in_be32(priv->reg_deu + TALITOS_EUISR_LO));
break;
case DESC_HDR_SEL0_MDEUA:
case DESC_HDR_SEL0_MDEUB:
dev_err(dev, "MDEUISR 0x%08x_%08x\n",
- in_be32(priv->reg + TALITOS_MDEUISR),
- in_be32(priv->reg + TALITOS_MDEUISR_LO));
+ in_be32(priv->reg_mdeu + TALITOS_EUISR),
+ in_be32(priv->reg_mdeu + TALITOS_EUISR_LO));
break;
case DESC_HDR_SEL0_RNG:
dev_err(dev, "RNGUISR 0x%08x_%08x\n",
- in_be32(priv->reg + TALITOS_RNGUISR),
- in_be32(priv->reg + TALITOS_RNGUISR_LO));
+ in_be32(priv->reg_rngu + TALITOS_ISR),
+ in_be32(priv->reg_rngu + TALITOS_ISR_LO));
break;
case DESC_HDR_SEL0_PKEU:
dev_err(dev, "PKEUISR 0x%08x_%08x\n",
- in_be32(priv->reg + TALITOS_PKEUISR),
- in_be32(priv->reg + TALITOS_PKEUISR_LO));
+ in_be32(priv->reg_pkeu + TALITOS_EUISR),
+ in_be32(priv->reg_pkeu + TALITOS_EUISR_LO));
break;
case DESC_HDR_SEL0_AESU:
dev_err(dev, "AESUISR 0x%08x_%08x\n",
- in_be32(priv->reg + TALITOS_AESUISR),
- in_be32(priv->reg + TALITOS_AESUISR_LO));
+ in_be32(priv->reg_aesu + TALITOS_EUISR),
+ in_be32(priv->reg_aesu + TALITOS_EUISR_LO));
break;
case DESC_HDR_SEL0_CRCU:
dev_err(dev, "CRCUISR 0x%08x_%08x\n",
- in_be32(priv->reg + TALITOS_CRCUISR),
- in_be32(priv->reg + TALITOS_CRCUISR_LO));
+ in_be32(priv->reg_crcu + TALITOS_EUISR),
+ in_be32(priv->reg_crcu + TALITOS_EUISR_LO));
break;
case DESC_HDR_SEL0_KEU:
dev_err(dev, "KEUISR 0x%08x_%08x\n",
- in_be32(priv->reg + TALITOS_KEUISR),
- in_be32(priv->reg + TALITOS_KEUISR_LO));
+ in_be32(priv->reg_pkeu + TALITOS_EUISR),
+ in_be32(priv->reg_pkeu + TALITOS_EUISR_LO));
break;
}
@@ -422,13 +519,13 @@ static void report_eu_error(struct device *dev, int ch, u32 desc_hdr)
case DESC_HDR_SEL1_MDEUA:
case DESC_HDR_SEL1_MDEUB:
dev_err(dev, "MDEUISR 0x%08x_%08x\n",
- in_be32(priv->reg + TALITOS_MDEUISR),
- in_be32(priv->reg + TALITOS_MDEUISR_LO));
+ in_be32(priv->reg_mdeu + TALITOS_EUISR),
+ in_be32(priv->reg_mdeu + TALITOS_EUISR_LO));
break;
case DESC_HDR_SEL1_CRCU:
dev_err(dev, "CRCUISR 0x%08x_%08x\n",
- in_be32(priv->reg + TALITOS_CRCUISR),
- in_be32(priv->reg + TALITOS_CRCUISR_LO));
+ in_be32(priv->reg_crcu + TALITOS_EUISR),
+ in_be32(priv->reg_crcu + TALITOS_EUISR_LO));
break;
}
@@ -445,17 +542,24 @@ static void talitos_error(struct device *dev, u32 isr, u32 isr_lo)
{
struct talitos_private *priv = dev_get_drvdata(dev);
unsigned int timeout = TALITOS_TIMEOUT;
- int ch, error, reset_dev = 0, reset_ch = 0;
- u32 v, v_lo;
+ int ch, error, reset_dev = 0;
+ u32 v_lo;
+ bool is_sec1 = has_ftr_sec1(priv);
+ int reset_ch = is_sec1 ? 1 : 0; /* only SEC2 supports continuation */
for (ch = 0; ch < priv->num_channels; ch++) {
/* skip channels without errors */
- if (!(isr & (1 << (ch * 2 + 1))))
- continue;
+ if (is_sec1) {
+ /* bits 29, 31, 17, 19 */
+ if (!(isr & (1 << (29 + (ch & 1) * 2 - (ch & 2) * 6))))
+ continue;
+ } else {
+ if (!(isr & (1 << (ch * 2 + 1))))
+ continue;
+ }
error = -EINVAL;
- v = in_be32(priv->chan[ch].reg + TALITOS_CCPSR);
v_lo = in_be32(priv->chan[ch].reg + TALITOS_CCPSR_LO);
if (v_lo & TALITOS_CCPSR_LO_DOF) {
@@ -471,23 +575,28 @@ static void talitos_error(struct device *dev, u32 isr, u32 isr_lo)
if (v_lo & TALITOS_CCPSR_LO_MDTE)
dev_err(dev, "master data transfer error\n");
if (v_lo & TALITOS_CCPSR_LO_SGDLZ)
- dev_err(dev, "s/g data length zero error\n");
+ dev_err(dev, is_sec1 ? "pointeur not complete error\n"
+ : "s/g data length zero error\n");
if (v_lo & TALITOS_CCPSR_LO_FPZ)
- dev_err(dev, "fetch pointer zero error\n");
+ dev_err(dev, is_sec1 ? "parity error\n"
+ : "fetch pointer zero error\n");
if (v_lo & TALITOS_CCPSR_LO_IDH)
dev_err(dev, "illegal descriptor header error\n");
if (v_lo & TALITOS_CCPSR_LO_IEU)
- dev_err(dev, "invalid execution unit error\n");
+ dev_err(dev, is_sec1 ? "static assignment error\n"
+ : "invalid exec unit error\n");
if (v_lo & TALITOS_CCPSR_LO_EU)
report_eu_error(dev, ch, current_desc_hdr(dev, ch));
- if (v_lo & TALITOS_CCPSR_LO_GB)
- dev_err(dev, "gather boundary error\n");
- if (v_lo & TALITOS_CCPSR_LO_GRL)
- dev_err(dev, "gather return/length error\n");
- if (v_lo & TALITOS_CCPSR_LO_SB)
- dev_err(dev, "scatter boundary error\n");
- if (v_lo & TALITOS_CCPSR_LO_SRL)
- dev_err(dev, "scatter return/length error\n");
+ if (!is_sec1) {
+ if (v_lo & TALITOS_CCPSR_LO_GB)
+ dev_err(dev, "gather boundary error\n");
+ if (v_lo & TALITOS_CCPSR_LO_GRL)
+ dev_err(dev, "gather return/length error\n");
+ if (v_lo & TALITOS_CCPSR_LO_SB)
+ dev_err(dev, "scatter boundary error\n");
+ if (v_lo & TALITOS_CCPSR_LO_SRL)
+ dev_err(dev, "scatter return/length error\n");
+ }
flush_channel(dev, ch, error, reset_ch);
@@ -495,10 +604,10 @@ static void talitos_error(struct device *dev, u32 isr, u32 isr_lo)
reset_channel(dev, ch);
} else {
setbits32(priv->chan[ch].reg + TALITOS_CCCR,
- TALITOS_CCCR_CONT);
+ TALITOS2_CCCR_CONT);
setbits32(priv->chan[ch].reg + TALITOS_CCCR_LO, 0);
while ((in_be32(priv->chan[ch].reg + TALITOS_CCCR) &
- TALITOS_CCCR_CONT) && --timeout)
+ TALITOS2_CCCR_CONT) && --timeout)
cpu_relax();
if (timeout == 0) {
dev_err(dev, "failed to restart channel %d\n",
@@ -507,9 +616,14 @@ static void talitos_error(struct device *dev, u32 isr, u32 isr_lo)
}
}
}
- if (reset_dev || isr & ~TALITOS_ISR_4CHERR || isr_lo) {
- dev_err(dev, "done overflow, internal time out, or rngu error: "
- "ISR 0x%08x_%08x\n", isr, isr_lo);
+ if (reset_dev || (is_sec1 && isr & ~TALITOS1_ISR_4CHERR) ||
+ (!is_sec1 && isr & ~TALITOS2_ISR_4CHERR) || isr_lo) {
+ if (is_sec1 && (isr_lo & TALITOS1_ISR_TEA_ERR))
+ dev_err(dev, "TEA error: ISR 0x%08x_%08x\n",
+ isr, isr_lo);
+ else
+ dev_err(dev, "done overflow, internal time out, or "
+ "rngu error: ISR 0x%08x_%08x\n", isr, isr_lo);
/* purge request queues */
for (ch = 0; ch < priv->num_channels; ch++)
@@ -520,8 +634,43 @@ static void talitos_error(struct device *dev, u32 isr, u32 isr_lo)
}
}
-#define DEF_TALITOS_INTERRUPT(name, ch_done_mask, ch_err_mask, tlet) \
-static irqreturn_t talitos_interrupt_##name(int irq, void *data) \
+#define DEF_TALITOS1_INTERRUPT(name, ch_done_mask, ch_err_mask, tlet) \
+static irqreturn_t talitos1_interrupt_##name(int irq, void *data) \
+{ \
+ struct device *dev = data; \
+ struct talitos_private *priv = dev_get_drvdata(dev); \
+ u32 isr, isr_lo; \
+ unsigned long flags; \
+ \
+ spin_lock_irqsave(&priv->reg_lock, flags); \
+ isr = in_be32(priv->reg + TALITOS_ISR); \
+ isr_lo = in_be32(priv->reg + TALITOS_ISR_LO); \
+ /* Acknowledge interrupt */ \
+ out_be32(priv->reg + TALITOS_ICR, isr & (ch_done_mask | ch_err_mask)); \
+ out_be32(priv->reg + TALITOS_ICR_LO, isr_lo); \
+ \
+ if (unlikely(isr & ch_err_mask || isr_lo & TALITOS1_IMR_LO_INIT)) { \
+ spin_unlock_irqrestore(&priv->reg_lock, flags); \
+ talitos_error(dev, isr & ch_err_mask, isr_lo); \
+ } \
+ else { \
+ if (likely(isr & ch_done_mask)) { \
+ /* mask further done interrupts. */ \
+ setbits32(priv->reg + TALITOS_IMR, ch_done_mask); \
+ /* done_task will unmask done interrupts at exit */ \
+ tasklet_schedule(&priv->done_task[tlet]); \
+ } \
+ spin_unlock_irqrestore(&priv->reg_lock, flags); \
+ } \
+ \
+ return (isr & (ch_done_mask | ch_err_mask) || isr_lo) ? IRQ_HANDLED : \
+ IRQ_NONE; \
+}
+
+DEF_TALITOS1_INTERRUPT(4ch, TALITOS1_ISR_4CHDONE, TALITOS1_ISR_4CHERR, 0)
+
+#define DEF_TALITOS2_INTERRUPT(name, ch_done_mask, ch_err_mask, tlet) \
+static irqreturn_t talitos2_interrupt_##name(int irq, void *data) \
{ \
struct device *dev = data; \
struct talitos_private *priv = dev_get_drvdata(dev); \
@@ -552,9 +701,12 @@ static irqreturn_t talitos_interrupt_##name(int irq, void *data) \
return (isr & (ch_done_mask | ch_err_mask) || isr_lo) ? IRQ_HANDLED : \
IRQ_NONE; \
}
-DEF_TALITOS_INTERRUPT(4ch, TALITOS_ISR_4CHDONE, TALITOS_ISR_4CHERR, 0)
-DEF_TALITOS_INTERRUPT(ch0_2, TALITOS_ISR_CH_0_2_DONE, TALITOS_ISR_CH_0_2_ERR, 0)
-DEF_TALITOS_INTERRUPT(ch1_3, TALITOS_ISR_CH_1_3_DONE, TALITOS_ISR_CH_1_3_ERR, 1)
+
+DEF_TALITOS2_INTERRUPT(4ch, TALITOS2_ISR_4CHDONE, TALITOS2_ISR_4CHERR, 0)
+DEF_TALITOS2_INTERRUPT(ch0_2, TALITOS2_ISR_CH_0_2_DONE, TALITOS2_ISR_CH_0_2_ERR,
+ 0)
+DEF_TALITOS2_INTERRUPT(ch1_3, TALITOS2_ISR_CH_1_3_DONE, TALITOS2_ISR_CH_1_3_ERR,
+ 1)
/*
* hwrng
@@ -567,7 +719,7 @@ static int talitos_rng_data_present(struct hwrng *rng, int wait)
int i;
for (i = 0; i < 20; i++) {
- ofl = in_be32(priv->reg + TALITOS_RNGUSR_LO) &
+ ofl = in_be32(priv->reg_rngu + TALITOS_EUSR_LO) &
TALITOS_RNGUSR_LO_OFL;
if (ofl || !wait)
break;
@@ -583,8 +735,8 @@ static int talitos_rng_data_read(struct hwrng *rng, u32 *data)
struct talitos_private *priv = dev_get_drvdata(dev);
/* rng fifo requires 64-bit accesses */
- *data = in_be32(priv->reg + TALITOS_RNGU_FIFO);
- *data = in_be32(priv->reg + TALITOS_RNGU_FIFO_LO);
+ *data = in_be32(priv->reg_rngu + TALITOS_EU_FIFO);
+ *data = in_be32(priv->reg_rngu + TALITOS_EU_FIFO_LO);
return sizeof(u32);
}
@@ -595,8 +747,9 @@ static int talitos_rng_init(struct hwrng *rng)
struct talitos_private *priv = dev_get_drvdata(dev);
unsigned int timeout = TALITOS_TIMEOUT;
- setbits32(priv->reg + TALITOS_RNGURCR_LO, TALITOS_RNGURCR_LO_SR);
- while (!(in_be32(priv->reg + TALITOS_RNGUSR_LO) & TALITOS_RNGUSR_LO_RD)
+ setbits32(priv->reg_rngu + TALITOS_EURCR_LO, TALITOS_RNGURCR_LO_SR);
+ while (!(in_be32(priv->reg_rngu + TALITOS_EUSR_LO)
+ & TALITOS_RNGUSR_LO_RD)
&& --timeout)
cpu_relax();
if (timeout == 0) {
@@ -605,7 +758,7 @@ static int talitos_rng_init(struct hwrng *rng)
}
/* start generating */
- setbits32(priv->reg + TALITOS_RNGUDSR_LO, 0);
+ setbits32(priv->reg_rngu + TALITOS_EUDSR_LO, 0);
return 0;
}
@@ -661,7 +814,7 @@ struct talitos_ahash_req_ctx {
unsigned int first;
unsigned int last;
unsigned int to_hash_later;
- u64 nbuf;
+ unsigned int nbuf;
struct scatterlist bufsl[2];
struct scatterlist *psrc;
};
@@ -712,9 +865,10 @@ badkey:
* @dst_chained: whether dst is chained or not
* @iv_dma: dma address of iv for checking continuity and link table
* @dma_len: length of dma mapped link_tbl space
- * @dma_link_tbl: bus physical address of link_tbl
+ * @dma_link_tbl: bus physical address of link_tbl/buf
* @desc: h/w descriptor
- * @link_tbl: input and output h/w link tables (if {src,dst}_nents > 1)
+ * @link_tbl: input and output h/w link tables (if {src,dst}_nents > 1) (SEC2)
+ * @buf: input and output buffeur (if {src,dst}_nents > 1) (SEC1)
*
* if decrypting (with authcheck), or either one of src_nents or dst_nents
* is greater than 1, an integrity check value is concatenated to the end
@@ -731,7 +885,10 @@ struct talitos_edesc {
int dma_len;
dma_addr_t dma_link_tbl;
struct talitos_desc desc;
- struct talitos_ptr link_tbl[0];
+ union {
+ struct talitos_ptr link_tbl[0];
+ u8 buf[0];
+ };
};
static int talitos_map_sg(struct device *dev, struct scatterlist *sg,
@@ -907,8 +1064,8 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count,
{
int n_sg = sg_count;
- while (n_sg--) {
- to_talitos_ptr(link_tbl_ptr, sg_dma_address(sg));
+ while (sg && n_sg--) {
+ to_talitos_ptr(link_tbl_ptr, sg_dma_address(sg), 0);
link_tbl_ptr->len = cpu_to_be16(sg_dma_len(sg));
link_tbl_ptr->j_extent = 0;
link_tbl_ptr++;
@@ -925,7 +1082,8 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count,
sg_count--;
link_tbl_ptr--;
}
- be16_add_cpu(&link_tbl_ptr->len, cryptlen);
+ link_tbl_ptr->len = cpu_to_be16(be16_to_cpu(link_tbl_ptr->len)
+ + cryptlen);
/* tag end of link table */
link_tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
@@ -953,7 +1111,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
/* hmac key */
map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key,
- 0, DMA_TO_DEVICE);
+ DMA_TO_DEVICE);
/* hmac data */
desc->ptr[1].len = cpu_to_be16(areq->assoclen + ivsize);
@@ -962,7 +1120,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
struct talitos_ptr *tbl_ptr = &edesc->link_tbl[tbl_off];
to_talitos_ptr(&desc->ptr[1], edesc->dma_link_tbl + tbl_off *
- sizeof(struct talitos_ptr));
+ sizeof(struct talitos_ptr), 0);
desc->ptr[1].j_extent = DESC_PTR_LNKTBL_JUMP;
/* assoc_nents - 1 entries for assoc, 1 for IV */
@@ -973,7 +1131,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
tbl_ptr += sg_count - 1;
tbl_ptr->j_extent = 0;
tbl_ptr++;
- to_talitos_ptr(tbl_ptr, edesc->iv_dma);
+ to_talitos_ptr(tbl_ptr, edesc->iv_dma, 0);
tbl_ptr->len = cpu_to_be16(ivsize);
tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
@@ -982,14 +1140,14 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
} else {
if (areq->assoclen)
to_talitos_ptr(&desc->ptr[1],
- sg_dma_address(areq->assoc));
+ sg_dma_address(areq->assoc), 0);
else
- to_talitos_ptr(&desc->ptr[1], edesc->iv_dma);
+ to_talitos_ptr(&desc->ptr[1], edesc->iv_dma, 0);
desc->ptr[1].j_extent = 0;
}
/* cipher iv */
- to_talitos_ptr(&desc->ptr[2], edesc->iv_dma);
+ to_talitos_ptr(&desc->ptr[2], edesc->iv_dma, 0);
desc->ptr[2].len = cpu_to_be16(ivsize);
desc->ptr[2].j_extent = 0;
/* Sync needed for the aead_givencrypt case */
@@ -997,7 +1155,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
/* cipher key */
map_single_talitos_ptr(dev, &desc->ptr[3], ctx->enckeylen,
- (char *)&ctx->key + ctx->authkeylen, 0,
+ (char *)&ctx->key + ctx->authkeylen,
DMA_TO_DEVICE);
/*
@@ -1015,7 +1173,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
edesc->src_chained);
if (sg_count == 1) {
- to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->src));
+ to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->src), 0);
} else {
sg_link_tbl_len = cryptlen;
@@ -1026,14 +1184,14 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
&edesc->link_tbl[0]);
if (sg_count > 1) {
desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
- to_talitos_ptr(&desc->ptr[4], edesc->dma_link_tbl);
+ to_talitos_ptr(&desc->ptr[4], edesc->dma_link_tbl, 0);
dma_sync_single_for_device(dev, edesc->dma_link_tbl,
edesc->dma_len,
DMA_BIDIRECTIONAL);
} else {
/* Only one segment now, so no link tbl needed */
to_talitos_ptr(&desc->ptr[4],
- sg_dma_address(areq->src));
+ sg_dma_address(areq->src), 0);
}
}
@@ -1047,13 +1205,13 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
DMA_FROM_DEVICE, edesc->dst_chained);
if (sg_count == 1) {
- to_talitos_ptr(&desc->ptr[5], sg_dma_address(areq->dst));
+ to_talitos_ptr(&desc->ptr[5], sg_dma_address(areq->dst), 0);
} else {
int tbl_off = edesc->src_nents + 1;
struct talitos_ptr *tbl_ptr = &edesc->link_tbl[tbl_off];
to_talitos_ptr(&desc->ptr[5], edesc->dma_link_tbl +
- tbl_off * sizeof(struct talitos_ptr));
+ tbl_off * sizeof(struct talitos_ptr), 0);
sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
tbl_ptr);
@@ -1068,14 +1226,14 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
to_talitos_ptr(tbl_ptr, edesc->dma_link_tbl +
(tbl_off + edesc->dst_nents + 1 +
edesc->assoc_nents) *
- sizeof(struct talitos_ptr));
+ sizeof(struct talitos_ptr), 0);
desc->ptr[5].j_extent |= DESC_PTR_LNKTBL_JUMP;
dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
edesc->dma_len, DMA_BIDIRECTIONAL);
}
/* iv out */
- map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv, 0,
+ map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv,
DMA_FROM_DEVICE);
ret = talitos_submit(dev, ctx->ch, desc, callback, areq);
@@ -1095,7 +1253,7 @@ static int sg_count(struct scatterlist *sg_list, int nbytes, bool *chained)
int sg_nents = 0;
*chained = false;
- while (nbytes > 0) {
+ while (nbytes > 0 && sg) {
sg_nents++;
nbytes -= sg->length;
if (!sg_is_last(sg) && (sg + 1)->length == 0)
@@ -1128,8 +1286,11 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
dma_addr_t iv_dma = 0;
gfp_t flags = cryptoflags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
GFP_ATOMIC;
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
+ int max_len = is_sec1 ? TALITOS1_MAX_DATA_LEN : TALITOS2_MAX_DATA_LEN;
- if (cryptlen + authsize > TALITOS_MAX_DATA_LEN) {
+ if (cryptlen + authsize > max_len) {
dev_err(dev, "length exceeds h/w max limit\n");
return ERR_PTR(-EINVAL);
}
@@ -1173,8 +1334,12 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
*/
alloc_len = sizeof(struct talitos_edesc);
if (assoc_nents || src_nents || dst_nents) {
- dma_len = (src_nents + dst_nents + 2 + assoc_nents) *
- sizeof(struct talitos_ptr) + authsize;
+ if (is_sec1)
+ dma_len = (src_nents ? cryptlen : 0) +
+ (dst_nents ? cryptlen : 0);
+ else
+ dma_len = (src_nents + dst_nents + 2 + assoc_nents) *
+ sizeof(struct talitos_ptr) + authsize;
alloc_len += dma_len;
} else {
dma_len = 0;
@@ -1327,16 +1492,43 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *cipher,
return 0;
}
+static void unmap_sg_talitos_ptr(struct device *dev, struct scatterlist *src,
+ struct scatterlist *dst, unsigned int len,
+ struct talitos_edesc *edesc)
+{
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
+
+ if (is_sec1) {
+ if (!edesc->src_nents) {
+ dma_unmap_sg(dev, src, 1,
+ dst != src ? DMA_TO_DEVICE
+ : DMA_BIDIRECTIONAL);
+ }
+ if (dst && edesc->dst_nents) {
+ dma_sync_single_for_device(dev,
+ edesc->dma_link_tbl + len,
+ len, DMA_FROM_DEVICE);
+ sg_copy_from_buffer(dst, edesc->dst_nents ? : 1,
+ edesc->buf + len, len);
+ } else if (dst && dst != src) {
+ dma_unmap_sg(dev, dst, 1, DMA_FROM_DEVICE);
+ }
+ } else {
+ talitos_sg_unmap(dev, edesc, src, dst);
+ }
+}
+
static void common_nonsnoop_unmap(struct device *dev,
struct talitos_edesc *edesc,
struct ablkcipher_request *areq)
{
unmap_single_talitos_ptr(dev, &edesc->desc.ptr[5], DMA_FROM_DEVICE);
+
+ unmap_sg_talitos_ptr(dev, areq->src, areq->dst, areq->nbytes, edesc);
unmap_single_talitos_ptr(dev, &edesc->desc.ptr[2], DMA_TO_DEVICE);
unmap_single_talitos_ptr(dev, &edesc->desc.ptr[1], DMA_TO_DEVICE);
- talitos_sg_unmap(dev, edesc, areq->src, areq->dst);
-
if (edesc->dma_len)
dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len,
DMA_BIDIRECTIONAL);
@@ -1358,6 +1550,102 @@ static void ablkcipher_done(struct device *dev,
areq->base.complete(&areq->base, err);
}
+int map_sg_in_talitos_ptr(struct device *dev, struct scatterlist *src,
+ unsigned int len, struct talitos_edesc *edesc,
+ enum dma_data_direction dir, struct talitos_ptr *ptr)
+{
+ int sg_count;
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
+
+ to_talitos_ptr_len(ptr, len, is_sec1);
+
+ if (is_sec1) {
+ sg_count = edesc->src_nents ? : 1;
+
+ if (sg_count == 1) {
+ dma_map_sg(dev, src, 1, dir);
+ to_talitos_ptr(ptr, sg_dma_address(src), is_sec1);
+ } else {
+ sg_copy_to_buffer(src, sg_count, edesc->buf, len);
+ to_talitos_ptr(ptr, edesc->dma_link_tbl, is_sec1);
+ dma_sync_single_for_device(dev, edesc->dma_link_tbl,
+ len, DMA_TO_DEVICE);
+ }
+ } else {
+ to_talitos_ptr_extent_clear(ptr, is_sec1);
+
+ sg_count = talitos_map_sg(dev, src, edesc->src_nents ? : 1, dir,
+ edesc->src_chained);
+
+ if (sg_count == 1) {
+ to_talitos_ptr(ptr, sg_dma_address(src), is_sec1);
+ } else {
+ sg_count = sg_to_link_tbl(src, sg_count, len,
+ &edesc->link_tbl[0]);
+ if (sg_count > 1) {
+ to_talitos_ptr(ptr, edesc->dma_link_tbl, 0);
+ ptr->j_extent |= DESC_PTR_LNKTBL_JUMP;
+ dma_sync_single_for_device(dev,
+ edesc->dma_link_tbl,
+ edesc->dma_len,
+ DMA_BIDIRECTIONAL);
+ } else {
+ /* Only one segment now, so no link tbl needed*/
+ to_talitos_ptr(ptr, sg_dma_address(src),
+ is_sec1);
+ }
+ }
+ }
+ return sg_count;
+}
+
+void map_sg_out_talitos_ptr(struct device *dev, struct scatterlist *dst,
+ unsigned int len, struct talitos_edesc *edesc,
+ enum dma_data_direction dir,
+ struct talitos_ptr *ptr, int sg_count)
+{
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
+
+ if (dir != DMA_NONE)
+ sg_count = talitos_map_sg(dev, dst, edesc->dst_nents ? : 1,
+ dir, edesc->dst_chained);
+
+ to_talitos_ptr_len(ptr, len, is_sec1);
+
+ if (is_sec1) {
+ if (sg_count == 1) {
+ if (dir != DMA_NONE)
+ dma_map_sg(dev, dst, 1, dir);
+ to_talitos_ptr(ptr, sg_dma_address(dst), is_sec1);
+ } else {
+ to_talitos_ptr(ptr, edesc->dma_link_tbl + len, is_sec1);
+ dma_sync_single_for_device(dev,
+ edesc->dma_link_tbl + len,
+ len, DMA_FROM_DEVICE);
+ }
+ } else {
+ to_talitos_ptr_extent_clear(ptr, is_sec1);
+
+ if (sg_count == 1) {
+ to_talitos_ptr(ptr, sg_dma_address(dst), is_sec1);
+ } else {
+ struct talitos_ptr *link_tbl_ptr =
+ &edesc->link_tbl[edesc->src_nents + 1];
+
+ to_talitos_ptr(ptr, edesc->dma_link_tbl +
+ (edesc->src_nents + 1) *
+ sizeof(struct talitos_ptr), 0);
+ ptr->j_extent |= DESC_PTR_LNKTBL_JUMP;
+ sg_to_link_tbl(dst, sg_count, len, link_tbl_ptr);
+ dma_sync_single_for_device(dev, edesc->dma_link_tbl,
+ edesc->dma_len,
+ DMA_BIDIRECTIONAL);
+ }
+ }
+}
+
static int common_nonsnoop(struct talitos_edesc *edesc,
struct ablkcipher_request *areq,
void (*callback) (struct device *dev,
@@ -1371,83 +1659,41 @@ static int common_nonsnoop(struct talitos_edesc *edesc,
unsigned int cryptlen = areq->nbytes;
unsigned int ivsize = crypto_ablkcipher_ivsize(cipher);
int sg_count, ret;
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
/* first DWORD empty */
- desc->ptr[0].len = 0;
- to_talitos_ptr(&desc->ptr[0], 0);
- desc->ptr[0].j_extent = 0;
+ desc->ptr[0] = zero_entry;
/* cipher iv */
- to_talitos_ptr(&desc->ptr[1], edesc->iv_dma);
- desc->ptr[1].len = cpu_to_be16(ivsize);
- desc->ptr[1].j_extent = 0;
+ to_talitos_ptr(&desc->ptr[1], edesc->iv_dma, is_sec1);
+ to_talitos_ptr_len(&desc->ptr[1], ivsize, is_sec1);
+ to_talitos_ptr_extent_clear(&desc->ptr[1], is_sec1);
/* cipher key */
map_single_talitos_ptr(dev, &desc->ptr[2], ctx->keylen,
- (char *)&ctx->key, 0, DMA_TO_DEVICE);
+ (char *)&ctx->key, DMA_TO_DEVICE);
/*
* cipher in
*/
- desc->ptr[3].len = cpu_to_be16(cryptlen);
- desc->ptr[3].j_extent = 0;
-
- sg_count = talitos_map_sg(dev, areq->src, edesc->src_nents ? : 1,
- (areq->src == areq->dst) ? DMA_BIDIRECTIONAL
- : DMA_TO_DEVICE,
- edesc->src_chained);
-
- if (sg_count == 1) {
- to_talitos_ptr(&desc->ptr[3], sg_dma_address(areq->src));
- } else {
- sg_count = sg_to_link_tbl(areq->src, sg_count, cryptlen,
- &edesc->link_tbl[0]);
- if (sg_count > 1) {
- to_talitos_ptr(&desc->ptr[3], edesc->dma_link_tbl);
- desc->ptr[3].j_extent |= DESC_PTR_LNKTBL_JUMP;
- dma_sync_single_for_device(dev, edesc->dma_link_tbl,
- edesc->dma_len,
- DMA_BIDIRECTIONAL);
- } else {
- /* Only one segment now, so no link tbl needed */
- to_talitos_ptr(&desc->ptr[3],
- sg_dma_address(areq->src));
- }
- }
+ sg_count = map_sg_in_talitos_ptr(dev, areq->src, cryptlen, edesc,
+ (areq->src == areq->dst) ?
+ DMA_BIDIRECTIONAL : DMA_TO_DEVICE,
+ &desc->ptr[3]);
/* cipher out */
- desc->ptr[4].len = cpu_to_be16(cryptlen);
- desc->ptr[4].j_extent = 0;
-
- if (areq->src != areq->dst)
- sg_count = talitos_map_sg(dev, areq->dst,
- edesc->dst_nents ? : 1,
- DMA_FROM_DEVICE, edesc->dst_chained);
-
- if (sg_count == 1) {
- to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->dst));
- } else {
- struct talitos_ptr *link_tbl_ptr =
- &edesc->link_tbl[edesc->src_nents + 1];
-
- to_talitos_ptr(&desc->ptr[4], edesc->dma_link_tbl +
- (edesc->src_nents + 1) *
- sizeof(struct talitos_ptr));
- desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
- sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
- link_tbl_ptr);
- dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
- edesc->dma_len, DMA_BIDIRECTIONAL);
- }
+ map_sg_out_talitos_ptr(dev, areq->dst, cryptlen, edesc,
+ (areq->src == areq->dst) ? DMA_NONE
+ : DMA_FROM_DEVICE,
+ &desc->ptr[4], sg_count);
/* iv out */
- map_single_talitos_ptr(dev, &desc->ptr[5], ivsize, ctx->iv, 0,
+ map_single_talitos_ptr(dev, &desc->ptr[5], ivsize, ctx->iv,
DMA_FROM_DEVICE);
/* last DWORD empty */
- desc->ptr[6].len = 0;
- to_talitos_ptr(&desc->ptr[6], 0);
- desc->ptr[6].j_extent = 0;
+ desc->ptr[6] = zero_entry;
ret = talitos_submit(dev, ctx->ch, desc, callback, areq);
if (ret != -EINPROGRESS) {
@@ -1507,20 +1753,22 @@ static void common_nonsnoop_hash_unmap(struct device *dev,
struct ahash_request *areq)
{
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
unmap_single_talitos_ptr(dev, &edesc->desc.ptr[5], DMA_FROM_DEVICE);
+ unmap_sg_talitos_ptr(dev, req_ctx->psrc, NULL, 0, edesc);
+
/* When using hashctx-in, must unmap it. */
- if (edesc->desc.ptr[1].len)
+ if (from_talitos_ptr_len(&edesc->desc.ptr[1], is_sec1))
unmap_single_talitos_ptr(dev, &edesc->desc.ptr[1],
DMA_TO_DEVICE);
- if (edesc->desc.ptr[2].len)
+ if (from_talitos_ptr_len(&edesc->desc.ptr[2], is_sec1))
unmap_single_talitos_ptr(dev, &edesc->desc.ptr[2],
DMA_TO_DEVICE);
- talitos_sg_unmap(dev, edesc, req_ctx->psrc, NULL);
-
if (edesc->dma_len)
dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len,
DMA_BIDIRECTIONAL);
@@ -1548,6 +1796,27 @@ static void ahash_done(struct device *dev,
areq->base.complete(&areq->base, err);
}
+/*
+ * SEC1 doesn't like hashing of 0 sized message, so we do the padding
+ * ourself and submit a padded block
+ */
+void talitos_handle_buggy_hash(struct talitos_ctx *ctx,
+ struct talitos_edesc *edesc,
+ struct talitos_ptr *ptr)
+{
+ static u8 padded_hash[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+
+ pr_err_once("Bug in SEC1, padding ourself\n");
+ edesc->desc.hdr &= ~DESC_HDR_MODE0_MDEU_PAD;
+ map_single_talitos_ptr(ctx->dev, ptr, sizeof(padded_hash),
+ (char *)padded_hash, DMA_TO_DEVICE);
+}
+
static int common_nonsnoop_hash(struct talitos_edesc *edesc,
struct ahash_request *areq, unsigned int length,
void (*callback) (struct device *dev,
@@ -1559,7 +1828,9 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
struct device *dev = ctx->dev;
struct talitos_desc *desc = &edesc->desc;
- int sg_count, ret;
+ int ret;
+ struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
/* first DWORD empty */
desc->ptr[0] = zero_entry;
@@ -1568,7 +1839,7 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
if (!req_ctx->first || req_ctx->swinit) {
map_single_talitos_ptr(dev, &desc->ptr[1],
req_ctx->hw_context_size,
- (char *)req_ctx->hw_context, 0,
+ (char *)req_ctx->hw_context,
DMA_TO_DEVICE);
req_ctx->swinit = 0;
} else {
@@ -1580,38 +1851,15 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
/* HMAC key */
if (ctx->keylen)
map_single_talitos_ptr(dev, &desc->ptr[2], ctx->keylen,
- (char *)&ctx->key, 0, DMA_TO_DEVICE);
+ (char *)&ctx->key, DMA_TO_DEVICE);
else
desc->ptr[2] = zero_entry;
/*
* data in
*/
- desc->ptr[3].len = cpu_to_be16(length);
- desc->ptr[3].j_extent = 0;
-
- sg_count = talitos_map_sg(dev, req_ctx->psrc,
- edesc->src_nents ? : 1,
- DMA_TO_DEVICE, edesc->src_chained);
-
- if (sg_count == 1) {
- to_talitos_ptr(&desc->ptr[3], sg_dma_address(req_ctx->psrc));
- } else {
- sg_count = sg_to_link_tbl(req_ctx->psrc, sg_count, length,
- &edesc->link_tbl[0]);
- if (sg_count > 1) {
- desc->ptr[3].j_extent |= DESC_PTR_LNKTBL_JUMP;
- to_talitos_ptr(&desc->ptr[3], edesc->dma_link_tbl);
- dma_sync_single_for_device(ctx->dev,
- edesc->dma_link_tbl,
- edesc->dma_len,
- DMA_BIDIRECTIONAL);
- } else {
- /* Only one segment now, so no link tbl needed */
- to_talitos_ptr(&desc->ptr[3],
- sg_dma_address(req_ctx->psrc));
- }
- }
+ map_sg_in_talitos_ptr(dev, req_ctx->psrc, length, edesc,
+ DMA_TO_DEVICE, &desc->ptr[3]);
/* fifth DWORD empty */
desc->ptr[4] = zero_entry;
@@ -1620,15 +1868,18 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
if (req_ctx->last)
map_single_talitos_ptr(dev, &desc->ptr[5],
crypto_ahash_digestsize(tfm),
- areq->result, 0, DMA_FROM_DEVICE);
+ areq->result, DMA_FROM_DEVICE);
else
map_single_talitos_ptr(dev, &desc->ptr[5],
req_ctx->hw_context_size,
- req_ctx->hw_context, 0, DMA_FROM_DEVICE);
+ req_ctx->hw_context, DMA_FROM_DEVICE);
/* last DWORD empty */
desc->ptr[6] = zero_entry;
+ if (is_sec1 && from_talitos_ptr_len(&desc->ptr[3], true) == 0)
+ talitos_handle_buggy_hash(ctx, edesc, &desc->ptr[3]);
+
ret = talitos_submit(dev, ctx->ch, desc, callback, areq);
if (ret != -EINPROGRESS) {
common_nonsnoop_hash_unmap(dev, edesc, areq);
@@ -2561,6 +2812,7 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev,
break;
default:
dev_err(dev, "unknown algorithm type %d\n", t_alg->algt.type);
+ kfree(t_alg);
return ERR_PTR(-EINVAL);
}
@@ -2581,29 +2833,35 @@ static int talitos_probe_irq(struct platform_device *ofdev)
struct device_node *np = ofdev->dev.of_node;
struct talitos_private *priv = dev_get_drvdata(dev);
int err;
+ bool is_sec1 = has_ftr_sec1(priv);
priv->irq[0] = irq_of_parse_and_map(np, 0);
if (!priv->irq[0]) {
dev_err(dev, "failed to map irq\n");
return -EINVAL;
}
+ if (is_sec1) {
+ err = request_irq(priv->irq[0], talitos1_interrupt_4ch, 0,
+ dev_driver_string(dev), dev);
+ goto primary_out;
+ }
priv->irq[1] = irq_of_parse_and_map(np, 1);
/* get the primary irq line */
if (!priv->irq[1]) {
- err = request_irq(priv->irq[0], talitos_interrupt_4ch, 0,
+ err = request_irq(priv->irq[0], talitos2_interrupt_4ch, 0,
dev_driver_string(dev), dev);
goto primary_out;
}
- err = request_irq(priv->irq[0], talitos_interrupt_ch0_2, 0,
+ err = request_irq(priv->irq[0], talitos2_interrupt_ch0_2, 0,
dev_driver_string(dev), dev);
if (err)
goto primary_out;
/* get the secondary irq line */
- err = request_irq(priv->irq[1], talitos_interrupt_ch1_3, 0,
+ err = request_irq(priv->irq[1], talitos2_interrupt_ch1_3, 0,
dev_driver_string(dev), dev);
if (err) {
dev_err(dev, "failed to request secondary irq\n");
@@ -2630,6 +2888,7 @@ static int talitos_probe(struct platform_device *ofdev)
struct talitos_private *priv;
const unsigned int *prop;
int i, err;
+ int stride;
priv = kzalloc(sizeof(struct talitos_private), GFP_KERNEL);
if (!priv)
@@ -2643,20 +2902,6 @@ static int talitos_probe(struct platform_device *ofdev)
spin_lock_init(&priv->reg_lock);
- err = talitos_probe_irq(ofdev);
- if (err)
- goto err_out;
-
- if (!priv->irq[1]) {
- tasklet_init(&priv->done_task[0], talitos_done_4ch,
- (unsigned long)dev);
- } else {
- tasklet_init(&priv->done_task[0], talitos_done_ch0_2,
- (unsigned long)dev);
- tasklet_init(&priv->done_task[1], talitos_done_ch1_3,
- (unsigned long)dev);
- }
-
priv->reg = of_iomap(np, 0);
if (!priv->reg) {
dev_err(dev, "failed to of_iomap\n");
@@ -2696,6 +2941,53 @@ static int talitos_probe(struct platform_device *ofdev)
TALITOS_FTR_SHA224_HWINIT |
TALITOS_FTR_HMAC_OK;
+ if (of_device_is_compatible(np, "fsl,sec1.0"))
+ priv->features |= TALITOS_FTR_SEC1;
+
+ if (of_device_is_compatible(np, "fsl,sec1.2")) {
+ priv->reg_deu = priv->reg + TALITOS12_DEU;
+ priv->reg_aesu = priv->reg + TALITOS12_AESU;
+ priv->reg_mdeu = priv->reg + TALITOS12_MDEU;
+ stride = TALITOS1_CH_STRIDE;
+ } else if (of_device_is_compatible(np, "fsl,sec1.0")) {
+ priv->reg_deu = priv->reg + TALITOS10_DEU;
+ priv->reg_aesu = priv->reg + TALITOS10_AESU;
+ priv->reg_mdeu = priv->reg + TALITOS10_MDEU;
+ priv->reg_afeu = priv->reg + TALITOS10_AFEU;
+ priv->reg_rngu = priv->reg + TALITOS10_RNGU;
+ priv->reg_pkeu = priv->reg + TALITOS10_PKEU;
+ stride = TALITOS1_CH_STRIDE;
+ } else {
+ priv->reg_deu = priv->reg + TALITOS2_DEU;
+ priv->reg_aesu = priv->reg + TALITOS2_AESU;
+ priv->reg_mdeu = priv->reg + TALITOS2_MDEU;
+ priv->reg_afeu = priv->reg + TALITOS2_AFEU;
+ priv->reg_rngu = priv->reg + TALITOS2_RNGU;
+ priv->reg_pkeu = priv->reg + TALITOS2_PKEU;
+ priv->reg_keu = priv->reg + TALITOS2_KEU;
+ priv->reg_crcu = priv->reg + TALITOS2_CRCU;
+ stride = TALITOS2_CH_STRIDE;
+ }
+
+ err = talitos_probe_irq(ofdev);
+ if (err)
+ goto err_out;
+
+ if (of_device_is_compatible(np, "fsl,sec1.0")) {
+ tasklet_init(&priv->done_task[0], talitos1_done_4ch,
+ (unsigned long)dev);
+ } else {
+ if (!priv->irq[1]) {
+ tasklet_init(&priv->done_task[0], talitos2_done_4ch,
+ (unsigned long)dev);
+ } else {
+ tasklet_init(&priv->done_task[0], talitos2_done_ch0_2,
+ (unsigned long)dev);
+ tasklet_init(&priv->done_task[1], talitos2_done_ch1_3,
+ (unsigned long)dev);
+ }
+ }
+
priv->chan = kzalloc(sizeof(struct talitos_channel) *
priv->num_channels, GFP_KERNEL);
if (!priv->chan) {
@@ -2707,7 +2999,7 @@ static int talitos_probe(struct platform_device *ofdev)
priv->fifo_len = roundup_pow_of_two(priv->chfifo_len);
for (i = 0; i < priv->num_channels; i++) {
- priv->chan[i].reg = priv->reg + TALITOS_CH_STRIDE * (i + 1);
+ priv->chan[i].reg = priv->reg + stride * (i + 1);
if (!priv->irq[1] || !(i & 1))
priv->chan[i].reg += TALITOS_CH_BASE_OFFSET;
@@ -2794,9 +3086,16 @@ err_out:
}
static const struct of_device_id talitos_match[] = {
+#ifdef CONFIG_CRYPTO_DEV_TALITOS1
+ {
+ .compatible = "fsl,sec1.0",
+ },
+#endif
+#ifdef CONFIG_CRYPTO_DEV_TALITOS2
{
.compatible = "fsl,sec2.0",
},
+#endif
{},
};
MODULE_DEVICE_TABLE(of, talitos_match);
diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h
index 61a14054aa39..314daf55e7f7 100644
--- a/drivers/crypto/talitos.h
+++ b/drivers/crypto/talitos.h
@@ -29,7 +29,8 @@
*/
#define TALITOS_TIMEOUT 100000
-#define TALITOS_MAX_DATA_LEN 65535
+#define TALITOS1_MAX_DATA_LEN 32768
+#define TALITOS2_MAX_DATA_LEN 65535
#define DESC_TYPE(desc_hdr) ((be32_to_cpu(desc_hdr) >> 3) & 0x1f)
#define PRIMARY_EU(desc_hdr) ((be32_to_cpu(desc_hdr) >> 28) & 0xf)
@@ -37,9 +38,17 @@
/* descriptor pointer entry */
struct talitos_ptr {
- __be16 len; /* length */
- u8 j_extent; /* jump to sg link table and/or extent */
- u8 eptr; /* extended address */
+ union {
+ struct { /* SEC2 format */
+ __be16 len; /* length */
+ u8 j_extent; /* jump to sg link table and/or extent*/
+ u8 eptr; /* extended address */
+ };
+ struct { /* SEC1 format */
+ __be16 res;
+ __be16 len1; /* length */
+ };
+ };
__be32 ptr; /* address */
};
@@ -53,10 +62,16 @@ static const struct talitos_ptr zero_entry = {
/* descriptor */
struct talitos_desc {
__be32 hdr; /* header high bits */
- __be32 hdr_lo; /* header low bits */
+ union {
+ __be32 hdr_lo; /* header low bits */
+ __be32 hdr1; /* header for SEC1 */
+ };
struct talitos_ptr ptr[7]; /* ptr/len pair array */
+ __be32 next_desc; /* next descriptor (SEC1) */
};
+#define TALITOS_DESC_SIZE (sizeof(struct talitos_desc) - sizeof(__be32))
+
/**
* talitos_request - descriptor submission request
* @desc: descriptor pointer (kernel virtual)
@@ -97,6 +112,14 @@ struct talitos_private {
struct device *dev;
struct platform_device *ofdev;
void __iomem *reg;
+ void __iomem *reg_deu;
+ void __iomem *reg_aesu;
+ void __iomem *reg_mdeu;
+ void __iomem *reg_afeu;
+ void __iomem *reg_rngu;
+ void __iomem *reg_pkeu;
+ void __iomem *reg_keu;
+ void __iomem *reg_crcu;
int irq[2];
/* SEC global registers lock */
@@ -144,49 +167,80 @@ extern int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
#define TALITOS_FTR_HW_AUTH_CHECK 0x00000002
#define TALITOS_FTR_SHA224_HWINIT 0x00000004
#define TALITOS_FTR_HMAC_OK 0x00000008
+#define TALITOS_FTR_SEC1 0x00000010
+
+/*
+ * If both CONFIG_CRYPTO_DEV_TALITOS1 and CONFIG_CRYPTO_DEV_TALITOS2 are
+ * defined, we check the features which are set according to the device tree.
+ * Otherwise, we answer true or false directly
+ */
+static inline bool has_ftr_sec1(struct talitos_private *priv)
+{
+#if defined(CONFIG_CRYPTO_DEV_TALITOS1) && defined(CONFIG_CRYPTO_DEV_TALITOS2)
+ return priv->features & TALITOS_FTR_SEC1 ? true : false;
+#elif defined(CONFIG_CRYPTO_DEV_TALITOS1)
+ return true;
+#else
+ return false;
+#endif
+}
/*
* TALITOS_xxx_LO addresses point to the low data bits (32-63) of the register
*/
+#define ISR1_FORMAT(x) (((x) << 28) | ((x) << 16))
+#define ISR2_FORMAT(x) (((x) << 4) | (x))
+
/* global register offset addresses */
#define TALITOS_MCR 0x1030 /* master control register */
#define TALITOS_MCR_RCA0 (1 << 15) /* remap channel 0 */
#define TALITOS_MCR_RCA1 (1 << 14) /* remap channel 1 */
#define TALITOS_MCR_RCA2 (1 << 13) /* remap channel 2 */
#define TALITOS_MCR_RCA3 (1 << 12) /* remap channel 3 */
-#define TALITOS_MCR_SWR 0x1 /* s/w reset */
+#define TALITOS1_MCR_SWR 0x1000000 /* s/w reset */
+#define TALITOS2_MCR_SWR 0x1 /* s/w reset */
#define TALITOS_MCR_LO 0x1034
#define TALITOS_IMR 0x1008 /* interrupt mask register */
-#define TALITOS_IMR_INIT 0x100ff /* enable channel IRQs */
-#define TALITOS_IMR_DONE 0x00055 /* done IRQs */
+/* enable channel IRQs */
+#define TALITOS1_IMR_INIT ISR1_FORMAT(0xf)
+#define TALITOS1_IMR_DONE ISR1_FORMAT(0x5) /* done IRQs */
+/* enable channel IRQs */
+#define TALITOS2_IMR_INIT (ISR2_FORMAT(0xf) | 0x10000)
+#define TALITOS2_IMR_DONE ISR1_FORMAT(0x5) /* done IRQs */
#define TALITOS_IMR_LO 0x100C
-#define TALITOS_IMR_LO_INIT 0x20000 /* allow RNGU error IRQs */
+#define TALITOS1_IMR_LO_INIT 0x2000000 /* allow RNGU error IRQs */
+#define TALITOS2_IMR_LO_INIT 0x20000 /* allow RNGU error IRQs */
#define TALITOS_ISR 0x1010 /* interrupt status register */
-#define TALITOS_ISR_4CHERR 0xaa /* 4 channel errors mask */
-#define TALITOS_ISR_4CHDONE 0x55 /* 4 channel done mask */
-#define TALITOS_ISR_CH_0_2_ERR 0x22 /* channels 0, 2 errors mask */
-#define TALITOS_ISR_CH_0_2_DONE 0x11 /* channels 0, 2 done mask */
-#define TALITOS_ISR_CH_1_3_ERR 0x88 /* channels 1, 3 errors mask */
-#define TALITOS_ISR_CH_1_3_DONE 0x44 /* channels 1, 3 done mask */
+#define TALITOS1_ISR_4CHERR ISR1_FORMAT(0xa) /* 4 ch errors mask */
+#define TALITOS1_ISR_4CHDONE ISR1_FORMAT(0x5) /* 4 ch done mask */
+#define TALITOS1_ISR_TEA_ERR 0x00000040
+#define TALITOS2_ISR_4CHERR ISR2_FORMAT(0xa) /* 4 ch errors mask */
+#define TALITOS2_ISR_4CHDONE ISR2_FORMAT(0x5) /* 4 ch done mask */
+#define TALITOS2_ISR_CH_0_2_ERR ISR2_FORMAT(0x2) /* ch 0, 2 err mask */
+#define TALITOS2_ISR_CH_0_2_DONE ISR2_FORMAT(0x1) /* ch 0, 2 done mask */
+#define TALITOS2_ISR_CH_1_3_ERR ISR2_FORMAT(0x8) /* ch 1, 3 err mask */
+#define TALITOS2_ISR_CH_1_3_DONE ISR2_FORMAT(0x4) /* ch 1, 3 done mask */
#define TALITOS_ISR_LO 0x1014
#define TALITOS_ICR 0x1018 /* interrupt clear register */
#define TALITOS_ICR_LO 0x101C
/* channel register address stride */
#define TALITOS_CH_BASE_OFFSET 0x1000 /* default channel map base */
-#define TALITOS_CH_STRIDE 0x100
+#define TALITOS1_CH_STRIDE 0x1000
+#define TALITOS2_CH_STRIDE 0x100
/* channel configuration register */
#define TALITOS_CCCR 0x8
-#define TALITOS_CCCR_CONT 0x2 /* channel continue */
-#define TALITOS_CCCR_RESET 0x1 /* channel reset */
+#define TALITOS2_CCCR_CONT 0x2 /* channel continue on SEC2 */
+#define TALITOS2_CCCR_RESET 0x1 /* channel reset on SEC2 */
#define TALITOS_CCCR_LO 0xc
#define TALITOS_CCCR_LO_IWSE 0x80 /* chan. ICCR writeback enab. */
#define TALITOS_CCCR_LO_EAE 0x20 /* extended address enable */
#define TALITOS_CCCR_LO_CDWE 0x10 /* chan. done writeback enab. */
#define TALITOS_CCCR_LO_NT 0x4 /* notification type */
#define TALITOS_CCCR_LO_CDIE 0x2 /* channel done IRQ enable */
+#define TALITOS1_CCCR_LO_RESET 0x1 /* channel reset on SEC1 */
/* CCPSR: channel pointer status register */
#define TALITOS_CCPSR 0x10
@@ -224,37 +278,48 @@ extern int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
#define TALITOS_SCATTER 0xe0
#define TALITOS_SCATTER_LO 0xe4
+/* execution unit registers base */
+#define TALITOS2_DEU 0x2000
+#define TALITOS2_AESU 0x4000
+#define TALITOS2_MDEU 0x6000
+#define TALITOS2_AFEU 0x8000
+#define TALITOS2_RNGU 0xa000
+#define TALITOS2_PKEU 0xc000
+#define TALITOS2_KEU 0xe000
+#define TALITOS2_CRCU 0xf000
+
+#define TALITOS12_AESU 0x4000
+#define TALITOS12_DEU 0x5000
+#define TALITOS12_MDEU 0x6000
+
+#define TALITOS10_AFEU 0x8000
+#define TALITOS10_DEU 0xa000
+#define TALITOS10_MDEU 0xc000
+#define TALITOS10_RNGU 0xe000
+#define TALITOS10_PKEU 0x10000
+#define TALITOS10_AESU 0x12000
+
/* execution unit interrupt status registers */
-#define TALITOS_DEUISR 0x2030 /* DES unit */
-#define TALITOS_DEUISR_LO 0x2034
-#define TALITOS_AESUISR 0x4030 /* AES unit */
-#define TALITOS_AESUISR_LO 0x4034
-#define TALITOS_MDEUISR 0x6030 /* message digest unit */
-#define TALITOS_MDEUISR_LO 0x6034
-#define TALITOS_MDEUICR 0x6038 /* interrupt control */
-#define TALITOS_MDEUICR_LO 0x603c
+#define TALITOS_EUDSR 0x10 /* data size */
+#define TALITOS_EUDSR_LO 0x14
+#define TALITOS_EURCR 0x18 /* reset control*/
+#define TALITOS_EURCR_LO 0x1c
+#define TALITOS_EUSR 0x28 /* rng status */
+#define TALITOS_EUSR_LO 0x2c
+#define TALITOS_EUISR 0x30
+#define TALITOS_EUISR_LO 0x34
+#define TALITOS_EUICR 0x38 /* int. control */
+#define TALITOS_EUICR_LO 0x3c
+#define TALITOS_EU_FIFO 0x800 /* output FIFO */
+#define TALITOS_EU_FIFO_LO 0x804 /* output FIFO */
+/* DES unit */
+#define TALITOS1_DEUICR_KPE 0x00200000 /* Key Parity Error */
+/* message digest unit */
#define TALITOS_MDEUICR_LO_ICE 0x4000 /* integrity check IRQ enable */
-#define TALITOS_AFEUISR 0x8030 /* arc4 unit */
-#define TALITOS_AFEUISR_LO 0x8034
-#define TALITOS_RNGUISR 0xa030 /* random number unit */
-#define TALITOS_RNGUISR_LO 0xa034
-#define TALITOS_RNGUSR 0xa028 /* rng status */
-#define TALITOS_RNGUSR_LO 0xa02c
+/* random number unit */
#define TALITOS_RNGUSR_LO_RD 0x1 /* reset done */
#define TALITOS_RNGUSR_LO_OFL 0xff0000/* output FIFO length */
-#define TALITOS_RNGUDSR 0xa010 /* data size */
-#define TALITOS_RNGUDSR_LO 0xa014
-#define TALITOS_RNGU_FIFO 0xa800 /* output FIFO */
-#define TALITOS_RNGU_FIFO_LO 0xa804 /* output FIFO */
-#define TALITOS_RNGURCR 0xa018 /* reset control */
-#define TALITOS_RNGURCR_LO 0xa01c
#define TALITOS_RNGURCR_LO_SR 0x1 /* software reset */
-#define TALITOS_PKEUISR 0xc030 /* public key unit */
-#define TALITOS_PKEUISR_LO 0xc034
-#define TALITOS_KEUISR 0xe030 /* kasumi unit */
-#define TALITOS_KEUISR_LO 0xe034
-#define TALITOS_CRCUISR 0xf030 /* cyclic redundancy check unit*/
-#define TALITOS_CRCUISR_LO 0xf034
#define TALITOS_MDEU_CONTEXT_SIZE_MD5_SHA1_SHA256 0x28
#define TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512 0x48
diff --git a/drivers/crypto/ux500/Kconfig b/drivers/crypto/ux500/Kconfig
index b35e5c4b025a..30796441b0a6 100644
--- a/drivers/crypto/ux500/Kconfig
+++ b/drivers/crypto/ux500/Kconfig
@@ -7,6 +7,8 @@
config CRYPTO_DEV_UX500_CRYP
tristate "UX500 crypto driver for CRYP block"
depends on CRYPTO_DEV_UX500
+ select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
select CRYPTO_DES
help
This selects the crypto driver for the UX500_CRYP hardware. It supports
@@ -16,7 +18,6 @@ config CRYPTO_DEV_UX500_HASH
tristate "UX500 crypto driver for HASH block"
depends on CRYPTO_DEV_UX500
select CRYPTO_HASH
- select CRYPTO_HMAC
help
This selects the hash driver for the UX500_HASH hardware.
Depends on UX500/STM DMA if running in DMA mode.
@@ -24,7 +25,6 @@ config CRYPTO_DEV_UX500_HASH
config CRYPTO_DEV_UX500_DEBUG
bool "Activate ux500 platform debug-mode for crypto and hash block"
depends on CRYPTO_DEV_UX500_CRYP || CRYPTO_DEV_UX500_HASH
- default n
help
Say Y if you want to add debug prints to ux500_hash and
ux500_cryp devices.
diff --git a/drivers/crypto/vmx/Kconfig b/drivers/crypto/vmx/Kconfig
index 771babf16aa0..89d8208d9851 100644
--- a/drivers/crypto/vmx/Kconfig
+++ b/drivers/crypto/vmx/Kconfig
@@ -1,6 +1,6 @@
config CRYPTO_DEV_VMX_ENCRYPT
tristate "Encryption acceleration support on P8 CPU"
- depends on PPC64 && CRYPTO_DEV_VMX
+ depends on CRYPTO_DEV_VMX
default y
help
Support for VMX cryptographic acceleration instructions on Power8 CPU.
diff --git a/drivers/crypto/vmx/Makefile b/drivers/crypto/vmx/Makefile
index c699c6e6c82e..d28ab96a2475 100644
--- a/drivers/crypto/vmx/Makefile
+++ b/drivers/crypto/vmx/Makefile
@@ -4,7 +4,7 @@ vmx-crypto-objs := vmx.o aesp8-ppc.o ghashp8-ppc.o aes.o aes_cbc.o aes_ctr.o gha
ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y)
TARGET := linux-ppc64le
else
-TARGET := linux-pcc64
+TARGET := linux-ppc64
endif
quiet_cmd_perl = PERL $@
diff --git a/drivers/crypto/vmx/aes.c b/drivers/crypto/vmx/aes.c
index ab300ea19434..e79e567e43aa 100644
--- a/drivers/crypto/vmx/aes.c
+++ b/drivers/crypto/vmx/aes.c
@@ -30,110 +30,118 @@
#include "aesp8-ppc.h"
struct p8_aes_ctx {
- struct crypto_cipher *fallback;
- struct aes_key enc_key;
- struct aes_key dec_key;
+ struct crypto_cipher *fallback;
+ struct aes_key enc_key;
+ struct aes_key dec_key;
};
static int p8_aes_init(struct crypto_tfm *tfm)
{
- const char *alg;
- struct crypto_cipher *fallback;
- struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
-
- if (!(alg = crypto_tfm_alg_name(tfm))) {
- printk(KERN_ERR "Failed to get algorithm name.\n");
- return -ENOENT;
- }
-
- fallback = crypto_alloc_cipher(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK);
- if (IS_ERR(fallback)) {
- printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n",
- alg, PTR_ERR(fallback));
- return PTR_ERR(fallback);
- }
- printk(KERN_INFO "Using '%s' as fallback implementation.\n",
- crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
-
- crypto_cipher_set_flags(fallback,
- crypto_cipher_get_flags((struct crypto_cipher *) tfm));
- ctx->fallback = fallback;
-
- return 0;
+ const char *alg;
+ struct crypto_cipher *fallback;
+ struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if (!(alg = crypto_tfm_alg_name(tfm))) {
+ printk(KERN_ERR "Failed to get algorithm name.\n");
+ return -ENOENT;
+ }
+
+ fallback = crypto_alloc_cipher(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(fallback)) {
+ printk(KERN_ERR
+ "Failed to allocate transformation for '%s': %ld\n",
+ alg, PTR_ERR(fallback));
+ return PTR_ERR(fallback);
+ }
+ printk(KERN_INFO "Using '%s' as fallback implementation.\n",
+ crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
+
+ crypto_cipher_set_flags(fallback,
+ crypto_cipher_get_flags((struct
+ crypto_cipher *)
+ tfm));
+ ctx->fallback = fallback;
+
+ return 0;
}
static void p8_aes_exit(struct crypto_tfm *tfm)
{
- struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
- if (ctx->fallback) {
- crypto_free_cipher(ctx->fallback);
- ctx->fallback = NULL;
- }
+ if (ctx->fallback) {
+ crypto_free_cipher(ctx->fallback);
+ ctx->fallback = NULL;
+ }
}
static int p8_aes_setkey(struct crypto_tfm *tfm, const u8 *key,
- unsigned int keylen)
+ unsigned int keylen)
{
- int ret;
- struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
-
- pagefault_disable();
- enable_kernel_altivec();
- ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
- ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key);
- pagefault_enable();
-
- ret += crypto_cipher_setkey(ctx->fallback, key, keylen);
- return ret;
+ int ret;
+ struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ preempt_disable();
+ pagefault_disable();
+ enable_kernel_altivec();
+ ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
+ ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key);
+ pagefault_enable();
+ preempt_enable();
+
+ ret += crypto_cipher_setkey(ctx->fallback, key, keylen);
+ return ret;
}
static void p8_aes_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
{
- struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
-
- if (in_interrupt()) {
- crypto_cipher_encrypt_one(ctx->fallback, dst, src);
- } else {
- pagefault_disable();
- enable_kernel_altivec();
- aes_p8_encrypt(src, dst, &ctx->enc_key);
- pagefault_enable();
- }
+ struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if (in_interrupt()) {
+ crypto_cipher_encrypt_one(ctx->fallback, dst, src);
+ } else {
+ preempt_disable();
+ pagefault_disable();
+ enable_kernel_altivec();
+ aes_p8_encrypt(src, dst, &ctx->enc_key);
+ pagefault_enable();
+ preempt_enable();
+ }
}
static void p8_aes_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
{
- struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
-
- if (in_interrupt()) {
- crypto_cipher_decrypt_one(ctx->fallback, dst, src);
- } else {
- pagefault_disable();
- enable_kernel_altivec();
- aes_p8_decrypt(src, dst, &ctx->dec_key);
- pagefault_enable();
- }
+ struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if (in_interrupt()) {
+ crypto_cipher_decrypt_one(ctx->fallback, dst, src);
+ } else {
+ preempt_disable();
+ pagefault_disable();
+ enable_kernel_altivec();
+ aes_p8_decrypt(src, dst, &ctx->dec_key);
+ pagefault_enable();
+ preempt_enable();
+ }
}
struct crypto_alg p8_aes_alg = {
- .cra_name = "aes",
- .cra_driver_name = "p8_aes",
- .cra_module = THIS_MODULE,
- .cra_priority = 1000,
- .cra_type = NULL,
- .cra_flags = CRYPTO_ALG_TYPE_CIPHER | CRYPTO_ALG_NEED_FALLBACK,
- .cra_alignmask = 0,
- .cra_blocksize = AES_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct p8_aes_ctx),
- .cra_init = p8_aes_init,
- .cra_exit = p8_aes_exit,
- .cra_cipher = {
- .cia_min_keysize = AES_MIN_KEY_SIZE,
- .cia_max_keysize = AES_MAX_KEY_SIZE,
- .cia_setkey = p8_aes_setkey,
- .cia_encrypt = p8_aes_encrypt,
- .cia_decrypt = p8_aes_decrypt,
- },
+ .cra_name = "aes",
+ .cra_driver_name = "p8_aes",
+ .cra_module = THIS_MODULE,
+ .cra_priority = 1000,
+ .cra_type = NULL,
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER | CRYPTO_ALG_NEED_FALLBACK,
+ .cra_alignmask = 0,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct p8_aes_ctx),
+ .cra_init = p8_aes_init,
+ .cra_exit = p8_aes_exit,
+ .cra_cipher = {
+ .cia_min_keysize = AES_MIN_KEY_SIZE,
+ .cia_max_keysize = AES_MAX_KEY_SIZE,
+ .cia_setkey = p8_aes_setkey,
+ .cia_encrypt = p8_aes_encrypt,
+ .cia_decrypt = p8_aes_decrypt,
+ },
};
-
diff --git a/drivers/crypto/vmx/aes_cbc.c b/drivers/crypto/vmx/aes_cbc.c
index 1a559b7dddb5..7299995c78ec 100644
--- a/drivers/crypto/vmx/aes_cbc.c
+++ b/drivers/crypto/vmx/aes_cbc.c
@@ -31,154 +31,168 @@
#include "aesp8-ppc.h"
struct p8_aes_cbc_ctx {
- struct crypto_blkcipher *fallback;
- struct aes_key enc_key;
- struct aes_key dec_key;
+ struct crypto_blkcipher *fallback;
+ struct aes_key enc_key;
+ struct aes_key dec_key;
};
static int p8_aes_cbc_init(struct crypto_tfm *tfm)
{
- const char *alg;
- struct crypto_blkcipher *fallback;
- struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
-
- if (!(alg = crypto_tfm_alg_name(tfm))) {
- printk(KERN_ERR "Failed to get algorithm name.\n");
- return -ENOENT;
- }
-
- fallback = crypto_alloc_blkcipher(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK);
- if (IS_ERR(fallback)) {
- printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n",
- alg, PTR_ERR(fallback));
- return PTR_ERR(fallback);
- }
- printk(KERN_INFO "Using '%s' as fallback implementation.\n",
- crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
-
- crypto_blkcipher_set_flags(fallback,
- crypto_blkcipher_get_flags((struct crypto_blkcipher *) tfm));
- ctx->fallback = fallback;
-
- return 0;
+ const char *alg;
+ struct crypto_blkcipher *fallback;
+ struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if (!(alg = crypto_tfm_alg_name(tfm))) {
+ printk(KERN_ERR "Failed to get algorithm name.\n");
+ return -ENOENT;
+ }
+
+ fallback =
+ crypto_alloc_blkcipher(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(fallback)) {
+ printk(KERN_ERR
+ "Failed to allocate transformation for '%s': %ld\n",
+ alg, PTR_ERR(fallback));
+ return PTR_ERR(fallback);
+ }
+ printk(KERN_INFO "Using '%s' as fallback implementation.\n",
+ crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
+
+ crypto_blkcipher_set_flags(
+ fallback,
+ crypto_blkcipher_get_flags((struct crypto_blkcipher *)tfm));
+ ctx->fallback = fallback;
+
+ return 0;
}
static void p8_aes_cbc_exit(struct crypto_tfm *tfm)
{
- struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
- if (ctx->fallback) {
- crypto_free_blkcipher(ctx->fallback);
- ctx->fallback = NULL;
- }
+ if (ctx->fallback) {
+ crypto_free_blkcipher(ctx->fallback);
+ ctx->fallback = NULL;
+ }
}
static int p8_aes_cbc_setkey(struct crypto_tfm *tfm, const u8 *key,
- unsigned int keylen)
+ unsigned int keylen)
{
- int ret;
- struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
-
- pagefault_disable();
- enable_kernel_altivec();
- ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
- ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key);
- pagefault_enable();
-
- ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen);
- return ret;
+ int ret;
+ struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ preempt_disable();
+ pagefault_disable();
+ enable_kernel_altivec();
+ ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
+ ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key);
+ pagefault_enable();
+ preempt_enable();
+
+ ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen);
+ return ret;
}
static int p8_aes_cbc_encrypt(struct blkcipher_desc *desc,
- struct scatterlist *dst, struct scatterlist *src,
- unsigned int nbytes)
+ struct scatterlist *dst,
+ struct scatterlist *src, unsigned int nbytes)
{
- int ret;
- struct blkcipher_walk walk;
- struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(
- crypto_blkcipher_tfm(desc->tfm));
- struct blkcipher_desc fallback_desc = {
- .tfm = ctx->fallback,
- .info = desc->info,
- .flags = desc->flags
- };
-
- if (in_interrupt()) {
- ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src, nbytes);
- } else {
- pagefault_disable();
- enable_kernel_altivec();
-
- blkcipher_walk_init(&walk, dst, src, nbytes);
- ret = blkcipher_walk_virt(desc, &walk);
- while ((nbytes = walk.nbytes)) {
- aes_p8_cbc_encrypt(walk.src.virt.addr, walk.dst.virt.addr,
- nbytes & AES_BLOCK_MASK, &ctx->enc_key, walk.iv, 1);
+ int ret;
+ struct blkcipher_walk walk;
+ struct p8_aes_cbc_ctx *ctx =
+ crypto_tfm_ctx(crypto_blkcipher_tfm(desc->tfm));
+ struct blkcipher_desc fallback_desc = {
+ .tfm = ctx->fallback,
+ .info = desc->info,
+ .flags = desc->flags
+ };
+
+ if (in_interrupt()) {
+ ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src,
+ nbytes);
+ } else {
+ preempt_disable();
+ pagefault_disable();
+ enable_kernel_altivec();
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ ret = blkcipher_walk_virt(desc, &walk);
+ while ((nbytes = walk.nbytes)) {
+ aes_p8_cbc_encrypt(walk.src.virt.addr,
+ walk.dst.virt.addr,
+ nbytes & AES_BLOCK_MASK,
+ &ctx->enc_key, walk.iv, 1);
nbytes &= AES_BLOCK_SIZE - 1;
ret = blkcipher_walk_done(desc, &walk, nbytes);
- }
+ }
- pagefault_enable();
- }
+ pagefault_enable();
+ preempt_enable();
+ }
- return ret;
+ return ret;
}
static int p8_aes_cbc_decrypt(struct blkcipher_desc *desc,
- struct scatterlist *dst, struct scatterlist *src,
- unsigned int nbytes)
+ struct scatterlist *dst,
+ struct scatterlist *src, unsigned int nbytes)
{
- int ret;
- struct blkcipher_walk walk;
- struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(
- crypto_blkcipher_tfm(desc->tfm));
- struct blkcipher_desc fallback_desc = {
- .tfm = ctx->fallback,
- .info = desc->info,
- .flags = desc->flags
- };
-
- if (in_interrupt()) {
- ret = crypto_blkcipher_decrypt(&fallback_desc, dst, src, nbytes);
- } else {
- pagefault_disable();
- enable_kernel_altivec();
-
- blkcipher_walk_init(&walk, dst, src, nbytes);
- ret = blkcipher_walk_virt(desc, &walk);
- while ((nbytes = walk.nbytes)) {
- aes_p8_cbc_encrypt(walk.src.virt.addr, walk.dst.virt.addr,
- nbytes & AES_BLOCK_MASK, &ctx->dec_key, walk.iv, 0);
+ int ret;
+ struct blkcipher_walk walk;
+ struct p8_aes_cbc_ctx *ctx =
+ crypto_tfm_ctx(crypto_blkcipher_tfm(desc->tfm));
+ struct blkcipher_desc fallback_desc = {
+ .tfm = ctx->fallback,
+ .info = desc->info,
+ .flags = desc->flags
+ };
+
+ if (in_interrupt()) {
+ ret = crypto_blkcipher_decrypt(&fallback_desc, dst, src,
+ nbytes);
+ } else {
+ preempt_disable();
+ pagefault_disable();
+ enable_kernel_altivec();
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ ret = blkcipher_walk_virt(desc, &walk);
+ while ((nbytes = walk.nbytes)) {
+ aes_p8_cbc_encrypt(walk.src.virt.addr,
+ walk.dst.virt.addr,
+ nbytes & AES_BLOCK_MASK,
+ &ctx->dec_key, walk.iv, 0);
nbytes &= AES_BLOCK_SIZE - 1;
ret = blkcipher_walk_done(desc, &walk, nbytes);
}
- pagefault_enable();
- }
+ pagefault_enable();
+ preempt_enable();
+ }
- return ret;
+ return ret;
}
struct crypto_alg p8_aes_cbc_alg = {
- .cra_name = "cbc(aes)",
- .cra_driver_name = "p8_aes_cbc",
- .cra_module = THIS_MODULE,
- .cra_priority = 1000,
- .cra_type = &crypto_blkcipher_type,
- .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
- .cra_alignmask = 0,
- .cra_blocksize = AES_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct p8_aes_cbc_ctx),
- .cra_init = p8_aes_cbc_init,
- .cra_exit = p8_aes_cbc_exit,
- .cra_blkcipher = {
- .ivsize = 0,
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- .setkey = p8_aes_cbc_setkey,
- .encrypt = p8_aes_cbc_encrypt,
- .decrypt = p8_aes_cbc_decrypt,
- },
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "p8_aes_cbc",
+ .cra_module = THIS_MODULE,
+ .cra_priority = 1000,
+ .cra_type = &crypto_blkcipher_type,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
+ .cra_alignmask = 0,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct p8_aes_cbc_ctx),
+ .cra_init = p8_aes_cbc_init,
+ .cra_exit = p8_aes_cbc_exit,
+ .cra_blkcipher = {
+ .ivsize = 0,
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = p8_aes_cbc_setkey,
+ .encrypt = p8_aes_cbc_encrypt,
+ .decrypt = p8_aes_cbc_decrypt,
+ },
};
-
diff --git a/drivers/crypto/vmx/aes_ctr.c b/drivers/crypto/vmx/aes_ctr.c
index 96dbee4bf4a6..7adae42a7b79 100644
--- a/drivers/crypto/vmx/aes_ctr.c
+++ b/drivers/crypto/vmx/aes_ctr.c
@@ -30,138 +30,147 @@
#include "aesp8-ppc.h"
struct p8_aes_ctr_ctx {
- struct crypto_blkcipher *fallback;
- struct aes_key enc_key;
+ struct crypto_blkcipher *fallback;
+ struct aes_key enc_key;
};
static int p8_aes_ctr_init(struct crypto_tfm *tfm)
{
- const char *alg;
- struct crypto_blkcipher *fallback;
- struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
-
- if (!(alg = crypto_tfm_alg_name(tfm))) {
- printk(KERN_ERR "Failed to get algorithm name.\n");
- return -ENOENT;
- }
-
- fallback = crypto_alloc_blkcipher(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK);
- if (IS_ERR(fallback)) {
- printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n",
- alg, PTR_ERR(fallback));
- return PTR_ERR(fallback);
- }
- printk(KERN_INFO "Using '%s' as fallback implementation.\n",
- crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
-
- crypto_blkcipher_set_flags(fallback,
- crypto_blkcipher_get_flags((struct crypto_blkcipher *) tfm));
- ctx->fallback = fallback;
-
- return 0;
+ const char *alg;
+ struct crypto_blkcipher *fallback;
+ struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if (!(alg = crypto_tfm_alg_name(tfm))) {
+ printk(KERN_ERR "Failed to get algorithm name.\n");
+ return -ENOENT;
+ }
+
+ fallback =
+ crypto_alloc_blkcipher(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(fallback)) {
+ printk(KERN_ERR
+ "Failed to allocate transformation for '%s': %ld\n",
+ alg, PTR_ERR(fallback));
+ return PTR_ERR(fallback);
+ }
+ printk(KERN_INFO "Using '%s' as fallback implementation.\n",
+ crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
+
+ crypto_blkcipher_set_flags(
+ fallback,
+ crypto_blkcipher_get_flags((struct crypto_blkcipher *)tfm));
+ ctx->fallback = fallback;
+
+ return 0;
}
static void p8_aes_ctr_exit(struct crypto_tfm *tfm)
{
- struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
- if (ctx->fallback) {
- crypto_free_blkcipher(ctx->fallback);
- ctx->fallback = NULL;
- }
+ if (ctx->fallback) {
+ crypto_free_blkcipher(ctx->fallback);
+ ctx->fallback = NULL;
+ }
}
static int p8_aes_ctr_setkey(struct crypto_tfm *tfm, const u8 *key,
- unsigned int keylen)
+ unsigned int keylen)
{
- int ret;
- struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
+ int ret;
+ struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
- pagefault_disable();
- enable_kernel_altivec();
- ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
- pagefault_enable();
+ pagefault_disable();
+ enable_kernel_altivec();
+ ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
+ pagefault_enable();
- ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen);
- return ret;
+ ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen);
+ return ret;
}
static void p8_aes_ctr_final(struct p8_aes_ctr_ctx *ctx,
- struct blkcipher_walk *walk)
+ struct blkcipher_walk *walk)
{
- u8 *ctrblk = walk->iv;
- u8 keystream[AES_BLOCK_SIZE];
- u8 *src = walk->src.virt.addr;
- u8 *dst = walk->dst.virt.addr;
- unsigned int nbytes = walk->nbytes;
-
- pagefault_disable();
- enable_kernel_altivec();
- aes_p8_encrypt(ctrblk, keystream, &ctx->enc_key);
- pagefault_enable();
-
- crypto_xor(keystream, src, nbytes);
- memcpy(dst, keystream, nbytes);
- crypto_inc(ctrblk, AES_BLOCK_SIZE);
+ u8 *ctrblk = walk->iv;
+ u8 keystream[AES_BLOCK_SIZE];
+ u8 *src = walk->src.virt.addr;
+ u8 *dst = walk->dst.virt.addr;
+ unsigned int nbytes = walk->nbytes;
+
+ pagefault_disable();
+ enable_kernel_altivec();
+ aes_p8_encrypt(ctrblk, keystream, &ctx->enc_key);
+ pagefault_enable();
+
+ crypto_xor(keystream, src, nbytes);
+ memcpy(dst, keystream, nbytes);
+ crypto_inc(ctrblk, AES_BLOCK_SIZE);
}
static int p8_aes_ctr_crypt(struct blkcipher_desc *desc,
- struct scatterlist *dst, struct scatterlist *src,
- unsigned int nbytes)
+ struct scatterlist *dst,
+ struct scatterlist *src, unsigned int nbytes)
{
- int ret;
- struct blkcipher_walk walk;
- struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(
- crypto_blkcipher_tfm(desc->tfm));
- struct blkcipher_desc fallback_desc = {
- .tfm = ctx->fallback,
- .info = desc->info,
- .flags = desc->flags
- };
-
- if (in_interrupt()) {
- ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src, nbytes);
- } else {
- blkcipher_walk_init(&walk, dst, src, nbytes);
- ret = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE);
- while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) {
- pagefault_disable();
- enable_kernel_altivec();
- aes_p8_ctr32_encrypt_blocks(walk.src.virt.addr, walk.dst.virt.addr,
- (nbytes & AES_BLOCK_MASK)/AES_BLOCK_SIZE, &ctx->enc_key, walk.iv);
- pagefault_enable();
-
- crypto_inc(walk.iv, AES_BLOCK_SIZE);
- nbytes &= AES_BLOCK_SIZE - 1;
- ret = blkcipher_walk_done(desc, &walk, nbytes);
- }
- if (walk.nbytes) {
- p8_aes_ctr_final(ctx, &walk);
- ret = blkcipher_walk_done(desc, &walk, 0);
- }
- }
-
- return ret;
+ int ret;
+ struct blkcipher_walk walk;
+ struct p8_aes_ctr_ctx *ctx =
+ crypto_tfm_ctx(crypto_blkcipher_tfm(desc->tfm));
+ struct blkcipher_desc fallback_desc = {
+ .tfm = ctx->fallback,
+ .info = desc->info,
+ .flags = desc->flags
+ };
+
+ if (in_interrupt()) {
+ ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src,
+ nbytes);
+ } else {
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ ret = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE);
+ while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) {
+ pagefault_disable();
+ enable_kernel_altivec();
+ aes_p8_ctr32_encrypt_blocks(walk.src.virt.addr,
+ walk.dst.virt.addr,
+ (nbytes &
+ AES_BLOCK_MASK) /
+ AES_BLOCK_SIZE,
+ &ctx->enc_key,
+ walk.iv);
+ pagefault_enable();
+
+ crypto_inc(walk.iv, AES_BLOCK_SIZE);
+ nbytes &= AES_BLOCK_SIZE - 1;
+ ret = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+ if (walk.nbytes) {
+ p8_aes_ctr_final(ctx, &walk);
+ ret = blkcipher_walk_done(desc, &walk, 0);
+ }
+ }
+
+ return ret;
}
struct crypto_alg p8_aes_ctr_alg = {
- .cra_name = "ctr(aes)",
- .cra_driver_name = "p8_aes_ctr",
- .cra_module = THIS_MODULE,
- .cra_priority = 1000,
- .cra_type = &crypto_blkcipher_type,
- .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
- .cra_alignmask = 0,
- .cra_blocksize = 1,
- .cra_ctxsize = sizeof(struct p8_aes_ctr_ctx),
- .cra_init = p8_aes_ctr_init,
- .cra_exit = p8_aes_ctr_exit,
- .cra_blkcipher = {
- .ivsize = 0,
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- .setkey = p8_aes_ctr_setkey,
- .encrypt = p8_aes_ctr_crypt,
- .decrypt = p8_aes_ctr_crypt,
- },
+ .cra_name = "ctr(aes)",
+ .cra_driver_name = "p8_aes_ctr",
+ .cra_module = THIS_MODULE,
+ .cra_priority = 1000,
+ .cra_type = &crypto_blkcipher_type,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
+ .cra_alignmask = 0,
+ .cra_blocksize = 1,
+ .cra_ctxsize = sizeof(struct p8_aes_ctr_ctx),
+ .cra_init = p8_aes_ctr_init,
+ .cra_exit = p8_aes_ctr_exit,
+ .cra_blkcipher = {
+ .ivsize = 0,
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = p8_aes_ctr_setkey,
+ .encrypt = p8_aes_ctr_crypt,
+ .decrypt = p8_aes_ctr_crypt,
+ },
};
diff --git a/drivers/crypto/vmx/aesp8-ppc.h b/drivers/crypto/vmx/aesp8-ppc.h
index e963945a83e1..4cd34ee54a94 100644
--- a/drivers/crypto/vmx/aesp8-ppc.h
+++ b/drivers/crypto/vmx/aesp8-ppc.h
@@ -4,17 +4,18 @@
#define AES_BLOCK_MASK (~(AES_BLOCK_SIZE-1))
struct aes_key {
- u8 key[AES_MAX_KEYLENGTH];
- int rounds;
+ u8 key[AES_MAX_KEYLENGTH];
+ int rounds;
};
int aes_p8_set_encrypt_key(const u8 *userKey, const int bits,
- struct aes_key *key);
+ struct aes_key *key);
int aes_p8_set_decrypt_key(const u8 *userKey, const int bits,
- struct aes_key *key);
+ struct aes_key *key);
void aes_p8_encrypt(const u8 *in, u8 *out, const struct aes_key *key);
-void aes_p8_decrypt(const u8 *in, u8 *out,const struct aes_key *key);
+void aes_p8_decrypt(const u8 *in, u8 *out, const struct aes_key *key);
void aes_p8_cbc_encrypt(const u8 *in, u8 *out, size_t len,
- const struct aes_key *key, u8 *iv, const int enc);
+ const struct aes_key *key, u8 *iv, const int enc);
void aes_p8_ctr32_encrypt_blocks(const u8 *in, u8 *out,
- size_t len, const struct aes_key *key, const u8 *iv);
+ size_t len, const struct aes_key *key,
+ const u8 *iv);
diff --git a/drivers/crypto/vmx/ghash.c b/drivers/crypto/vmx/ghash.c
index d0ffe277af5c..b5e29002b666 100644
--- a/drivers/crypto/vmx/ghash.c
+++ b/drivers/crypto/vmx/ghash.c
@@ -39,176 +39,188 @@
void gcm_init_p8(u128 htable[16], const u64 Xi[2]);
void gcm_gmult_p8(u64 Xi[2], const u128 htable[16]);
void gcm_ghash_p8(u64 Xi[2], const u128 htable[16],
- const u8 *in,size_t len);
+ const u8 *in, size_t len);
struct p8_ghash_ctx {
- u128 htable[16];
- struct crypto_shash *fallback;
+ u128 htable[16];
+ struct crypto_shash *fallback;
};
struct p8_ghash_desc_ctx {
- u64 shash[2];
- u8 buffer[GHASH_DIGEST_SIZE];
- int bytes;
- struct shash_desc fallback_desc;
+ u64 shash[2];
+ u8 buffer[GHASH_DIGEST_SIZE];
+ int bytes;
+ struct shash_desc fallback_desc;
};
static int p8_ghash_init_tfm(struct crypto_tfm *tfm)
{
- const char *alg;
- struct crypto_shash *fallback;
- struct crypto_shash *shash_tfm = __crypto_shash_cast(tfm);
- struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm);
-
- if (!(alg = crypto_tfm_alg_name(tfm))) {
- printk(KERN_ERR "Failed to get algorithm name.\n");
- return -ENOENT;
- }
-
- fallback = crypto_alloc_shash(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK);
- if (IS_ERR(fallback)) {
- printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n",
- alg, PTR_ERR(fallback));
- return PTR_ERR(fallback);
- }
- printk(KERN_INFO "Using '%s' as fallback implementation.\n",
- crypto_tfm_alg_driver_name(crypto_shash_tfm(fallback)));
-
- crypto_shash_set_flags(fallback,
- crypto_shash_get_flags((struct crypto_shash *) tfm));
- ctx->fallback = fallback;
-
- shash_tfm->descsize = sizeof(struct p8_ghash_desc_ctx)
- + crypto_shash_descsize(fallback);
-
- return 0;
+ const char *alg;
+ struct crypto_shash *fallback;
+ struct crypto_shash *shash_tfm = __crypto_shash_cast(tfm);
+ struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if (!(alg = crypto_tfm_alg_name(tfm))) {
+ printk(KERN_ERR "Failed to get algorithm name.\n");
+ return -ENOENT;
+ }
+
+ fallback = crypto_alloc_shash(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(fallback)) {
+ printk(KERN_ERR
+ "Failed to allocate transformation for '%s': %ld\n",
+ alg, PTR_ERR(fallback));
+ return PTR_ERR(fallback);
+ }
+ printk(KERN_INFO "Using '%s' as fallback implementation.\n",
+ crypto_tfm_alg_driver_name(crypto_shash_tfm(fallback)));
+
+ crypto_shash_set_flags(fallback,
+ crypto_shash_get_flags((struct crypto_shash
+ *) tfm));
+ ctx->fallback = fallback;
+
+ shash_tfm->descsize = sizeof(struct p8_ghash_desc_ctx)
+ + crypto_shash_descsize(fallback);
+
+ return 0;
}
static void p8_ghash_exit_tfm(struct crypto_tfm *tfm)
{
- struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm);
- if (ctx->fallback) {
- crypto_free_shash(ctx->fallback);
- ctx->fallback = NULL;
- }
+ if (ctx->fallback) {
+ crypto_free_shash(ctx->fallback);
+ ctx->fallback = NULL;
+ }
}
static int p8_ghash_init(struct shash_desc *desc)
{
- struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
- struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
-
- dctx->bytes = 0;
- memset(dctx->shash, 0, GHASH_DIGEST_SIZE);
- dctx->fallback_desc.tfm = ctx->fallback;
- dctx->fallback_desc.flags = desc->flags;
- return crypto_shash_init(&dctx->fallback_desc);
+ struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
+ struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+ dctx->bytes = 0;
+ memset(dctx->shash, 0, GHASH_DIGEST_SIZE);
+ dctx->fallback_desc.tfm = ctx->fallback;
+ dctx->fallback_desc.flags = desc->flags;
+ return crypto_shash_init(&dctx->fallback_desc);
}
static int p8_ghash_setkey(struct crypto_shash *tfm, const u8 *key,
- unsigned int keylen)
+ unsigned int keylen)
{
- struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(tfm));
-
- if (keylen != GHASH_KEY_LEN)
- return -EINVAL;
-
- pagefault_disable();
- enable_kernel_altivec();
- enable_kernel_fp();
- gcm_init_p8(ctx->htable, (const u64 *) key);
- pagefault_enable();
- return crypto_shash_setkey(ctx->fallback, key, keylen);
+ struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(tfm));
+
+ if (keylen != GHASH_KEY_LEN)
+ return -EINVAL;
+
+ preempt_disable();
+ pagefault_disable();
+ enable_kernel_altivec();
+ enable_kernel_fp();
+ gcm_init_p8(ctx->htable, (const u64 *) key);
+ pagefault_enable();
+ preempt_enable();
+ return crypto_shash_setkey(ctx->fallback, key, keylen);
}
static int p8_ghash_update(struct shash_desc *desc,
- const u8 *src, unsigned int srclen)
+ const u8 *src, unsigned int srclen)
{
- unsigned int len;
- struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
- struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
-
- if (IN_INTERRUPT) {
- return crypto_shash_update(&dctx->fallback_desc, src, srclen);
- } else {
- if (dctx->bytes) {
- if (dctx->bytes + srclen < GHASH_DIGEST_SIZE) {
- memcpy(dctx->buffer + dctx->bytes, src, srclen);
- dctx->bytes += srclen;
- return 0;
- }
- memcpy(dctx->buffer + dctx->bytes, src,
- GHASH_DIGEST_SIZE - dctx->bytes);
- pagefault_disable();
- enable_kernel_altivec();
- enable_kernel_fp();
- gcm_ghash_p8(dctx->shash, ctx->htable, dctx->buffer,
- GHASH_DIGEST_SIZE);
- pagefault_enable();
- src += GHASH_DIGEST_SIZE - dctx->bytes;
- srclen -= GHASH_DIGEST_SIZE - dctx->bytes;
- dctx->bytes = 0;
- }
- len = srclen & ~(GHASH_DIGEST_SIZE - 1);
- if (len) {
- pagefault_disable();
- enable_kernel_altivec();
- enable_kernel_fp();
- gcm_ghash_p8(dctx->shash, ctx->htable, src, len);
- pagefault_enable();
- src += len;
- srclen -= len;
- }
- if (srclen) {
- memcpy(dctx->buffer, src, srclen);
- dctx->bytes = srclen;
- }
- return 0;
- }
+ unsigned int len;
+ struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
+ struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+ if (IN_INTERRUPT) {
+ return crypto_shash_update(&dctx->fallback_desc, src,
+ srclen);
+ } else {
+ if (dctx->bytes) {
+ if (dctx->bytes + srclen < GHASH_DIGEST_SIZE) {
+ memcpy(dctx->buffer + dctx->bytes, src,
+ srclen);
+ dctx->bytes += srclen;
+ return 0;
+ }
+ memcpy(dctx->buffer + dctx->bytes, src,
+ GHASH_DIGEST_SIZE - dctx->bytes);
+ preempt_disable();
+ pagefault_disable();
+ enable_kernel_altivec();
+ enable_kernel_fp();
+ gcm_ghash_p8(dctx->shash, ctx->htable,
+ dctx->buffer, GHASH_DIGEST_SIZE);
+ pagefault_enable();
+ preempt_enable();
+ src += GHASH_DIGEST_SIZE - dctx->bytes;
+ srclen -= GHASH_DIGEST_SIZE - dctx->bytes;
+ dctx->bytes = 0;
+ }
+ len = srclen & ~(GHASH_DIGEST_SIZE - 1);
+ if (len) {
+ preempt_disable();
+ pagefault_disable();
+ enable_kernel_altivec();
+ enable_kernel_fp();
+ gcm_ghash_p8(dctx->shash, ctx->htable, src, len);
+ pagefault_enable();
+ preempt_enable();
+ src += len;
+ srclen -= len;
+ }
+ if (srclen) {
+ memcpy(dctx->buffer, src, srclen);
+ dctx->bytes = srclen;
+ }
+ return 0;
+ }
}
static int p8_ghash_final(struct shash_desc *desc, u8 *out)
{
- int i;
- struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
- struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
-
- if (IN_INTERRUPT) {
- return crypto_shash_final(&dctx->fallback_desc, out);
- } else {
- if (dctx->bytes) {
- for (i = dctx->bytes; i < GHASH_DIGEST_SIZE; i++)
- dctx->buffer[i] = 0;
- pagefault_disable();
- enable_kernel_altivec();
- enable_kernel_fp();
- gcm_ghash_p8(dctx->shash, ctx->htable, dctx->buffer,
- GHASH_DIGEST_SIZE);
- pagefault_enable();
- dctx->bytes = 0;
- }
- memcpy(out, dctx->shash, GHASH_DIGEST_SIZE);
- return 0;
- }
+ int i;
+ struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
+ struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+ if (IN_INTERRUPT) {
+ return crypto_shash_final(&dctx->fallback_desc, out);
+ } else {
+ if (dctx->bytes) {
+ for (i = dctx->bytes; i < GHASH_DIGEST_SIZE; i++)
+ dctx->buffer[i] = 0;
+ preempt_disable();
+ pagefault_disable();
+ enable_kernel_altivec();
+ enable_kernel_fp();
+ gcm_ghash_p8(dctx->shash, ctx->htable,
+ dctx->buffer, GHASH_DIGEST_SIZE);
+ pagefault_enable();
+ preempt_enable();
+ dctx->bytes = 0;
+ }
+ memcpy(out, dctx->shash, GHASH_DIGEST_SIZE);
+ return 0;
+ }
}
struct shash_alg p8_ghash_alg = {
- .digestsize = GHASH_DIGEST_SIZE,
- .init = p8_ghash_init,
- .update = p8_ghash_update,
- .final = p8_ghash_final,
- .setkey = p8_ghash_setkey,
- .descsize = sizeof(struct p8_ghash_desc_ctx),
- .base = {
- .cra_name = "ghash",
- .cra_driver_name = "p8_ghash",
- .cra_priority = 1000,
- .cra_flags = CRYPTO_ALG_TYPE_SHASH | CRYPTO_ALG_NEED_FALLBACK,
- .cra_blocksize = GHASH_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct p8_ghash_ctx),
- .cra_module = THIS_MODULE,
- .cra_init = p8_ghash_init_tfm,
- .cra_exit = p8_ghash_exit_tfm,
- },
+ .digestsize = GHASH_DIGEST_SIZE,
+ .init = p8_ghash_init,
+ .update = p8_ghash_update,
+ .final = p8_ghash_final,
+ .setkey = p8_ghash_setkey,
+ .descsize = sizeof(struct p8_ghash_desc_ctx),
+ .base = {
+ .cra_name = "ghash",
+ .cra_driver_name = "p8_ghash",
+ .cra_priority = 1000,
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH | CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = GHASH_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct p8_ghash_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_init = p8_ghash_init_tfm,
+ .cra_exit = p8_ghash_exit_tfm,
+ },
};
diff --git a/drivers/crypto/vmx/vmx.c b/drivers/crypto/vmx/vmx.c
index 44d8d5cfe40d..e163d5770438 100644
--- a/drivers/crypto/vmx/vmx.c
+++ b/drivers/crypto/vmx/vmx.c
@@ -32,57 +32,57 @@ extern struct crypto_alg p8_aes_alg;
extern struct crypto_alg p8_aes_cbc_alg;
extern struct crypto_alg p8_aes_ctr_alg;
static struct crypto_alg *algs[] = {
- &p8_aes_alg,
- &p8_aes_cbc_alg,
- &p8_aes_ctr_alg,
- NULL,
+ &p8_aes_alg,
+ &p8_aes_cbc_alg,
+ &p8_aes_ctr_alg,
+ NULL,
};
int __init p8_init(void)
{
- int ret = 0;
- struct crypto_alg **alg_it;
+ int ret = 0;
+ struct crypto_alg **alg_it;
- if (!(cur_cpu_spec->cpu_user_features2 & PPC_FEATURE2_VEC_CRYPTO))
- return -ENODEV;
+ if (!(cur_cpu_spec->cpu_user_features2 & PPC_FEATURE2_VEC_CRYPTO))
+ return -ENODEV;
- for (alg_it = algs; *alg_it; alg_it++) {
- ret = crypto_register_alg(*alg_it);
- printk(KERN_INFO "crypto_register_alg '%s' = %d\n",
- (*alg_it)->cra_name, ret);
- if (ret) {
- for (alg_it--; alg_it >= algs; alg_it--)
- crypto_unregister_alg(*alg_it);
- break;
- }
- }
- if (ret)
- return ret;
+ for (alg_it = algs; *alg_it; alg_it++) {
+ ret = crypto_register_alg(*alg_it);
+ printk(KERN_INFO "crypto_register_alg '%s' = %d\n",
+ (*alg_it)->cra_name, ret);
+ if (ret) {
+ for (alg_it--; alg_it >= algs; alg_it--)
+ crypto_unregister_alg(*alg_it);
+ break;
+ }
+ }
+ if (ret)
+ return ret;
- ret = crypto_register_shash(&p8_ghash_alg);
- if (ret) {
- for (alg_it = algs; *alg_it; alg_it++)
- crypto_unregister_alg(*alg_it);
- }
- return ret;
+ ret = crypto_register_shash(&p8_ghash_alg);
+ if (ret) {
+ for (alg_it = algs; *alg_it; alg_it++)
+ crypto_unregister_alg(*alg_it);
+ }
+ return ret;
}
void __exit p8_exit(void)
{
- struct crypto_alg **alg_it;
+ struct crypto_alg **alg_it;
- for (alg_it = algs; *alg_it; alg_it++) {
- printk(KERN_INFO "Removing '%s'\n", (*alg_it)->cra_name);
- crypto_unregister_alg(*alg_it);
- }
- crypto_unregister_shash(&p8_ghash_alg);
+ for (alg_it = algs; *alg_it; alg_it++) {
+ printk(KERN_INFO "Removing '%s'\n", (*alg_it)->cra_name);
+ crypto_unregister_alg(*alg_it);
+ }
+ crypto_unregister_shash(&p8_ghash_alg);
}
module_init(p8_init);
module_exit(p8_exit);
MODULE_AUTHOR("Marcelo Cerri<mhcerri@br.ibm.com>");
-MODULE_DESCRIPTION("IBM VMX cryptogaphic acceleration instructions support on Power 8");
+MODULE_DESCRIPTION("IBM VMX cryptographic acceleration instructions "
+ "support on Power 8");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0.0");
-
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index fd7ac13f2574..bda2cb06dc7a 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -437,6 +437,7 @@ config IMG_MDC_DMA
config XGENE_DMA
tristate "APM X-Gene DMA support"
+ depends on ARCH_XGENE || COMPILE_TEST
select DMA_ENGINE
select DMA_ENGINE_RAID
select ASYNC_TX_ENABLE_CHANNEL_SWITCH
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index 933e4b338459..7992164ea9ec 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -174,6 +174,8 @@
#define AT_XDMAC_MBR_UBC_NDV3 (0x3 << 27) /* Next Descriptor View 3 */
#define AT_XDMAC_MAX_CHAN 0x20
+#define AT_XDMAC_MAX_CSIZE 16 /* 16 data */
+#define AT_XDMAC_MAX_DWIDTH 8 /* 64 bits */
#define AT_XDMAC_DMA_BUSWIDTHS\
(BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) |\
@@ -192,20 +194,17 @@ struct at_xdmac_chan {
struct dma_chan chan;
void __iomem *ch_regs;
u32 mask; /* Channel Mask */
- u32 cfg[2]; /* Channel Configuration Register */
- #define AT_XDMAC_DEV_TO_MEM_CFG 0 /* Predifined dev to mem channel conf */
- #define AT_XDMAC_MEM_TO_DEV_CFG 1 /* Predifined mem to dev channel conf */
+ u32 cfg; /* Channel Configuration Register */
u8 perid; /* Peripheral ID */
u8 perif; /* Peripheral Interface */
u8 memif; /* Memory Interface */
- u32 per_src_addr;
- u32 per_dst_addr;
u32 save_cc;
u32 save_cim;
u32 save_cnda;
u32 save_cndc;
unsigned long status;
struct tasklet_struct tasklet;
+ struct dma_slave_config sconfig;
spinlock_t lock;
@@ -415,8 +414,9 @@ static dma_cookie_t at_xdmac_tx_submit(struct dma_async_tx_descriptor *tx)
struct at_xdmac_desc *desc = txd_to_at_desc(tx);
struct at_xdmac_chan *atchan = to_at_xdmac_chan(tx->chan);
dma_cookie_t cookie;
+ unsigned long irqflags;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, irqflags);
cookie = dma_cookie_assign(tx);
dev_vdbg(chan2dev(tx->chan), "%s: atchan 0x%p, add desc 0x%p to xfers_list\n",
@@ -425,7 +425,7 @@ static dma_cookie_t at_xdmac_tx_submit(struct dma_async_tx_descriptor *tx)
if (list_is_singular(&atchan->xfers_list))
at_xdmac_start_xfer(atchan, desc);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, irqflags);
return cookie;
}
@@ -494,61 +494,94 @@ static struct dma_chan *at_xdmac_xlate(struct of_phandle_args *dma_spec,
return chan;
}
+static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
+ enum dma_transfer_direction direction)
+{
+ struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
+ int csize, dwidth;
+
+ if (direction == DMA_DEV_TO_MEM) {
+ atchan->cfg =
+ AT91_XDMAC_DT_PERID(atchan->perid)
+ | AT_XDMAC_CC_DAM_INCREMENTED_AM
+ | AT_XDMAC_CC_SAM_FIXED_AM
+ | AT_XDMAC_CC_DIF(atchan->memif)
+ | AT_XDMAC_CC_SIF(atchan->perif)
+ | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
+ | AT_XDMAC_CC_DSYNC_PER2MEM
+ | AT_XDMAC_CC_MBSIZE_SIXTEEN
+ | AT_XDMAC_CC_TYPE_PER_TRAN;
+ csize = ffs(atchan->sconfig.src_maxburst) - 1;
+ if (csize < 0) {
+ dev_err(chan2dev(chan), "invalid src maxburst value\n");
+ return -EINVAL;
+ }
+ atchan->cfg |= AT_XDMAC_CC_CSIZE(csize);
+ dwidth = ffs(atchan->sconfig.src_addr_width) - 1;
+ if (dwidth < 0) {
+ dev_err(chan2dev(chan), "invalid src addr width value\n");
+ return -EINVAL;
+ }
+ atchan->cfg |= AT_XDMAC_CC_DWIDTH(dwidth);
+ } else if (direction == DMA_MEM_TO_DEV) {
+ atchan->cfg =
+ AT91_XDMAC_DT_PERID(atchan->perid)
+ | AT_XDMAC_CC_DAM_FIXED_AM
+ | AT_XDMAC_CC_SAM_INCREMENTED_AM
+ | AT_XDMAC_CC_DIF(atchan->perif)
+ | AT_XDMAC_CC_SIF(atchan->memif)
+ | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
+ | AT_XDMAC_CC_DSYNC_MEM2PER
+ | AT_XDMAC_CC_MBSIZE_SIXTEEN
+ | AT_XDMAC_CC_TYPE_PER_TRAN;
+ csize = ffs(atchan->sconfig.dst_maxburst) - 1;
+ if (csize < 0) {
+ dev_err(chan2dev(chan), "invalid src maxburst value\n");
+ return -EINVAL;
+ }
+ atchan->cfg |= AT_XDMAC_CC_CSIZE(csize);
+ dwidth = ffs(atchan->sconfig.dst_addr_width) - 1;
+ if (dwidth < 0) {
+ dev_err(chan2dev(chan), "invalid dst addr width value\n");
+ return -EINVAL;
+ }
+ atchan->cfg |= AT_XDMAC_CC_DWIDTH(dwidth);
+ }
+
+ dev_dbg(chan2dev(chan), "%s: cfg=0x%08x\n", __func__, atchan->cfg);
+
+ return 0;
+}
+
+/*
+ * Only check that maxburst and addr width values are supported by the
+ * the controller but not that the configuration is good to perform the
+ * transfer since we don't know the direction at this stage.
+ */
+static int at_xdmac_check_slave_config(struct dma_slave_config *sconfig)
+{
+ if ((sconfig->src_maxburst > AT_XDMAC_MAX_CSIZE)
+ || (sconfig->dst_maxburst > AT_XDMAC_MAX_CSIZE))
+ return -EINVAL;
+
+ if ((sconfig->src_addr_width > AT_XDMAC_MAX_DWIDTH)
+ || (sconfig->dst_addr_width > AT_XDMAC_MAX_DWIDTH))
+ return -EINVAL;
+
+ return 0;
+}
+
static int at_xdmac_set_slave_config(struct dma_chan *chan,
struct dma_slave_config *sconfig)
{
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
- u8 dwidth;
- int csize;
- atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG] =
- AT91_XDMAC_DT_PERID(atchan->perid)
- | AT_XDMAC_CC_DAM_INCREMENTED_AM
- | AT_XDMAC_CC_SAM_FIXED_AM
- | AT_XDMAC_CC_DIF(atchan->memif)
- | AT_XDMAC_CC_SIF(atchan->perif)
- | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
- | AT_XDMAC_CC_DSYNC_PER2MEM
- | AT_XDMAC_CC_MBSIZE_SIXTEEN
- | AT_XDMAC_CC_TYPE_PER_TRAN;
- csize = at_xdmac_csize(sconfig->src_maxburst);
- if (csize < 0) {
- dev_err(chan2dev(chan), "invalid src maxburst value\n");
+ if (at_xdmac_check_slave_config(sconfig)) {
+ dev_err(chan2dev(chan), "invalid slave configuration\n");
return -EINVAL;
}
- atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG] |= AT_XDMAC_CC_CSIZE(csize);
- dwidth = ffs(sconfig->src_addr_width) - 1;
- atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG] |= AT_XDMAC_CC_DWIDTH(dwidth);
-
-
- atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG] =
- AT91_XDMAC_DT_PERID(atchan->perid)
- | AT_XDMAC_CC_DAM_FIXED_AM
- | AT_XDMAC_CC_SAM_INCREMENTED_AM
- | AT_XDMAC_CC_DIF(atchan->perif)
- | AT_XDMAC_CC_SIF(atchan->memif)
- | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
- | AT_XDMAC_CC_DSYNC_MEM2PER
- | AT_XDMAC_CC_MBSIZE_SIXTEEN
- | AT_XDMAC_CC_TYPE_PER_TRAN;
- csize = at_xdmac_csize(sconfig->dst_maxburst);
- if (csize < 0) {
- dev_err(chan2dev(chan), "invalid src maxburst value\n");
- return -EINVAL;
- }
- atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG] |= AT_XDMAC_CC_CSIZE(csize);
- dwidth = ffs(sconfig->dst_addr_width) - 1;
- atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG] |= AT_XDMAC_CC_DWIDTH(dwidth);
-
- /* Src and dst addr are needed to configure the link list descriptor. */
- atchan->per_src_addr = sconfig->src_addr;
- atchan->per_dst_addr = sconfig->dst_addr;
- dev_dbg(chan2dev(chan),
- "%s: cfg[dev2mem]=0x%08x, cfg[mem2dev]=0x%08x, per_src_addr=0x%08x, per_dst_addr=0x%08x\n",
- __func__, atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG],
- atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG],
- atchan->per_src_addr, atchan->per_dst_addr);
+ memcpy(&atchan->sconfig, sconfig, sizeof(atchan->sconfig));
return 0;
}
@@ -563,6 +596,8 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct scatterlist *sg;
int i;
unsigned int xfer_size = 0;
+ unsigned long irqflags;
+ struct dma_async_tx_descriptor *ret = NULL;
if (!sgl)
return NULL;
@@ -578,7 +613,10 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
flags);
/* Protect dma_sconfig field that can be modified by set_slave_conf. */
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, irqflags);
+
+ if (at_xdmac_compute_chan_conf(chan, direction))
+ goto spin_unlock;
/* Prepare descriptors. */
for_each_sg(sgl, sg, sg_len, i) {
@@ -589,8 +627,7 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
mem = sg_dma_address(sg);
if (unlikely(!len)) {
dev_err(chan2dev(chan), "sg data length is zero\n");
- spin_unlock_bh(&atchan->lock);
- return NULL;
+ goto spin_unlock;
}
dev_dbg(chan2dev(chan), "%s: * sg%d len=%u, mem=0x%08x\n",
__func__, i, len, mem);
@@ -600,20 +637,18 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
dev_err(chan2dev(chan), "can't get descriptor\n");
if (first)
list_splice_init(&first->descs_list, &atchan->free_descs_list);
- spin_unlock_bh(&atchan->lock);
- return NULL;
+ goto spin_unlock;
}
/* Linked list descriptor setup. */
if (direction == DMA_DEV_TO_MEM) {
- desc->lld.mbr_sa = atchan->per_src_addr;
+ desc->lld.mbr_sa = atchan->sconfig.src_addr;
desc->lld.mbr_da = mem;
- desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG];
} else {
desc->lld.mbr_sa = mem;
- desc->lld.mbr_da = atchan->per_dst_addr;
- desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG];
+ desc->lld.mbr_da = atchan->sconfig.dst_addr;
}
+ desc->lld.mbr_cfg = atchan->cfg;
dwidth = at_xdmac_get_dwidth(desc->lld.mbr_cfg);
fixed_dwidth = IS_ALIGNED(len, 1 << dwidth)
? at_xdmac_get_dwidth(desc->lld.mbr_cfg)
@@ -645,13 +680,15 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
xfer_size += len;
}
- spin_unlock_bh(&atchan->lock);
first->tx_dma_desc.flags = flags;
first->xfer_size = xfer_size;
first->direction = direction;
+ ret = &first->tx_dma_desc;
- return &first->tx_dma_desc;
+spin_unlock:
+ spin_unlock_irqrestore(&atchan->lock, irqflags);
+ return ret;
}
static struct dma_async_tx_descriptor *
@@ -664,6 +701,7 @@ at_xdmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr,
struct at_xdmac_desc *first = NULL, *prev = NULL;
unsigned int periods = buf_len / period_len;
int i;
+ unsigned long irqflags;
dev_dbg(chan2dev(chan), "%s: buf_addr=%pad, buf_len=%zd, period_len=%zd, dir=%s, flags=0x%lx\n",
__func__, &buf_addr, buf_len, period_len,
@@ -679,32 +717,34 @@ at_xdmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr,
return NULL;
}
+ if (at_xdmac_compute_chan_conf(chan, direction))
+ return NULL;
+
for (i = 0; i < periods; i++) {
struct at_xdmac_desc *desc = NULL;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, irqflags);
desc = at_xdmac_get_desc(atchan);
if (!desc) {
dev_err(chan2dev(chan), "can't get descriptor\n");
if (first)
list_splice_init(&first->descs_list, &atchan->free_descs_list);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, irqflags);
return NULL;
}
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, irqflags);
dev_dbg(chan2dev(chan),
"%s: desc=0x%p, tx_dma_desc.phys=%pad\n",
__func__, desc, &desc->tx_dma_desc.phys);
if (direction == DMA_DEV_TO_MEM) {
- desc->lld.mbr_sa = atchan->per_src_addr;
+ desc->lld.mbr_sa = atchan->sconfig.src_addr;
desc->lld.mbr_da = buf_addr + i * period_len;
- desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG];
} else {
desc->lld.mbr_sa = buf_addr + i * period_len;
- desc->lld.mbr_da = atchan->per_dst_addr;
- desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG];
+ desc->lld.mbr_da = atchan->sconfig.dst_addr;
}
+ desc->lld.mbr_cfg = atchan->cfg;
desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV1
| AT_XDMAC_MBR_UBC_NDEN
| AT_XDMAC_MBR_UBC_NSEN
@@ -766,6 +806,7 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
| AT_XDMAC_CC_SIF(0)
| AT_XDMAC_CC_MBSIZE_SIXTEEN
| AT_XDMAC_CC_TYPE_MEM_TRAN;
+ unsigned long irqflags;
dev_dbg(chan2dev(chan), "%s: src=%pad, dest=%pad, len=%zd, flags=0x%lx\n",
__func__, &src, &dest, len, flags);
@@ -798,9 +839,9 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
dev_dbg(chan2dev(chan), "%s: remaining_size=%zu\n", __func__, remaining_size);
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, irqflags);
desc = at_xdmac_get_desc(atchan);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, irqflags);
if (!desc) {
dev_err(chan2dev(chan), "can't get descriptor\n");
if (first)
@@ -886,6 +927,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
int residue;
u32 cur_nda, mask, value;
u8 dwidth = 0;
+ unsigned long flags;
ret = dma_cookie_status(chan, cookie, txstate);
if (ret == DMA_COMPLETE)
@@ -894,7 +936,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
if (!txstate)
return ret;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, xfer_node);
@@ -904,8 +946,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
*/
if (!desc->active_xfer) {
dma_set_residue(txstate, desc->xfer_size);
- spin_unlock_bh(&atchan->lock);
- return ret;
+ goto spin_unlock;
}
residue = desc->xfer_size;
@@ -936,14 +977,14 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
}
residue += at_xdmac_chan_read(atchan, AT_XDMAC_CUBC) << dwidth;
- spin_unlock_bh(&atchan->lock);
-
dma_set_residue(txstate, residue);
dev_dbg(chan2dev(chan),
"%s: desc=0x%p, tx_dma_desc.phys=%pad, tx_status=%d, cookie=%d, residue=%d\n",
__func__, desc, &desc->tx_dma_desc.phys, ret, cookie, residue);
+spin_unlock:
+ spin_unlock_irqrestore(&atchan->lock, flags);
return ret;
}
@@ -964,8 +1005,9 @@ static void at_xdmac_remove_xfer(struct at_xdmac_chan *atchan,
static void at_xdmac_advance_work(struct at_xdmac_chan *atchan)
{
struct at_xdmac_desc *desc;
+ unsigned long flags;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
/*
* If channel is enabled, do nothing, advance_work will be triggered
@@ -980,7 +1022,7 @@ static void at_xdmac_advance_work(struct at_xdmac_chan *atchan)
at_xdmac_start_xfer(atchan, desc);
}
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
}
static void at_xdmac_handle_cyclic(struct at_xdmac_chan *atchan)
@@ -1116,12 +1158,13 @@ static int at_xdmac_device_config(struct dma_chan *chan,
{
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
int ret;
+ unsigned long flags;
dev_dbg(chan2dev(chan), "%s\n", __func__);
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
ret = at_xdmac_set_slave_config(chan, config);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
return ret;
}
@@ -1130,18 +1173,19 @@ static int at_xdmac_device_pause(struct dma_chan *chan)
{
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device);
+ unsigned long flags;
dev_dbg(chan2dev(chan), "%s\n", __func__);
if (test_and_set_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status))
return 0;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
at_xdmac_write(atxdmac, AT_XDMAC_GRWS, atchan->mask);
while (at_xdmac_chan_read(atchan, AT_XDMAC_CC)
& (AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP))
cpu_relax();
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
return 0;
}
@@ -1150,18 +1194,19 @@ static int at_xdmac_device_resume(struct dma_chan *chan)
{
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device);
+ unsigned long flags;
dev_dbg(chan2dev(chan), "%s\n", __func__);
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
if (!at_xdmac_chan_is_paused(atchan)) {
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
return 0;
}
at_xdmac_write(atxdmac, AT_XDMAC_GRWR, atchan->mask);
clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
return 0;
}
@@ -1171,10 +1216,11 @@ static int at_xdmac_device_terminate_all(struct dma_chan *chan)
struct at_xdmac_desc *desc, *_desc;
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device);
+ unsigned long flags;
dev_dbg(chan2dev(chan), "%s\n", __func__);
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask);
while (at_xdmac_read(atxdmac, AT_XDMAC_GS) & atchan->mask)
cpu_relax();
@@ -1184,7 +1230,7 @@ static int at_xdmac_device_terminate_all(struct dma_chan *chan)
at_xdmac_remove_xfer(atchan, desc);
clear_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status);
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
return 0;
}
@@ -1194,8 +1240,9 @@ static int at_xdmac_alloc_chan_resources(struct dma_chan *chan)
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
struct at_xdmac_desc *desc;
int i;
+ unsigned long flags;
- spin_lock_bh(&atchan->lock);
+ spin_lock_irqsave(&atchan->lock, flags);
if (at_xdmac_chan_is_enabled(atchan)) {
dev_err(chan2dev(chan),
@@ -1226,7 +1273,7 @@ static int at_xdmac_alloc_chan_resources(struct dma_chan *chan)
dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i);
spin_unlock:
- spin_unlock_bh(&atchan->lock);
+ spin_unlock_irqrestore(&atchan->lock, flags);
return i;
}
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 0e035a8cf401..3ddfd1f6c23c 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -487,7 +487,11 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
caps->directions = device->directions;
caps->residue_granularity = device->residue_granularity;
- caps->cmd_pause = !!device->device_pause;
+ /*
+ * Some devices implement only pause (e.g. to get residuum) but no
+ * resume. However cmd_pause is advertised as pause AND resume.
+ */
+ caps->cmd_pause = !!(device->device_pause && device->device_resume);
caps->cmd_terminate = !!device->device_terminate_all;
return 0;
@@ -571,11 +575,15 @@ struct dma_chan *dma_get_any_slave_channel(struct dma_device *device)
chan = private_candidate(&mask, device, NULL, NULL);
if (chan) {
+ dma_cap_set(DMA_PRIVATE, device->cap_mask);
+ device->privatecnt++;
err = dma_chan_get(chan);
if (err) {
pr_debug("%s: failed to get %s: (%d)\n",
__func__, dma_chan_name(chan), err);
chan = NULL;
+ if (--device->privatecnt == 0)
+ dma_cap_clear(DMA_PRIVATE, device->cap_mask);
}
}
diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c
index 9b84def7a353..f42f71e37e73 100644
--- a/drivers/dma/hsu/hsu.c
+++ b/drivers/dma/hsu/hsu.c
@@ -384,7 +384,10 @@ static int hsu_dma_terminate_all(struct dma_chan *chan)
spin_lock_irqsave(&hsuc->vchan.lock, flags);
hsu_dma_stop_channel(hsuc);
- hsuc->desc = NULL;
+ if (hsuc->desc) {
+ hsu_dma_desc_free(&hsuc->desc->vdesc);
+ hsuc->desc = NULL;
+ }
vchan_get_all_descriptors(&hsuc->vchan, &head);
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
diff --git a/drivers/dma/mic_x100_dma.c b/drivers/dma/mic_x100_dma.c
index 6de2e677be04..74d9db05a5ad 100644
--- a/drivers/dma/mic_x100_dma.c
+++ b/drivers/dma/mic_x100_dma.c
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/io.h>
#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
#include "mic_x100_dma.h"
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index a7d9d3029b14..340f9e607cd8 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2127,6 +2127,7 @@ static int pl330_terminate_all(struct dma_chan *chan)
struct pl330_dmac *pl330 = pch->dmac;
LIST_HEAD(list);
+ pm_runtime_get_sync(pl330->ddma.dev);
spin_lock_irqsave(&pch->lock, flags);
spin_lock(&pl330->lock);
_stop(pch->thread);
@@ -2151,6 +2152,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
list_splice_tail_init(&pch->work_list, &pl330->desc_pool);
list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
spin_unlock_irqrestore(&pch->lock, flags);
+ pm_runtime_mark_last_busy(pl330->ddma.dev);
+ pm_runtime_put_autosuspend(pl330->ddma.dev);
return 0;
}
diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
index f705798ce3eb..ebd8a5f398b0 100644
--- a/drivers/dma/sh/usb-dmac.c
+++ b/drivers/dma/sh/usb-dmac.c
@@ -673,6 +673,7 @@ static struct dma_chan *usb_dmac_of_xlate(struct of_phandle_args *dma_spec,
* Power management
*/
+#ifdef CONFIG_PM
static int usb_dmac_runtime_suspend(struct device *dev)
{
struct usb_dmac *dmac = dev_get_drvdata(dev);
@@ -690,6 +691,7 @@ static int usb_dmac_runtime_resume(struct device *dev)
return usb_dmac_init(dmac);
}
+#endif /* CONFIG_PM */
static const struct dev_pm_ops usb_dmac_pm = {
SET_RUNTIME_PM_OPS(usb_dmac_runtime_suspend, usb_dmac_runtime_resume,
diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c
index de67fce18984..e45d1f13f445 100644
--- a/drivers/extcon/extcon-usb-gpio.c
+++ b/drivers/extcon/extcon-usb-gpio.c
@@ -119,6 +119,18 @@ static int usb_extcon_probe(struct platform_device *pdev)
return PTR_ERR(info->id_gpiod);
}
+ info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
+ if (IS_ERR(info->edev)) {
+ dev_err(dev, "failed to allocate extcon device\n");
+ return -ENOMEM;
+ }
+
+ ret = devm_extcon_dev_register(dev, info->edev);
+ if (ret < 0) {
+ dev_err(dev, "failed to register extcon device\n");
+ return ret;
+ }
+
ret = gpiod_set_debounce(info->id_gpiod,
USB_GPIO_DEBOUNCE_MS * 1000);
if (ret < 0)
@@ -142,18 +154,6 @@ static int usb_extcon_probe(struct platform_device *pdev)
return ret;
}
- info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
- if (IS_ERR(info->edev)) {
- dev_err(dev, "failed to allocate extcon device\n");
- return -ENOMEM;
- }
-
- ret = devm_extcon_dev_register(dev, info->edev);
- if (ret < 0) {
- dev_err(dev, "failed to register extcon device\n");
- return ret;
- }
-
platform_set_drvdata(pdev, info);
device_init_wakeup(dev, 1);
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c
index c22606fe3d44..6bac03999fd4 100644
--- a/drivers/firewire/sbp2.c
+++ b/drivers/firewire/sbp2.c
@@ -1611,7 +1611,6 @@ static struct scsi_host_template scsi_driver_template = {
.this_id = -1,
.sg_tablesize = SG_ALL,
.use_clustering = ENABLE_CLUSTERING,
- .cmd_per_lun = 1,
.can_queue = 1,
.sdev_attrs = sbp2_scsi_sysfs_attrs,
};
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 6e45a43ffe84..97b1616aa391 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -499,19 +499,19 @@ static int __init dmi_present(const u8 *buf)
buf += 16;
if (memcmp(buf, "_DMI_", 5) == 0 && dmi_checksum(buf, 15)) {
+ if (smbios_ver)
+ dmi_ver = smbios_ver;
+ else
+ dmi_ver = (buf[14] & 0xF0) << 4 | (buf[14] & 0x0F);
dmi_num = get_unaligned_le16(buf + 12);
dmi_len = get_unaligned_le16(buf + 6);
dmi_base = get_unaligned_le32(buf + 8);
if (dmi_walk_early(dmi_decode) == 0) {
if (smbios_ver) {
- dmi_ver = smbios_ver;
- pr_info("SMBIOS %d.%d%s present.\n",
- dmi_ver >> 8, dmi_ver & 0xFF,
- (dmi_ver < 0x0300) ? "" : ".x");
+ pr_info("SMBIOS %d.%d present.\n",
+ dmi_ver >> 8, dmi_ver & 0xFF);
} else {
- dmi_ver = (buf[14] & 0xF0) << 4 |
- (buf[14] & 0x0F);
pr_info("Legacy DMI %d.%d present.\n",
dmi_ver >> 8, dmi_ver & 0xFF);
}
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 8de4da5c9ab6..54071c148340 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -18,6 +18,11 @@ config EFI_VARS
Subsequent efibootmgr releases may be found at:
<http://github.com/vathpela/efibootmgr>
+config EFI_ESRT
+ bool
+ depends on EFI && !IA64
+ default y
+
config EFI_VARS_PSTORE
tristate "Register efivars backend for pstore"
depends on EFI_VARS && PSTORE
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index d8be608a9f3b..6fd3da938717 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_EFI) += efi.o vars.o reboot.o
obj-$(CONFIG_EFI_VARS) += efivars.o
+obj-$(CONFIG_EFI_ESRT) += esrt.o
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
obj-$(CONFIG_UEFI_CPER) += cper.o
obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 3061bb8629dc..ca617f40574a 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -39,6 +39,7 @@ struct efi __read_mostly efi = {
.fw_vendor = EFI_INVALID_TABLE_ADDR,
.runtime = EFI_INVALID_TABLE_ADDR,
.config_table = EFI_INVALID_TABLE_ADDR,
+ .esrt = EFI_INVALID_TABLE_ADDR,
};
EXPORT_SYMBOL(efi);
@@ -64,7 +65,7 @@ static int __init parse_efi_cmdline(char *str)
}
early_param("efi", parse_efi_cmdline);
-static struct kobject *efi_kobj;
+struct kobject *efi_kobj;
static struct kobject *efivars_kobj;
/*
@@ -85,10 +86,15 @@ static ssize_t systab_show(struct kobject *kobj,
str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20);
if (efi.acpi != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
- if (efi.smbios != EFI_INVALID_TABLE_ADDR)
- str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
+ /*
+ * If both SMBIOS and SMBIOS3 entry points are implemented, the
+ * SMBIOS3 entry point shall be preferred, so we list it first to
+ * let applications stop parsing after the first match.
+ */
if (efi.smbios3 != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3);
+ if (efi.smbios != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp);
if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
@@ -232,6 +238,84 @@ err_put:
subsys_initcall(efisubsys_init);
+/*
+ * Find the efi memory descriptor for a given physical address. Given a
+ * physicall address, determine if it exists within an EFI Memory Map entry,
+ * and if so, populate the supplied memory descriptor with the appropriate
+ * data.
+ */
+int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
+{
+ struct efi_memory_map *map = efi.memmap;
+ void *p, *e;
+
+ if (!efi_enabled(EFI_MEMMAP)) {
+ pr_err_once("EFI_MEMMAP is not enabled.\n");
+ return -EINVAL;
+ }
+
+ if (!map) {
+ pr_err_once("efi.memmap is not set.\n");
+ return -EINVAL;
+ }
+ if (!out_md) {
+ pr_err_once("out_md is null.\n");
+ return -EINVAL;
+ }
+ if (WARN_ON_ONCE(!map->phys_map))
+ return -EINVAL;
+ if (WARN_ON_ONCE(map->nr_map == 0) || WARN_ON_ONCE(map->desc_size == 0))
+ return -EINVAL;
+
+ e = map->phys_map + map->nr_map * map->desc_size;
+ for (p = map->phys_map; p < e; p += map->desc_size) {
+ efi_memory_desc_t *md;
+ u64 size;
+ u64 end;
+
+ /*
+ * If a driver calls this after efi_free_boot_services,
+ * ->map will be NULL, and the target may also not be mapped.
+ * So just always get our own virtual map on the CPU.
+ *
+ */
+ md = early_memremap((phys_addr_t)p, sizeof (*md));
+ if (!md) {
+ pr_err_once("early_memremap(%p, %zu) failed.\n",
+ p, sizeof (*md));
+ return -ENOMEM;
+ }
+
+ if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+ md->type != EFI_BOOT_SERVICES_DATA &&
+ md->type != EFI_RUNTIME_SERVICES_DATA) {
+ early_memunmap(md, sizeof (*md));
+ continue;
+ }
+
+ size = md->num_pages << EFI_PAGE_SHIFT;
+ end = md->phys_addr + size;
+ if (phys_addr >= md->phys_addr && phys_addr < end) {
+ memcpy(out_md, md, sizeof(*out_md));
+ early_memunmap(md, sizeof (*md));
+ return 0;
+ }
+
+ early_memunmap(md, sizeof (*md));
+ }
+ pr_err_once("requested map not found.\n");
+ return -ENOENT;
+}
+
+/*
+ * Calculate the highest address of an efi memory descriptor.
+ */
+u64 __init efi_mem_desc_end(efi_memory_desc_t *md)
+{
+ u64 size = md->num_pages << EFI_PAGE_SHIFT;
+ u64 end = md->phys_addr + size;
+ return end;
+}
/*
* We can't ioremap data in EFI boot services RAM, because we've already mapped
@@ -274,6 +358,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
{SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3},
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
+ {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
{NULL_GUID, NULL, NULL},
};
diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c
index 7b2e0496e0c0..756eca8c4cf8 100644
--- a/drivers/firmware/efi/efivars.c
+++ b/drivers/firmware/efi/efivars.c
@@ -535,7 +535,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
* efivar_create_sysfs_entry - create a new entry in sysfs
* @new_var: efivar entry to create
*
- * Returns 1 on failure, 0 on success
+ * Returns 0 on success, negative error code on failure
*/
static int
efivar_create_sysfs_entry(struct efivar_entry *new_var)
@@ -544,6 +544,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
char *short_name;
unsigned long variable_name_size;
efi_char16_t *variable_name;
+ int ret;
variable_name = new_var->var.VariableName;
variable_name_size = ucs2_strlen(variable_name) * sizeof(efi_char16_t);
@@ -558,7 +559,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
short_name = kzalloc(short_name_size, GFP_KERNEL);
if (!short_name)
- return 1;
+ return -ENOMEM;
/* Convert Unicode to normal chars (assume top bits are 0),
ala UTF-8 */
@@ -574,11 +575,11 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
new_var->kobj.kset = efivars_kset;
- i = kobject_init_and_add(&new_var->kobj, &efivar_ktype,
+ ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype,
NULL, "%s", short_name);
kfree(short_name);
- if (i)
- return 1;
+ if (ret)
+ return ret;
kobject_uevent(&new_var->kobj, KOBJ_ADD);
efivar_entry_add(new_var, &efivar_sysfs_list);
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
new file mode 100644
index 000000000000..a5b95d61ae71
--- /dev/null
+++ b/drivers/firmware/efi/esrt.c
@@ -0,0 +1,471 @@
+/*
+ * esrt.c
+ *
+ * This module exports EFI System Resource Table (ESRT) entries into userspace
+ * through the sysfs file system. The ESRT provides a read-only catalog of
+ * system components for which the system accepts firmware upgrades via UEFI's
+ * "Capsule Update" feature. This module allows userland utilities to evaluate
+ * what firmware updates can be applied to this system, and potentially arrange
+ * for those updates to occur.
+ *
+ * Data is currently found below /sys/firmware/efi/esrt/...
+ */
+#define pr_fmt(fmt) "esrt: " fmt
+
+#include <linux/capability.h>
+#include <linux/device.h>
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/early_ioremap.h>
+
+struct efi_system_resource_entry_v1 {
+ efi_guid_t fw_class;
+ u32 fw_type;
+ u32 fw_version;
+ u32 lowest_supported_fw_version;
+ u32 capsule_flags;
+ u32 last_attempt_version;
+ u32 last_attempt_status;
+};
+
+/*
+ * _count and _version are what they seem like. _max is actually just
+ * accounting info for the firmware when creating the table; it should never
+ * have been exposed to us. To wit, the spec says:
+ * The maximum number of resource array entries that can be within the
+ * table without reallocating the table, must not be zero.
+ * Since there's no guidance about what that means in terms of memory layout,
+ * it means nothing to us.
+ */
+struct efi_system_resource_table {
+ u32 fw_resource_count;
+ u32 fw_resource_count_max;
+ u64 fw_resource_version;
+ u8 entries[];
+};
+
+static phys_addr_t esrt_data;
+static size_t esrt_data_size;
+
+static struct efi_system_resource_table *esrt;
+
+struct esre_entry {
+ union {
+ struct efi_system_resource_entry_v1 *esre1;
+ } esre;
+
+ struct kobject kobj;
+ struct list_head list;
+};
+
+/* global list of esre_entry. */
+static LIST_HEAD(entry_list);
+
+/* entry attribute */
+struct esre_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct esre_entry *entry, char *buf);
+ ssize_t (*store)(struct esre_entry *entry,
+ const char *buf, size_t count);
+};
+
+static struct esre_entry *to_entry(struct kobject *kobj)
+{
+ return container_of(kobj, struct esre_entry, kobj);
+}
+
+static struct esre_attribute *to_attr(struct attribute *attr)
+{
+ return container_of(attr, struct esre_attribute, attr);
+}
+
+static ssize_t esre_attr_show(struct kobject *kobj,
+ struct attribute *_attr, char *buf)
+{
+ struct esre_entry *entry = to_entry(kobj);
+ struct esre_attribute *attr = to_attr(_attr);
+
+ /* Don't tell normal users what firmware versions we've got... */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ return attr->show(entry, buf);
+}
+
+static const struct sysfs_ops esre_attr_ops = {
+ .show = esre_attr_show,
+};
+
+/* Generic ESRT Entry ("ESRE") support. */
+static ssize_t esre_fw_class_show(struct esre_entry *entry, char *buf)
+{
+ char *str = buf;
+
+ efi_guid_to_str(&entry->esre.esre1->fw_class, str);
+ str += strlen(str);
+ str += sprintf(str, "\n");
+
+ return str - buf;
+}
+
+static struct esre_attribute esre_fw_class = __ATTR(fw_class, 0400,
+ esre_fw_class_show, NULL);
+
+#define esre_attr_decl(name, size, fmt) \
+static ssize_t esre_##name##_show(struct esre_entry *entry, char *buf) \
+{ \
+ return sprintf(buf, fmt "\n", \
+ le##size##_to_cpu(entry->esre.esre1->name)); \
+} \
+\
+static struct esre_attribute esre_##name = __ATTR(name, 0400, \
+ esre_##name##_show, NULL)
+
+esre_attr_decl(fw_type, 32, "%u");
+esre_attr_decl(fw_version, 32, "%u");
+esre_attr_decl(lowest_supported_fw_version, 32, "%u");
+esre_attr_decl(capsule_flags, 32, "0x%x");
+esre_attr_decl(last_attempt_version, 32, "%u");
+esre_attr_decl(last_attempt_status, 32, "%u");
+
+static struct attribute *esre1_attrs[] = {
+ &esre_fw_class.attr,
+ &esre_fw_type.attr,
+ &esre_fw_version.attr,
+ &esre_lowest_supported_fw_version.attr,
+ &esre_capsule_flags.attr,
+ &esre_last_attempt_version.attr,
+ &esre_last_attempt_status.attr,
+ NULL
+};
+static void esre_release(struct kobject *kobj)
+{
+ struct esre_entry *entry = to_entry(kobj);
+
+ list_del(&entry->list);
+ kfree(entry);
+}
+
+static struct kobj_type esre1_ktype = {
+ .release = esre_release,
+ .sysfs_ops = &esre_attr_ops,
+ .default_attrs = esre1_attrs,
+};
+
+
+static struct kobject *esrt_kobj;
+static struct kset *esrt_kset;
+
+static int esre_create_sysfs_entry(void *esre, int entry_num)
+{
+ struct esre_entry *entry;
+ char name[20];
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ sprintf(name, "entry%d", entry_num);
+
+ entry->kobj.kset = esrt_kset;
+
+ if (esrt->fw_resource_version == 1) {
+ int rc = 0;
+
+ entry->esre.esre1 = esre;
+ rc = kobject_init_and_add(&entry->kobj, &esre1_ktype, NULL,
+ "%s", name);
+ if (rc) {
+ kfree(entry);
+ return rc;
+ }
+ }
+
+ list_add_tail(&entry->list, &entry_list);
+ return 0;
+}
+
+/* support for displaying ESRT fields at the top level */
+#define esrt_attr_decl(name, size, fmt) \
+static ssize_t esrt_##name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf)\
+{ \
+ return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
+} \
+\
+static struct kobj_attribute esrt_##name = __ATTR(name, 0400, \
+ esrt_##name##_show, NULL)
+
+esrt_attr_decl(fw_resource_count, 32, "%u");
+esrt_attr_decl(fw_resource_count_max, 32, "%u");
+esrt_attr_decl(fw_resource_version, 64, "%llu");
+
+static struct attribute *esrt_attrs[] = {
+ &esrt_fw_resource_count.attr,
+ &esrt_fw_resource_count_max.attr,
+ &esrt_fw_resource_version.attr,
+ NULL,
+};
+
+static inline int esrt_table_exists(void)
+{
+ if (!efi_enabled(EFI_CONFIG_TABLES))
+ return 0;
+ if (efi.esrt == EFI_INVALID_TABLE_ADDR)
+ return 0;
+ return 1;
+}
+
+static umode_t esrt_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ if (!esrt_table_exists())
+ return 0;
+ return attr->mode;
+}
+
+static struct attribute_group esrt_attr_group = {
+ .attrs = esrt_attrs,
+ .is_visible = esrt_attr_is_visible,
+};
+
+/*
+ * remap the table, copy it to kmalloced pages, and unmap it.
+ */
+void __init efi_esrt_init(void)
+{
+ void *va;
+ struct efi_system_resource_table tmpesrt;
+ struct efi_system_resource_entry_v1 *v1_entries;
+ size_t size, max, entry_size, entries_size;
+ efi_memory_desc_t md;
+ int rc;
+ phys_addr_t end;
+
+ pr_debug("esrt-init: loading.\n");
+ if (!esrt_table_exists())
+ return;
+
+ rc = efi_mem_desc_lookup(efi.esrt, &md);
+ if (rc < 0) {
+ pr_err("ESRT header is not in the memory map.\n");
+ return;
+ }
+
+ max = efi_mem_desc_end(&md);
+ if (max < efi.esrt) {
+ pr_err("EFI memory descriptor is invalid. (esrt: %p max: %p)\n",
+ (void *)efi.esrt, (void *)max);
+ return;
+ }
+
+ size = sizeof(*esrt);
+ max -= efi.esrt;
+
+ if (max < size) {
+ pr_err("ESRT header doen't fit on single memory map entry. (size: %zu max: %zu)\n",
+ size, max);
+ return;
+ }
+
+ va = early_memremap(efi.esrt, size);
+ if (!va) {
+ pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
+ size);
+ return;
+ }
+
+ memcpy(&tmpesrt, va, sizeof(tmpesrt));
+
+ if (tmpesrt.fw_resource_version == 1) {
+ entry_size = sizeof (*v1_entries);
+ } else {
+ pr_err("Unsupported ESRT version %lld.\n",
+ tmpesrt.fw_resource_version);
+ return;
+ }
+
+ if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
+ pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
+ max - size, entry_size);
+ goto err_memunmap;
+ }
+
+ /*
+ * The format doesn't really give us any boundary to test here,
+ * so I'm making up 128 as the max number of individually updatable
+ * components we support.
+ * 128 should be pretty excessive, but there's still some chance
+ * somebody will do that someday and we'll need to raise this.
+ */
+ if (tmpesrt.fw_resource_count > 128) {
+ pr_err("ESRT says fw_resource_count has very large value %d.\n",
+ tmpesrt.fw_resource_count);
+ goto err_memunmap;
+ }
+
+ /*
+ * We know it can't be larger than N * sizeof() here, and N is limited
+ * by the previous test to a small number, so there's no overflow.
+ */
+ entries_size = tmpesrt.fw_resource_count * entry_size;
+ if (max < size + entries_size) {
+ pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n",
+ size, max);
+ goto err_memunmap;
+ }
+
+ /* remap it with our (plausible) new pages */
+ early_memunmap(va, size);
+ size += entries_size;
+ va = early_memremap(efi.esrt, size);
+ if (!va) {
+ pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
+ size);
+ return;
+ }
+
+ esrt_data = (phys_addr_t)efi.esrt;
+ esrt_data_size = size;
+
+ end = esrt_data + size;
+ pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
+ memblock_reserve(esrt_data, esrt_data_size);
+
+ pr_debug("esrt-init: loaded.\n");
+err_memunmap:
+ early_memunmap(va, size);
+}
+
+static int __init register_entries(void)
+{
+ struct efi_system_resource_entry_v1 *v1_entries = (void *)esrt->entries;
+ int i, rc;
+
+ if (!esrt_table_exists())
+ return 0;
+
+ for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
+ void *esre = NULL;
+ if (esrt->fw_resource_version == 1) {
+ esre = &v1_entries[i];
+ } else {
+ pr_err("Unsupported ESRT version %lld.\n",
+ esrt->fw_resource_version);
+ return -EINVAL;
+ }
+
+ rc = esre_create_sysfs_entry(esre, i);
+ if (rc < 0) {
+ pr_err("ESRT entry creation failed with error %d.\n",
+ rc);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static void cleanup_entry_list(void)
+{
+ struct esre_entry *entry, *next;
+
+ list_for_each_entry_safe(entry, next, &entry_list, list) {
+ kobject_put(&entry->kobj);
+ }
+}
+
+static int __init esrt_sysfs_init(void)
+{
+ int error;
+ struct efi_system_resource_table __iomem *ioesrt;
+
+ pr_debug("esrt-sysfs: loading.\n");
+ if (!esrt_data || !esrt_data_size)
+ return -ENOSYS;
+
+ ioesrt = ioremap(esrt_data, esrt_data_size);
+ if (!ioesrt) {
+ pr_err("ioremap(%pa, %zu) failed.\n", &esrt_data,
+ esrt_data_size);
+ return -ENOMEM;
+ }
+
+ esrt = kmalloc(esrt_data_size, GFP_KERNEL);
+ if (!esrt) {
+ pr_err("kmalloc failed. (wanted %zu bytes)\n", esrt_data_size);
+ iounmap(ioesrt);
+ return -ENOMEM;
+ }
+
+ memcpy_fromio(esrt, ioesrt, esrt_data_size);
+
+ esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
+ if (!esrt_kobj) {
+ pr_err("Firmware table registration failed.\n");
+ error = -ENOMEM;
+ goto err;
+ }
+
+ error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
+ if (error) {
+ pr_err("Sysfs attribute export failed with error %d.\n",
+ error);
+ goto err_remove_esrt;
+ }
+
+ esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
+ if (!esrt_kset) {
+ pr_err("kset creation failed.\n");
+ error = -ENOMEM;
+ goto err_remove_group;
+ }
+
+ error = register_entries();
+ if (error)
+ goto err_cleanup_list;
+
+ memblock_remove(esrt_data, esrt_data_size);
+
+ pr_debug("esrt-sysfs: loaded.\n");
+
+ return 0;
+err_cleanup_list:
+ cleanup_entry_list();
+ kset_unregister(esrt_kset);
+err_remove_group:
+ sysfs_remove_group(esrt_kobj, &esrt_attr_group);
+err_remove_esrt:
+ kobject_put(esrt_kobj);
+err:
+ kfree(esrt);
+ esrt = NULL;
+ return error;
+}
+
+static void __exit esrt_sysfs_exit(void)
+{
+ pr_debug("esrt-sysfs: unloading.\n");
+ cleanup_entry_list();
+ kset_unregister(esrt_kset);
+ sysfs_remove_group(esrt_kobj, &esrt_attr_group);
+ kfree(esrt);
+ esrt = NULL;
+ kobject_del(esrt_kobj);
+ kobject_put(esrt_kobj);
+}
+
+module_init(esrt_sysfs_init);
+module_exit(esrt_sysfs_exit);
+
+MODULE_AUTHOR("Peter Jones <pjones@redhat.com>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c
index 87b8e3b900d2..5c55227a34c8 100644
--- a/drivers/firmware/efi/runtime-map.c
+++ b/drivers/firmware/efi/runtime-map.c
@@ -120,7 +120,8 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
kset_unregister(map_kset);
- return entry;
+ map_kset = NULL;
+ return ERR_PTR(-ENOMEM);
}
memcpy(&entry->md, efi_runtime_map + nr * efi_memdesc_size,
@@ -132,6 +133,7 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
if (ret) {
kobject_put(&entry->kobj);
kset_unregister(map_kset);
+ map_kset = NULL;
return ERR_PTR(ret);
}
@@ -195,8 +197,6 @@ out_add_entry:
entry = *(map_entries + j);
kobject_put(&entry->kobj);
}
- if (map_kset)
- kset_unregister(map_kset);
out:
return ret;
}
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c
index 071c2c969eec..72791232e46b 100644
--- a/drivers/firmware/iscsi_ibft.c
+++ b/drivers/firmware/iscsi_ibft.c
@@ -186,8 +186,20 @@ struct ibft_kobject {
static struct iscsi_boot_kset *boot_kset;
+/* fully null address */
static const char nulls[16];
+/* IPv4-mapped IPv6 ::ffff:0.0.0.0 */
+static const char mapped_nulls[16] = { 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00 };
+
+static int address_not_null(u8 *ip)
+{
+ return (memcmp(ip, nulls, 16) && memcmp(ip, mapped_nulls, 16));
+}
+
/*
* Helper functions to parse data properly.
*/
@@ -445,7 +457,7 @@ static umode_t ibft_check_nic_for(void *data, int type)
rc = S_IRUGO;
break;
case ISCSI_BOOT_ETH_IP_ADDR:
- if (memcmp(nic->ip_addr, nulls, sizeof(nic->ip_addr)))
+ if (address_not_null(nic->ip_addr))
rc = S_IRUGO;
break;
case ISCSI_BOOT_ETH_SUBNET_MASK:
@@ -456,21 +468,19 @@ static umode_t ibft_check_nic_for(void *data, int type)
rc = S_IRUGO;
break;
case ISCSI_BOOT_ETH_GATEWAY:
- if (memcmp(nic->gateway, nulls, sizeof(nic->gateway)))
+ if (address_not_null(nic->gateway))
rc = S_IRUGO;
break;
case ISCSI_BOOT_ETH_PRIMARY_DNS:
- if (memcmp(nic->primary_dns, nulls,
- sizeof(nic->primary_dns)))
+ if (address_not_null(nic->primary_dns))
rc = S_IRUGO;
break;
case ISCSI_BOOT_ETH_SECONDARY_DNS:
- if (memcmp(nic->secondary_dns, nulls,
- sizeof(nic->secondary_dns)))
+ if (address_not_null(nic->secondary_dns))
rc = S_IRUGO;
break;
case ISCSI_BOOT_ETH_DHCP:
- if (memcmp(nic->dhcp, nulls, sizeof(nic->dhcp)))
+ if (address_not_null(nic->dhcp))
rc = S_IRUGO;
break;
case ISCSI_BOOT_ETH_VLAN:
@@ -536,23 +546,19 @@ static umode_t __init ibft_check_initiator_for(void *data, int type)
rc = S_IRUGO;
break;
case ISCSI_BOOT_INI_ISNS_SERVER:
- if (memcmp(init->isns_server, nulls,
- sizeof(init->isns_server)))
+ if (address_not_null(init->isns_server))
rc = S_IRUGO;
break;
case ISCSI_BOOT_INI_SLP_SERVER:
- if (memcmp(init->slp_server, nulls,
- sizeof(init->slp_server)))
+ if (address_not_null(init->slp_server))
rc = S_IRUGO;
break;
case ISCSI_BOOT_INI_PRI_RADIUS_SERVER:
- if (memcmp(init->pri_radius_server, nulls,
- sizeof(init->pri_radius_server)))
+ if (address_not_null(init->pri_radius_server))
rc = S_IRUGO;
break;
case ISCSI_BOOT_INI_SEC_RADIUS_SERVER:
- if (memcmp(init->sec_radius_server, nulls,
- sizeof(init->sec_radius_server)))
+ if (address_not_null(init->sec_radius_server))
rc = S_IRUGO;
break;
case ISCSI_BOOT_INI_INITIATOR_NAME:
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index caefe806db5e..8f1fe739c985 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -126,6 +126,14 @@ config GPIO_BCM_KONA
help
Turn on GPIO support for Broadcom "Kona" chips.
+config GPIO_BRCMSTB
+ tristate "BRCMSTB GPIO support"
+ default y if ARCH_BRCMSTB
+ depends on OF_GPIO && (ARCH_BRCMSTB || COMPILE_TEST)
+ select GPIO_GENERIC
+ help
+ Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs.
+
config GPIO_CLPS711X
tristate "CLPS711X GPIO support"
depends on ARCH_CLPS711X || COMPILE_TEST
@@ -159,6 +167,14 @@ config GPIO_EP93XX
depends on ARCH_EP93XX
select GPIO_GENERIC
+config GPIO_ETRAXFS
+ bool "Axis ETRAX FS General I/O"
+ depends on CRIS || COMPILE_TEST
+ depends on OF
+ select GPIO_GENERIC
+ help
+ Say yes here to support the GPIO controller on Axis ETRAX FS SoCs.
+
config GPIO_F7188X
tristate "F71869, F71869A, F71882FG and F71889F GPIO support"
depends on X86
@@ -230,6 +246,14 @@ config GPIO_LOONGSON
help
driver for GPIO functionality on Loongson-2F/3A/3B processors.
+config GPIO_LPC18XX
+ bool "NXP LPC18XX/43XX GPIO support"
+ default y if ARCH_LPC18XX
+ depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
+ help
+ Select this option to enable GPIO driver for
+ NXP LPC18XX/43XX devices.
+
config GPIO_LYNXPOINT
tristate "Intel Lynxpoint GPIO support"
depends on ACPI && X86
@@ -308,7 +332,7 @@ config GPIO_OCTEON
family of SOCs.
config GPIO_OMAP
- bool "TI OMAP GPIO support" if COMPILE_TEST && !ARCH_OMAP2PLUS
+ tristate "TI OMAP GPIO support" if ARCH_OMAP2PLUS || COMPILE_TEST
default y if ARCH_OMAP
depends on ARM
select GENERIC_IRQ_CHIP
@@ -488,6 +512,17 @@ config GPIO_XILINX
help
Say yes here to support the Xilinx FPGA GPIO device
+config GPIO_XLP
+ tristate "Netlogic XLP GPIO support"
+ depends on CPU_XLP
+ select GPIOLIB_IRQCHIP
+ help
+ This driver provides support for GPIO interface on Netlogic XLP MIPS64
+ SoCs. Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX,
+ XLP9XX and XLP5XX.
+
+ If unsure, say N.
+
config GPIO_XTENSA
bool "Xtensa GPIO32 support"
depends on XTENSA
@@ -505,7 +540,7 @@ config GPIO_ZEVIO
config GPIO_ZYNQ
tristate "Xilinx Zynq GPIO support"
- depends on ARCH_ZYNQ
+ depends on ARCH_ZYNQ || ARCH_ZYNQMP
select GPIOLIB_IRQCHIP
help
Say yes here to support Xilinx Zynq GPIO controller.
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index f71bb971329c..f82cd678ce08 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
+obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
@@ -32,6 +33,7 @@ obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o
obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o
obj-$(CONFIG_GPIO_EM) += gpio-em.o
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
+obj-$(CONFIG_GPIO_ETRAXFS) += gpio-etraxfs.o
obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
@@ -44,6 +46,7 @@ obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o
obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o
obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o
obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o
+obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o
obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o
obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
@@ -109,6 +112,7 @@ obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o
obj-$(CONFIG_GPIO_XGENE) += gpio-xgene.o
obj-$(CONFIG_GPIO_XGENE_SB) += gpio-xgene-sb.o
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
+obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c
index 449fb46cb8a0..0f3d336d6303 100644
--- a/drivers/gpio/gpio-altera.c
+++ b/drivers/gpio/gpio-altera.c
@@ -107,7 +107,8 @@ static int altera_gpio_irq_set_type(struct irq_data *d,
return -EINVAL;
}
-static unsigned int altera_gpio_irq_startup(struct irq_data *d) {
+static unsigned int altera_gpio_irq_startup(struct irq_data *d)
+{
altera_gpio_irq_unmask(d);
return 0;
diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c
index b164ce837b43..a6e79225886d 100644
--- a/drivers/gpio/gpio-bcm-kona.c
+++ b/drivers/gpio/gpio-bcm-kona.c
@@ -122,6 +122,16 @@ static void bcm_kona_gpio_unlock_gpio(struct bcm_kona_gpio *kona_gpio,
spin_unlock_irqrestore(&kona_gpio->lock, flags);
}
+static int bcm_kona_gpio_get_dir(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcm_kona_gpio *kona_gpio = to_kona_gpio(chip);
+ void __iomem *reg_base = kona_gpio->reg_base;
+ u32 val;
+
+ val = readl(reg_base + GPIO_CONTROL(gpio)) & GPIO_GPCTR0_IOTR_MASK;
+ return val ? GPIOF_DIR_IN : GPIOF_DIR_OUT;
+}
+
static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
{
struct bcm_kona_gpio *kona_gpio;
@@ -135,12 +145,8 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
reg_base = kona_gpio->reg_base;
spin_lock_irqsave(&kona_gpio->lock, flags);
- /* determine the GPIO pin direction */
- val = readl(reg_base + GPIO_CONTROL(gpio));
- val &= GPIO_GPCTR0_IOTR_MASK;
-
/* this function only applies to output pin */
- if (GPIO_GPCTR0_IOTR_CMD_INPUT == val)
+ if (bcm_kona_gpio_get_dir(chip, gpio) == GPIOF_DIR_IN)
goto out;
reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id);
@@ -166,13 +172,12 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio)
reg_base = kona_gpio->reg_base;
spin_lock_irqsave(&kona_gpio->lock, flags);
- /* determine the GPIO pin direction */
- val = readl(reg_base + GPIO_CONTROL(gpio));
- val &= GPIO_GPCTR0_IOTR_MASK;
+ if (bcm_kona_gpio_get_dir(chip, gpio) == GPIOF_DIR_IN)
+ reg_offset = GPIO_IN_STATUS(bank_id);
+ else
+ reg_offset = GPIO_OUT_STATUS(bank_id);
/* read the GPIO bank status */
- reg_offset = (GPIO_GPCTR0_IOTR_CMD_INPUT == val) ?
- GPIO_IN_STATUS(bank_id) : GPIO_OUT_STATUS(bank_id);
val = readl(reg_base + reg_offset);
spin_unlock_irqrestore(&kona_gpio->lock, flags);
@@ -310,6 +315,7 @@ static struct gpio_chip template_chip = {
.owner = THIS_MODULE,
.request = bcm_kona_gpio_request,
.free = bcm_kona_gpio_free,
+ .get_direction = bcm_kona_gpio_get_dir,
.direction_input = bcm_kona_gpio_direction_input,
.get = bcm_kona_gpio_get,
.direction_output = bcm_kona_gpio_direction_output,
diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
new file mode 100644
index 000000000000..7a3cb1fa0a76
--- /dev/null
+++ b/drivers/gpio/gpio-brcmstb.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/module.h>
+#include <linux/basic_mmio_gpio.h>
+
+#define GIO_BANK_SIZE 0x20
+#define GIO_ODEN(bank) (((bank) * GIO_BANK_SIZE) + 0x00)
+#define GIO_DATA(bank) (((bank) * GIO_BANK_SIZE) + 0x04)
+#define GIO_IODIR(bank) (((bank) * GIO_BANK_SIZE) + 0x08)
+#define GIO_EC(bank) (((bank) * GIO_BANK_SIZE) + 0x0c)
+#define GIO_EI(bank) (((bank) * GIO_BANK_SIZE) + 0x10)
+#define GIO_MASK(bank) (((bank) * GIO_BANK_SIZE) + 0x14)
+#define GIO_LEVEL(bank) (((bank) * GIO_BANK_SIZE) + 0x18)
+#define GIO_STAT(bank) (((bank) * GIO_BANK_SIZE) + 0x1c)
+
+struct brcmstb_gpio_bank {
+ struct list_head node;
+ int id;
+ struct bgpio_chip bgc;
+ struct brcmstb_gpio_priv *parent_priv;
+ u32 width;
+};
+
+struct brcmstb_gpio_priv {
+ struct list_head bank_list;
+ void __iomem *reg_base;
+ int num_banks;
+ struct platform_device *pdev;
+ int gpio_base;
+};
+
+#define MAX_GPIO_PER_BANK 32
+#define GPIO_BANK(gpio) ((gpio) >> 5)
+/* assumes MAX_GPIO_PER_BANK is a multiple of 2 */
+#define GPIO_BIT(gpio) ((gpio) & (MAX_GPIO_PER_BANK - 1))
+
+static inline struct brcmstb_gpio_bank *
+brcmstb_gpio_gc_to_bank(struct gpio_chip *gc)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ return container_of(bgc, struct brcmstb_gpio_bank, bgc);
+}
+
+static inline struct brcmstb_gpio_priv *
+brcmstb_gpio_gc_to_priv(struct gpio_chip *gc)
+{
+ struct brcmstb_gpio_bank *bank = brcmstb_gpio_gc_to_bank(gc);
+ return bank->parent_priv;
+}
+
+/* Make sure that the number of banks matches up between properties */
+static int brcmstb_gpio_sanity_check_banks(struct device *dev,
+ struct device_node *np, struct resource *res)
+{
+ int res_num_banks = resource_size(res) / GIO_BANK_SIZE;
+ int num_banks =
+ of_property_count_u32_elems(np, "brcm,gpio-bank-widths");
+
+ if (res_num_banks != num_banks) {
+ dev_err(dev, "Mismatch in banks: res had %d, bank-widths had %d\n",
+ res_num_banks, num_banks);
+ return -EINVAL;
+ } else {
+ return 0;
+ }
+}
+
+static int brcmstb_gpio_remove(struct platform_device *pdev)
+{
+ struct brcmstb_gpio_priv *priv = platform_get_drvdata(pdev);
+ struct list_head *pos;
+ struct brcmstb_gpio_bank *bank;
+ int ret = 0;
+
+ list_for_each(pos, &priv->bank_list) {
+ bank = list_entry(pos, struct brcmstb_gpio_bank, node);
+ ret = bgpio_remove(&bank->bgc);
+ if (ret)
+ dev_err(&pdev->dev, "gpiochip_remove fail in cleanup");
+ }
+ return ret;
+}
+
+static int brcmstb_gpio_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec, u32 *flags)
+{
+ struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc);
+ struct brcmstb_gpio_bank *bank = brcmstb_gpio_gc_to_bank(gc);
+ int offset;
+
+ if (gc->of_gpio_n_cells != 2) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+ return -EINVAL;
+
+ offset = gpiospec->args[0] - (gc->base - priv->gpio_base);
+ if (offset >= gc->ngpio)
+ return -EINVAL;
+
+ if (unlikely(offset >= bank->width)) {
+ dev_warn_ratelimited(&priv->pdev->dev,
+ "Received request for invalid GPIO offset %d\n",
+ gpiospec->args[0]);
+ }
+
+ if (flags)
+ *flags = gpiospec->args[1];
+
+ return offset;
+}
+
+static int brcmstb_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ void __iomem *reg_base;
+ struct brcmstb_gpio_priv *priv;
+ struct resource *res;
+ struct property *prop;
+ const __be32 *p;
+ u32 bank_width;
+ int err;
+ static int gpio_base;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(reg_base))
+ return PTR_ERR(reg_base);
+
+ priv->gpio_base = gpio_base;
+ priv->reg_base = reg_base;
+ priv->pdev = pdev;
+
+ INIT_LIST_HEAD(&priv->bank_list);
+ if (brcmstb_gpio_sanity_check_banks(dev, np, res))
+ return -EINVAL;
+
+ of_property_for_each_u32(np, "brcm,gpio-bank-widths", prop, p,
+ bank_width) {
+ struct brcmstb_gpio_bank *bank;
+ struct bgpio_chip *bgc;
+ struct gpio_chip *gc;
+
+ bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
+ if (!bank) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ bank->parent_priv = priv;
+ bank->id = priv->num_banks;
+ if (bank_width <= 0 || bank_width > MAX_GPIO_PER_BANK) {
+ dev_err(dev, "Invalid bank width %d\n", bank_width);
+ goto fail;
+ } else {
+ bank->width = bank_width;
+ }
+
+ /*
+ * Regs are 4 bytes wide, have data reg, no set/clear regs,
+ * and direction bits have 0 = output and 1 = input
+ */
+ bgc = &bank->bgc;
+ err = bgpio_init(bgc, dev, 4,
+ reg_base + GIO_DATA(bank->id),
+ NULL, NULL, NULL,
+ reg_base + GIO_IODIR(bank->id), 0);
+ if (err) {
+ dev_err(dev, "bgpio_init() failed\n");
+ goto fail;
+ }
+
+ gc = &bgc->gc;
+ gc->of_node = np;
+ gc->owner = THIS_MODULE;
+ gc->label = np->full_name;
+ gc->base = gpio_base;
+ gc->of_gpio_n_cells = 2;
+ gc->of_xlate = brcmstb_gpio_of_xlate;
+ /* not all ngpio lines are valid, will use bank width later */
+ gc->ngpio = MAX_GPIO_PER_BANK;
+
+ err = gpiochip_add(gc);
+ if (err) {
+ dev_err(dev, "Could not add gpiochip for bank %d\n",
+ bank->id);
+ goto fail;
+ }
+ gpio_base += gc->ngpio;
+ dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id,
+ gc->base, gc->ngpio, bank->width);
+
+ /* Everything looks good, so add bank to list */
+ list_add(&bank->node, &priv->bank_list);
+
+ priv->num_banks++;
+ }
+
+ dev_info(dev, "Registered %d banks (GPIO(s): %d-%d)\n",
+ priv->num_banks, priv->gpio_base, gpio_base - 1);
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+
+fail:
+ (void) brcmstb_gpio_remove(pdev);
+ return err;
+}
+
+static const struct of_device_id brcmstb_gpio_of_match[] = {
+ { .compatible = "brcm,brcmstb-gpio" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, brcmstb_gpio_of_match);
+
+static struct platform_driver brcmstb_gpio_driver = {
+ .driver = {
+ .name = "brcmstb-gpio",
+ .of_match_table = brcmstb_gpio_of_match,
+ },
+ .probe = brcmstb_gpio_probe,
+ .remove = brcmstb_gpio_remove,
+};
+module_platform_driver(brcmstb_gpio_driver);
+
+MODULE_AUTHOR("Gregory Fong");
+MODULE_DESCRIPTION("Driver for Broadcom BRCMSTB SoC UPG GPIO");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c
index 91a7ffe83135..fddd204dc9b6 100644
--- a/drivers/gpio/gpio-crystalcove.c
+++ b/drivers/gpio/gpio-crystalcove.c
@@ -16,6 +16,7 @@
*/
#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/seq_file.h>
@@ -94,9 +95,8 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type)
{
int reg;
- if (gpio == 94) {
+ if (gpio == 94)
return GPIOPANELCTL;
- }
if (reg_type == CTRL_IN) {
if (gpio < 8)
@@ -255,6 +255,7 @@ static struct irq_chip crystalcove_irqchip = {
.irq_set_type = crystalcove_irq_type,
.irq_bus_lock = crystalcove_bus_lock,
.irq_bus_sync_unlock = crystalcove_bus_sync_unlock,
+ .flags = IRQCHIP_SKIP_SET_WAKE,
};
static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data)
diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c
index dbdb4de82c6d..6685712c15cf 100644
--- a/drivers/gpio/gpio-dln2.c
+++ b/drivers/gpio/gpio-dln2.c
@@ -466,7 +466,6 @@ static int dln2_gpio_probe(struct platform_device *pdev)
dln2->gpio.owner = THIS_MODULE;
dln2->gpio.base = -1;
dln2->gpio.ngpio = pins;
- dln2->gpio.exported = true;
dln2->gpio.can_sleep = true;
dln2->gpio.irq_not_threaded = true;
dln2->gpio.set = dln2_gpio_set;
diff --git a/drivers/gpio/gpio-etraxfs.c b/drivers/gpio/gpio-etraxfs.c
new file mode 100644
index 000000000000..28071f4a5672
--- /dev/null
+++ b/drivers/gpio/gpio-etraxfs.c
@@ -0,0 +1,176 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/basic_mmio_gpio.h>
+
+#define ETRAX_FS_rw_pa_dout 0
+#define ETRAX_FS_r_pa_din 4
+#define ETRAX_FS_rw_pa_oe 8
+#define ETRAX_FS_rw_intr_cfg 12
+#define ETRAX_FS_rw_intr_mask 16
+#define ETRAX_FS_rw_ack_intr 20
+#define ETRAX_FS_r_intr 24
+#define ETRAX_FS_rw_pb_dout 32
+#define ETRAX_FS_r_pb_din 36
+#define ETRAX_FS_rw_pb_oe 40
+#define ETRAX_FS_rw_pc_dout 48
+#define ETRAX_FS_r_pc_din 52
+#define ETRAX_FS_rw_pc_oe 56
+#define ETRAX_FS_rw_pd_dout 64
+#define ETRAX_FS_r_pd_din 68
+#define ETRAX_FS_rw_pd_oe 72
+#define ETRAX_FS_rw_pe_dout 80
+#define ETRAX_FS_r_pe_din 84
+#define ETRAX_FS_rw_pe_oe 88
+
+struct etraxfs_gpio_port {
+ const char *label;
+ unsigned int oe;
+ unsigned int dout;
+ unsigned int din;
+ unsigned int ngpio;
+};
+
+struct etraxfs_gpio_info {
+ unsigned int num_ports;
+ const struct etraxfs_gpio_port *ports;
+};
+
+static const struct etraxfs_gpio_port etraxfs_gpio_etraxfs_ports[] = {
+ {
+ .label = "A",
+ .ngpio = 8,
+ .oe = ETRAX_FS_rw_pa_oe,
+ .dout = ETRAX_FS_rw_pa_dout,
+ .din = ETRAX_FS_r_pa_din,
+ },
+ {
+ .label = "B",
+ .ngpio = 18,
+ .oe = ETRAX_FS_rw_pb_oe,
+ .dout = ETRAX_FS_rw_pb_dout,
+ .din = ETRAX_FS_r_pb_din,
+ },
+ {
+ .label = "C",
+ .ngpio = 18,
+ .oe = ETRAX_FS_rw_pc_oe,
+ .dout = ETRAX_FS_rw_pc_dout,
+ .din = ETRAX_FS_r_pc_din,
+ },
+ {
+ .label = "D",
+ .ngpio = 18,
+ .oe = ETRAX_FS_rw_pd_oe,
+ .dout = ETRAX_FS_rw_pd_dout,
+ .din = ETRAX_FS_r_pd_din,
+ },
+ {
+ .label = "E",
+ .ngpio = 18,
+ .oe = ETRAX_FS_rw_pe_oe,
+ .dout = ETRAX_FS_rw_pe_dout,
+ .din = ETRAX_FS_r_pe_din,
+ },
+};
+
+static const struct etraxfs_gpio_info etraxfs_gpio_etraxfs = {
+ .num_ports = ARRAY_SIZE(etraxfs_gpio_etraxfs_ports),
+ .ports = etraxfs_gpio_etraxfs_ports,
+};
+
+static int etraxfs_gpio_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec,
+ u32 *flags)
+{
+ /*
+ * Port numbers are A to E, and the properties are integers, so we
+ * specify them as 0xA - 0xE.
+ */
+ if (gc->label[0] - 'A' + 0xA != gpiospec->args[2])
+ return -EINVAL;
+
+ return of_gpio_simple_xlate(gc, gpiospec, flags);
+}
+
+static const struct of_device_id etraxfs_gpio_of_table[] = {
+ {
+ .compatible = "axis,etraxfs-gio",
+ .data = &etraxfs_gpio_etraxfs,
+ },
+ {},
+};
+
+static int etraxfs_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct etraxfs_gpio_info *info;
+ const struct of_device_id *match;
+ struct bgpio_chip *chips;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+ int i;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (!regs)
+ return -ENOMEM;
+
+ match = of_match_node(etraxfs_gpio_of_table, dev->of_node);
+ if (!match)
+ return -EINVAL;
+
+ info = match->data;
+
+ chips = devm_kzalloc(dev, sizeof(*chips) * info->num_ports, GFP_KERNEL);
+ if (!chips)
+ return -ENOMEM;
+
+ for (i = 0; i < info->num_ports; i++) {
+ struct bgpio_chip *bgc = &chips[i];
+ const struct etraxfs_gpio_port *port = &info->ports[i];
+
+ ret = bgpio_init(bgc, dev, 4,
+ regs + port->din, /* dat */
+ regs + port->dout, /* set */
+ NULL, /* clr */
+ regs + port->oe, /* dirout */
+ NULL, /* dirin */
+ BGPIOF_UNREADABLE_REG_SET);
+ if (ret)
+ return ret;
+
+ bgc->gc.ngpio = port->ngpio;
+ bgc->gc.label = port->label;
+
+ bgc->gc.of_node = dev->of_node;
+ bgc->gc.of_gpio_n_cells = 3;
+ bgc->gc.of_xlate = etraxfs_gpio_of_xlate;
+
+ ret = gpiochip_add(&bgc->gc);
+ if (ret)
+ dev_err(dev, "Unable to register port %s\n",
+ bgc->gc.label);
+ }
+
+ return 0;
+}
+
+static struct platform_driver etraxfs_gpio_driver = {
+ .driver = {
+ .name = "etraxfs-gpio",
+ .of_match_table = of_match_ptr(etraxfs_gpio_of_table),
+ },
+ .probe = etraxfs_gpio_probe,
+};
+
+static int __init etraxfs_gpio_init(void)
+{
+ return platform_driver_register(&etraxfs_gpio_driver);
+}
+
+device_initcall(etraxfs_gpio_init);
diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c
index dbda8433c4f7..5e3c4fa67d82 100644
--- a/drivers/gpio/gpio-f7188x.c
+++ b/drivers/gpio/gpio-f7188x.c
@@ -172,7 +172,7 @@ static struct f7188x_gpio_bank f71869a_gpio_bank[] = {
};
static struct f7188x_gpio_bank f71882_gpio_bank[] = {
- F7188X_GPIO_BANK(0 , 8, 0xF0),
+ F7188X_GPIO_BANK(0, 8, 0xF0),
F7188X_GPIO_BANK(10, 8, 0xE0),
F7188X_GPIO_BANK(20, 8, 0xD0),
F7188X_GPIO_BANK(30, 4, 0xC0),
@@ -180,7 +180,7 @@ static struct f7188x_gpio_bank f71882_gpio_bank[] = {
};
static struct f7188x_gpio_bank f71889_gpio_bank[] = {
- F7188X_GPIO_BANK(0 , 7, 0xF0),
+ F7188X_GPIO_BANK(0, 7, 0xF0),
F7188X_GPIO_BANK(10, 7, 0xE0),
F7188X_GPIO_BANK(20, 8, 0xD0),
F7188X_GPIO_BANK(30, 8, 0xC0),
diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
index b92a690f5765..9bda3727fac1 100644
--- a/drivers/gpio/gpio-generic.c
+++ b/drivers/gpio/gpio-generic.c
@@ -135,6 +135,17 @@ static unsigned long bgpio_pin2mask_be(struct bgpio_chip *bgc,
return 1 << (bgc->bits - 1 - pin);
}
+static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long pinmask = bgc->pin2mask(bgc, gpio);
+
+ if (bgc->dir & pinmask)
+ return bgc->read_reg(bgc->reg_set) & pinmask;
+ else
+ return bgc->read_reg(bgc->reg_dat) & pinmask;
+}
+
static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
@@ -416,7 +427,8 @@ static int bgpio_setup_accessors(struct device *dev,
static int bgpio_setup_io(struct bgpio_chip *bgc,
void __iomem *dat,
void __iomem *set,
- void __iomem *clr)
+ void __iomem *clr,
+ unsigned long flags)
{
bgc->reg_dat = dat;
@@ -437,7 +449,11 @@ static int bgpio_setup_io(struct bgpio_chip *bgc,
bgc->gc.set_multiple = bgpio_set_multiple;
}
- bgc->gc.get = bgpio_get;
+ if (!(flags & BGPIOF_UNREADABLE_REG_SET) &&
+ (flags & BGPIOF_READ_OUTPUT_REG_SET))
+ bgc->gc.get = bgpio_get_set;
+ else
+ bgc->gc.get = bgpio_get;
return 0;
}
@@ -500,7 +516,7 @@ int bgpio_init(struct bgpio_chip *bgc, struct device *dev,
bgc->gc.ngpio = bgc->bits;
bgc->gc.request = bgpio_request;
- ret = bgpio_setup_io(bgc, dat, set, clr);
+ ret = bgpio_setup_io(bgc, dat, set, clr, flags);
if (ret)
return ret;
diff --git a/drivers/gpio/gpio-it8761e.c b/drivers/gpio/gpio-it8761e.c
index dadfc245cf09..30a8f24c92c5 100644
--- a/drivers/gpio/gpio-it8761e.c
+++ b/drivers/gpio/gpio-it8761e.c
@@ -123,7 +123,7 @@ static void it8761e_gpio_set(struct gpio_chip *gc,
curr_vals = inb(reg);
if (val)
- outb(curr_vals | (1 << bit) , reg);
+ outb(curr_vals | (1 << bit), reg);
else
outb(curr_vals & ~(1 << bit), reg);
diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c
index 6b8115f34208..83f281dda1e0 100644
--- a/drivers/gpio/gpio-kempld.c
+++ b/drivers/gpio/gpio-kempld.c
@@ -117,7 +117,7 @@ static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
= container_of(chip, struct kempld_gpio_data, chip);
struct kempld_device_data *pld = gpio->pld;
- return kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset);
+ return !kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset);
}
static int kempld_gpio_pincount(struct kempld_device_data *pld)
diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c
new file mode 100644
index 000000000000..eb68603136b0
--- /dev/null
+++ b/drivers/gpio/gpio-lpc18xx.c
@@ -0,0 +1,180 @@
+/*
+ * GPIO driver for NXP LPC18xx/43xx.
+ *
+ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/driver.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+
+/* LPC18xx GPIO register offsets */
+#define LPC18XX_REG_DIR(n) (0x2000 + n * sizeof(u32))
+
+#define LPC18XX_MAX_PORTS 8
+#define LPC18XX_PINS_PER_PORT 32
+
+struct lpc18xx_gpio_chip {
+ struct gpio_chip gpio;
+ void __iomem *base;
+ struct clk *clk;
+ spinlock_t lock;
+};
+
+static inline struct lpc18xx_gpio_chip *to_lpc18xx_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct lpc18xx_gpio_chip, gpio);
+}
+
+static int lpc18xx_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(offset);
+}
+
+static void lpc18xx_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(offset);
+}
+
+static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct lpc18xx_gpio_chip *gc = to_lpc18xx_gpio(chip);
+ writeb(value ? 1 : 0, gc->base + offset);
+}
+
+static int lpc18xx_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct lpc18xx_gpio_chip *gc = to_lpc18xx_gpio(chip);
+ return !!readb(gc->base + offset);
+}
+
+static int lpc18xx_gpio_direction(struct gpio_chip *chip, unsigned offset,
+ bool out)
+{
+ struct lpc18xx_gpio_chip *gc = to_lpc18xx_gpio(chip);
+ unsigned long flags;
+ u32 port, pin, dir;
+
+ port = offset / LPC18XX_PINS_PER_PORT;
+ pin = offset % LPC18XX_PINS_PER_PORT;
+
+ spin_lock_irqsave(&gc->lock, flags);
+ dir = readl(gc->base + LPC18XX_REG_DIR(port));
+ if (out)
+ dir |= BIT(pin);
+ else
+ dir &= ~BIT(pin);
+ writel(dir, gc->base + LPC18XX_REG_DIR(port));
+ spin_unlock_irqrestore(&gc->lock, flags);
+
+ return 0;
+}
+
+static int lpc18xx_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ return lpc18xx_gpio_direction(chip, offset, false);
+}
+
+static int lpc18xx_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ lpc18xx_gpio_set(chip, offset, value);
+ return lpc18xx_gpio_direction(chip, offset, true);
+}
+
+static struct gpio_chip lpc18xx_chip = {
+ .label = "lpc18xx/43xx-gpio",
+ .request = lpc18xx_gpio_request,
+ .free = lpc18xx_gpio_free,
+ .direction_input = lpc18xx_gpio_direction_input,
+ .direction_output = lpc18xx_gpio_direction_output,
+ .set = lpc18xx_gpio_set,
+ .get = lpc18xx_gpio_get,
+ .ngpio = LPC18XX_MAX_PORTS * LPC18XX_PINS_PER_PORT,
+ .owner = THIS_MODULE,
+};
+
+static int lpc18xx_gpio_probe(struct platform_device *pdev)
+{
+ struct lpc18xx_gpio_chip *gc;
+ struct resource *res;
+ int ret;
+
+ gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
+ if (!gc)
+ return -ENOMEM;
+
+ gc->gpio = lpc18xx_chip;
+ platform_set_drvdata(pdev, gc);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gc->base))
+ return PTR_ERR(gc->base);
+
+ gc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(gc->clk)) {
+ dev_err(&pdev->dev, "input clock not found\n");
+ return PTR_ERR(gc->clk);
+ }
+
+ ret = clk_prepare_enable(gc->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable clock\n");
+ return ret;
+ }
+
+ spin_lock_init(&gc->lock);
+
+ gc->gpio.dev = &pdev->dev;
+
+ ret = gpiochip_add(&gc->gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add gpio chip\n");
+ clk_disable_unprepare(gc->clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lpc18xx_gpio_remove(struct platform_device *pdev)
+{
+ struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&gc->gpio);
+ clk_disable_unprepare(gc->clk);
+
+ return 0;
+}
+
+static const struct of_device_id lpc18xx_gpio_match[] = {
+ { .compatible = "nxp,lpc1850-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lpc18xx_gpio_match);
+
+static struct platform_driver lpc18xx_gpio_driver = {
+ .probe = lpc18xx_gpio_probe,
+ .remove = lpc18xx_gpio_remove,
+ .driver = {
+ .name = "lpc18xx-gpio",
+ .of_match_table = lpc18xx_gpio_match,
+ },
+};
+module_platform_driver(lpc18xx_gpio_driver);
+
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c
index 127c755b38dc..153af464c7a7 100644
--- a/drivers/gpio/gpio-lynxpoint.c
+++ b/drivers/gpio/gpio-lynxpoint.c
@@ -72,7 +72,7 @@ struct lp_gpio {
*
* per gpio specific registers consist of two 32bit registers per gpio
* (LP_CONFIG1 and LP_CONFIG2), with 94 gpios there's a total of
- * 188 config registes.
+ * 188 config registers.
*
* A simplified view of the register layout look like this:
*
diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c
index 0fa4543c5e02..aed4ca9338bc 100644
--- a/drivers/gpio/gpio-max732x.c
+++ b/drivers/gpio/gpio-max732x.c
@@ -429,6 +429,14 @@ static int max732x_irq_set_type(struct irq_data *d, unsigned int type)
return 0;
}
+static int max732x_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ struct max732x_chip *chip = irq_data_get_irq_chip_data(data);
+
+ irq_set_irq_wake(chip->client->irq, on);
+ return 0;
+}
+
static struct irq_chip max732x_irq_chip = {
.name = "max732x",
.irq_mask = max732x_irq_mask,
@@ -436,6 +444,7 @@ static struct irq_chip max732x_irq_chip = {
.irq_bus_lock = max732x_irq_bus_lock,
.irq_bus_sync_unlock = max732x_irq_bus_sync_unlock,
.irq_set_type = max732x_irq_set_type,
+ .irq_set_wake = max732x_irq_set_wake,
};
static uint8_t max732x_irq_pending(struct max732x_chip *chip)
@@ -507,12 +516,10 @@ static int max732x_irq_setup(struct max732x_chip *chip,
chip->irq_features = has_irq;
mutex_init(&chip->irq_lock);
- ret = devm_request_threaded_irq(&client->dev,
- client->irq,
- NULL,
- max732x_irq_handler,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- dev_name(&client->dev), chip);
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, max732x_irq_handler, IRQF_ONESHOT |
+ IRQF_TRIGGER_FALLING | IRQF_SHARED,
+ dev_name(&client->dev), chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
client->irq);
@@ -521,7 +528,7 @@ static int max732x_irq_setup(struct max732x_chip *chip,
ret = gpiochip_irqchip_add(&chip->gpio_chip,
&max732x_irq_chip,
irq_base,
- handle_edge_irq,
+ handle_simple_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_err(&client->dev,
diff --git a/drivers/gpio/gpio-moxart.c b/drivers/gpio/gpio-moxart.c
index c3ab46e595da..abd8676ce2b6 100644
--- a/drivers/gpio/gpio-moxart.c
+++ b/drivers/gpio/gpio-moxart.c
@@ -39,17 +39,6 @@ static void moxart_gpio_free(struct gpio_chip *chip, unsigned offset)
pinctrl_free_gpio(offset);
}
-static int moxart_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct bgpio_chip *bgc = to_bgpio_chip(chip);
- u32 ret = bgc->read_reg(bgc->reg_dir);
-
- if (ret & BIT(offset))
- return !!(bgc->read_reg(bgc->reg_set) & BIT(offset));
- else
- return !!(bgc->read_reg(bgc->reg_dat) & BIT(offset));
-}
-
static int moxart_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -68,8 +57,9 @@ static int moxart_gpio_probe(struct platform_device *pdev)
return PTR_ERR(base);
ret = bgpio_init(bgc, dev, 4, base + GPIO_DATA_IN,
- base + GPIO_DATA_OUT, NULL,
- base + GPIO_PIN_DIRECTION, NULL, 0);
+ base + GPIO_DATA_OUT, NULL,
+ base + GPIO_PIN_DIRECTION, NULL,
+ BGPIOF_READ_OUTPUT_REG_SET);
if (ret) {
dev_err(&pdev->dev, "bgpio_init failed\n");
return ret;
@@ -78,7 +68,6 @@ static int moxart_gpio_probe(struct platform_device *pdev)
bgc->gc.label = "moxart-gpio";
bgc->gc.request = moxart_gpio_request;
bgc->gc.free = moxart_gpio_free;
- bgc->gc.get = moxart_gpio_get;
bgc->data = bgc->read_reg(bgc->reg_set);
bgc->gc.base = 0;
bgc->gc.ngpio = 32;
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 9f7446a7ac64..ec1eb1b7250f 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -131,7 +131,7 @@ static struct mxc_gpio_hwdata *mxc_gpio_hwdata;
#define GPIO_INT_FALL_EDGE (mxc_gpio_hwdata->fall_edge)
#define GPIO_INT_BOTH_EDGES 0x4
-static struct platform_device_id mxc_gpio_devtype[] = {
+static const struct platform_device_id mxc_gpio_devtype[] = {
{
.name = "imx1-gpio",
.driver_data = IMX1_GPIO,
@@ -437,20 +437,20 @@ static int mxc_gpio_probe(struct platform_device *pdev)
irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
} else {
/* setup one handler for each entry */
- irq_set_chained_handler(port->irq, mx3_gpio_irq_handler);
- irq_set_handler_data(port->irq, port);
- if (port->irq_high > 0) {
+ irq_set_chained_handler_and_data(port->irq,
+ mx3_gpio_irq_handler, port);
+ if (port->irq_high > 0)
/* setup handler for GPIO 16 to 31 */
- irq_set_chained_handler(port->irq_high,
- mx3_gpio_irq_handler);
- irq_set_handler_data(port->irq_high, port);
- }
+ irq_set_chained_handler_and_data(port->irq_high,
+ mx3_gpio_irq_handler,
+ port);
}
err = bgpio_init(&port->bgc, &pdev->dev, 4,
port->base + GPIO_PSR,
port->base + GPIO_DR, NULL,
- port->base + GPIO_GDIR, NULL, 0);
+ port->base + GPIO_GDIR, NULL,
+ BGPIOF_READ_OUTPUT_REG_SET);
if (err)
goto out_bgio;
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
index 84cbda6acdda..551d15d7c369 100644
--- a/drivers/gpio/gpio-mxs.c
+++ b/drivers/gpio/gpio-mxs.c
@@ -239,7 +239,7 @@ static int mxs_gpio_get_direction(struct gpio_chip *gc, unsigned offset)
return !(dir & mask);
}
-static struct platform_device_id mxs_gpio_ids[] = {
+static const struct platform_device_id mxs_gpio_ids[] = {
{
.name = "imx23-gpio",
.driver_data = IMX23_GPIO,
@@ -320,8 +320,8 @@ static int mxs_gpio_probe(struct platform_device *pdev)
mxs_gpio_init_gc(port, irq_base);
/* setup one handler for each entry */
- irq_set_chained_handler(port->irq, mxs_gpio_irq_handler);
- irq_set_handler_data(port->irq, port);
+ irq_set_chained_handler_and_data(port->irq, mxs_gpio_irq_handler,
+ port);
err = bgpio_init(&port->bgc, &pdev->dev, 4,
port->base + PINCTRL_DIN(port),
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index cd1d5bf48f36..b0c57d505be7 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -488,9 +488,6 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
unsigned long flags;
unsigned offset = d->hwirq;
- if (!BANK_USED(bank))
- pm_runtime_get_sync(bank->dev);
-
if (type & ~IRQ_TYPE_SENSE_MASK)
return -EINVAL;
@@ -498,12 +495,18 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
(type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
return -EINVAL;
+ if (!BANK_USED(bank))
+ pm_runtime_get_sync(bank->dev);
+
spin_lock_irqsave(&bank->lock, flags);
retval = omap_set_gpio_triggering(bank, offset, type);
+ if (retval)
+ goto error;
omap_gpio_init_irq(bank, offset);
if (!omap_gpio_is_input(bank, offset)) {
spin_unlock_irqrestore(&bank->lock, flags);
- return -EINVAL;
+ retval = -EINVAL;
+ goto error;
}
spin_unlock_irqrestore(&bank->lock, flags);
@@ -512,6 +515,11 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
__irq_set_handler_locked(d->irq, handle_edge_irq);
+ return 0;
+
+error:
+ if (!BANK_USED(bank))
+ pm_runtime_put(bank->dev);
return retval;
}
@@ -638,15 +646,6 @@ static int omap_set_gpio_wakeup(struct gpio_bank *bank, unsigned offset,
return 0;
}
-static void omap_reset_gpio(struct gpio_bank *bank, unsigned offset)
-{
- omap_set_gpio_direction(bank, offset, 1);
- omap_set_gpio_irqenable(bank, offset, 0);
- omap_clear_gpio_irqstatus(bank, offset);
- omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
- omap_clear_gpio_debounce(bank, offset);
-}
-
/* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
static int omap_gpio_wake_enable(struct irq_data *d, unsigned int enable)
{
@@ -669,14 +668,7 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
pm_runtime_get_sync(bank->dev);
spin_lock_irqsave(&bank->lock, flags);
- /* Set trigger to none. You need to enable the desired trigger with
- * request_irq() or set_irq_type(). Only do this if the IRQ line has
- * not already been requested.
- */
- if (!LINE_USED(bank->irq_usage, offset)) {
- omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
- omap_enable_gpio_module(bank, offset);
- }
+ omap_enable_gpio_module(bank, offset);
bank->mod_usage |= BIT(offset);
spin_unlock_irqrestore(&bank->lock, flags);
@@ -690,8 +682,11 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
spin_lock_irqsave(&bank->lock, flags);
bank->mod_usage &= ~(BIT(offset));
+ if (!LINE_USED(bank->irq_usage, offset)) {
+ omap_set_gpio_direction(bank, offset, 1);
+ omap_clear_gpio_debounce(bank, offset);
+ }
omap_disable_gpio_module(bank, offset);
- omap_reset_gpio(bank, offset);
spin_unlock_irqrestore(&bank->lock, flags);
/*
@@ -795,11 +790,23 @@ static unsigned int omap_gpio_irq_startup(struct irq_data *d)
pm_runtime_get_sync(bank->dev);
spin_lock_irqsave(&bank->lock, flags);
- omap_gpio_init_irq(bank, offset);
+
+ if (!LINE_USED(bank->mod_usage, offset))
+ omap_set_gpio_direction(bank, offset, 1);
+ else if (!omap_gpio_is_input(bank, offset))
+ goto err;
+ omap_enable_gpio_module(bank, offset);
+ bank->irq_usage |= BIT(offset);
+
spin_unlock_irqrestore(&bank->lock, flags);
omap_gpio_unmask_irq(d);
return 0;
+err:
+ spin_unlock_irqrestore(&bank->lock, flags);
+ if (!BANK_USED(bank))
+ pm_runtime_put(bank->dev);
+ return -EINVAL;
}
static void omap_gpio_irq_shutdown(struct irq_data *d)
@@ -810,8 +817,12 @@ static void omap_gpio_irq_shutdown(struct irq_data *d)
spin_lock_irqsave(&bank->lock, flags);
bank->irq_usage &= ~(BIT(offset));
+ omap_set_gpio_irqenable(bank, offset, 0);
+ omap_clear_gpio_irqstatus(bank, offset);
+ omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
+ if (!LINE_USED(bank->mod_usage, offset))
+ omap_clear_gpio_debounce(bank, offset);
omap_disable_gpio_module(bank, offset);
- omap_reset_gpio(bank, offset);
spin_unlock_irqrestore(&bank->lock, flags);
/*
@@ -1054,38 +1065,8 @@ static void omap_gpio_mod_init(struct gpio_bank *bank)
dev_err(bank->dev, "Could not get gpio dbck\n");
}
-static void
-omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start,
- unsigned int num)
-{
- struct irq_chip_generic *gc;
- struct irq_chip_type *ct;
-
- gc = irq_alloc_generic_chip("MPUIO", 1, irq_start, bank->base,
- handle_simple_irq);
- if (!gc) {
- dev_err(bank->dev, "Memory alloc failed for gc\n");
- return;
- }
-
- ct = gc->chip_types;
-
- /* NOTE: No ack required, reading IRQ status clears it. */
- ct->chip.irq_mask = irq_gc_mask_set_bit;
- ct->chip.irq_unmask = irq_gc_mask_clr_bit;
- ct->chip.irq_set_type = omap_gpio_irq_type;
-
- if (bank->regs->wkup_en)
- ct->chip.irq_set_wake = omap_gpio_wake_enable;
-
- ct->regs.mask = OMAP_MPUIO_GPIO_INT / bank->stride;
- irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
- IRQ_NOREQUEST | IRQ_NOPROBE, 0);
-}
-
static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
{
- int j;
static int gpio;
int irq_base = 0;
int ret;
@@ -1132,6 +1113,15 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
}
#endif
+ /* MPUIO is a bit different, reading IRQ status clears it */
+ if (bank->is_mpuio) {
+ irqc->irq_ack = dummy_irq_chip.irq_ack;
+ irqc->irq_mask = irq_gc_mask_set_bit;
+ irqc->irq_unmask = irq_gc_mask_clr_bit;
+ if (!bank->regs->wkup_en)
+ irqc->irq_set_wake = NULL;
+ }
+
ret = gpiochip_irqchip_add(&bank->chip, irqc,
irq_base, omap_gpio_irq_handler,
IRQ_TYPE_NONE);
@@ -1145,15 +1135,6 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
gpiochip_set_chained_irqchip(&bank->chip, irqc,
bank->irq, omap_gpio_irq_handler);
- for (j = 0; j < bank->width; j++) {
- int irq = irq_find_mapping(bank->chip.irqdomain, j);
- if (bank->is_mpuio) {
- omap_mpuio_alloc_gc(bank, irq, bank->width);
- irq_set_chip_and_handler(irq, NULL, NULL);
- set_irq_flags(irq, 0);
- }
- }
-
return 0;
}
@@ -1263,6 +1244,17 @@ static int omap_gpio_probe(struct platform_device *pdev)
return 0;
}
+static int omap_gpio_remove(struct platform_device *pdev)
+{
+ struct gpio_bank *bank = platform_get_drvdata(pdev);
+
+ list_del(&bank->node);
+ gpiochip_remove(&bank->chip);
+ pm_runtime_disable(bank->dev);
+
+ return 0;
+}
+
#ifdef CONFIG_ARCH_OMAP2PLUS
#if defined(CONFIG_PM)
@@ -1448,6 +1440,7 @@ static int omap_gpio_runtime_resume(struct device *dev)
}
#endif /* CONFIG_PM */
+#if IS_BUILTIN(CONFIG_GPIO_OMAP)
void omap2_gpio_prepare_for_idle(int pwr_mode)
{
struct gpio_bank *bank;
@@ -1473,6 +1466,7 @@ void omap2_gpio_resume_after_idle(void)
pm_runtime_get_sync(bank->dev);
}
}
+#endif
#if defined(CONFIG_PM)
static void omap_gpio_init_context(struct gpio_bank *p)
@@ -1628,6 +1622,7 @@ MODULE_DEVICE_TABLE(of, omap_gpio_match);
static struct platform_driver omap_gpio_driver = {
.probe = omap_gpio_probe,
+ .remove = omap_gpio_remove,
.driver = {
.name = "omap_gpio",
.pm = &gpio_pm_ops,
@@ -1645,3 +1640,13 @@ static int __init omap_gpio_drv_reg(void)
return platform_driver_register(&omap_gpio_driver);
}
postcore_initcall(omap_gpio_drv_reg);
+
+static void __exit omap_gpio_exit(void)
+{
+ platform_driver_unregister(&omap_gpio_driver);
+}
+module_exit(omap_gpio_exit);
+
+MODULE_DESCRIPTION("omap gpio driver");
+MODULE_ALIAS("platform:gpio-omap");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index e2da64abbccd..d233eb3b8132 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -443,12 +443,13 @@ static struct irq_chip pca953x_irq_chip = {
.irq_set_type = pca953x_irq_set_type,
};
-static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
+static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
{
u8 cur_stat[MAX_BANK];
u8 old_stat[MAX_BANK];
- u8 pendings = 0;
- u8 trigger[MAX_BANK], triggers = 0;
+ bool pending_seen = false;
+ bool trigger_seen = false;
+ u8 trigger[MAX_BANK];
int ret, i, offset = 0;
switch (chip->chip_type) {
@@ -461,7 +462,7 @@ static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
}
ret = pca953x_read_regs(chip, offset, cur_stat);
if (ret)
- return 0;
+ return false;
/* Remove output pins from the equation */
for (i = 0; i < NBANK(chip); i++)
@@ -471,11 +472,12 @@ static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
for (i = 0; i < NBANK(chip); i++) {
trigger[i] = (cur_stat[i] ^ old_stat[i]) & chip->irq_mask[i];
- triggers += trigger[i];
+ if (trigger[i])
+ trigger_seen = true;
}
- if (!triggers)
- return 0;
+ if (!trigger_seen)
+ return false;
memcpy(chip->irq_stat, cur_stat, NBANK(chip));
@@ -483,10 +485,11 @@ static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
pending[i] = (old_stat[i] & chip->irq_trig_fall[i]) |
(cur_stat[i] & chip->irq_trig_raise[i]);
pending[i] &= trigger[i];
- pendings += pending[i];
+ if (pending[i])
+ pending_seen = true;
}
- return pendings;
+ return pending_seen;
}
static irqreturn_t pca953x_irq_handler(int irq, void *devid)
@@ -630,7 +633,7 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
memset(val, 0, NBANK(chip));
pca953x_write_regs(chip, PCA957X_INVRT, val);
- /* To enable register 6, 7 to controll pull up and pull down */
+ /* To enable register 6, 7 to control pull up and pull down */
memset(val, 0x02, NBANK(chip));
pca953x_write_regs(chip, PCA957X_BKEN, val);
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index 945f0cda8529..404f3c61ef9b 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -91,6 +91,8 @@ struct pcf857x {
spinlock_t slock; /* protect irq demux */
unsigned out; /* software latch */
unsigned status; /* current status */
+ unsigned int irq_parent;
+ unsigned irq_enabled; /* enabled irqs */
int (*write)(struct i2c_client *client, unsigned data);
int (*read)(struct i2c_client *client);
@@ -194,7 +196,7 @@ static irqreturn_t pcf857x_irq(int irq, void *data)
* interrupt source, just to avoid bad irqs
*/
- change = (gpio->status ^ status);
+ change = (gpio->status ^ status) & gpio->irq_enabled;
for_each_set_bit(i, &change, gpio->chip.ngpio)
handle_nested_irq(irq_find_mapping(gpio->chip.irqdomain, i));
gpio->status = status;
@@ -209,29 +211,62 @@ static irqreturn_t pcf857x_irq(int irq, void *data)
*/
static void noop(struct irq_data *data) { }
-static unsigned int noop_ret(struct irq_data *data)
+static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on)
{
- return 0;
+ struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+
+ int error = 0;
+
+ if (gpio->irq_parent) {
+ error = irq_set_irq_wake(gpio->irq_parent, on);
+ if (error) {
+ dev_dbg(&gpio->client->dev,
+ "irq %u doesn't support irq_set_wake\n",
+ gpio->irq_parent);
+ gpio->irq_parent = 0;
+ }
+ }
+ return error;
}
-static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on)
+static void pcf857x_irq_enable(struct irq_data *data)
{
struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
- irq_set_irq_wake(gpio->client->irq, on);
- return 0;
+ gpio->irq_enabled |= (1 << data->hwirq);
+}
+
+static void pcf857x_irq_disable(struct irq_data *data)
+{
+ struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+
+ gpio->irq_enabled &= ~(1 << data->hwirq);
+}
+
+static void pcf857x_irq_bus_lock(struct irq_data *data)
+{
+ struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&gpio->lock);
+}
+
+static void pcf857x_irq_bus_sync_unlock(struct irq_data *data)
+{
+ struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+
+ mutex_unlock(&gpio->lock);
}
static struct irq_chip pcf857x_irq_chip = {
.name = "pcf857x",
- .irq_startup = noop_ret,
- .irq_shutdown = noop,
- .irq_enable = noop,
- .irq_disable = noop,
+ .irq_enable = pcf857x_irq_enable,
+ .irq_disable = pcf857x_irq_disable,
.irq_ack = noop,
.irq_mask = noop,
.irq_unmask = noop,
.irq_set_wake = pcf857x_irq_set_wake,
+ .irq_bus_lock = pcf857x_irq_bus_lock,
+ .irq_bus_sync_unlock = pcf857x_irq_bus_sync_unlock,
};
/*-------------------------------------------------------------------------*/
@@ -364,6 +399,7 @@ static int pcf857x_probe(struct i2c_client *client,
gpiochip_set_chained_irqchip(&gpio->chip, &pcf857x_irq_chip,
client->irq, NULL);
+ gpio->irq_parent = client->irq;
}
/* Let platform code set up the GPIOs and their users.
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index fd3977465948..1e14a6c74ed1 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -177,8 +177,17 @@ static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gpio_rcar_priv *p = container_of(gc, struct gpio_rcar_priv,
gpio_chip);
-
- irq_set_irq_wake(p->irq_parent, on);
+ int error;
+
+ if (p->irq_parent) {
+ error = irq_set_irq_wake(p->irq_parent, on);
+ if (error) {
+ dev_dbg(&p->pdev->dev,
+ "irq %u doesn't support irq_set_wake\n",
+ p->irq_parent);
+ p->irq_parent = 0;
+ }
+ }
if (!p->clk)
return 0;
diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c
index 202361eb7279..81bdbe7ba2a4 100644
--- a/drivers/gpio/gpio-stp-xway.c
+++ b/drivers/gpio/gpio-stp-xway.c
@@ -58,7 +58,7 @@
#define XWAY_STP_ADSL_MASK 0x3
/* 2 groups of 3 bits can be driven by the phys */
-#define XWAY_STP_PHY_MASK 0x3
+#define XWAY_STP_PHY_MASK 0x7
#define XWAY_STP_PHY1_SHIFT 27
#define XWAY_STP_PHY2_SHIFT 15
@@ -200,7 +200,7 @@ static int xway_stp_hw_init(struct xway_stp *chip)
static int xway_stp_probe(struct platform_device *pdev)
{
struct resource *res;
- const __be32 *shadow, *groups, *dsl, *phy;
+ u32 shadow, groups, dsl, phy;
struct xway_stp *chip;
struct clk *clk;
int ret = 0;
@@ -223,33 +223,28 @@ static int xway_stp_probe(struct platform_device *pdev)
chip->gc.owner = THIS_MODULE;
/* store the shadow value if one was passed by the devicetree */
- shadow = of_get_property(pdev->dev.of_node, "lantiq,shadow", NULL);
- if (shadow)
- chip->shadow = be32_to_cpu(*shadow);
+ if (!of_property_read_u32(pdev->dev.of_node, "lantiq,shadow", &shadow))
+ chip->shadow = shadow;
/* find out which gpio groups should be enabled */
- groups = of_get_property(pdev->dev.of_node, "lantiq,groups", NULL);
- if (groups)
- chip->groups = be32_to_cpu(*groups) & XWAY_STP_GROUP_MASK;
+ if (!of_property_read_u32(pdev->dev.of_node, "lantiq,groups", &groups))
+ chip->groups = groups & XWAY_STP_GROUP_MASK;
else
chip->groups = XWAY_STP_GROUP0;
chip->gc.ngpio = fls(chip->groups) * 8;
/* find out which gpios are controlled by the dsl core */
- dsl = of_get_property(pdev->dev.of_node, "lantiq,dsl", NULL);
- if (dsl)
- chip->dsl = be32_to_cpu(*dsl) & XWAY_STP_ADSL_MASK;
+ if (!of_property_read_u32(pdev->dev.of_node, "lantiq,dsl", &dsl))
+ chip->dsl = dsl & XWAY_STP_ADSL_MASK;
/* find out which gpios are controlled by the phys */
if (of_machine_is_compatible("lantiq,ar9") ||
of_machine_is_compatible("lantiq,gr9") ||
of_machine_is_compatible("lantiq,vr9")) {
- phy = of_get_property(pdev->dev.of_node, "lantiq,phy1", NULL);
- if (phy)
- chip->phy1 = be32_to_cpu(*phy) & XWAY_STP_PHY_MASK;
- phy = of_get_property(pdev->dev.of_node, "lantiq,phy2", NULL);
- if (phy)
- chip->phy2 = be32_to_cpu(*phy) & XWAY_STP_PHY_MASK;
+ if (!of_property_read_u32(pdev->dev.of_node, "lantiq,phy1", &phy))
+ chip->phy1 = phy & XWAY_STP_PHY_MASK;
+ if (!of_property_read_u32(pdev->dev.of_node, "lantiq,phy2", &phy))
+ chip->phy2 = phy & XWAY_STP_PHY_MASK;
}
/* check which edge trigger we should use, default to a falling edge */
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
index 46b89614aa91..12c99d969b98 100644
--- a/drivers/gpio/gpio-tb10x.c
+++ b/drivers/gpio/gpio-tb10x.c
@@ -292,7 +292,6 @@ static int tb10x_gpio_remove(struct platform_device *pdev)
BIT(tb10x_gpio->gc.ngpio) - 1, 0, 0);
kfree(tb10x_gpio->domain->gc);
irq_domain_remove(tb10x_gpio->domain);
- free_irq(tb10x_gpio->irq, tb10x_gpio);
}
gpiochip_remove(&tb10x_gpio->gc);
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 1741981d53c8..9b25c90f725c 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -288,7 +288,7 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
tegra_gpio_writel(1 << pin, GPIO_INT_CLR(gpio));
/* if gpio is edge triggered, clear condition
- * before executing the hander so that we don't
+ * before executing the handler so that we don't
* miss edges
*/
if (lvl & (0x100 << pin)) {
@@ -515,8 +515,8 @@ static int tegra_gpio_probe(struct platform_device *pdev)
for (i = 0; i < tegra_gpio_bank_count; i++) {
bank = &tegra_gpio_banks[i];
- irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler);
- irq_set_handler_data(bank->irq, bank);
+ irq_set_chained_handler_and_data(bank->irq,
+ tegra_gpio_irq_handler, bank);
for (j = 0; j < 4; j++)
spin_lock_init(&bank->lvl_lock[j]);
diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c
index 92fbabd82879..b29a102d136b 100644
--- a/drivers/gpio/gpio-ts5500.c
+++ b/drivers/gpio/gpio-ts5500.c
@@ -440,7 +440,7 @@ static int ts5500_dio_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id ts5500_dio_ids[] = {
+static const struct platform_device_id ts5500_dio_ids[] = {
{ "ts5500-dio1", TS5500_DIO1 },
{ "ts5500-dio2", TS5500_DIO2 },
{ "ts5500-dio-lcd", TS5500_LCD },
diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c
index fb9d29a5d584..d57068b9083e 100644
--- a/drivers/gpio/gpio-xgene-sb.c
+++ b/drivers/gpio/gpio-xgene-sb.c
@@ -25,8 +25,11 @@
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
+#include <linux/acpi.h>
#include <linux/basic_mmio_gpio.h>
+#include "gpiolib.h"
+
#define XGENE_MAX_GPIO_DS 22
#define XGENE_MAX_GPIO_DS_IRQ 6
@@ -112,7 +115,6 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!priv->irq)
return -ENOMEM;
- memset(priv->irq, 0, sizeof(u32) * XGENE_MAX_GPIO_DS);
for (i = 0; i < priv->nirq; i++) {
priv->irq[default_lines[i]] = platform_get_irq(pdev, i);
@@ -129,6 +131,11 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
else
dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");
+ if (priv->nirq > 0) {
+ /* Register interrupt handlers for gpio signaled acpi events */
+ acpi_gpiochip_request_interrupts(&priv->bgc.gc);
+ }
+
return ret;
}
@@ -136,6 +143,10 @@ static int xgene_gpio_sb_remove(struct platform_device *pdev)
{
struct xgene_gpio_sb *priv = platform_get_drvdata(pdev);
+ if (priv->nirq > 0) {
+ acpi_gpiochip_free_interrupts(&priv->bgc.gc);
+ }
+
return bgpio_remove(&priv->bgc);
}
@@ -145,10 +156,19 @@ static const struct of_device_id xgene_gpio_sb_of_match[] = {
};
MODULE_DEVICE_TABLE(of, xgene_gpio_sb_of_match);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_gpio_sb_acpi_match[] = {
+ {"APMC0D15", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, xgene_gpio_sb_acpi_match);
+#endif
+
static struct platform_driver xgene_gpio_sb_driver = {
.driver = {
.name = "xgene-gpio-sb",
.of_match_table = xgene_gpio_sb_of_match,
+ .acpi_match_table = ACPI_PTR(xgene_gpio_sb_acpi_match),
},
.probe = xgene_gpio_sb_probe,
.remove = xgene_gpio_sb_remove,
diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c
index 61243d177740..77fe5d3cb105 100644
--- a/drivers/gpio/gpio-xilinx.c
+++ b/drivers/gpio/gpio-xilinx.c
@@ -41,10 +41,10 @@
/**
* struct xgpio_instance - Stores information about GPIO device
* @mmchip: OF GPIO chip for memory mapped banks
+ * @gpio_width: GPIO width for every channel
* @gpio_state: GPIO state shadow register
* @gpio_dir: GPIO direction shadow register
* @gpio_lock: Lock used for synchronization
- * @inited: True if the port has been inited
*/
struct xgpio_instance {
struct of_mm_gpio_chip mmchip;
@@ -231,6 +231,8 @@ static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc)
* @pdev: pointer to the platform device
*
* This function remove gpiochips and frees all the allocated resources.
+ *
+ * Return: 0 always
*/
static int xgpio_remove(struct platform_device *pdev)
{
diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c
new file mode 100644
index 000000000000..9bdab7203d65
--- /dev/null
+++ b/drivers/gpio/gpio-xlp.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2003-2015 Broadcom Corporation
+ * 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.
+ */
+
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+/*
+ * XLP GPIO has multiple 32 bit registers for each feature where each register
+ * controls 32 pins. So, pins up to 64 require 2 32-bit registers and up to 96
+ * require 3 32-bit registers for each feature.
+ * Here we only define offset of the first register for each feature. Offset of
+ * the registers for pins greater than 32 can be calculated as following(Use
+ * GPIO_INT_STAT as example):
+ *
+ * offset = (gpio / XLP_GPIO_REGSZ) * 4;
+ * reg_addr = addr + offset;
+ *
+ * where addr is base address of the that feature register and gpio is the pin.
+ */
+#define GPIO_OUTPUT_EN 0x00
+#define GPIO_PADDRV 0x08
+#define GPIO_INT_EN00 0x18
+#define GPIO_INT_EN10 0x20
+#define GPIO_INT_EN20 0x28
+#define GPIO_INT_EN30 0x30
+#define GPIO_INT_POL 0x38
+#define GPIO_INT_TYPE 0x40
+#define GPIO_INT_STAT 0x48
+
+#define GPIO_9XX_BYTESWAP 0X00
+#define GPIO_9XX_CTRL 0X04
+#define GPIO_9XX_OUTPUT_EN 0x14
+#define GPIO_9XX_PADDRV 0x24
+/*
+ * Only for 4 interrupt enable reg are defined for now,
+ * total reg available are 12.
+ */
+#define GPIO_9XX_INT_EN00 0x44
+#define GPIO_9XX_INT_EN10 0x54
+#define GPIO_9XX_INT_EN20 0x64
+#define GPIO_9XX_INT_EN30 0x74
+#define GPIO_9XX_INT_POL 0x104
+#define GPIO_9XX_INT_TYPE 0x114
+#define GPIO_9XX_INT_STAT 0x124
+
+#define GPIO_3XX_INT_EN00 0x18
+#define GPIO_3XX_INT_EN10 0x20
+#define GPIO_3XX_INT_EN20 0x28
+#define GPIO_3XX_INT_EN30 0x30
+#define GPIO_3XX_INT_POL 0x78
+#define GPIO_3XX_INT_TYPE 0x80
+#define GPIO_3XX_INT_STAT 0x88
+
+/* Interrupt type register mask */
+#define XLP_GPIO_IRQ_TYPE_LVL 0x0
+#define XLP_GPIO_IRQ_TYPE_EDGE 0x1
+
+/* Interrupt polarity register mask */
+#define XLP_GPIO_IRQ_POL_HIGH 0x0
+#define XLP_GPIO_IRQ_POL_LOW 0x1
+
+#define XLP_GPIO_REGSZ 32
+#define XLP_GPIO_IRQ_BASE 768
+#define XLP_MAX_NR_GPIO 96
+
+/* XLP variants supported by this driver */
+enum {
+ XLP_GPIO_VARIANT_XLP832 = 1,
+ XLP_GPIO_VARIANT_XLP316,
+ XLP_GPIO_VARIANT_XLP208,
+ XLP_GPIO_VARIANT_XLP980,
+ XLP_GPIO_VARIANT_XLP532
+};
+
+struct xlp_gpio_priv {
+ struct gpio_chip chip;
+ DECLARE_BITMAP(gpio_enabled_mask, XLP_MAX_NR_GPIO);
+ void __iomem *gpio_intr_en; /* pointer to first intr enable reg */
+ void __iomem *gpio_intr_stat; /* pointer to first intr status reg */
+ void __iomem *gpio_intr_type; /* pointer to first intr type reg */
+ void __iomem *gpio_intr_pol; /* pointer to first intr polarity reg */
+ void __iomem *gpio_out_en; /* pointer to first output enable reg */
+ void __iomem *gpio_paddrv; /* pointer to first pad drive reg */
+ spinlock_t lock;
+};
+
+static struct xlp_gpio_priv *gpio_chip_to_xlp_priv(struct gpio_chip *gc)
+{
+ return container_of(gc, struct xlp_gpio_priv, chip);
+}
+
+static int xlp_gpio_get_reg(void __iomem *addr, unsigned gpio)
+{
+ u32 pos, regset;
+
+ pos = gpio % XLP_GPIO_REGSZ;
+ regset = (gpio / XLP_GPIO_REGSZ) * 4;
+ return !!(readl(addr + regset) & BIT(pos));
+}
+
+static void xlp_gpio_set_reg(void __iomem *addr, unsigned gpio, int state)
+{
+ u32 value, pos, regset;
+
+ pos = gpio % XLP_GPIO_REGSZ;
+ regset = (gpio / XLP_GPIO_REGSZ) * 4;
+ value = readl(addr + regset);
+
+ if (state)
+ value |= BIT(pos);
+ else
+ value &= ~BIT(pos);
+
+ writel(value, addr + regset);
+}
+
+static void xlp_gpio_irq_disable(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ xlp_gpio_set_reg(priv->gpio_intr_en, d->hwirq, 0x0);
+ __clear_bit(d->hwirq, priv->gpio_enabled_mask);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void xlp_gpio_irq_mask_ack(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ xlp_gpio_set_reg(priv->gpio_intr_en, d->hwirq, 0x0);
+ xlp_gpio_set_reg(priv->gpio_intr_stat, d->hwirq, 0x1);
+ __clear_bit(d->hwirq, priv->gpio_enabled_mask);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void xlp_gpio_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ xlp_gpio_set_reg(priv->gpio_intr_en, d->hwirq, 0x1);
+ __set_bit(d->hwirq, priv->gpio_enabled_mask);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int xlp_gpio_set_irq_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+ int pol, irq_type;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ irq_type = XLP_GPIO_IRQ_TYPE_EDGE;
+ pol = XLP_GPIO_IRQ_POL_HIGH;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ irq_type = XLP_GPIO_IRQ_TYPE_EDGE;
+ pol = XLP_GPIO_IRQ_POL_LOW;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ irq_type = XLP_GPIO_IRQ_TYPE_LVL;
+ pol = XLP_GPIO_IRQ_POL_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ irq_type = XLP_GPIO_IRQ_TYPE_LVL;
+ pol = XLP_GPIO_IRQ_POL_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ xlp_gpio_set_reg(priv->gpio_intr_type, d->hwirq, irq_type);
+ xlp_gpio_set_reg(priv->gpio_intr_pol, d->hwirq, pol);
+
+ return 0;
+}
+
+static struct irq_chip xlp_gpio_irq_chip = {
+ .name = "XLP-GPIO",
+ .irq_mask_ack = xlp_gpio_irq_mask_ack,
+ .irq_disable = xlp_gpio_irq_disable,
+ .irq_set_type = xlp_gpio_set_irq_type,
+ .irq_unmask = xlp_gpio_irq_unmask,
+ .flags = IRQCHIP_ONESHOT_SAFE,
+};
+
+static irqreturn_t xlp_gpio_generic_handler(int irq, void *data)
+{
+ struct xlp_gpio_priv *priv = data;
+ int gpio, regoff;
+ u32 gpio_stat;
+
+ regoff = -1;
+ gpio_stat = 0;
+ for_each_set_bit(gpio, priv->gpio_enabled_mask, XLP_MAX_NR_GPIO) {
+ if (regoff != gpio / XLP_GPIO_REGSZ) {
+ regoff = gpio / XLP_GPIO_REGSZ;
+ gpio_stat = readl(priv->gpio_intr_stat + regoff * 4);
+ }
+ if (gpio_stat & BIT(gpio % XLP_GPIO_REGSZ))
+ generic_handle_irq(irq_find_mapping(
+ priv->chip.irqdomain, gpio));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int xlp_gpio_dir_output(struct gpio_chip *gc, unsigned gpio, int state)
+{
+ struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+
+ BUG_ON(gpio >= gc->ngpio);
+ xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x1);
+
+ return 0;
+}
+
+static int xlp_gpio_dir_input(struct gpio_chip *gc, unsigned gpio)
+{
+ struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+
+ BUG_ON(gpio >= gc->ngpio);
+ xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x0);
+
+ return 0;
+}
+
+static int xlp_gpio_get(struct gpio_chip *gc, unsigned gpio)
+{
+ struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+
+ BUG_ON(gpio >= gc->ngpio);
+ return xlp_gpio_get_reg(priv->gpio_paddrv, gpio);
+}
+
+static void xlp_gpio_set(struct gpio_chip *gc, unsigned gpio, int state)
+{
+ struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+
+ BUG_ON(gpio >= gc->ngpio);
+ xlp_gpio_set_reg(priv->gpio_paddrv, gpio, state);
+}
+
+static const struct of_device_id xlp_gpio_of_ids[] = {
+ {
+ .compatible = "netlogic,xlp832-gpio",
+ .data = (void *)XLP_GPIO_VARIANT_XLP832,
+ },
+ {
+ .compatible = "netlogic,xlp316-gpio",
+ .data = (void *)XLP_GPIO_VARIANT_XLP316,
+ },
+ {
+ .compatible = "netlogic,xlp208-gpio",
+ .data = (void *)XLP_GPIO_VARIANT_XLP208,
+ },
+ {
+ .compatible = "netlogic,xlp980-gpio",
+ .data = (void *)XLP_GPIO_VARIANT_XLP980,
+ },
+ {
+ .compatible = "netlogic,xlp532-gpio",
+ .data = (void *)XLP_GPIO_VARIANT_XLP532,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, xlp_gpio_of_ids);
+
+static int xlp_gpio_probe(struct platform_device *pdev)
+{
+ struct gpio_chip *gc;
+ struct resource *iores;
+ struct xlp_gpio_priv *priv;
+ const struct of_device_id *of_id;
+ void __iomem *gpio_base;
+ int irq_base, irq, err;
+ int ngpio;
+ u32 soc_type;
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iores)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ gpio_base = devm_ioremap_resource(&pdev->dev, iores);
+ if (IS_ERR(gpio_base))
+ return PTR_ERR(gpio_base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ of_id = of_match_device(xlp_gpio_of_ids, &pdev->dev);
+ if (!of_id) {
+ dev_err(&pdev->dev, "Failed to get soc type!\n");
+ return -ENODEV;
+ }
+
+ soc_type = (uintptr_t) of_id->data;
+
+ switch (soc_type) {
+ case XLP_GPIO_VARIANT_XLP832:
+ priv->gpio_out_en = gpio_base + GPIO_OUTPUT_EN;
+ priv->gpio_paddrv = gpio_base + GPIO_PADDRV;
+ priv->gpio_intr_stat = gpio_base + GPIO_INT_STAT;
+ priv->gpio_intr_type = gpio_base + GPIO_INT_TYPE;
+ priv->gpio_intr_pol = gpio_base + GPIO_INT_POL;
+ priv->gpio_intr_en = gpio_base + GPIO_INT_EN00;
+ ngpio = 41;
+ break;
+ case XLP_GPIO_VARIANT_XLP208:
+ case XLP_GPIO_VARIANT_XLP316:
+ priv->gpio_out_en = gpio_base + GPIO_OUTPUT_EN;
+ priv->gpio_paddrv = gpio_base + GPIO_PADDRV;
+ priv->gpio_intr_stat = gpio_base + GPIO_3XX_INT_STAT;
+ priv->gpio_intr_type = gpio_base + GPIO_3XX_INT_TYPE;
+ priv->gpio_intr_pol = gpio_base + GPIO_3XX_INT_POL;
+ priv->gpio_intr_en = gpio_base + GPIO_3XX_INT_EN00;
+
+ ngpio = (soc_type == XLP_GPIO_VARIANT_XLP208) ? 42 : 57;
+ break;
+ case XLP_GPIO_VARIANT_XLP980:
+ case XLP_GPIO_VARIANT_XLP532:
+ priv->gpio_out_en = gpio_base + GPIO_9XX_OUTPUT_EN;
+ priv->gpio_paddrv = gpio_base + GPIO_9XX_PADDRV;
+ priv->gpio_intr_stat = gpio_base + GPIO_9XX_INT_STAT;
+ priv->gpio_intr_type = gpio_base + GPIO_9XX_INT_TYPE;
+ priv->gpio_intr_pol = gpio_base + GPIO_9XX_INT_POL;
+ priv->gpio_intr_en = gpio_base + GPIO_9XX_INT_EN00;
+
+ ngpio = (soc_type == XLP_GPIO_VARIANT_XLP980) ? 66 : 67;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unknown Processor type!\n");
+ return -ENODEV;
+ }
+
+ bitmap_zero(priv->gpio_enabled_mask, XLP_MAX_NR_GPIO);
+
+ gc = &priv->chip;
+
+ gc->owner = THIS_MODULE;
+ gc->label = dev_name(&pdev->dev);
+ gc->base = 0;
+ gc->dev = &pdev->dev;
+ gc->ngpio = ngpio;
+ gc->of_node = pdev->dev.of_node;
+ gc->direction_output = xlp_gpio_dir_output;
+ gc->direction_input = xlp_gpio_dir_input;
+ gc->set = xlp_gpio_set;
+ gc->get = xlp_gpio_get;
+
+ spin_lock_init(&priv->lock);
+
+ err = devm_request_irq(&pdev->dev, irq, xlp_gpio_generic_handler,
+ IRQ_TYPE_NONE, pdev->name, priv);
+ if (err)
+ return err;
+
+ irq_base = irq_alloc_descs(-1, XLP_GPIO_IRQ_BASE, gc->ngpio, 0);
+ if (irq_base < 0) {
+ dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n");
+ return err;
+ }
+
+ err = gpiochip_add(gc);
+ if (err < 0)
+ goto out_free_desc;
+
+ err = gpiochip_irqchip_add(gc, &xlp_gpio_irq_chip, irq_base,
+ handle_level_irq, IRQ_TYPE_NONE);
+ if (err) {
+ dev_err(&pdev->dev, "Could not connect irqchip to gpiochip!\n");
+ goto out_gpio_remove;
+ }
+
+ dev_info(&pdev->dev, "registered %d GPIOs\n", gc->ngpio);
+
+ return 0;
+
+out_gpio_remove:
+ gpiochip_remove(gc);
+out_free_desc:
+ irq_free_descs(irq_base, gc->ngpio);
+ return err;
+}
+
+static struct platform_driver xlp_gpio_driver = {
+ .driver = {
+ .name = "xlp-gpio",
+ .of_match_table = xlp_gpio_of_ids,
+ },
+ .probe = xlp_gpio_probe,
+};
+module_platform_driver(xlp_gpio_driver);
+
+MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
+MODULE_AUTHOR("Ganesan Ramalingam <ganesanr@broadcom.com>");
+MODULE_DESCRIPTION("Netlogic XLP GPIO Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index 184c4b1b2558..2e87c4b8da26 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -18,34 +18,47 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
#define DRIVER_NAME "zynq-gpio"
/* Maximum banks */
#define ZYNQ_GPIO_MAX_BANK 4
+#define ZYNQMP_GPIO_MAX_BANK 6
#define ZYNQ_GPIO_BANK0_NGPIO 32
#define ZYNQ_GPIO_BANK1_NGPIO 22
#define ZYNQ_GPIO_BANK2_NGPIO 32
#define ZYNQ_GPIO_BANK3_NGPIO 32
-#define ZYNQ_GPIO_NR_GPIOS (ZYNQ_GPIO_BANK0_NGPIO + \
- ZYNQ_GPIO_BANK1_NGPIO + \
- ZYNQ_GPIO_BANK2_NGPIO + \
- ZYNQ_GPIO_BANK3_NGPIO)
-
-#define ZYNQ_GPIO_BANK0_PIN_MIN 0
-#define ZYNQ_GPIO_BANK0_PIN_MAX (ZYNQ_GPIO_BANK0_PIN_MIN + \
- ZYNQ_GPIO_BANK0_NGPIO - 1)
-#define ZYNQ_GPIO_BANK1_PIN_MIN (ZYNQ_GPIO_BANK0_PIN_MAX + 1)
-#define ZYNQ_GPIO_BANK1_PIN_MAX (ZYNQ_GPIO_BANK1_PIN_MIN + \
- ZYNQ_GPIO_BANK1_NGPIO - 1)
-#define ZYNQ_GPIO_BANK2_PIN_MIN (ZYNQ_GPIO_BANK1_PIN_MAX + 1)
-#define ZYNQ_GPIO_BANK2_PIN_MAX (ZYNQ_GPIO_BANK2_PIN_MIN + \
- ZYNQ_GPIO_BANK2_NGPIO - 1)
-#define ZYNQ_GPIO_BANK3_PIN_MIN (ZYNQ_GPIO_BANK2_PIN_MAX + 1)
-#define ZYNQ_GPIO_BANK3_PIN_MAX (ZYNQ_GPIO_BANK3_PIN_MIN + \
- ZYNQ_GPIO_BANK3_NGPIO - 1)
+#define ZYNQMP_GPIO_BANK0_NGPIO 26
+#define ZYNQMP_GPIO_BANK1_NGPIO 26
+#define ZYNQMP_GPIO_BANK2_NGPIO 26
+#define ZYNQMP_GPIO_BANK3_NGPIO 32
+#define ZYNQMP_GPIO_BANK4_NGPIO 32
+#define ZYNQMP_GPIO_BANK5_NGPIO 32
+
+#define ZYNQ_GPIO_NR_GPIOS 118
+#define ZYNQMP_GPIO_NR_GPIOS 174
+
+#define ZYNQ_GPIO_BANK0_PIN_MIN(str) 0
+#define ZYNQ_GPIO_BANK0_PIN_MAX(str) (ZYNQ_GPIO_BANK0_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK0_NGPIO - 1)
+#define ZYNQ_GPIO_BANK1_PIN_MIN(str) (ZYNQ_GPIO_BANK0_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK1_PIN_MAX(str) (ZYNQ_GPIO_BANK1_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK1_NGPIO - 1)
+#define ZYNQ_GPIO_BANK2_PIN_MIN(str) (ZYNQ_GPIO_BANK1_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK2_PIN_MAX(str) (ZYNQ_GPIO_BANK2_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK2_NGPIO - 1)
+#define ZYNQ_GPIO_BANK3_PIN_MIN(str) (ZYNQ_GPIO_BANK2_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK3_PIN_MAX(str) (ZYNQ_GPIO_BANK3_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK3_NGPIO - 1)
+#define ZYNQ_GPIO_BANK4_PIN_MIN(str) (ZYNQ_GPIO_BANK3_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK4_PIN_MAX(str) (ZYNQ_GPIO_BANK4_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK4_NGPIO - 1)
+#define ZYNQ_GPIO_BANK5_PIN_MIN(str) (ZYNQ_GPIO_BANK4_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK5_PIN_MAX(str) (ZYNQ_GPIO_BANK5_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK5_NGPIO - 1)
/* Register offsets for the GPIO device */
@@ -89,12 +102,30 @@
* @base_addr: base address of the GPIO device
* @clk: clock resource for this controller
* @irq: interrupt for the GPIO device
+ * @p_data: pointer to platform data
*/
struct zynq_gpio {
struct gpio_chip chip;
void __iomem *base_addr;
struct clk *clk;
int irq;
+ const struct zynq_platform_data *p_data;
+};
+
+/**
+ * struct zynq_platform_data - zynq gpio platform data structure
+ * @label: string to store in gpio->label
+ * @ngpio: max number of gpio pins
+ * @max_bank: maximum number of gpio banks
+ * @bank_min: this array represents bank's min pin
+ * @bank_max: this array represents bank's max pin
+*/
+struct zynq_platform_data {
+ const char *label;
+ u16 ngpio;
+ int max_bank;
+ int bank_min[ZYNQMP_GPIO_MAX_BANK];
+ int bank_max[ZYNQMP_GPIO_MAX_BANK];
};
static struct irq_chip zynq_gpio_level_irqchip;
@@ -112,39 +143,26 @@ static struct irq_chip zynq_gpio_edge_irqchip;
*/
static inline void zynq_gpio_get_bank_pin(unsigned int pin_num,
unsigned int *bank_num,
- unsigned int *bank_pin_num)
+ unsigned int *bank_pin_num,
+ struct zynq_gpio *gpio)
{
- switch (pin_num) {
- case ZYNQ_GPIO_BANK0_PIN_MIN ... ZYNQ_GPIO_BANK0_PIN_MAX:
- *bank_num = 0;
- *bank_pin_num = pin_num;
- break;
- case ZYNQ_GPIO_BANK1_PIN_MIN ... ZYNQ_GPIO_BANK1_PIN_MAX:
- *bank_num = 1;
- *bank_pin_num = pin_num - ZYNQ_GPIO_BANK1_PIN_MIN;
- break;
- case ZYNQ_GPIO_BANK2_PIN_MIN ... ZYNQ_GPIO_BANK2_PIN_MAX:
- *bank_num = 2;
- *bank_pin_num = pin_num - ZYNQ_GPIO_BANK2_PIN_MIN;
- break;
- case ZYNQ_GPIO_BANK3_PIN_MIN ... ZYNQ_GPIO_BANK3_PIN_MAX:
- *bank_num = 3;
- *bank_pin_num = pin_num - ZYNQ_GPIO_BANK3_PIN_MIN;
- break;
- default:
- WARN(true, "invalid GPIO pin number: %u", pin_num);
- *bank_num = 0;
- *bank_pin_num = 0;
- break;
+ int bank;
+
+ for (bank = 0; bank < gpio->p_data->max_bank; bank++) {
+ if ((pin_num >= gpio->p_data->bank_min[bank]) &&
+ (pin_num <= gpio->p_data->bank_max[bank])) {
+ *bank_num = bank;
+ *bank_pin_num = pin_num -
+ gpio->p_data->bank_min[bank];
+ return;
+ }
}
-}
-static const unsigned int zynq_gpio_bank_offset[] = {
- ZYNQ_GPIO_BANK0_PIN_MIN,
- ZYNQ_GPIO_BANK1_PIN_MIN,
- ZYNQ_GPIO_BANK2_PIN_MIN,
- ZYNQ_GPIO_BANK3_PIN_MIN,
-};
+ /* default */
+ WARN(true, "invalid GPIO pin number: %u", pin_num);
+ *bank_num = 0;
+ *bank_pin_num = 0;
+}
/**
* zynq_gpio_get_value - Get the state of the specified pin of GPIO device
@@ -161,7 +179,7 @@ static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin)
unsigned int bank_num, bank_pin_num;
struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
- zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+ zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
data = readl_relaxed(gpio->base_addr +
ZYNQ_GPIO_DATA_RO_OFFSET(bank_num));
@@ -185,7 +203,7 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
unsigned int reg_offset, bank_num, bank_pin_num;
struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
- zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+ zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
if (bank_pin_num >= ZYNQ_GPIO_MID_PIN_NUM) {
/* only 16 data bits in bit maskable reg */
@@ -222,7 +240,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
unsigned int bank_num, bank_pin_num;
struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
- zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+ zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
/* bank 0 pins 7 and 8 are special and cannot be used as inputs */
if (bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8))
@@ -255,7 +273,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
unsigned int bank_num, bank_pin_num;
struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
- zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+ zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
/* set the GPIO pin as output */
reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
@@ -286,7 +304,7 @@ static void zynq_gpio_irq_mask(struct irq_data *irq_data)
struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
device_pin_num = irq_data->hwirq;
- zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+ zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
writel_relaxed(BIT(bank_pin_num),
gpio->base_addr + ZYNQ_GPIO_INTDIS_OFFSET(bank_num));
}
@@ -306,7 +324,7 @@ static void zynq_gpio_irq_unmask(struct irq_data *irq_data)
struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
device_pin_num = irq_data->hwirq;
- zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+ zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
writel_relaxed(BIT(bank_pin_num),
gpio->base_addr + ZYNQ_GPIO_INTEN_OFFSET(bank_num));
}
@@ -325,7 +343,7 @@ static void zynq_gpio_irq_ack(struct irq_data *irq_data)
struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
device_pin_num = irq_data->hwirq;
- zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+ zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
writel_relaxed(BIT(bank_pin_num),
gpio->base_addr + ZYNQ_GPIO_INTSTS_OFFSET(bank_num));
}
@@ -335,7 +353,7 @@ static void zynq_gpio_irq_ack(struct irq_data *irq_data)
* @irq_data: irq data containing irq number of gpio pin for the interrupt
* to enable
*
- * Clears the INTSTS bit and unmasks the given interrrupt.
+ * Clears the INTSTS bit and unmasks the given interrupt.
*/
static void zynq_gpio_irq_enable(struct irq_data *irq_data)
{
@@ -375,7 +393,7 @@ static int zynq_gpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
device_pin_num = irq_data->hwirq;
- zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+ zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
int_type = readl_relaxed(gpio->base_addr +
ZYNQ_GPIO_INTTYPE_OFFSET(bank_num));
@@ -470,7 +488,7 @@ static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio,
unsigned int bank_num,
unsigned long pending)
{
- unsigned int bank_offset = zynq_gpio_bank_offset[bank_num];
+ unsigned int bank_offset = gpio->p_data->bank_min[bank_num];
struct irq_domain *irqdomain = gpio->chip.irqdomain;
int offset;
@@ -505,7 +523,7 @@ static void zynq_gpio_irqhandler(unsigned int irq, struct irq_desc *desc)
chained_irq_enter(irqchip, desc);
- for (bank_num = 0; bank_num < ZYNQ_GPIO_MAX_BANK; bank_num++) {
+ for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) {
int_sts = readl_relaxed(gpio->base_addr +
ZYNQ_GPIO_INTSTS_OFFSET(bank_num));
int_enb = readl_relaxed(gpio->base_addr +
@@ -582,6 +600,46 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
zynq_gpio_runtime_resume, NULL)
};
+static const struct zynq_platform_data zynqmp_gpio_def = {
+ .label = "zynqmp_gpio",
+ .ngpio = ZYNQMP_GPIO_NR_GPIOS,
+ .max_bank = ZYNQMP_GPIO_MAX_BANK,
+ .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(MP),
+ .bank_max[0] = ZYNQ_GPIO_BANK0_PIN_MAX(MP),
+ .bank_min[1] = ZYNQ_GPIO_BANK1_PIN_MIN(MP),
+ .bank_max[1] = ZYNQ_GPIO_BANK1_PIN_MAX(MP),
+ .bank_min[2] = ZYNQ_GPIO_BANK2_PIN_MIN(MP),
+ .bank_max[2] = ZYNQ_GPIO_BANK2_PIN_MAX(MP),
+ .bank_min[3] = ZYNQ_GPIO_BANK3_PIN_MIN(MP),
+ .bank_max[3] = ZYNQ_GPIO_BANK3_PIN_MAX(MP),
+ .bank_min[4] = ZYNQ_GPIO_BANK4_PIN_MIN(MP),
+ .bank_max[4] = ZYNQ_GPIO_BANK4_PIN_MAX(MP),
+ .bank_min[5] = ZYNQ_GPIO_BANK5_PIN_MIN(MP),
+ .bank_max[5] = ZYNQ_GPIO_BANK5_PIN_MAX(MP),
+};
+
+static const struct zynq_platform_data zynq_gpio_def = {
+ .label = "zynq_gpio",
+ .ngpio = ZYNQ_GPIO_NR_GPIOS,
+ .max_bank = ZYNQ_GPIO_MAX_BANK,
+ .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(),
+ .bank_max[0] = ZYNQ_GPIO_BANK0_PIN_MAX(),
+ .bank_min[1] = ZYNQ_GPIO_BANK1_PIN_MIN(),
+ .bank_max[1] = ZYNQ_GPIO_BANK1_PIN_MAX(),
+ .bank_min[2] = ZYNQ_GPIO_BANK2_PIN_MIN(),
+ .bank_max[2] = ZYNQ_GPIO_BANK2_PIN_MAX(),
+ .bank_min[3] = ZYNQ_GPIO_BANK3_PIN_MIN(),
+ .bank_max[3] = ZYNQ_GPIO_BANK3_PIN_MAX(),
+};
+
+static const struct of_device_id zynq_gpio_of_match[] = {
+ { .compatible = "xlnx,zynq-gpio-1.0", .data = (void *)&zynq_gpio_def },
+ { .compatible = "xlnx,zynqmp-gpio-1.0",
+ .data = (void *)&zynqmp_gpio_def },
+ { /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
+
/**
* zynq_gpio_probe - Initialization method for a zynq_gpio device
* @pdev: platform device instance
@@ -599,11 +657,18 @@ static int zynq_gpio_probe(struct platform_device *pdev)
struct zynq_gpio *gpio;
struct gpio_chip *chip;
struct resource *res;
+ const struct of_device_id *match;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
+ match = of_match_node(zynq_gpio_of_match, pdev->dev.of_node);
+ if (!match) {
+ dev_err(&pdev->dev, "of_match_node() failed\n");
+ return -EINVAL;
+ }
+ gpio->p_data = match->data;
platform_set_drvdata(pdev, gpio);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -619,7 +684,7 @@ static int zynq_gpio_probe(struct platform_device *pdev)
/* configure the gpio chip */
chip = &gpio->chip;
- chip->label = "zynq_gpio";
+ chip->label = gpio->p_data->label;
chip->owner = THIS_MODULE;
chip->dev = &pdev->dev;
chip->get = zynq_gpio_get_value;
@@ -629,7 +694,7 @@ static int zynq_gpio_probe(struct platform_device *pdev)
chip->direction_input = zynq_gpio_dir_in;
chip->direction_output = zynq_gpio_dir_out;
chip->base = -1;
- chip->ngpio = ZYNQ_GPIO_NR_GPIOS;
+ chip->ngpio = gpio->p_data->ngpio;
/* Enable GPIO clock */
gpio->clk = devm_clk_get(&pdev->dev, NULL);
@@ -651,7 +716,7 @@ static int zynq_gpio_probe(struct platform_device *pdev)
}
/* disable interrupts for all banks */
- for (bank_num = 0; bank_num < ZYNQ_GPIO_MAX_BANK; bank_num++)
+ for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++)
writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr +
ZYNQ_GPIO_INTDIS_OFFSET(bank_num));
@@ -695,12 +760,6 @@ static int zynq_gpio_remove(struct platform_device *pdev)
return 0;
}
-static struct of_device_id zynq_gpio_of_match[] = {
- { .compatible = "xlnx,zynq-gpio-1.0", },
- { /* end of table */ }
-};
-MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
-
static struct platform_driver zynq_gpio_driver = {
.driver = {
.name = DRIVER_NAME,
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index d2303d50f561..533fe5dbe6f8 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -114,10 +114,11 @@ static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip,
* @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1")
* @pin: ACPI GPIO pin number (0-based, controller-relative)
*
- * Returns GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR
- * error value
+ * Return: GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR
+ * error value. Specifically returns %-EPROBE_DEFER if the referenced GPIO
+ * controller does not have gpiochip registered at the moment. This is to
+ * support probe deferral.
*/
-
static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
{
struct gpio_chip *chip;
@@ -131,7 +132,7 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
chip = gpiochip_find(handle, acpi_gpiochip_find);
if (!chip)
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-EPROBE_DEFER);
offset = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
if (offset < 0)
@@ -307,6 +308,7 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
acpi_walk_resources(handle, "_AEI",
acpi_gpiochip_request_interrupt, acpi_gpio);
}
+EXPORT_SYMBOL_GPL(acpi_gpiochip_request_interrupts);
/**
* acpi_gpiochip_free_interrupts() - Free GPIO ACPI event interrupts.
@@ -346,6 +348,7 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
kfree(event);
}
}
+EXPORT_SYMBOL_GPL(acpi_gpiochip_free_interrupts);
int acpi_dev_add_driver_gpios(struct acpi_device *adev,
const struct acpi_gpio_mapping *gpios)
@@ -514,6 +517,35 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT);
}
+/**
+ * acpi_dev_gpio_irq_get() - Find GpioInt and translate it to Linux IRQ number
+ * @adev: pointer to a ACPI device to get IRQ from
+ * @index: index of GpioInt resource (starting from %0)
+ *
+ * If the device has one or more GpioInt resources, this function can be
+ * used to translate from the GPIO offset in the resource to the Linux IRQ
+ * number.
+ *
+ * Return: Linux IRQ number (>%0) on success, negative errno on failure.
+ */
+int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
+{
+ int idx, i;
+
+ for (i = 0, idx = 0; idx <= index; i++) {
+ struct acpi_gpio_info info;
+ struct gpio_desc *desc;
+
+ desc = acpi_get_gpiod_by_index(adev, NULL, i, &info);
+ if (IS_ERR(desc))
+ break;
+ if (info.gpioint && idx++ == index)
+ return gpiod_to_irq(desc);
+ }
+ return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_get);
+
static acpi_status
acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
u32 bits, u64 *value, void *handler_context,
@@ -550,7 +582,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
length = min(agpio->pin_table_length, (u16)(pin_index + bits));
for (i = pin_index; i < length; ++i) {
- unsigned pin = agpio->pin_table[i];
+ int pin = agpio->pin_table[i];
struct acpi_gpio_connection *conn;
struct gpio_desc *desc;
bool found;
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index a6c67c6b4680..9a0ec48a4737 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -242,7 +242,7 @@ int of_gpio_simple_xlate(struct gpio_chip *gc,
{
/*
* We're discouraging gpio_cells < 2, since that way you'll have to
- * write your own xlate function (that will have to retrive the GPIO
+ * write your own xlate function (that will have to retrieve the GPIO
* number and the flags from a single gpio cell -- this is possible,
* but not recommended).
*/
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 7722ed53bd65..b57ed8e55ab5 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -6,14 +6,29 @@
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/kdev_t.h>
+#include <linux/slab.h>
#include "gpiolib.h"
-static DEFINE_IDR(dirent_idr);
+#define GPIO_IRQF_TRIGGER_FALLING BIT(0)
+#define GPIO_IRQF_TRIGGER_RISING BIT(1)
+#define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \
+ GPIO_IRQF_TRIGGER_RISING)
+struct gpiod_data {
+ struct gpio_desc *desc;
-/* lock protects against unexport_gpio() being called while
- * sysfs files are active.
+ struct mutex mutex;
+ struct kernfs_node *value_kn;
+ int irq;
+ unsigned char irq_flags;
+
+ bool direction_can_change;
+};
+
+/*
+ * Lock to serialise gpiod export and unexport, and prevent re-export of
+ * gpiod whose chip is being unregistered.
*/
static DEFINE_MUTEX(sysfs_lock);
@@ -38,38 +53,35 @@ static DEFINE_MUTEX(sysfs_lock);
* /edge configuration
*/
-static ssize_t gpio_direction_show(struct device *dev,
+static ssize_t direction_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct gpio_desc *desc = dev_get_drvdata(dev);
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpio_desc *desc = data->desc;
ssize_t status;
- mutex_lock(&sysfs_lock);
+ mutex_lock(&data->mutex);
- if (!test_bit(FLAG_EXPORT, &desc->flags)) {
- status = -EIO;
- } else {
- gpiod_get_direction(desc);
- status = sprintf(buf, "%s\n",
+ gpiod_get_direction(desc);
+ status = sprintf(buf, "%s\n",
test_bit(FLAG_IS_OUT, &desc->flags)
? "out" : "in");
- }
- mutex_unlock(&sysfs_lock);
+ mutex_unlock(&data->mutex);
+
return status;
}
-static ssize_t gpio_direction_store(struct device *dev,
+static ssize_t direction_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
- struct gpio_desc *desc = dev_get_drvdata(dev);
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpio_desc *desc = data->desc;
ssize_t status;
- mutex_lock(&sysfs_lock);
+ mutex_lock(&data->mutex);
- if (!test_bit(FLAG_EXPORT, &desc->flags))
- status = -EIO;
- else if (sysfs_streq(buf, "high"))
+ if (sysfs_streq(buf, "high"))
status = gpiod_direction_output_raw(desc, 1);
else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low"))
status = gpiod_direction_output_raw(desc, 0);
@@ -78,43 +90,40 @@ static ssize_t gpio_direction_store(struct device *dev,
else
status = -EINVAL;
- mutex_unlock(&sysfs_lock);
+ mutex_unlock(&data->mutex);
+
return status ? : size;
}
+static DEVICE_ATTR_RW(direction);
-static /* const */ DEVICE_ATTR(direction, 0644,
- gpio_direction_show, gpio_direction_store);
-
-static ssize_t gpio_value_show(struct device *dev,
+static ssize_t value_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct gpio_desc *desc = dev_get_drvdata(dev);
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpio_desc *desc = data->desc;
ssize_t status;
- mutex_lock(&sysfs_lock);
+ mutex_lock(&data->mutex);
- if (!test_bit(FLAG_EXPORT, &desc->flags))
- status = -EIO;
- else
- status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc));
+ status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc));
+
+ mutex_unlock(&data->mutex);
- mutex_unlock(&sysfs_lock);
return status;
}
-static ssize_t gpio_value_store(struct device *dev,
+static ssize_t value_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
- struct gpio_desc *desc = dev_get_drvdata(dev);
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpio_desc *desc = data->desc;
ssize_t status;
- mutex_lock(&sysfs_lock);
+ mutex_lock(&data->mutex);
- if (!test_bit(FLAG_EXPORT, &desc->flags))
- status = -EIO;
- else if (!test_bit(FLAG_IS_OUT, &desc->flags))
+ if (!test_bit(FLAG_IS_OUT, &desc->flags)) {
status = -EPERM;
- else {
+ } else {
long value;
status = kstrtol(buf, 0, &value);
@@ -124,172 +133,168 @@ static ssize_t gpio_value_store(struct device *dev,
}
}
- mutex_unlock(&sysfs_lock);
+ mutex_unlock(&data->mutex);
+
return status;
}
-
-static DEVICE_ATTR(value, 0644,
- gpio_value_show, gpio_value_store);
+static DEVICE_ATTR_RW(value);
static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
{
- struct kernfs_node *value_sd = priv;
+ struct gpiod_data *data = priv;
+
+ sysfs_notify_dirent(data->value_kn);
- sysfs_notify_dirent(value_sd);
return IRQ_HANDLED;
}
-static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
- unsigned long gpio_flags)
+/* Caller holds gpiod-data mutex. */
+static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
{
- struct kernfs_node *value_sd;
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpio_desc *desc = data->desc;
unsigned long irq_flags;
- int ret, irq, id;
+ int ret;
- if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags)
- return 0;
-
- irq = gpiod_to_irq(desc);
- if (irq < 0)
+ data->irq = gpiod_to_irq(desc);
+ if (data->irq < 0)
return -EIO;
- id = desc->flags >> ID_SHIFT;
- value_sd = idr_find(&dirent_idr, id);
- if (value_sd)
- free_irq(irq, value_sd);
-
- desc->flags &= ~GPIO_TRIGGER_MASK;
-
- if (!gpio_flags) {
- gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
- ret = 0;
- goto free_id;
- }
+ data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value");
+ if (!data->value_kn)
+ return -ENODEV;
irq_flags = IRQF_SHARED;
- if (test_bit(FLAG_TRIG_FALL, &gpio_flags))
+ if (flags & GPIO_IRQF_TRIGGER_FALLING)
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
- if (test_bit(FLAG_TRIG_RISE, &gpio_flags))
+ if (flags & GPIO_IRQF_TRIGGER_RISING)
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
- if (!value_sd) {
- value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
- if (!value_sd) {
- ret = -ENODEV;
- goto err_out;
- }
-
- ret = idr_alloc(&dirent_idr, value_sd, 1, 0, GFP_KERNEL);
- if (ret < 0)
- goto free_sd;
- id = ret;
-
- desc->flags &= GPIO_FLAGS_MASK;
- desc->flags |= (unsigned long)id << ID_SHIFT;
-
- if (desc->flags >> ID_SHIFT != id) {
- ret = -ERANGE;
- goto free_id;
- }
- }
+ /*
+ * FIXME: This should be done in the irq_request_resources callback
+ * when the irq is requested, but a few drivers currently fail
+ * to do so.
+ *
+ * Remove this redundant call (along with the corresponding
+ * unlock) when those drivers have been fixed.
+ */
+ ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+ if (ret < 0)
+ goto err_put_kn;
- ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags,
- "gpiolib", value_sd);
+ ret = request_any_context_irq(data->irq, gpio_sysfs_irq, irq_flags,
+ "gpiolib", data);
if (ret < 0)
- goto free_id;
+ goto err_unlock;
- ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
- if (ret < 0) {
- gpiod_warn(desc, "failed to flag the GPIO for IRQ\n");
- goto free_id;
- }
+ data->irq_flags = flags;
- desc->flags |= gpio_flags;
return 0;
-free_id:
- idr_remove(&dirent_idr, id);
- desc->flags &= GPIO_FLAGS_MASK;
-free_sd:
- if (value_sd)
- sysfs_put(value_sd);
-err_out:
+err_unlock:
+ gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+err_put_kn:
+ sysfs_put(data->value_kn);
+
return ret;
}
+/*
+ * Caller holds gpiod-data mutex (unless called after class-device
+ * deregistration).
+ */
+static void gpio_sysfs_free_irq(struct device *dev)
+{
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpio_desc *desc = data->desc;
+
+ data->irq_flags = 0;
+ free_irq(data->irq, data);
+ gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+ sysfs_put(data->value_kn);
+}
+
static const struct {
const char *name;
- unsigned long flags;
+ unsigned char flags;
} trigger_types[] = {
{ "none", 0 },
- { "falling", BIT(FLAG_TRIG_FALL) },
- { "rising", BIT(FLAG_TRIG_RISE) },
- { "both", BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE) },
+ { "falling", GPIO_IRQF_TRIGGER_FALLING },
+ { "rising", GPIO_IRQF_TRIGGER_RISING },
+ { "both", GPIO_IRQF_TRIGGER_BOTH },
};
-static ssize_t gpio_edge_show(struct device *dev,
+static ssize_t edge_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- const struct gpio_desc *desc = dev_get_drvdata(dev);
- ssize_t status;
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ ssize_t status = 0;
+ int i;
- mutex_lock(&sysfs_lock);
-
- if (!test_bit(FLAG_EXPORT, &desc->flags))
- status = -EIO;
- else {
- int i;
+ mutex_lock(&data->mutex);
- status = 0;
- for (i = 0; i < ARRAY_SIZE(trigger_types); i++)
- if ((desc->flags & GPIO_TRIGGER_MASK)
- == trigger_types[i].flags) {
- status = sprintf(buf, "%s\n",
- trigger_types[i].name);
- break;
- }
+ for (i = 0; i < ARRAY_SIZE(trigger_types); i++) {
+ if (data->irq_flags == trigger_types[i].flags) {
+ status = sprintf(buf, "%s\n", trigger_types[i].name);
+ break;
+ }
}
- mutex_unlock(&sysfs_lock);
+ mutex_unlock(&data->mutex);
+
return status;
}
-static ssize_t gpio_edge_store(struct device *dev,
+static ssize_t edge_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
- struct gpio_desc *desc = dev_get_drvdata(dev);
- ssize_t status;
- int i;
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ unsigned char flags;
+ ssize_t status = size;
+ int i;
- for (i = 0; i < ARRAY_SIZE(trigger_types); i++)
+ for (i = 0; i < ARRAY_SIZE(trigger_types); i++) {
if (sysfs_streq(trigger_types[i].name, buf))
- goto found;
- return -EINVAL;
+ break;
+ }
-found:
- mutex_lock(&sysfs_lock);
+ if (i == ARRAY_SIZE(trigger_types))
+ return -EINVAL;
- if (!test_bit(FLAG_EXPORT, &desc->flags))
- status = -EIO;
- else {
- status = gpio_setup_irq(desc, dev, trigger_types[i].flags);
+ flags = trigger_types[i].flags;
+
+ mutex_lock(&data->mutex);
+
+ if (flags == data->irq_flags) {
+ status = size;
+ goto out_unlock;
+ }
+
+ if (data->irq_flags)
+ gpio_sysfs_free_irq(dev);
+
+ if (flags) {
+ status = gpio_sysfs_request_irq(dev, flags);
if (!status)
status = size;
}
- mutex_unlock(&sysfs_lock);
+out_unlock:
+ mutex_unlock(&data->mutex);
return status;
}
+static DEVICE_ATTR_RW(edge);
-static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);
-
-static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev,
- int value)
+/* Caller holds gpiod-data mutex. */
+static int gpio_sysfs_set_active_low(struct device *dev, int value)
{
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpio_desc *desc = data->desc;
int status = 0;
+ unsigned int flags = data->irq_flags;
if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value)
return 0;
@@ -300,69 +305,59 @@ static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev,
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
/* reconfigure poll(2) support if enabled on one edge only */
- if (dev != NULL && (!!test_bit(FLAG_TRIG_RISE, &desc->flags) ^
- !!test_bit(FLAG_TRIG_FALL, &desc->flags))) {
- unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK;
-
- gpio_setup_irq(desc, dev, 0);
- status = gpio_setup_irq(desc, dev, trigger_flags);
+ if (flags == GPIO_IRQF_TRIGGER_FALLING ||
+ flags == GPIO_IRQF_TRIGGER_RISING) {
+ gpio_sysfs_free_irq(dev);
+ status = gpio_sysfs_request_irq(dev, flags);
}
return status;
}
-static ssize_t gpio_active_low_show(struct device *dev,
+static ssize_t active_low_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- const struct gpio_desc *desc = dev_get_drvdata(dev);
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpio_desc *desc = data->desc;
ssize_t status;
- mutex_lock(&sysfs_lock);
+ mutex_lock(&data->mutex);
- if (!test_bit(FLAG_EXPORT, &desc->flags))
- status = -EIO;
- else
- status = sprintf(buf, "%d\n",
+ status = sprintf(buf, "%d\n",
!!test_bit(FLAG_ACTIVE_LOW, &desc->flags));
- mutex_unlock(&sysfs_lock);
+ mutex_unlock(&data->mutex);
return status;
}
-static ssize_t gpio_active_low_store(struct device *dev,
+static ssize_t active_low_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
- struct gpio_desc *desc = dev_get_drvdata(dev);
+ struct gpiod_data *data = dev_get_drvdata(dev);
ssize_t status;
+ long value;
- mutex_lock(&sysfs_lock);
-
- if (!test_bit(FLAG_EXPORT, &desc->flags)) {
- status = -EIO;
- } else {
- long value;
+ mutex_lock(&data->mutex);
- status = kstrtol(buf, 0, &value);
- if (status == 0)
- status = sysfs_set_active_low(desc, dev, value != 0);
- }
+ status = kstrtol(buf, 0, &value);
+ if (status == 0)
+ status = gpio_sysfs_set_active_low(dev, value);
- mutex_unlock(&sysfs_lock);
+ mutex_unlock(&data->mutex);
return status ? : size;
}
-
-static DEVICE_ATTR(active_low, 0644,
- gpio_active_low_show, gpio_active_low_store);
+static DEVICE_ATTR_RW(active_low);
static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
- struct gpio_desc *desc = dev_get_drvdata(dev);
+ struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpio_desc *desc = data->desc;
umode_t mode = attr->mode;
- bool show_direction = test_bit(FLAG_SYSFS_DIR, &desc->flags);
+ bool show_direction = data->direction_can_change;
if (attr == &dev_attr_direction.attr) {
if (!show_direction)
@@ -402,32 +397,32 @@ static const struct attribute_group *gpio_groups[] = {
* /ngpio ... matching gpio_chip.ngpio
*/
-static ssize_t chip_base_show(struct device *dev,
+static ssize_t base_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const struct gpio_chip *chip = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", chip->base);
}
-static DEVICE_ATTR(base, 0444, chip_base_show, NULL);
+static DEVICE_ATTR_RO(base);
-static ssize_t chip_label_show(struct device *dev,
+static ssize_t label_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const struct gpio_chip *chip = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", chip->label ? : "");
}
-static DEVICE_ATTR(label, 0444, chip_label_show, NULL);
+static DEVICE_ATTR_RO(label);
-static ssize_t chip_ngpio_show(struct device *dev,
+static ssize_t ngpio_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const struct gpio_chip *chip = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", chip->ngpio);
}
-static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL);
+static DEVICE_ATTR_RO(ngpio);
static struct attribute *gpiochip_attrs[] = {
&dev_attr_base.attr,
@@ -551,6 +546,8 @@ static struct class gpio_class = {
*/
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
+ struct gpio_chip *chip;
+ struct gpiod_data *data;
unsigned long flags;
int status;
const char *ioname = NULL;
@@ -568,8 +565,16 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
return -EINVAL;
}
+ chip = desc->chip;
+
mutex_lock(&sysfs_lock);
+ /* check if chip is being removed */
+ if (!chip || !chip->cdev) {
+ status = -ENODEV;
+ goto err_unlock;
+ }
+
spin_lock_irqsave(&gpio_lock, flags);
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
test_bit(FLAG_EXPORT, &desc->flags)) {
@@ -579,43 +584,54 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
test_bit(FLAG_REQUESTED, &desc->flags),
test_bit(FLAG_EXPORT, &desc->flags));
status = -EPERM;
- goto fail_unlock;
+ goto err_unlock;
}
+ spin_unlock_irqrestore(&gpio_lock, flags);
- if (desc->chip->direction_input && desc->chip->direction_output &&
- direction_may_change) {
- set_bit(FLAG_SYSFS_DIR, &desc->flags);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ status = -ENOMEM;
+ goto err_unlock;
}
- spin_unlock_irqrestore(&gpio_lock, flags);
+ data->desc = desc;
+ mutex_init(&data->mutex);
+ if (chip->direction_input && chip->direction_output)
+ data->direction_can_change = direction_may_change;
+ else
+ data->direction_can_change = false;
offset = gpio_chip_hwgpio(desc);
- if (desc->chip->names && desc->chip->names[offset])
- ioname = desc->chip->names[offset];
+ if (chip->names && chip->names[offset])
+ ioname = chip->names[offset];
- dev = device_create_with_groups(&gpio_class, desc->chip->dev,
- MKDEV(0, 0), desc, gpio_groups,
+ dev = device_create_with_groups(&gpio_class, chip->dev,
+ MKDEV(0, 0), data, gpio_groups,
ioname ? ioname : "gpio%u",
desc_to_gpio(desc));
if (IS_ERR(dev)) {
status = PTR_ERR(dev);
- goto fail_unlock;
+ goto err_free_data;
}
set_bit(FLAG_EXPORT, &desc->flags);
mutex_unlock(&sysfs_lock);
return 0;
-fail_unlock:
+err_free_data:
+ kfree(data);
+err_unlock:
mutex_unlock(&sysfs_lock);
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
return status;
}
EXPORT_SYMBOL_GPL(gpiod_export);
-static int match_export(struct device *dev, const void *data)
+static int match_export(struct device *dev, const void *desc)
{
- return dev_get_drvdata(dev) == data;
+ struct gpiod_data *data = dev_get_drvdata(dev);
+
+ return data->desc == desc;
}
/**
@@ -632,82 +648,26 @@ static int match_export(struct device *dev, const void *data)
int gpiod_export_link(struct device *dev, const char *name,
struct gpio_desc *desc)
{
- int status = -EINVAL;
+ struct device *cdev;
+ int ret;
if (!desc) {
pr_warn("%s: invalid GPIO\n", __func__);
return -EINVAL;
}
- mutex_lock(&sysfs_lock);
+ cdev = class_find_device(&gpio_class, NULL, desc, match_export);
+ if (!cdev)
+ return -ENODEV;
- if (test_bit(FLAG_EXPORT, &desc->flags)) {
- struct device *tdev;
+ ret = sysfs_create_link(&dev->kobj, &cdev->kobj, name);
+ put_device(cdev);
- tdev = class_find_device(&gpio_class, NULL, desc, match_export);
- if (tdev != NULL) {
- status = sysfs_create_link(&dev->kobj, &tdev->kobj,
- name);
- put_device(tdev);
- } else {
- status = -ENODEV;
- }
- }
-
- mutex_unlock(&sysfs_lock);
-
- if (status)
- gpiod_dbg(desc, "%s: status %d\n", __func__, status);
-
- return status;
+ return ret;
}
EXPORT_SYMBOL_GPL(gpiod_export_link);
/**
- * gpiod_sysfs_set_active_low - set the polarity of gpio sysfs value
- * @gpio: gpio to change
- * @value: non-zero to use active low, i.e. inverted values
- *
- * Set the polarity of /sys/class/gpio/gpioN/value sysfs attribute.
- * The GPIO does not have to be exported yet. If poll(2) support has
- * been enabled for either rising or falling edge, it will be
- * reconfigured to follow the new polarity.
- *
- * Returns zero on success, else an error.
- */
-int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value)
-{
- struct device *dev = NULL;
- int status = -EINVAL;
-
- if (!desc) {
- pr_warn("%s: invalid GPIO\n", __func__);
- return -EINVAL;
- }
-
- mutex_lock(&sysfs_lock);
-
- if (test_bit(FLAG_EXPORT, &desc->flags)) {
- dev = class_find_device(&gpio_class, NULL, desc, match_export);
- if (dev == NULL) {
- status = -ENODEV;
- goto unlock;
- }
- }
-
- status = sysfs_set_active_low(desc, dev, value);
- put_device(dev);
-unlock:
- mutex_unlock(&sysfs_lock);
-
- if (status)
- gpiod_dbg(desc, "%s: status %d\n", __func__, status);
-
- return status;
-}
-EXPORT_SYMBOL_GPL(gpiod_sysfs_set_active_low);
-
-/**
* gpiod_unexport - reverse effect of gpio_export()
* @gpio: gpio to make unavailable
*
@@ -715,8 +675,8 @@ EXPORT_SYMBOL_GPL(gpiod_sysfs_set_active_low);
*/
void gpiod_unexport(struct gpio_desc *desc)
{
- int status = 0;
- struct device *dev = NULL;
+ struct gpiod_data *data;
+ struct device *dev;
if (!desc) {
pr_warn("%s: invalid GPIO\n", __func__);
@@ -725,78 +685,85 @@ void gpiod_unexport(struct gpio_desc *desc)
mutex_lock(&sysfs_lock);
- if (test_bit(FLAG_EXPORT, &desc->flags)) {
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ goto err_unlock;
- dev = class_find_device(&gpio_class, NULL, desc, match_export);
- if (dev) {
- gpio_setup_irq(desc, dev, 0);
- clear_bit(FLAG_SYSFS_DIR, &desc->flags);
- clear_bit(FLAG_EXPORT, &desc->flags);
- } else
- status = -ENODEV;
- }
+ dev = class_find_device(&gpio_class, NULL, desc, match_export);
+ if (!dev)
+ goto err_unlock;
+
+ data = dev_get_drvdata(dev);
+
+ clear_bit(FLAG_EXPORT, &desc->flags);
+
+ device_unregister(dev);
+
+ /*
+ * Release irq after deregistration to prevent race with edge_store.
+ */
+ if (data->irq_flags)
+ gpio_sysfs_free_irq(dev);
mutex_unlock(&sysfs_lock);
- if (dev) {
- device_unregister(dev);
- put_device(dev);
- }
+ put_device(dev);
+ kfree(data);
- if (status)
- gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+ return;
+
+err_unlock:
+ mutex_unlock(&sysfs_lock);
}
EXPORT_SYMBOL_GPL(gpiod_unexport);
-int gpiochip_export(struct gpio_chip *chip)
+int gpiochip_sysfs_register(struct gpio_chip *chip)
{
- int status;
struct device *dev;
- /* Many systems register gpio chips for SOC support very early,
+ /*
+ * Many systems add gpio chips for SOC support very early,
* before driver model support is available. In those cases we
- * export this later, in gpiolib_sysfs_init() ... here we just
+ * register later, in gpiolib_sysfs_init() ... here we just
* verify that _some_ field of gpio_class got initialized.
*/
if (!gpio_class.p)
return 0;
/* use chip->base for the ID; it's already known to be unique */
- mutex_lock(&sysfs_lock);
dev = device_create_with_groups(&gpio_class, chip->dev, MKDEV(0, 0),
chip, gpiochip_groups,
"gpiochip%d", chip->base);
if (IS_ERR(dev))
- status = PTR_ERR(dev);
- else
- status = 0;
- chip->exported = (status == 0);
- mutex_unlock(&sysfs_lock);
+ return PTR_ERR(dev);
- if (status)
- chip_dbg(chip, "%s: status %d\n", __func__, status);
+ mutex_lock(&sysfs_lock);
+ chip->cdev = dev;
+ mutex_unlock(&sysfs_lock);
- return status;
+ return 0;
}
-void gpiochip_unexport(struct gpio_chip *chip)
+void gpiochip_sysfs_unregister(struct gpio_chip *chip)
{
- int status;
- struct device *dev;
+ struct gpio_desc *desc;
+ unsigned int i;
+
+ if (!chip->cdev)
+ return;
+ device_unregister(chip->cdev);
+
+ /* prevent further gpiod exports */
mutex_lock(&sysfs_lock);
- dev = class_find_device(&gpio_class, NULL, chip, match_export);
- if (dev) {
- put_device(dev);
- device_unregister(dev);
- chip->exported = false;
- status = 0;
- } else
- status = -ENODEV;
+ chip->cdev = NULL;
mutex_unlock(&sysfs_lock);
- if (status)
- chip_dbg(chip, "%s: status %d\n", __func__, status);
+ /* unregister gpiod class devices owned by sysfs */
+ for (i = 0; i < chip->ngpio; i++) {
+ desc = &chip->desc[i];
+ if (test_and_clear_bit(FLAG_SYSFS, &desc->flags))
+ gpiod_free(desc);
+ }
}
static int __init gpiolib_sysfs_init(void)
@@ -817,19 +784,20 @@ static int __init gpiolib_sysfs_init(void)
*/
spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(chip, &gpio_chips, list) {
- if (chip->exported)
+ if (chip->cdev)
continue;
/*
- * TODO we yield gpio_lock here because gpiochip_export()
- * acquires a mutex. This is unsafe and needs to be fixed.
+ * TODO we yield gpio_lock here because
+ * gpiochip_sysfs_register() acquires a mutex. This is unsafe
+ * and needs to be fixed.
*
* Also it would be nice to use gpiochip_find() here so we
* can keep gpio_chips local to gpiolib.c, but the yield of
* gpio_lock prevents us from doing this.
*/
spin_unlock_irqrestore(&gpio_lock, flags);
- status = gpiochip_export(chip);
+ status = gpiochip_sysfs_register(chip);
spin_lock_irqsave(&gpio_lock, flags);
}
spin_unlock_irqrestore(&gpio_lock, flags);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 59eaa23767d8..be42ab368a80 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -53,6 +53,11 @@ static DEFINE_MUTEX(gpio_lookup_lock);
static LIST_HEAD(gpio_lookup_list);
LIST_HEAD(gpio_chips);
+
+static void gpiochip_free_hogs(struct gpio_chip *chip);
+static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
+
+
static inline void desc_set_label(struct gpio_desc *d, const char *label)
{
d->label = label;
@@ -285,7 +290,7 @@ int gpiochip_add(struct gpio_chip *chip)
of_gpiochip_add(chip);
acpi_gpiochip_add(chip);
- status = gpiochip_export(chip);
+ status = gpiochip_sysfs_register(chip);
if (status)
goto err_remove_chip;
@@ -297,6 +302,7 @@ int gpiochip_add(struct gpio_chip *chip)
err_remove_chip:
acpi_gpiochip_remove(chip);
+ gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
spin_lock_irqsave(&gpio_lock, flags);
list_del(&chip->list);
@@ -313,10 +319,6 @@ err_free_descs:
}
EXPORT_SYMBOL_GPL(gpiochip_add);
-/* Forward-declaration */
-static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
-static void gpiochip_free_hogs(struct gpio_chip *chip);
-
/**
* gpiochip_remove() - unregister a gpio_chip
* @chip: the chip to unregister
@@ -325,10 +327,12 @@ static void gpiochip_free_hogs(struct gpio_chip *chip);
*/
void gpiochip_remove(struct gpio_chip *chip)
{
+ struct gpio_desc *desc;
unsigned long flags;
unsigned id;
+ bool requested = false;
- gpiochip_unexport(chip);
+ gpiochip_sysfs_unregister(chip);
gpiochip_irqchip_remove(chip);
@@ -339,15 +343,17 @@ void gpiochip_remove(struct gpio_chip *chip)
spin_lock_irqsave(&gpio_lock, flags);
for (id = 0; id < chip->ngpio; id++) {
- if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags))
- dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
+ desc = &chip->desc[id];
+ desc->chip = NULL;
+ if (test_bit(FLAG_REQUESTED, &desc->flags))
+ requested = true;
}
- for (id = 0; id < chip->ngpio; id++)
- chip->desc[id].chip = NULL;
-
list_del(&chip->list);
spin_unlock_irqrestore(&gpio_lock, flags);
+ if (requested)
+ dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
+
kfree(chip->desc);
chip->desc = NULL;
}
@@ -439,6 +445,8 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
*/
irq_set_handler_data(parent_irq, gpiochip);
irq_set_chained_handler(parent_irq, parent_handler);
+
+ gpiochip->irq_parent = parent_irq;
}
/* Set the parent IRQ for all affected IRQs */
@@ -547,6 +555,11 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
acpi_gpiochip_free_interrupts(gpiochip);
+ if (gpiochip->irq_parent) {
+ irq_set_chained_handler(gpiochip->irq_parent, NULL);
+ irq_set_handler_data(gpiochip->irq_parent, NULL);
+ }
+
/* Remove all IRQ mappings and delete the domain */
if (gpiochip->irqdomain) {
for (offset = 0; offset < gpiochip->ngpio; offset++)
@@ -606,7 +619,7 @@ int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
of_node = gpiochip->dev->of_node;
#ifdef CONFIG_OF_GPIO
/*
- * If the gpiochip has an assigned OF node this takes precendence
+ * If the gpiochip has an assigned OF node this takes precedence
* FIXME: get rid of this and use gpiochip->dev->of_node everywhere
*/
if (gpiochip->of_node)
@@ -1209,7 +1222,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
/*
* _gpio_set_open_drain_value() - Set the open drain gpio's value.
* @desc: gpio descriptor whose state need to be set.
- * @value: Non-zero for setting it HIGH otherise it will set to LOW.
+ * @value: Non-zero for setting it HIGH otherwise it will set to LOW.
*/
static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
{
@@ -1236,7 +1249,7 @@ static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
/*
* _gpio_set_open_source_value() - Set the open source gpio's value.
* @desc: gpio descriptor whose state need to be set.
- * @value: Non-zero for setting it HIGH otherise it will set to LOW.
+ * @value: Non-zero for setting it HIGH otherwise it will set to LOW.
*/
static void _gpio_set_open_source_value(struct gpio_desc *desc, bool value)
{
@@ -1298,17 +1311,16 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
continue;
}
/* set outputs if the corresponding mask bit is set */
- if (__test_and_clear_bit(i, mask)) {
+ if (__test_and_clear_bit(i, mask))
chip->set(chip, i, test_bit(i, bits));
- }
}
}
}
-static void gpiod_set_array_priv(bool raw, bool can_sleep,
- unsigned int array_size,
- struct gpio_desc **desc_array,
- int *value_array)
+static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
+ unsigned int array_size,
+ struct gpio_desc **desc_array,
+ int *value_array)
{
int i = 0;
@@ -1318,9 +1330,9 @@ static void gpiod_set_array_priv(bool raw, bool can_sleep,
unsigned long bits[BITS_TO_LONGS(chip->ngpio)];
int count = 0;
- if (!can_sleep) {
+ if (!can_sleep)
WARN_ON(chip->can_sleep);
- }
+
memset(mask, 0, sizeof(mask));
do {
struct gpio_desc *desc = desc_array[i];
@@ -1335,24 +1347,22 @@ static void gpiod_set_array_priv(bool raw, bool can_sleep,
* open drain and open source outputs are set individually
*/
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
- _gpio_set_open_drain_value(desc,value);
+ _gpio_set_open_drain_value(desc, value);
} else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
_gpio_set_open_source_value(desc, value);
} else {
__set_bit(hwgpio, mask);
- if (value) {
+ if (value)
__set_bit(hwgpio, bits);
- } else {
+ else
__clear_bit(hwgpio, bits);
- }
count++;
}
i++;
} while ((i < array_size) && (desc_array[i]->chip == chip));
/* push collected bits to outputs */
- if (count != 0) {
+ if (count != 0)
gpio_chip_set_multiple(chip, mask, bits);
- }
}
}
@@ -1401,7 +1411,7 @@ void gpiod_set_value(struct gpio_desc *desc, int value)
EXPORT_SYMBOL_GPL(gpiod_set_value);
/**
- * gpiod_set_raw_array() - assign values to an array of GPIOs
+ * gpiod_set_raw_array_value() - assign values to an array of GPIOs
* @array_size: number of elements in the descriptor / value arrays
* @desc_array: array of GPIO descriptors whose values will be assigned
* @value_array: array of values to assign
@@ -1412,17 +1422,18 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
* This function should be called from contexts where we cannot sleep, and will
* complain if the GPIO chip functions potentially sleep.
*/
-void gpiod_set_raw_array(unsigned int array_size,
+void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array, int *value_array)
{
if (!desc_array)
return;
- gpiod_set_array_priv(true, false, array_size, desc_array, value_array);
+ gpiod_set_array_value_priv(true, false, array_size, desc_array,
+ value_array);
}
-EXPORT_SYMBOL_GPL(gpiod_set_raw_array);
+EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
/**
- * gpiod_set_array() - assign values to an array of GPIOs
+ * gpiod_set_array_value() - assign values to an array of GPIOs
* @array_size: number of elements in the descriptor / value arrays
* @desc_array: array of GPIO descriptors whose values will be assigned
* @value_array: array of values to assign
@@ -1433,14 +1444,15 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array);
* This function should be called from contexts where we cannot sleep, and will
* complain if the GPIO chip functions potentially sleep.
*/
-void gpiod_set_array(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+void gpiod_set_array_value(unsigned int array_size,
+ struct gpio_desc **desc_array, int *value_array)
{
if (!desc_array)
return;
- gpiod_set_array_priv(false, false, array_size, desc_array, value_array);
+ gpiod_set_array_value_priv(false, false, array_size, desc_array,
+ value_array);
}
-EXPORT_SYMBOL_GPL(gpiod_set_array);
+EXPORT_SYMBOL_GPL(gpiod_set_array_value);
/**
* gpiod_cansleep() - report whether gpio value access may sleep
@@ -1602,7 +1614,7 @@ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
/**
- * gpiod_set_raw_array_cansleep() - assign values to an array of GPIOs
+ * gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs
* @array_size: number of elements in the descriptor / value arrays
* @desc_array: array of GPIO descriptors whose values will be assigned
* @value_array: array of values to assign
@@ -1612,19 +1624,20 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*
* This function is to be called from contexts that can sleep.
*/
-void gpiod_set_raw_array_cansleep(unsigned int array_size,
- struct gpio_desc **desc_array,
- int *value_array)
+void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
+ struct gpio_desc **desc_array,
+ int *value_array)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
- gpiod_set_array_priv(true, true, array_size, desc_array, value_array);
+ gpiod_set_array_value_priv(true, true, array_size, desc_array,
+ value_array);
}
-EXPORT_SYMBOL_GPL(gpiod_set_raw_array_cansleep);
+EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);
/**
- * gpiod_set_array_cansleep() - assign values to an array of GPIOs
+ * gpiod_set_array_value_cansleep() - assign values to an array of GPIOs
* @array_size: number of elements in the descriptor / value arrays
* @desc_array: array of GPIO descriptors whose values will be assigned
* @value_array: array of values to assign
@@ -1634,16 +1647,17 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_cansleep);
*
* This function is to be called from contexts that can sleep.
*/
-void gpiod_set_array_cansleep(unsigned int array_size,
- struct gpio_desc **desc_array,
- int *value_array)
+void gpiod_set_array_value_cansleep(unsigned int array_size,
+ struct gpio_desc **desc_array,
+ int *value_array)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
- gpiod_set_array_priv(false, true, array_size, desc_array, value_array);
+ gpiod_set_array_value_priv(false, true, array_size, desc_array,
+ value_array);
}
-EXPORT_SYMBOL_GPL(gpiod_set_array_cansleep);
+EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
/**
* gpiod_add_lookup_table() - register GPIO device consumers
@@ -1878,7 +1892,7 @@ EXPORT_SYMBOL_GPL(gpiod_count);
*
* Return the GPIO descriptor corresponding to the function con_id of device
* dev, -ENOENT if no GPIO has been assigned to the requested function, or
- * another IS_ERR() code if an error occured while trying to acquire the GPIO.
+ * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
*/
struct gpio_desc *__must_check __gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags)
@@ -1958,7 +1972,7 @@ static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
*
* Return a valid GPIO descriptor, -ENOENT if no GPIO has been assigned to the
* requested function and/or index, or another IS_ERR() code if an error
- * occured while trying to acquire the GPIO.
+ * occurred while trying to acquire the GPIO.
*/
struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
const char *con_id,
@@ -2116,13 +2130,15 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
local_desc = gpiochip_request_own_desc(chip, hwnum, name);
if (IS_ERR(local_desc)) {
- pr_debug("requesting own GPIO %s failed\n", name);
+ pr_err("requesting hog GPIO %s (chip %s, offset %d) failed\n",
+ name, chip->label, hwnum);
return PTR_ERR(local_desc);
}
status = gpiod_configure_flags(desc, name, lflags, dflags);
if (status < 0) {
- pr_debug("setup of GPIO %s failed\n", name);
+ pr_err("setup of hog GPIO %s (chip %s, offset %d) failed\n",
+ name, chip->label, hwnum);
gpiochip_free_own_desc(desc);
return status;
}
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 594b1798c0e7..bf343004b008 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -83,20 +83,12 @@ struct gpio_desc {
#define FLAG_IS_OUT 1
#define FLAG_EXPORT 2 /* protected by sysfs_lock */
#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
-#define FLAG_TRIG_FALL 4 /* trigger on falling edge */
-#define FLAG_TRIG_RISE 5 /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 6 /* value has active low */
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
-#define FLAG_SYSFS_DIR 10 /* show sysfs direction attribute */
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
-#define ID_SHIFT 16 /* add new flags before this one */
-
-#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
-#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
-
const char *label;
};
@@ -151,17 +143,17 @@ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
#ifdef CONFIG_GPIO_SYSFS
-int gpiochip_export(struct gpio_chip *chip);
-void gpiochip_unexport(struct gpio_chip *chip);
+int gpiochip_sysfs_register(struct gpio_chip *chip);
+void gpiochip_sysfs_unregister(struct gpio_chip *chip);
#else
-static inline int gpiochip_export(struct gpio_chip *chip)
+static inline int gpiochip_sysfs_register(struct gpio_chip *chip)
{
return 0;
}
-static inline void gpiochip_unexport(struct gpio_chip *chip)
+static inline void gpiochip_sysfs_unregister(struct gpio_chip *chip)
{
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index 69af73f15310..596ee5cd3b84 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -430,9 +430,10 @@ static int unregister_process_nocpsch(struct device_queue_manager *dqm,
BUG_ON(!dqm || !qpd);
- BUG_ON(!list_empty(&qpd->queues_list));
+ pr_debug("In func %s\n", __func__);
- pr_debug("kfd: In func %s\n", __func__);
+ pr_debug("qpd->queues_list is %s\n",
+ list_empty(&qpd->queues_list) ? "empty" : "not empty");
retval = 0;
mutex_lock(&dqm->lock);
@@ -882,6 +883,8 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q,
return -ENOMEM;
}
+ init_sdma_vm(dqm, q, qpd);
+
retval = mqd->init_mqd(mqd, &q->mqd, &q->mqd_mem_obj,
&q->gart_mqd_addr, &q->properties);
if (retval != 0)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
index 661c6605d31b..c25728bc388a 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
@@ -684,8 +684,6 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
dev->node_props.cpu_core_id_base);
sysfs_show_32bit_prop(buffer, "simd_id_base",
dev->node_props.simd_id_base);
- sysfs_show_32bit_prop(buffer, "capability",
- dev->node_props.capability);
sysfs_show_32bit_prop(buffer, "max_waves_per_simd",
dev->node_props.max_waves_per_simd);
sysfs_show_32bit_prop(buffer, "lds_size_in_kb",
@@ -728,14 +726,16 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
sysfs_show_32bit_prop(buffer, "max_engine_clk_fcompute",
dev->gpu->kfd2kgd->get_max_engine_clock_in_mhz(
dev->gpu->kgd));
+
sysfs_show_64bit_prop(buffer, "local_mem_size",
- dev->gpu->kfd2kgd->get_vmem_size(
- dev->gpu->kgd));
+ (unsigned long long int) 0);
sysfs_show_32bit_prop(buffer, "fw_version",
dev->gpu->kfd2kgd->get_fw_version(
dev->gpu->kgd,
KGD_ENGINE_MEC1));
+ sysfs_show_32bit_prop(buffer, "capability",
+ dev->node_props.capability);
}
return sysfs_show_32bit_prop(buffer, "max_engine_clk_ccompute",
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 266dcd6cdf3b..0a957828b3bd 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -36,9 +36,6 @@
#include <linux/pci.h>
#include <linux/export.h>
-#ifdef CONFIG_X86
-#include <asm/mtrr.h>
-#endif
static int drm_version(struct drm_device *dev, void *data,
struct drm_file *file_priv);
@@ -197,16 +194,7 @@ static int drm_getmap(struct drm_device *dev, void *data,
map->type = r_list->map->type;
map->flags = r_list->map->flags;
map->handle = (void *)(unsigned long) r_list->user_token;
-
-#ifdef CONFIG_X86
- /*
- * There appears to be exactly one user of the mtrr index: dritest.
- * It's easy enough to keep it working on non-PAT systems.
- */
- map->mtrr = phys_wc_to_mtrr_index(r_list->map->mtrr);
-#else
- map->mtrr = -1;
-#endif
+ map->mtrr = arch_phys_wc_index(r_list->map->mtrr);
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index c8a34476570a..af9662e58272 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -131,12 +131,11 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
/* Reinitialize corresponding vblank timestamp if high-precision query
* available. Skip this step if query unsupported or failed. Will
- * reinitialize delayed at next vblank interrupt in that case.
+ * reinitialize delayed at next vblank interrupt in that case and
+ * assign 0 for now, to mark the vblanktimestamp as invalid.
*/
- if (rc) {
- tslot = atomic_read(&vblank->count) + diff;
- vblanktimestamp(dev, crtc, tslot) = t_vblank;
- }
+ tslot = atomic_read(&vblank->count) + diff;
+ vblanktimestamp(dev, crtc, tslot) = rc ? t_vblank : (struct timeval) {0, 0};
smp_mb__before_atomic();
atomic_add(diff, &vblank->count);
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index 40c1db9ad7c3..2f0ed11024eb 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -465,6 +465,9 @@ int drm_plane_helper_commit(struct drm_plane *plane,
if (!crtc[i])
continue;
+ if (crtc[i]->cursor == plane)
+ continue;
+
/* There's no other way to figure out whether the crtc is running. */
ret = drm_crtc_vblank_get(crtc[i]);
if (ret == 0) {
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index ffc305fc2076..eb7e61078a5b 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -217,7 +217,7 @@ static ssize_t status_store(struct device *device,
mutex_unlock(&dev->mode_config.mutex);
- return ret;
+ return ret ? ret : count;
}
static ssize_t status_show(struct device *device,
diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
index 1f7e33f59de6..6714e5b193ea 100644
--- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
@@ -91,7 +91,7 @@ static void decon_wait_for_vblank(struct exynos_drm_crtc *crtc)
static void decon_clear_channel(struct decon_context *ctx)
{
- int win, ch_enabled = 0;
+ unsigned int win, ch_enabled = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -710,7 +710,7 @@ static void decon_dpms(struct exynos_drm_crtc *crtc, int mode)
}
}
-static struct exynos_drm_crtc_ops decon_crtc_ops = {
+static const struct exynos_drm_crtc_ops decon_crtc_ops = {
.dpms = decon_dpms,
.mode_fixup = decon_mode_fixup,
.commit = decon_commit,
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index 1dbfba58f909..30feb7d06624 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -32,7 +32,6 @@
#include <drm/bridge/ptn3460.h>
#include "exynos_dp_core.h"
-#include "exynos_drm_fimd.h"
#define ctx_from_connector(c) container_of(c, struct exynos_dp_device, \
connector)
@@ -196,7 +195,7 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
}
}
- dev_err(dp->dev, "EDID Read success!\n");
+ dev_dbg(dp->dev, "EDID Read success!\n");
return 0;
}
@@ -1066,6 +1065,8 @@ static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
static void exynos_dp_poweron(struct exynos_dp_device *dp)
{
+ struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
+
if (dp->dpms_mode == DRM_MODE_DPMS_ON)
return;
@@ -1076,7 +1077,8 @@ static void exynos_dp_poweron(struct exynos_dp_device *dp)
}
}
- fimd_dp_clock_enable(dp_to_crtc(dp), true);
+ if (crtc->ops->clock_enable)
+ crtc->ops->clock_enable(dp_to_crtc(dp), true);
clk_prepare_enable(dp->clock);
exynos_dp_phy_init(dp);
@@ -1087,6 +1089,8 @@ static void exynos_dp_poweron(struct exynos_dp_device *dp)
static void exynos_dp_poweroff(struct exynos_dp_device *dp)
{
+ struct exynos_drm_crtc *crtc = dp_to_crtc(dp);
+
if (dp->dpms_mode != DRM_MODE_DPMS_ON)
return;
@@ -1102,7 +1106,8 @@ static void exynos_dp_poweroff(struct exynos_dp_device *dp)
exynos_dp_phy_exit(dp);
clk_disable_unprepare(dp->clock);
- fimd_dp_clock_enable(dp_to_crtc(dp), false);
+ if (crtc->ops->clock_enable)
+ crtc->ops->clock_enable(dp_to_crtc(dp), false);
if (dp->panel) {
if (drm_panel_unprepare(dp->panel))
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index eb49195cec5c..9006b947e03c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -238,11 +238,11 @@ static struct drm_crtc_funcs exynos_crtc_funcs = {
};
struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
- struct drm_plane *plane,
- int pipe,
- enum exynos_drm_output_type type,
- struct exynos_drm_crtc_ops *ops,
- void *ctx)
+ struct drm_plane *plane,
+ int pipe,
+ enum exynos_drm_output_type type,
+ const struct exynos_drm_crtc_ops *ops,
+ void *ctx)
{
struct exynos_drm_crtc *exynos_crtc;
struct exynos_drm_private *private = drm_dev->dev_private;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
index 0ecd8fc45cff..0f3aa70818e3 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
@@ -18,11 +18,11 @@
#include "exynos_drm_drv.h"
struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
- struct drm_plane *plane,
- int pipe,
- enum exynos_drm_output_type type,
- struct exynos_drm_crtc_ops *ops,
- void *context);
+ struct drm_plane *plane,
+ int pipe,
+ enum exynos_drm_output_type type,
+ const struct exynos_drm_crtc_ops *ops,
+ void *context);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index e12ecb5d5d9a..29e3fb78c615 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -71,13 +71,6 @@ enum exynos_drm_output_type {
* @dma_addr: array of bus(accessed by dma) address to the memory region
* allocated for a overlay.
* @zpos: order of overlay layer(z position).
- * @index_color: if using color key feature then this value would be used
- * as index color.
- * @default_win: a window to be enabled.
- * @color_key: color key on or off.
- * @local_path: in case of lcd type, local path mode on or off.
- * @transparency: transparency on or off.
- * @activated: activated or not.
* @enabled: enabled or not.
* @resume: to resume or not.
*
@@ -108,13 +101,7 @@ struct exynos_drm_plane {
uint32_t pixel_format;
dma_addr_t dma_addr[MAX_FB_BUFFER];
unsigned int zpos;
- unsigned int index_color;
- bool default_win:1;
- bool color_key:1;
- bool local_path:1;
- bool transparency:1;
- bool activated:1;
bool enabled:1;
bool resume:1;
};
@@ -181,6 +168,10 @@ struct exynos_drm_display {
* @win_disable: disable hardware specific overlay.
* @te_handler: trigger to transfer video image at the tearing effect
* synchronization signal if there is a page flip request.
+ * @clock_enable: optional function enabling/disabling display domain clock,
+ * called from exynos-dp driver before powering up (with
+ * 'enable' argument as true) and after powering down (with
+ * 'enable' as false).
*/
struct exynos_drm_crtc;
struct exynos_drm_crtc_ops {
@@ -195,6 +186,7 @@ struct exynos_drm_crtc_ops {
void (*win_commit)(struct exynos_drm_crtc *crtc, unsigned int zpos);
void (*win_disable)(struct exynos_drm_crtc *crtc, unsigned int zpos);
void (*te_handler)(struct exynos_drm_crtc *crtc);
+ void (*clock_enable)(struct exynos_drm_crtc *crtc, bool enable);
};
/*
@@ -221,7 +213,7 @@ struct exynos_drm_crtc {
unsigned int dpms;
wait_queue_head_t pending_flip_queue;
struct drm_pending_vblank_event *event;
- struct exynos_drm_crtc_ops *ops;
+ const struct exynos_drm_crtc_ops *ops;
void *ctx;
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index 929cb03a8eab..142eb4e3f59e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -171,43 +171,6 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
return &exynos_fb->fb;
}
-static u32 exynos_drm_format_num_buffers(struct drm_mode_fb_cmd2 *mode_cmd)
-{
- unsigned int cnt = 0;
-
- if (mode_cmd->pixel_format != DRM_FORMAT_NV12)
- return drm_format_num_planes(mode_cmd->pixel_format);
-
- while (cnt != MAX_FB_BUFFER) {
- if (!mode_cmd->handles[cnt])
- break;
- cnt++;
- }
-
- /*
- * check if NV12 or NV12M.
- *
- * NV12
- * handles[0] = base1, offsets[0] = 0
- * handles[1] = base1, offsets[1] = Y_size
- *
- * NV12M
- * handles[0] = base1, offsets[0] = 0
- * handles[1] = base2, offsets[1] = 0
- */
- if (cnt == 2) {
- /*
- * in case of NV12 format, offsets[1] is not 0 and
- * handles[0] is same as handles[1].
- */
- if (mode_cmd->offsets[1] &&
- mode_cmd->handles[0] == mode_cmd->handles[1])
- cnt = 1;
- }
-
- return cnt;
-}
-
static struct drm_framebuffer *
exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
struct drm_mode_fb_cmd2 *mode_cmd)
@@ -230,7 +193,7 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd);
exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj);
- exynos_fb->buf_cnt = exynos_drm_format_num_buffers(mode_cmd);
+ exynos_fb->buf_cnt = drm_format_num_planes(mode_cmd->pixel_format);
DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 9819fa6a9e2a..a0edab833148 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -33,7 +33,6 @@
#include "exynos_drm_crtc.h"
#include "exynos_drm_plane.h"
#include "exynos_drm_iommu.h"
-#include "exynos_drm_fimd.h"
/*
* FIMD stands for Fully Interactive Mobile Display and
@@ -216,7 +215,7 @@ static void fimd_wait_for_vblank(struct exynos_drm_crtc *crtc)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
-static void fimd_enable_video_output(struct fimd_context *ctx, int win,
+static void fimd_enable_video_output(struct fimd_context *ctx, unsigned int win,
bool enable)
{
u32 val = readl(ctx->regs + WINCON(win));
@@ -229,7 +228,8 @@ static void fimd_enable_video_output(struct fimd_context *ctx, int win,
writel(val, ctx->regs + WINCON(win));
}
-static void fimd_enable_shadow_channel_path(struct fimd_context *ctx, int win,
+static void fimd_enable_shadow_channel_path(struct fimd_context *ctx,
+ unsigned int win,
bool enable)
{
u32 val = readl(ctx->regs + SHADOWCON);
@@ -244,7 +244,7 @@ static void fimd_enable_shadow_channel_path(struct fimd_context *ctx, int win,
static void fimd_clear_channel(struct fimd_context *ctx)
{
- int win, ch_enabled = 0;
+ unsigned int win, ch_enabled = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -946,7 +946,24 @@ static void fimd_te_handler(struct exynos_drm_crtc *crtc)
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
}
-static struct exynos_drm_crtc_ops fimd_crtc_ops = {
+static void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable)
+{
+ struct fimd_context *ctx = crtc->ctx;
+ u32 val;
+
+ /*
+ * Only Exynos 5250, 5260, 5410 and 542x requires enabling DP/MIE
+ * clock. On these SoCs the bootloader may enable it but any
+ * power domain off/on will reset it to disable state.
+ */
+ if (ctx->driver_data != &exynos5_fimd_driver_data)
+ return;
+
+ val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE;
+ writel(DP_MIE_CLK_DP_ENABLE, ctx->regs + DP_MIE_CLKCON);
+}
+
+static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
.dpms = fimd_dpms,
.mode_fixup = fimd_mode_fixup,
.commit = fimd_commit,
@@ -956,6 +973,7 @@ static struct exynos_drm_crtc_ops fimd_crtc_ops = {
.win_commit = fimd_win_commit,
.win_disable = fimd_win_disable,
.te_handler = fimd_te_handler,
+ .clock_enable = fimd_dp_clock_enable,
};
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
@@ -1025,12 +1043,7 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
if (ctx->display)
exynos_drm_create_enc_conn(drm_dev, ctx->display);
- ret = fimd_iommu_attach_devices(ctx, drm_dev);
- if (ret)
- return ret;
-
- return 0;
-
+ return fimd_iommu_attach_devices(ctx, drm_dev);
}
static void fimd_unbind(struct device *dev, struct device *master,
@@ -1192,24 +1205,6 @@ static int fimd_remove(struct platform_device *pdev)
return 0;
}
-void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable)
-{
- struct fimd_context *ctx = crtc->ctx;
- u32 val;
-
- /*
- * Only Exynos 5250, 5260, 5410 and 542x requires enabling DP/MIE
- * clock. On these SoCs the bootloader may enable it but any
- * power domain off/on will reset it to disable state.
- */
- if (ctx->driver_data != &exynos5_fimd_driver_data)
- return;
-
- val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE;
- writel(DP_MIE_CLK_DP_ENABLE, ctx->regs + DP_MIE_CLKCON);
-}
-EXPORT_SYMBOL_GPL(fimd_dp_clock_enable);
-
struct platform_driver fimd_driver = {
.probe = fimd_probe,
.remove = fimd_remove,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.h b/drivers/gpu/drm/exynos/exynos_drm_fimd.h
deleted file mode 100644
index b4fcaa568456..000000000000
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_DRM_FIMD_H_
-#define _EXYNOS_DRM_FIMD_H_
-
-extern void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable);
-
-#endif /* _EXYNOS_DRM_FIMD_H_ */
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index 13ea3349363b..b1180fbe7546 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -76,7 +76,7 @@ int exynos_check_plane(struct drm_plane *plane, struct drm_framebuffer *fb)
return -EFAULT;
}
- exynos_plane->dma_addr[i] = buffer->dma_addr;
+ exynos_plane->dma_addr[i] = buffer->dma_addr + fb->offsets[i];
DRM_DEBUG_KMS("buffer: %d, dma_addr = 0x%lx\n",
i, (unsigned long)exynos_plane->dma_addr[i]);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index 27e84ec21694..1b3479a8db5f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -217,7 +217,7 @@ static int vidi_ctx_initialize(struct vidi_context *ctx,
return 0;
}
-static struct exynos_drm_crtc_ops vidi_crtc_ops = {
+static const struct exynos_drm_crtc_ops vidi_crtc_ops = {
.dpms = vidi_dpms,
.enable_vblank = vidi_enable_vblank,
.disable_vblank = vidi_disable_vblank,
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index fbec750574e6..8874c1fcb3ab 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -44,6 +44,12 @@
#define MIXER_WIN_NR 3
#define MIXER_DEFAULT_WIN 0
+/* The pixelformats that are natively supported by the mixer. */
+#define MXR_FORMAT_RGB565 4
+#define MXR_FORMAT_ARGB1555 5
+#define MXR_FORMAT_ARGB4444 6
+#define MXR_FORMAT_ARGB8888 7
+
struct mixer_resources {
int irq;
void __iomem *mixer_regs;
@@ -327,7 +333,8 @@ static void mixer_cfg_rgb_fmt(struct mixer_context *ctx, unsigned int height)
mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_RGB_FMT_MASK);
}
-static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
+static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
+ bool enable)
{
struct mixer_resources *res = &ctx->mixer_res;
u32 val = enable ? ~0 : 0;
@@ -359,8 +366,6 @@ static void mixer_run(struct mixer_context *ctx)
struct mixer_resources *res = &ctx->mixer_res;
mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
-
- mixer_regs_dump(ctx);
}
static void mixer_stop(struct mixer_context *ctx)
@@ -373,16 +378,13 @@ static void mixer_stop(struct mixer_context *ctx)
while (!(mixer_reg_read(res, MXR_STATUS) & MXR_STATUS_REG_IDLE) &&
--timeout)
usleep_range(10000, 12000);
-
- mixer_regs_dump(ctx);
}
-static void vp_video_buffer(struct mixer_context *ctx, int win)
+static void vp_video_buffer(struct mixer_context *ctx, unsigned int win)
{
struct mixer_resources *res = &ctx->mixer_res;
unsigned long flags;
struct exynos_drm_plane *plane;
- unsigned int buf_num = 1;
dma_addr_t luma_addr[2], chroma_addr[2];
bool tiled_mode = false;
bool crcb_mode = false;
@@ -393,27 +395,18 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
switch (plane->pixel_format) {
case DRM_FORMAT_NV12:
crcb_mode = false;
- buf_num = 2;
break;
- /* TODO: single buffer format NV12, NV21 */
+ case DRM_FORMAT_NV21:
+ crcb_mode = true;
+ break;
default:
- /* ignore pixel format at disable time */
- if (!plane->dma_addr[0])
- break;
-
DRM_ERROR("pixel format for vp is wrong [%d].\n",
plane->pixel_format);
return;
}
- if (buf_num == 2) {
- luma_addr[0] = plane->dma_addr[0];
- chroma_addr[0] = plane->dma_addr[1];
- } else {
- luma_addr[0] = plane->dma_addr[0];
- chroma_addr[0] = plane->dma_addr[0]
- + (plane->pitch * plane->fb_height);
- }
+ luma_addr[0] = plane->dma_addr[0];
+ chroma_addr[0] = plane->dma_addr[1];
if (plane->scan_flag & DRM_MODE_FLAG_INTERLACE) {
ctx->interlace = true;
@@ -484,6 +477,7 @@ static void vp_video_buffer(struct mixer_context *ctx, int win)
mixer_vsync_set_update(ctx, true);
spin_unlock_irqrestore(&res->reg_slock, flags);
+ mixer_regs_dump(ctx);
vp_regs_dump(ctx);
}
@@ -518,7 +512,7 @@ fail:
return -ENOTSUPP;
}
-static void mixer_graph_buffer(struct mixer_context *ctx, int win)
+static void mixer_graph_buffer(struct mixer_context *ctx, unsigned int win)
{
struct mixer_resources *res = &ctx->mixer_res;
unsigned long flags;
@@ -531,20 +525,27 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
plane = &ctx->planes[win];
- #define RGB565 4
- #define ARGB1555 5
- #define ARGB4444 6
- #define ARGB8888 7
+ switch (plane->pixel_format) {
+ case DRM_FORMAT_XRGB4444:
+ fmt = MXR_FORMAT_ARGB4444;
+ break;
- switch (plane->bpp) {
- case 16:
- fmt = ARGB4444;
+ case DRM_FORMAT_XRGB1555:
+ fmt = MXR_FORMAT_ARGB1555;
break;
- case 32:
- fmt = ARGB8888;
+
+ case DRM_FORMAT_RGB565:
+ fmt = MXR_FORMAT_RGB565;
+ break;
+
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB8888:
+ fmt = MXR_FORMAT_ARGB8888;
break;
+
default:
- fmt = ARGB8888;
+ DRM_DEBUG_KMS("pixelformat unsupported by mixer\n");
+ return;
}
/* check if mixer supports requested scaling setup */
@@ -617,6 +618,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win)
mixer_vsync_set_update(ctx, true);
spin_unlock_irqrestore(&res->reg_slock, flags);
+
+ mixer_regs_dump(ctx);
}
static void vp_win_reset(struct mixer_context *ctx)
@@ -1070,6 +1073,7 @@ static void mixer_poweroff(struct mixer_context *ctx)
mutex_unlock(&ctx->mixer_mutex);
mixer_stop(ctx);
+ mixer_regs_dump(ctx);
mixer_window_suspend(ctx);
ctx->int_en = mixer_reg_read(res, MXR_INT_EN);
@@ -1126,7 +1130,7 @@ int mixer_check_mode(struct drm_display_mode *mode)
return -EINVAL;
}
-static struct exynos_drm_crtc_ops mixer_crtc_ops = {
+static const struct exynos_drm_crtc_ops mixer_crtc_ops = {
.dpms = mixer_dpms,
.enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank,
@@ -1156,7 +1160,7 @@ static struct mixer_drv_data exynos4210_mxr_drv_data = {
.has_sclk = 1,
};
-static struct platform_device_id mixer_driver_types[] = {
+static const struct platform_device_id mixer_driver_types[] = {
{
.name = "s5p-mixer",
.driver_data = (unsigned long)&exynos4210_mxr_drv_data,
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 007c7d7d8295..dc55c51964ab 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -1667,12 +1667,15 @@ static int i915_sr_status(struct seq_file *m, void *unused)
if (HAS_PCH_SPLIT(dev))
sr_enabled = I915_READ(WM1_LP_ILK) & WM1_LP_SR_EN;
- else if (IS_CRESTLINE(dev) || IS_I945G(dev) || IS_I945GM(dev))
+ else if (IS_CRESTLINE(dev) || IS_G4X(dev) ||
+ IS_I945G(dev) || IS_I945GM(dev))
sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN;
else if (IS_I915GM(dev))
sr_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN;
else if (IS_PINEVIEW(dev))
sr_enabled = I915_READ(DSPFW3) & PINEVIEW_SELF_REFRESH_EN;
+ else if (IS_VALLEYVIEW(dev))
+ sr_enabled = I915_READ(FW_BLC_SELF_VLV) & FW_CSPWRDWNEN;
intel_runtime_pm_put(dev_priv);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index c302ffb5a168..a19d2c71e205 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -699,6 +699,16 @@ static int i915_drm_resume(struct drm_device *dev)
intel_init_pch_refclk(dev);
drm_mode_config_reset(dev);
+ /*
+ * Interrupts have to be enabled before any batches are run. If not the
+ * GPU will hang. i915_gem_init_hw() will initiate batches to
+ * update/restore the context.
+ *
+ * Modeset enabling in intel_modeset_init_hw() also needs working
+ * interrupts.
+ */
+ intel_runtime_pm_enable_interrupts(dev_priv);
+
mutex_lock(&dev->struct_mutex);
if (i915_gem_init_hw(dev)) {
DRM_ERROR("failed to re-initialize GPU, declaring wedged!\n");
@@ -706,9 +716,6 @@ static int i915_drm_resume(struct drm_device *dev)
}
mutex_unlock(&dev->struct_mutex);
- /* We need working interrupts for modeset enabling ... */
- intel_runtime_pm_enable_interrupts(dev_priv);
-
intel_modeset_init_hw(dev);
spin_lock_irq(&dev_priv->irq_lock);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 53394f998a1f..2d0995e7afc3 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -3003,8 +3003,8 @@ int i915_vma_unbind(struct i915_vma *vma)
} else if (vma->ggtt_view.pages) {
sg_free_table(vma->ggtt_view.pages);
kfree(vma->ggtt_view.pages);
- vma->ggtt_view.pages = NULL;
}
+ vma->ggtt_view.pages = NULL;
}
drm_mm_remove_node(&vma->node);
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index a3190e793ed4..cc552a4c1f3b 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -32,6 +32,7 @@
#include "i915_trace.h"
#include "intel_drv.h"
#include <linux/dma_remapping.h>
+#include <linux/uaccess.h>
#define __EXEC_OBJECT_HAS_PIN (1<<31)
#define __EXEC_OBJECT_HAS_FENCE (1<<30)
@@ -465,7 +466,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
}
/* We can't wait for rendering with pagefaults disabled */
- if (obj->active && in_atomic())
+ if (obj->active && pagefault_disabled())
return -EFAULT;
if (use_cpu_reloc(obj))
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 3da1af46625c..773d1d24e604 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -6074,6 +6074,8 @@ enum skl_disp_power_wells {
#define GTFIFOCTL 0x120008
#define GT_FIFO_FREE_ENTRIES_MASK 0x7f
#define GT_FIFO_NUM_RESERVED_ENTRIES 20
+#define GT_FIFO_CTL_BLOCK_ALL_POLICY_STALL (1 << 12)
+#define GT_FIFO_CTL_RC6_POLICY_STALL (1 << 11)
#define HSW_IDICR 0x9008
#define IDIHASHMSK(x) (((x) & 0x3f) << 16)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index d547d9c8dda2..d0f3cbc87474 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -13635,9 +13635,6 @@ static const struct intel_dmi_quirk intel_dmi_quirks[] = {
};
static struct intel_quirk intel_quirks[] = {
- /* HP Mini needs pipe A force quirk (LP: #322104) */
- { 0x27ae, 0x103c, 0x361a, quirk_pipea_force },
-
/* Toshiba Protege R-205, S-209 needs pipe A force quirk */
{ 0x2592, 0x1179, 0x0001, quirk_pipea_force },
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index d0237102c27e..d714a4b5711e 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -880,10 +880,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
DP_AUX_CH_CTL_RECEIVE_ERROR))
continue;
if (status & DP_AUX_CH_CTL_DONE)
- break;
+ goto done;
}
- if (status & DP_AUX_CH_CTL_DONE)
- break;
}
if ((status & DP_AUX_CH_CTL_DONE) == 0) {
@@ -892,6 +890,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
goto out;
}
+done:
/* Check for timeout or receive error.
* Timeouts occur when the sink is not connected
*/
@@ -1348,7 +1347,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
pipe_config->has_dp_encoder = true;
pipe_config->has_drrs = false;
- pipe_config->has_audio = intel_dp->has_audio;
+ pipe_config->has_audio = intel_dp->has_audio && port != PORT_A;
if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) {
intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
@@ -2211,8 +2210,8 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
int dotclock;
tmp = I915_READ(intel_dp->output_reg);
- if (tmp & DP_AUDIO_OUTPUT_ENABLE)
- pipe_config->has_audio = true;
+
+ pipe_config->has_audio = tmp & DP_AUDIO_OUTPUT_ENABLE && port != PORT_A;
if ((port == PORT_A) || !HAS_PCH_CPT(dev)) {
if (tmp & DP_SYNC_HS_HIGH)
@@ -3812,7 +3811,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
if (val == 0)
break;
- intel_dp->sink_rates[i] = val * 200;
+ /* Value read is in kHz while drm clock is saved in deca-kHz */
+ intel_dp->sink_rates[i] = (val * 200) / 10;
}
intel_dp->num_sink_rates = i;
}
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index 56e437e31580..ae628001fd97 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -435,7 +435,7 @@ gmbus_xfer(struct i2c_adapter *adapter,
struct intel_gmbus,
adapter);
struct drm_i915_private *dev_priv = bus->dev_priv;
- int i, reg_offset;
+ int i = 0, inc, try = 0, reg_offset;
int ret = 0;
intel_aux_display_runtime_get(dev_priv);
@@ -448,12 +448,14 @@ gmbus_xfer(struct i2c_adapter *adapter,
reg_offset = dev_priv->gpio_mmio_base;
+retry:
I915_WRITE(GMBUS0 + reg_offset, bus->reg0);
- for (i = 0; i < num; i++) {
+ for (; i < num; i += inc) {
+ inc = 1;
if (gmbus_is_index_read(msgs, i, num)) {
ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
- i += 1; /* set i to the index of the read xfer */
+ inc = 2; /* an index read is two msgs */
} else if (msgs[i].flags & I2C_M_RD) {
ret = gmbus_xfer_read(dev_priv, &msgs[i], 0);
} else {
@@ -525,6 +527,18 @@ clear_err:
adapter->name, msgs[i].addr,
(msgs[i].flags & I2C_M_RD) ? 'r' : 'w', msgs[i].len);
+ /*
+ * Passive adapters sometimes NAK the first probe. Retry the first
+ * message once on -ENXIO for GMBUS transfers; the bit banging algorithm
+ * has retries internally. See also the retry loop in
+ * drm_do_probe_ddc_edid, which bails out on the first -ENXIO.
+ */
+ if (ret == -ENXIO && i == 0 && try++ == 0) {
+ DRM_DEBUG_KMS("GMBUS [%s] NAK on first message, retry\n",
+ adapter->name);
+ goto retry;
+ }
+
goto out;
timeout:
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 09df74b8e917..424e62197787 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -1134,6 +1134,12 @@ static int gen8_init_common_ring(struct intel_engine_cs *ring)
I915_WRITE_IMR(ring, ~(ring->irq_enable_mask | ring->irq_keep_mask));
I915_WRITE(RING_HWSTAM(ring->mmio_base), 0xffffffff);
+ if (ring->status_page.obj) {
+ I915_WRITE(RING_HWS_PGA(ring->mmio_base),
+ (u32)ring->status_page.gfx_addr);
+ POSTING_READ(RING_HWS_PGA(ring->mmio_base));
+ }
+
I915_WRITE(RING_MODE_GEN7(ring),
_MASKED_BIT_DISABLE(GFX_REPLAY_MODE) |
_MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE));
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 5abda1d2c018..fbcc7dff0d63 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -813,12 +813,28 @@ static int intel_dual_link_lvds_callback(const struct dmi_system_id *id)
static const struct dmi_system_id intel_dual_link_lvds[] = {
{
.callback = intel_dual_link_lvds_callback,
- .ident = "Apple MacBook Pro (Core i5/i7 Series)",
+ .ident = "Apple MacBook Pro 15\" (2010)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6,2"),
+ },
+ },
+ {
+ .callback = intel_dual_link_lvds_callback,
+ .ident = "Apple MacBook Pro 15\" (2011)",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"),
},
},
+ {
+ .callback = intel_dual_link_lvds_callback,
+ .ident = "Apple MacBook Pro 15\" (2012)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro9,1"),
+ },
+ },
{ } /* terminating entry */
};
@@ -848,6 +864,11 @@ static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder)
if (i915.lvds_channel_mode > 0)
return i915.lvds_channel_mode == 2;
+ /* single channel LVDS is limited to 112 MHz */
+ if (lvds_encoder->attached_connector->base.panel.fixed_mode->clock
+ > 112999)
+ return true;
+
if (dmi_check_system(intel_dual_link_lvds))
return true;
@@ -1111,6 +1132,8 @@ void intel_lvds_init(struct drm_device *dev)
out:
mutex_unlock(&dev->mode_config.mutex);
+ intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
+
lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder);
DRM_DEBUG_KMS("detected %s-link lvds configuration\n",
lvds_encoder->is_dual_link ? "dual" : "single");
@@ -1125,7 +1148,6 @@ out:
}
drm_connector_register(connector);
- intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
intel_panel_setup_backlight(connector, INVALID_PIPE);
return;
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index 71e87abdcae7..481337436f72 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -396,16 +396,6 @@ int intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state)
return -EINVAL;
}
-/*
- * If the vendor backlight interface is not in use and ACPI backlight interface
- * is broken, do not bother processing backlight change requests from firmware.
- */
-static bool should_ignore_backlight_request(void)
-{
- return acpi_video_backlight_support() &&
- !acpi_video_verify_backlight_support();
-}
-
static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -414,7 +404,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp);
- if (should_ignore_backlight_request()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_native) {
DRM_DEBUG_KMS("opregion backlight request ignored\n");
return 0;
}
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index fa4ccb346389..555b896d2bda 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2045,22 +2045,20 @@ static void ilk_compute_wm_parameters(struct drm_crtc *crtc,
p->pipe_htotal = intel_crtc->config->base.adjusted_mode.crtc_htotal;
p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc);
- if (crtc->primary->state->fb) {
- p->pri.enabled = true;
+ if (crtc->primary->state->fb)
p->pri.bytes_per_pixel =
crtc->primary->state->fb->bits_per_pixel / 8;
- } else {
- p->pri.enabled = false;
- p->pri.bytes_per_pixel = 0;
- }
+ else
+ p->pri.bytes_per_pixel = 4;
+
+ p->cur.bytes_per_pixel = 4;
+ /*
+ * TODO: for now, assume primary and cursor planes are always enabled.
+ * Setting them to false makes the screen flicker.
+ */
+ p->pri.enabled = true;
+ p->cur.enabled = true;
- if (crtc->cursor->state->fb) {
- p->cur.enabled = true;
- p->cur.bytes_per_pixel = 4;
- } else {
- p->cur.enabled = false;
- p->cur.bytes_per_pixel = 0;
- }
p->pri.horiz_pixels = intel_crtc->config->pipe_src_w;
p->cur.horiz_pixels = intel_crtc->base.cursor->state->crtc_w;
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 441e2502b889..005b5e04de4d 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -901,13 +901,6 @@ static int chv_init_workarounds(struct intel_engine_cs *ring)
GEN6_WIZ_HASHING_MASK,
GEN6_WIZ_HASHING_16x4);
- if (INTEL_REVID(dev) == SKL_REVID_C0 ||
- INTEL_REVID(dev) == SKL_REVID_D0)
- /* WaBarrierPerformanceFixDisable:skl */
- WA_SET_BIT_MASKED(HDC_CHICKEN0,
- HDC_FENCE_DEST_SLM_DISABLE |
- HDC_BARRIER_PERFORMANCE_DISABLE);
-
return 0;
}
@@ -1024,6 +1017,13 @@ static int skl_init_workarounds(struct intel_engine_cs *ring)
WA_SET_BIT_MASKED(HIZ_CHICKEN,
BDW_HIZ_POWER_COMPILER_CLOCK_GATING_DISABLE);
+ if (INTEL_REVID(dev) == SKL_REVID_C0 ||
+ INTEL_REVID(dev) == SKL_REVID_D0)
+ /* WaBarrierPerformanceFixDisable:skl */
+ WA_SET_BIT_MASKED(HDC_CHICKEN0,
+ HDC_FENCE_DEST_SLM_DISABLE |
+ HDC_BARRIER_PERFORMANCE_DISABLE);
+
return skl_tune_iz_hashing(ring);
}
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index e87d2f418de4..987b81f31b0e 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2550,7 +2550,7 @@ intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device)
DRM_DEBUG_KMS("initialising analog device %d\n", device);
- intel_sdvo_connector = kzalloc(sizeof(*intel_sdvo_connector), GFP_KERNEL);
+ intel_sdvo_connector = intel_sdvo_connector_alloc();
if (!intel_sdvo_connector)
return false;
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index ab5cc94588e1..ff2a74651dd4 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -360,6 +360,14 @@ static void __intel_uncore_early_sanitize(struct drm_device *dev,
__raw_i915_write32(dev_priv, GTFIFODBG,
__raw_i915_read32(dev_priv, GTFIFODBG));
+ /* WaDisableShadowRegForCpd:chv */
+ if (IS_CHERRYVIEW(dev)) {
+ __raw_i915_write32(dev_priv, GTFIFOCTL,
+ __raw_i915_read32(dev_priv, GTFIFOCTL) |
+ GT_FIFO_CTL_BLOCK_ALL_POLICY_STALL |
+ GT_FIFO_CTL_RC6_POLICY_STALL);
+ }
+
intel_uncore_forcewake_reset(dev, restore_forcewake);
}
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index 6e84df9369a6..ad4b9010dfb0 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -1526,6 +1526,11 @@ static int mga_vga_mode_valid(struct drm_connector *connector,
return MODE_BANDWIDTH;
}
+ if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 ||
+ (mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) {
+ return MODE_H_ILLEGAL;
+ }
+
if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 ||
mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 ||
mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 ||
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index 94a5bee69fe7..bbdcab0a56c1 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -384,7 +384,7 @@ void adreno_gpu_cleanup(struct adreno_gpu *gpu)
if (gpu->memptrs_bo) {
if (gpu->memptrs_iova)
msm_gem_put_iova(gpu->memptrs_bo, gpu->base.id);
- drm_gem_object_unreference(gpu->memptrs_bo);
+ drm_gem_object_unreference_unlocked(gpu->memptrs_bo);
}
release_firmware(gpu->pm4);
release_firmware(gpu->pfp);
diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c
index 28d1f95a90cc..ad50b80225f5 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.c
+++ b/drivers/gpu/drm/msm/dsi/dsi.c
@@ -177,6 +177,11 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev,
goto fail;
}
+ for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) {
+ encoders[i]->bridge = msm_dsi->bridge;
+ msm_dsi->encoders[i] = encoders[i];
+ }
+
msm_dsi->connector = msm_dsi_manager_connector_init(msm_dsi->id);
if (IS_ERR(msm_dsi->connector)) {
ret = PTR_ERR(msm_dsi->connector);
@@ -185,11 +190,6 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev,
goto fail;
}
- for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) {
- encoders[i]->bridge = msm_dsi->bridge;
- msm_dsi->encoders[i] = encoders[i];
- }
-
priv->bridges[priv->num_bridges++] = msm_dsi->bridge;
priv->connectors[priv->num_connectors++] = msm_dsi->connector;
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index 956b22492c9a..649d20d29f92 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -1023,7 +1023,7 @@ static int dsi_short_read1_resp(u8 *buf, const struct mipi_dsi_msg *msg)
*data = buf[1]; /* strip out dcs type */
return 1;
} else {
- pr_err("%s: read data does not match with rx_buf len %d\n",
+ pr_err("%s: read data does not match with rx_buf len %zu\n",
__func__, msg->rx_len);
return -EINVAL;
}
@@ -1040,7 +1040,7 @@ static int dsi_short_read2_resp(u8 *buf, const struct mipi_dsi_msg *msg)
data[1] = buf[2];
return 2;
} else {
- pr_err("%s: read data does not match with rx_buf len %d\n",
+ pr_err("%s: read data does not match with rx_buf len %zu\n",
__func__, msg->rx_len);
return -EINVAL;
}
@@ -1093,7 +1093,6 @@ static int dsi_cmd_dma_rx(struct msm_dsi_host *msm_host,
{
u32 *lp, *temp, data;
int i, j = 0, cnt;
- bool ack_error = false;
u32 read_cnt;
u8 reg[16];
int repeated_bytes = 0;
@@ -1105,15 +1104,10 @@ static int dsi_cmd_dma_rx(struct msm_dsi_host *msm_host,
if (cnt > 4)
cnt = 4; /* 4 x 32 bits registers only */
- /* Calculate real read data count */
- read_cnt = dsi_read(msm_host, 0x1d4) >> 16;
-
- ack_error = (rx_byte == 4) ?
- (read_cnt == 8) : /* short pkt + 4-byte error pkt */
- (read_cnt == (pkt_size + 6 + 4)); /* long pkt+4-byte error pkt*/
-
- if (ack_error)
- read_cnt -= 4; /* Remove 4 byte error pkt */
+ if (rx_byte == 4)
+ read_cnt = 4;
+ else
+ read_cnt = pkt_size + 6;
/*
* In case of multiple reads from the panel, after the first read, there
@@ -1215,7 +1209,7 @@ static void dsi_err_worker(struct work_struct *work)
container_of(work, struct msm_dsi_host, err_work);
u32 status = msm_host->err_work_state;
- pr_err("%s: status=%x\n", __func__, status);
+ pr_err_ratelimited("%s: status=%x\n", __func__, status);
if (status & DSI_ERR_STATE_MDP_FIFO_UNDERFLOW)
dsi_sw_reset_restore(msm_host);
@@ -1797,6 +1791,7 @@ int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host,
case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
pr_err("%s: rx ACK_ERR_PACLAGE\n", __func__);
ret = 0;
+ break;
case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
ret = dsi_short_read1_resp(buf, msg);
diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c
index ee3ebcaa33f5..0a40f3c64e8b 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_manager.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c
@@ -462,7 +462,7 @@ struct drm_connector *msm_dsi_manager_connector_init(u8 id)
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
struct drm_connector *connector = NULL;
struct dsi_connector *dsi_connector;
- int ret;
+ int ret, i;
dsi_connector = devm_kzalloc(msm_dsi->dev->dev,
sizeof(*dsi_connector), GFP_KERNEL);
@@ -495,6 +495,10 @@ struct drm_connector *msm_dsi_manager_connector_init(u8 id)
if (ret)
goto fail;
+ for (i = 0; i < MSM_DSI_ENCODER_NUM; i++)
+ drm_mode_connector_attach_encoder(connector,
+ msm_dsi->encoders[i]);
+
return connector;
fail:
diff --git a/drivers/gpu/drm/msm/edp/edp_aux.c b/drivers/gpu/drm/msm/edp/edp_aux.c
index 5f5a84f6074c..208f9d47f82e 100644
--- a/drivers/gpu/drm/msm/edp/edp_aux.c
+++ b/drivers/gpu/drm/msm/edp/edp_aux.c
@@ -132,7 +132,7 @@ ssize_t edp_aux_transfer(struct drm_dp_aux *drm_aux, struct drm_dp_aux_msg *msg)
/* msg sanity check */
if ((native && (msg->size > AUX_CMD_NATIVE_MAX)) ||
(msg->size > AUX_CMD_I2C_MAX)) {
- pr_err("%s: invalid msg: size(%d), request(%x)\n",
+ pr_err("%s: invalid msg: size(%zu), request(%x)\n",
__func__, msg->size, msg->request);
return -EINVAL;
}
@@ -155,7 +155,7 @@ ssize_t edp_aux_transfer(struct drm_dp_aux *drm_aux, struct drm_dp_aux_msg *msg)
*/
edp_write(aux->base + REG_EDP_AUX_TRANS_CTRL, 0);
msm_edp_aux_ctrl(aux, 1);
- pr_err("%s: aux timeout, %d\n", __func__, ret);
+ pr_err("%s: aux timeout, %zd\n", __func__, ret);
goto unlock_exit;
}
DBG("completion");
diff --git a/drivers/gpu/drm/msm/edp/edp_connector.c b/drivers/gpu/drm/msm/edp/edp_connector.c
index d8812e84da54..b4d1b469862a 100644
--- a/drivers/gpu/drm/msm/edp/edp_connector.c
+++ b/drivers/gpu/drm/msm/edp/edp_connector.c
@@ -151,6 +151,8 @@ struct drm_connector *msm_edp_connector_init(struct msm_edp *edp)
if (ret)
goto fail;
+ drm_mode_connector_attach_encoder(connector, edp->encoder);
+
return connector;
fail:
diff --git a/drivers/gpu/drm/msm/edp/edp_ctrl.c b/drivers/gpu/drm/msm/edp/edp_ctrl.c
index 0ec5abdba5c4..29e52d7c61c0 100644
--- a/drivers/gpu/drm/msm/edp/edp_ctrl.c
+++ b/drivers/gpu/drm/msm/edp/edp_ctrl.c
@@ -1149,12 +1149,13 @@ int msm_edp_ctrl_init(struct msm_edp *edp)
ctrl->aux = msm_edp_aux_init(dev, ctrl->base, &ctrl->drm_aux);
if (!ctrl->aux || !ctrl->drm_aux) {
pr_err("%s:failed to init aux\n", __func__);
- return ret;
+ return -ENOMEM;
}
ctrl->phy = msm_edp_phy_init(dev, ctrl->base);
if (!ctrl->phy) {
pr_err("%s:failed to init phy\n", __func__);
+ ret = -ENOMEM;
goto err_destory_aux;
}
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
index e001e6b2296a..8b9a7931b162 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
@@ -72,14 +72,13 @@ const struct mdp5_cfg_hw msm8x74_config = {
.base = { 0x12d00, 0x12e00, 0x12f00 },
},
.intf = {
- .count = 4,
.base = { 0x12500, 0x12700, 0x12900, 0x12b00 },
- },
- .intfs = {
- [0] = INTF_eDP,
- [1] = INTF_DSI,
- [2] = INTF_DSI,
- [3] = INTF_HDMI,
+ .connect = {
+ [0] = INTF_eDP,
+ [1] = INTF_DSI,
+ [2] = INTF_DSI,
+ [3] = INTF_HDMI,
+ },
},
.max_clk = 200000000,
};
@@ -142,14 +141,13 @@ const struct mdp5_cfg_hw apq8084_config = {
.base = { 0x12f00, 0x13000, 0x13100, 0x13200 },
},
.intf = {
- .count = 5,
.base = { 0x12500, 0x12700, 0x12900, 0x12b00, 0x12d00 },
- },
- .intfs = {
- [0] = INTF_eDP,
- [1] = INTF_DSI,
- [2] = INTF_DSI,
- [3] = INTF_HDMI,
+ .connect = {
+ [0] = INTF_eDP,
+ [1] = INTF_DSI,
+ [2] = INTF_DSI,
+ [3] = INTF_HDMI,
+ },
},
.max_clk = 320000000,
};
@@ -196,10 +194,12 @@ const struct mdp5_cfg_hw msm8x16_config = {
},
.intf = {
- .count = 1, /* INTF_1 */
- .base = { 0x6B800 },
+ .base = { 0x00000, 0x6b800 },
+ .connect = {
+ [0] = INTF_DISABLED,
+ [1] = INTF_DSI,
+ },
},
- /* TODO enable .intfs[] with [1] = INTF_DSI, once DSI is implemented */
.max_clk = 320000000,
};
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h
index 3a551b0892d8..69349abe59f2 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h
@@ -59,6 +59,11 @@ struct mdp5_smp_block {
#define MDP5_INTF_NUM_MAX 5
+struct mdp5_intf_block {
+ uint32_t base[MAX_BASES];
+ u32 connect[MDP5_INTF_NUM_MAX]; /* array of enum mdp5_intf_type */
+};
+
struct mdp5_cfg_hw {
char *name;
@@ -72,9 +77,7 @@ struct mdp5_cfg_hw {
struct mdp5_sub_block dspp;
struct mdp5_sub_block ad;
struct mdp5_sub_block pp;
- struct mdp5_sub_block intf;
-
- u32 intfs[MDP5_INTF_NUM_MAX]; /* array of enum mdp5_intf_type */
+ struct mdp5_intf_block intf;
uint32_t max_clk;
};
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index dfa8beb9343a..bbacf9d2b738 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -206,8 +206,8 @@ static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
static int get_dsi_id_from_intf(const struct mdp5_cfg_hw *hw_cfg, int intf_num)
{
- const int intf_cnt = hw_cfg->intf.count;
- const u32 *intfs = hw_cfg->intfs;
+ const enum mdp5_intf_type *intfs = hw_cfg->intf.connect;
+ const int intf_cnt = ARRAY_SIZE(hw_cfg->intf.connect);
int id = 0, i;
for (i = 0; i < intf_cnt; i++) {
@@ -228,7 +228,7 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
struct msm_drm_private *priv = dev->dev_private;
const struct mdp5_cfg_hw *hw_cfg =
mdp5_cfg_get_hw_config(mdp5_kms->cfg);
- enum mdp5_intf_type intf_type = hw_cfg->intfs[intf_num];
+ enum mdp5_intf_type intf_type = hw_cfg->intf.connect[intf_num];
struct drm_encoder *encoder;
int ret = 0;
@@ -365,7 +365,7 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
/* Construct encoders and modeset initialize connector devices
* for each external display interface.
*/
- for (i = 0; i < ARRAY_SIZE(hw_cfg->intfs); i++) {
+ for (i = 0; i < ARRAY_SIZE(hw_cfg->intf.connect); i++) {
ret = modeset_init_intf(mdp5_kms, i);
if (ret)
goto fail;
@@ -514,8 +514,8 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
*/
mdp5_enable(mdp5_kms);
for (i = 0; i < MDP5_INTF_NUM_MAX; i++) {
- if (!config->hw->intf.base[i] ||
- mdp5_cfg_intf_is_virtual(config->hw->intfs[i]))
+ if (mdp5_cfg_intf_is_virtual(config->hw->intf.connect[i]) ||
+ !config->hw->intf.base[i])
continue;
mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0);
}
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index 18a3d203b174..57b8f56ae9d0 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -273,7 +273,7 @@ static void set_scanout_locked(struct drm_plane *plane,
mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe),
msm_framebuffer_iova(fb, mdp5_kms->id, 2));
mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe),
- msm_framebuffer_iova(fb, mdp5_kms->id, 4));
+ msm_framebuffer_iova(fb, mdp5_kms->id, 3));
plane->fb = fb;
}
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 47f4dd407671..c80a6bee2b18 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -21,9 +21,11 @@
static void msm_fb_output_poll_changed(struct drm_device *dev)
{
+#ifdef CONFIG_DRM_MSM_FBDEV
struct msm_drm_private *priv = dev->dev_private;
if (priv->fbdev)
drm_fb_helper_hotplug_event(priv->fbdev);
+#endif
}
static const struct drm_mode_config_funcs mode_config_funcs = {
@@ -94,7 +96,7 @@ void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
}
if (reglog)
- printk(KERN_DEBUG "IO:region %s %08x %08lx\n", dbgname, (u32)ptr, size);
+ printk(KERN_DEBUG "IO:region %s %p %08lx\n", dbgname, ptr, size);
return ptr;
}
@@ -102,7 +104,7 @@ void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
void msm_writel(u32 data, void __iomem *addr)
{
if (reglog)
- printk(KERN_DEBUG "IO:W %08x %08x\n", (u32)addr, data);
+ printk(KERN_DEBUG "IO:W %p %08x\n", addr, data);
writel(data, addr);
}
@@ -110,7 +112,7 @@ u32 msm_readl(const void __iomem *addr)
{
u32 val = readl(addr);
if (reglog)
- printk(KERN_ERR "IO:R %08x %08x\n", (u32)addr, val);
+ printk(KERN_ERR "IO:R %p %08x\n", addr, val);
return val;
}
@@ -143,8 +145,8 @@ static int msm_unload(struct drm_device *dev)
if (gpu) {
mutex_lock(&dev->struct_mutex);
gpu->funcs->pm_suspend(gpu);
- gpu->funcs->destroy(gpu);
mutex_unlock(&dev->struct_mutex);
+ gpu->funcs->destroy(gpu);
}
if (priv->vram.paddr) {
@@ -177,7 +179,7 @@ static int get_mdp_ver(struct platform_device *pdev)
const struct of_device_id *match;
match = of_match_node(match_types, dev->of_node);
if (match)
- return (int)match->data;
+ return (int)(unsigned long)match->data;
#endif
return 4;
}
@@ -216,7 +218,7 @@ static int msm_init_vram(struct drm_device *dev)
if (ret)
return ret;
size = r.end - r.start;
- DRM_INFO("using VRAM carveout: %lx@%08x\n", size, r.start);
+ DRM_INFO("using VRAM carveout: %lx@%pa\n", size, &r.start);
} else
#endif
@@ -283,10 +285,6 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
drm_mode_config_init(dev);
- ret = msm_init_vram(dev);
- if (ret)
- goto fail;
-
platform_set_drvdata(pdev, dev);
/* Bind all our sub-components: */
@@ -294,6 +292,10 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
if (ret)
return ret;
+ ret = msm_init_vram(dev);
+ if (ret)
+ goto fail;
+
switch (get_mdp_ver(pdev)) {
case 4:
kms = mdp4_kms_init(dev);
@@ -419,9 +421,11 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file)
static void msm_lastclose(struct drm_device *dev)
{
+#ifdef CONFIG_DRM_MSM_FBDEV
struct msm_drm_private *priv = dev->dev_private;
if (priv->fbdev)
drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
+#endif
}
static irqreturn_t msm_irq(int irq, void *arg)
diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c
index 6b573e612f27..121713281417 100644
--- a/drivers/gpu/drm/msm/msm_fb.c
+++ b/drivers/gpu/drm/msm/msm_fb.c
@@ -172,8 +172,8 @@ struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
{
struct msm_drm_private *priv = dev->dev_private;
struct msm_kms *kms = priv->kms;
- struct msm_framebuffer *msm_fb;
- struct drm_framebuffer *fb = NULL;
+ struct msm_framebuffer *msm_fb = NULL;
+ struct drm_framebuffer *fb;
const struct msm_format *format;
int ret, i, n;
unsigned int hsub, vsub;
@@ -239,8 +239,7 @@ struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
return fb;
fail:
- if (fb)
- msm_framebuffer_destroy(fb);
+ kfree(msm_fb);
return ERR_PTR(ret);
}
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 479d8af72bcb..52839769eb6c 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -483,7 +483,7 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
uint64_t off = drm_vma_node_start(&obj->vma_node);
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
- seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %p %d\n",
+ seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %p %zu\n",
msm_obj->flags, is_active(msm_obj) ? 'A' : 'I',
msm_obj->read_fence, msm_obj->write_fence,
obj->name, obj->refcount.refcount.counter,
diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c
index 7acdaa5688b7..7ac2f1997e4a 100644
--- a/drivers/gpu/drm/msm/msm_iommu.c
+++ b/drivers/gpu/drm/msm/msm_iommu.c
@@ -60,7 +60,7 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint32_t iova,
u32 pa = sg_phys(sg) - sg->offset;
size_t bytes = sg->length + sg->offset;
- VERB("map[%d]: %08x %08x(%x)", i, iova, pa, bytes);
+ VERB("map[%d]: %08x %08x(%zx)", i, iova, pa, bytes);
ret = iommu_map(domain, da, pa, bytes, prot);
if (ret)
@@ -99,7 +99,7 @@ static int msm_iommu_unmap(struct msm_mmu *mmu, uint32_t iova,
if (unmapped < bytes)
return unmapped;
- VERB("unmap[%d]: %08x(%x)", i, iova, bytes);
+ VERB("unmap[%d]: %08x(%zx)", i, iova, bytes);
BUG_ON(!PAGE_ALIGNED(bytes));
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c
index 8171537dd7d1..1f14b908b221 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.c
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.c
@@ -56,6 +56,6 @@ fail:
void msm_ringbuffer_destroy(struct msm_ringbuffer *ring)
{
if (ring->bo)
- drm_gem_object_unreference(ring->bo);
+ drm_gem_object_unreference_unlocked(ring->bo);
kfree(ring);
}
diff --git a/drivers/gpu/drm/nouveau/include/nvif/class.h b/drivers/gpu/drm/nouveau/include/nvif/class.h
index 0b5af0fe8659..64f8b2f687d2 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/class.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/class.h
@@ -14,7 +14,7 @@
#define FERMI_TWOD_A 0x0000902d
-#define FERMI_MEMORY_TO_MEMORY_FORMAT_A 0x0000903d
+#define FERMI_MEMORY_TO_MEMORY_FORMAT_A 0x00009039
#define KEPLER_INLINE_TO_MEMORY_A 0x0000a040
#define KEPLER_INLINE_TO_MEMORY_B 0x0000a140
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm204.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm204.c
index 2f5eadd12a9b..fdb1dcf16a59 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm204.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gm204.c
@@ -329,7 +329,6 @@ gm204_gr_init(struct nvkm_object *object)
nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008);
for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
- printk(KERN_ERR "ppc %d %d\n", gpc, priv->ppc_nr[gpc]);
for (ppc = 0; ppc < priv->ppc_nr[gpc]; ppc++)
nv_wr32(priv, PPC_UNIT(gpc, ppc, 0x038), 0xc0000000);
nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c
index e8778c67578e..c61102f70805 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gf100.c
@@ -90,12 +90,14 @@ gf100_devinit_disable(struct nvkm_devinit *devinit)
return disable;
}
-static int
+int
gf100_devinit_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
struct nvkm_oclass *oclass, void *data, u32 size,
struct nvkm_object **pobject)
{
+ struct nvkm_devinit_impl *impl = (void *)oclass;
struct nv50_devinit_priv *priv;
+ u64 disable;
int ret;
ret = nvkm_devinit_create(parent, engine, oclass, &priv);
@@ -103,7 +105,8 @@ gf100_devinit_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
if (ret)
return ret;
- if (nv_rd32(priv, 0x022500) & 0x00000001)
+ disable = impl->disable(&priv->base);
+ if (disable & (1ULL << NVDEV_ENGINE_DISP))
priv->base.post = true;
return 0;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c
index b345a53e881d..87ca0ece37b4 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm107.c
@@ -48,7 +48,7 @@ struct nvkm_oclass *
gm107_devinit_oclass = &(struct nvkm_devinit_impl) {
.base.handle = NV_SUBDEV(DEVINIT, 0x07),
.base.ofuncs = &(struct nvkm_ofuncs) {
- .ctor = nv50_devinit_ctor,
+ .ctor = gf100_devinit_ctor,
.dtor = _nvkm_devinit_dtor,
.init = nv50_devinit_init,
.fini = _nvkm_devinit_fini,
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c
index 535172c5f1ad..1076fcf0d716 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/gm204.c
@@ -161,7 +161,7 @@ struct nvkm_oclass *
gm204_devinit_oclass = &(struct nvkm_devinit_impl) {
.base.handle = NV_SUBDEV(DEVINIT, 0x07),
.base.ofuncs = &(struct nvkm_ofuncs) {
- .ctor = nv50_devinit_ctor,
+ .ctor = gf100_devinit_ctor,
.dtor = _nvkm_devinit_dtor,
.init = nv50_devinit_init,
.fini = _nvkm_devinit_fini,
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h
index b882b65ff3cd..9243521c80ac 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.h
@@ -15,6 +15,9 @@ int nv50_devinit_pll_set(struct nvkm_devinit *, u32, u32);
int gt215_devinit_pll_set(struct nvkm_devinit *, u32, u32);
+int gf100_devinit_ctor(struct nvkm_object *, struct nvkm_object *,
+ struct nvkm_oclass *, void *, u32,
+ struct nvkm_object **);
int gf100_devinit_pll_set(struct nvkm_devinit *, u32, u32);
u64 gm107_devinit_disable(struct nvkm_devinit *);
diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
index 92be50c39ffd..ab89eed9ddd9 100644
--- a/drivers/gpu/drm/radeon/atombios.h
+++ b/drivers/gpu/drm/radeon/atombios.h
@@ -7944,8 +7944,8 @@ typedef struct {
typedef struct {
AMD_ACPI_DESCRIPTION_HEADER SHeader;
UCHAR TableUUID[16]; //0x24
- ULONG VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the stucture.
- ULONG Lib1ImageOffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the stucture.
+ ULONG VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the structure.
+ ULONG Lib1ImageOffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the structure.
ULONG Reserved[4]; //0x3C
}UEFI_ACPI_VFCT;
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 3e3290c203c6..b435c859dcbc 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -421,19 +421,21 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector)
{
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
u8 msg[DP_DPCD_SIZE];
- int ret;
+ int ret, i;
- ret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_DPCD_REV, msg,
- DP_DPCD_SIZE);
- if (ret > 0) {
- memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE);
+ for (i = 0; i < 7; i++) {
+ ret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_DPCD_REV, msg,
+ DP_DPCD_SIZE);
+ if (ret == DP_DPCD_SIZE) {
+ memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE);
- DRM_DEBUG_KMS("DPCD: %*ph\n", (int)sizeof(dig_connector->dpcd),
- dig_connector->dpcd);
+ DRM_DEBUG_KMS("DPCD: %*ph\n", (int)sizeof(dig_connector->dpcd),
+ dig_connector->dpcd);
- radeon_dp_probe_oui(radeon_connector);
+ radeon_dp_probe_oui(radeon_connector);
- return true;
+ return true;
+ }
}
dig_connector->dpcd[0] = 0;
return false;
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index f57c1ab617bc..dd39f434b4a7 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -1761,17 +1761,15 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
int encoder_mode = atombios_get_encoder_mode(encoder);
DRM_DEBUG_KMS("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n",
radeon_encoder->encoder_id, mode, radeon_encoder->devices,
radeon_encoder->active_device);
- if (connector && (radeon_audio != 0) &&
+ if ((radeon_audio != 0) &&
((encoder_mode == ATOM_ENCODER_MODE_HDMI) ||
- (ENCODER_MODE_IS_DP(encoder_mode) &&
- drm_detect_monitor_audio(radeon_connector_edid(connector)))))
+ ENCODER_MODE_IS_DP(encoder_mode)))
radeon_audio_dpms(encoder, mode);
switch (radeon_encoder->encoder_id) {
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index 28faea9996f9..ba50f3c1c2e0 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -5837,7 +5837,7 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev)
/* restore context1-15 */
/* set vm size, must be a multiple of 4 */
WREG32(VM_CONTEXT1_PAGE_TABLE_START_ADDR, 0);
- WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, rdev->vm_manager.max_pfn);
+ WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, rdev->vm_manager.max_pfn - 1);
for (i = 1; i < 16; i++) {
if (i < 8)
WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2),
diff --git a/drivers/gpu/drm/radeon/dce3_1_afmt.c b/drivers/gpu/drm/radeon/dce3_1_afmt.c
index f04205170b8a..cfa3a84a2af0 100644
--- a/drivers/gpu/drm/radeon/dce3_1_afmt.c
+++ b/drivers/gpu/drm/radeon/dce3_1_afmt.c
@@ -173,7 +173,7 @@ void dce3_2_hdmi_update_acr(struct drm_encoder *encoder, long offset,
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
- WREG32(HDMI0_ACR_PACKET_CONTROL + offset,
+ WREG32(DCE3_HDMI0_ACR_PACKET_CONTROL + offset,
HDMI0_ACR_SOURCE | /* select SW CTS value */
HDMI0_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
diff --git a/drivers/gpu/drm/radeon/dce6_afmt.c b/drivers/gpu/drm/radeon/dce6_afmt.c
index 3adc2afe32aa..68fd9fc677e3 100644
--- a/drivers/gpu/drm/radeon/dce6_afmt.c
+++ b/drivers/gpu/drm/radeon/dce6_afmt.c
@@ -295,28 +295,3 @@ void dce6_dp_audio_set_dto(struct radeon_device *rdev,
WREG32(DCCG_AUDIO_DTO1_MODULE, clock);
}
}
-
-void dce6_dp_enable(struct drm_encoder *encoder, bool enable)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
-
- if (!dig || !dig->afmt)
- return;
-
- if (enable) {
- WREG32(EVERGREEN_DP_SEC_TIMESTAMP + dig->afmt->offset,
- EVERGREEN_DP_SEC_TIMESTAMP_MODE(1));
- WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset,
- EVERGREEN_DP_SEC_ASP_ENABLE | /* Audio packet transmission */
- EVERGREEN_DP_SEC_ATP_ENABLE | /* Audio timestamp packet transmission */
- EVERGREEN_DP_SEC_AIP_ENABLE | /* Audio infoframe packet transmission */
- EVERGREEN_DP_SEC_STREAM_ENABLE); /* Master enable for secondary stream engine */
- } else {
- WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset, 0);
- }
-
- dig->afmt->enabled = enable;
-}
diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c
index c18d4ecbd95d..9953356fe263 100644
--- a/drivers/gpu/drm/radeon/evergreen_hdmi.c
+++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c
@@ -219,13 +219,9 @@ void evergreen_set_avi_packet(struct radeon_device *rdev, u32 offset,
WREG32(AFMT_AVI_INFO3 + offset,
frame[0xC] | (frame[0xD] << 8) | (buffer[1] << 24));
- WREG32_OR(HDMI_INFOFRAME_CONTROL0 + offset,
- HDMI_AVI_INFO_SEND | /* enable AVI info frames */
- HDMI_AVI_INFO_CONT); /* required for audio info values to be updated */
-
WREG32_P(HDMI_INFOFRAME_CONTROL1 + offset,
- HDMI_AVI_INFO_LINE(2), /* anything other than 0 */
- ~HDMI_AVI_INFO_LINE_MASK);
+ HDMI_AVI_INFO_LINE(2), /* anything other than 0 */
+ ~HDMI_AVI_INFO_LINE_MASK);
}
void dce4_hdmi_audio_set_dto(struct radeon_device *rdev,
@@ -370,9 +366,13 @@ void dce4_set_audio_packet(struct drm_encoder *encoder, u32 offset)
WREG32(AFMT_AUDIO_PACKET_CONTROL2 + offset,
AFMT_AUDIO_CHANNEL_ENABLE(0xff));
+ WREG32(HDMI_AUDIO_PACKET_CONTROL + offset,
+ HDMI_AUDIO_DELAY_EN(1) | /* set the default audio delay */
+ HDMI_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */
+
/* allow 60958 channel status and send audio packets fields to be updated */
- WREG32(AFMT_AUDIO_PACKET_CONTROL + offset,
- AFMT_AUDIO_SAMPLE_SEND | AFMT_RESET_FIFO_WHEN_AUDIO_DIS | AFMT_60958_CS_UPDATE);
+ WREG32_OR(AFMT_AUDIO_PACKET_CONTROL + offset,
+ AFMT_RESET_FIFO_WHEN_AUDIO_DIS | AFMT_60958_CS_UPDATE);
}
@@ -398,17 +398,26 @@ void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable)
return;
if (enable) {
- WREG32(HDMI_INFOFRAME_CONTROL1 + dig->afmt->offset,
- HDMI_AUDIO_INFO_LINE(2)); /* anything other than 0 */
-
- WREG32(HDMI_AUDIO_PACKET_CONTROL + dig->afmt->offset,
- HDMI_AUDIO_DELAY_EN(1) | /* set the default audio delay */
- HDMI_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
- WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset,
- HDMI_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */
- HDMI_AUDIO_INFO_CONT); /* required for audio info values to be updated */
+ if (connector && drm_detect_monitor_audio(radeon_connector_edid(connector))) {
+ WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset,
+ HDMI_AVI_INFO_SEND | /* enable AVI info frames */
+ HDMI_AVI_INFO_CONT | /* required for audio info values to be updated */
+ HDMI_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */
+ HDMI_AUDIO_INFO_CONT); /* required for audio info values to be updated */
+ WREG32_OR(AFMT_AUDIO_PACKET_CONTROL + dig->afmt->offset,
+ AFMT_AUDIO_SAMPLE_SEND);
+ } else {
+ WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset,
+ HDMI_AVI_INFO_SEND | /* enable AVI info frames */
+ HDMI_AVI_INFO_CONT); /* required for audio info values to be updated */
+ WREG32_AND(AFMT_AUDIO_PACKET_CONTROL + dig->afmt->offset,
+ ~AFMT_AUDIO_SAMPLE_SEND);
+ }
} else {
+ WREG32_AND(AFMT_AUDIO_PACKET_CONTROL + dig->afmt->offset,
+ ~AFMT_AUDIO_SAMPLE_SEND);
WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset, 0);
}
@@ -424,20 +433,25 @@ void evergreen_dp_enable(struct drm_encoder *encoder, bool enable)
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
if (!dig || !dig->afmt)
return;
- if (enable) {
+ if (enable && connector &&
+ drm_detect_monitor_audio(radeon_connector_edid(connector))) {
struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *dig_connector;
uint32_t val;
+ WREG32_OR(AFMT_AUDIO_PACKET_CONTROL + dig->afmt->offset,
+ AFMT_AUDIO_SAMPLE_SEND);
+
WREG32(EVERGREEN_DP_SEC_TIMESTAMP + dig->afmt->offset,
EVERGREEN_DP_SEC_TIMESTAMP_MODE(1));
- if (radeon_connector->con_priv) {
+ if (!ASIC_IS_DCE6(rdev) && radeon_connector->con_priv) {
dig_connector = radeon_connector->con_priv;
val = RREG32(EVERGREEN_DP_SEC_AUD_N + dig->afmt->offset);
val &= ~EVERGREEN_DP_SEC_N_BASE_MULTIPLE(0xf);
@@ -457,6 +471,8 @@ void evergreen_dp_enable(struct drm_encoder *encoder, bool enable)
EVERGREEN_DP_SEC_STREAM_ENABLE); /* Master enable for secondary stream engine */
} else {
WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset, 0);
+ WREG32_AND(AFMT_AUDIO_PACKET_CONTROL + dig->afmt->offset,
+ ~AFMT_AUDIO_SAMPLE_SEND);
}
dig->afmt->enabled = enable;
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index e8a496ff007e..64d3a771920d 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -1301,7 +1301,8 @@ static int cayman_pcie_gart_enable(struct radeon_device *rdev)
*/
for (i = 1; i < 8; i++) {
WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR + (i << 2), 0);
- WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR + (i << 2), rdev->vm_manager.max_pfn);
+ WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR + (i << 2),
+ rdev->vm_manager.max_pfn - 1);
WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2),
rdev->vm_manager.saved_table_addr[i]);
}
diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c
index dd6606b8e23c..e85894ade95c 100644
--- a/drivers/gpu/drm/radeon/r600_hdmi.c
+++ b/drivers/gpu/drm/radeon/r600_hdmi.c
@@ -228,12 +228,13 @@ void r600_set_avi_packet(struct radeon_device *rdev, u32 offset,
WREG32(HDMI0_AVI_INFO3 + offset,
frame[0xC] | (frame[0xD] << 8) | (buffer[1] << 24));
+ WREG32_OR(HDMI0_INFOFRAME_CONTROL1 + offset,
+ HDMI0_AVI_INFO_LINE(2)); /* anything other than 0 */
+
WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset,
- HDMI0_AVI_INFO_SEND | /* enable AVI info frames */
- HDMI0_AVI_INFO_CONT); /* send AVI info frames every frame/field */
+ HDMI0_AVI_INFO_SEND | /* enable AVI info frames */
+ HDMI0_AVI_INFO_CONT); /* send AVI info frames every frame/field */
- WREG32_OR(HDMI0_INFOFRAME_CONTROL1 + offset,
- HDMI0_AVI_INFO_LINE(2)); /* anything other than 0 */
}
/*
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index d2abe481954f..46eb0fa75a61 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -1673,7 +1673,6 @@ struct radeon_uvd {
struct radeon_bo *vcpu_bo;
void *cpu_addr;
uint64_t gpu_addr;
- void *saved_bo;
atomic_t handles[RADEON_MAX_UVD_HANDLES];
struct drm_file *filp[RADEON_MAX_UVD_HANDLES];
unsigned img_size[RADEON_MAX_UVD_HANDLES];
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index fafd8ce4d58f..8dbf5083c4ff 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -1202,7 +1202,7 @@ static struct radeon_asic rs780_asic = {
static struct radeon_asic_ring rv770_uvd_ring = {
.ib_execute = &uvd_v1_0_ib_execute,
.emit_fence = &uvd_v2_2_fence_emit,
- .emit_semaphore = &uvd_v1_0_semaphore_emit,
+ .emit_semaphore = &uvd_v2_2_semaphore_emit,
.cs_parse = &radeon_uvd_cs_parse,
.ring_test = &uvd_v1_0_ring_test,
.ib_test = &uvd_v1_0_ib_test,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index cf0a90bb61ca..a3ca8cd305c5 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -949,6 +949,10 @@ void uvd_v1_0_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
int uvd_v2_2_resume(struct radeon_device *rdev);
void uvd_v2_2_fence_emit(struct radeon_device *rdev,
struct radeon_fence *fence);
+bool uvd_v2_2_semaphore_emit(struct radeon_device *rdev,
+ struct radeon_ring *ring,
+ struct radeon_semaphore *semaphore,
+ bool emit_wait);
/* uvd v3.1 */
bool uvd_v3_1_semaphore_emit(struct radeon_device *rdev,
diff --git a/drivers/gpu/drm/radeon/radeon_audio.c b/drivers/gpu/drm/radeon/radeon_audio.c
index 48d49e651a30..25191f126f3b 100644
--- a/drivers/gpu/drm/radeon/radeon_audio.c
+++ b/drivers/gpu/drm/radeon/radeon_audio.c
@@ -102,7 +102,6 @@ static void radeon_audio_dp_mode_set(struct drm_encoder *encoder,
void r600_hdmi_enable(struct drm_encoder *encoder, bool enable);
void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable);
void evergreen_dp_enable(struct drm_encoder *encoder, bool enable);
-void dce6_dp_enable(struct drm_encoder *encoder, bool enable);
static const u32 pin_offsets[7] =
{
@@ -240,7 +239,7 @@ static struct radeon_audio_funcs dce6_dp_funcs = {
.set_avi_packet = evergreen_set_avi_packet,
.set_audio_packet = dce4_set_audio_packet,
.mode_set = radeon_audio_dp_mode_set,
- .dpms = dce6_dp_enable,
+ .dpms = evergreen_dp_enable,
};
static void radeon_audio_interface_init(struct radeon_device *rdev)
@@ -462,6 +461,10 @@ void radeon_audio_detect(struct drm_connector *connector,
return;
rdev = connector->encoder->dev->dev_private;
+
+ if (!radeon_audio_chipset_supported(rdev))
+ return;
+
radeon_encoder = to_radeon_encoder(connector->encoder);
dig = radeon_encoder->enc_priv;
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index 4d0f96cc3da4..ab39b85e0f76 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -88,7 +88,7 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
p->dma_reloc_idx = 0;
/* FIXME: we assume that each relocs use 4 dwords */
p->nrelocs = chunk->length_dw / 4;
- p->relocs = kcalloc(p->nrelocs, sizeof(struct radeon_bo_list), GFP_KERNEL);
+ p->relocs = drm_calloc_large(p->nrelocs, sizeof(struct radeon_bo_list));
if (p->relocs == NULL) {
return -ENOMEM;
}
@@ -428,7 +428,7 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bo
}
}
kfree(parser->track);
- kfree(parser->relocs);
+ drm_free_large(parser->relocs);
drm_free_large(parser->vm_bos);
for (i = 0; i < parser->nchunks; i++)
drm_free_large(parser->chunks[i].kdata);
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index b7ca4c514621..a7fdfa4f0857 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1463,6 +1463,21 @@ int radeon_device_init(struct radeon_device *rdev,
if (r)
DRM_ERROR("ib ring test failed (%d).\n", r);
+ /*
+ * Turks/Thames GPU will freeze whole laptop if DPM is not restarted
+ * after the CP ring have chew one packet at least. Hence here we stop
+ * and restart DPM after the radeon_ib_ring_tests().
+ */
+ if (rdev->pm.dpm_enabled &&
+ (rdev->pm.pm_method == PM_METHOD_DPM) &&
+ (rdev->family == CHIP_TURKS) &&
+ (rdev->flags & RADEON_IS_MOBILITY)) {
+ mutex_lock(&rdev->pm.mutex);
+ radeon_dpm_disable(rdev);
+ radeon_dpm_enable(rdev);
+ mutex_unlock(&rdev->pm.mutex);
+ }
+
if ((radeon_testing & 1)) {
if (rdev->accel_working)
radeon_test_moves(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_dp_auxch.c b/drivers/gpu/drm/radeon/radeon_dp_auxch.c
index bf1fecc6cceb..fcbd60bb0349 100644
--- a/drivers/gpu/drm/radeon/radeon_dp_auxch.c
+++ b/drivers/gpu/drm/radeon/radeon_dp_auxch.c
@@ -30,8 +30,6 @@
AUX_SW_RX_HPD_DISCON | \
AUX_SW_RX_PARTIAL_BYTE | \
AUX_SW_NON_AUX_MODE | \
- AUX_SW_RX_MIN_COUNT_VIOL | \
- AUX_SW_RX_INVALID_STOP | \
AUX_SW_RX_SYNC_INVALID_L | \
AUX_SW_RX_SYNC_INVALID_H | \
AUX_SW_RX_INVALID_START | \
diff --git a/drivers/gpu/drm/radeon/radeon_dp_mst.c b/drivers/gpu/drm/radeon/radeon_dp_mst.c
index 1017338a49d9..257b10be5cda 100644
--- a/drivers/gpu/drm/radeon/radeon_dp_mst.c
+++ b/drivers/gpu/drm/radeon/radeon_dp_mst.c
@@ -663,9 +663,17 @@ int
radeon_dp_mst_probe(struct radeon_connector *radeon_connector)
{
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+ struct drm_device *dev = radeon_connector->base.dev;
+ struct radeon_device *rdev = dev->dev_private;
int ret;
u8 msg[1];
+ if (!radeon_mst)
+ return 0;
+
+ if (!ASIC_IS_DCE5(rdev))
+ return 0;
+
if (dig_connector->dpcd[DP_DPCD_REV] < 0x12)
return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index 7b2a7335cc5d..b0acf50d9558 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -576,6 +576,9 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file
if (radeon_get_allowed_info_register(rdev, *value, value))
return -EINVAL;
break;
+ case RADEON_INFO_VA_UNMAP_WORKING:
+ *value = true;
+ break;
default:
DRM_DEBUG_KMS("Invalid request %d\n", info->request);
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon_mn.c b/drivers/gpu/drm/radeon/radeon_mn.c
index 01701376b239..eef006c48584 100644
--- a/drivers/gpu/drm/radeon/radeon_mn.c
+++ b/drivers/gpu/drm/radeon/radeon_mn.c
@@ -135,28 +135,31 @@ static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn,
while (it) {
struct radeon_mn_node *node;
struct radeon_bo *bo;
- int r;
+ long r;
node = container_of(it, struct radeon_mn_node, it);
it = interval_tree_iter_next(it, start, end);
list_for_each_entry(bo, &node->bos, mn_list) {
+ if (!bo->tbo.ttm || bo->tbo.ttm->state != tt_bound)
+ continue;
+
r = radeon_bo_reserve(bo, true);
if (r) {
- DRM_ERROR("(%d) failed to reserve user bo\n", r);
+ DRM_ERROR("(%ld) failed to reserve user bo\n", r);
continue;
}
r = reservation_object_wait_timeout_rcu(bo->tbo.resv,
true, false, MAX_SCHEDULE_TIMEOUT);
- if (r)
- DRM_ERROR("(%d) failed to wait for user bo\n", r);
+ if (r <= 0)
+ DRM_ERROR("(%ld) failed to wait for user bo\n", r);
radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU);
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
if (r)
- DRM_ERROR("(%d) failed to validate user bo\n", r);
+ DRM_ERROR("(%ld) failed to validate user bo\n", r);
radeon_bo_unreserve(bo);
}
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index b292aca0f342..edafd3c2b170 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -591,8 +591,7 @@ static void radeon_ttm_tt_unpin_userptr(struct ttm_tt *ttm)
{
struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
struct radeon_ttm_tt *gtt = (void *)ttm;
- struct scatterlist *sg;
- int i;
+ struct sg_page_iter sg_iter;
int write = !(gtt->userflags & RADEON_GEM_USERPTR_READONLY);
enum dma_data_direction direction = write ?
@@ -605,9 +604,8 @@ static void radeon_ttm_tt_unpin_userptr(struct ttm_tt *ttm)
/* free the sg table and pages again */
dma_unmap_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction);
- for_each_sg(ttm->sg->sgl, sg, ttm->sg->nents, i) {
- struct page *page = sg_page(sg);
-
+ for_each_sg_page(ttm->sg->sgl, &sg_iter, ttm->sg->nents, 0) {
+ struct page *page = sg_page_iter_page(&sg_iter);
if (!(gtt->userflags & RADEON_GEM_USERPTR_READONLY))
set_page_dirty(page);
diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c
index c10b2aec6450..6edcb5485092 100644
--- a/drivers/gpu/drm/radeon/radeon_uvd.c
+++ b/drivers/gpu/drm/radeon/radeon_uvd.c
@@ -204,28 +204,32 @@ void radeon_uvd_fini(struct radeon_device *rdev)
int radeon_uvd_suspend(struct radeon_device *rdev)
{
- unsigned size;
- void *ptr;
- int i;
+ int i, r;
if (rdev->uvd.vcpu_bo == NULL)
return 0;
- for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i)
- if (atomic_read(&rdev->uvd.handles[i]))
- break;
+ for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) {
+ uint32_t handle = atomic_read(&rdev->uvd.handles[i]);
+ if (handle != 0) {
+ struct radeon_fence *fence;
- if (i == RADEON_MAX_UVD_HANDLES)
- return 0;
+ radeon_uvd_note_usage(rdev);
- size = radeon_bo_size(rdev->uvd.vcpu_bo);
- size -= rdev->uvd_fw->size;
+ r = radeon_uvd_get_destroy_msg(rdev,
+ R600_RING_TYPE_UVD_INDEX, handle, &fence);
+ if (r) {
+ DRM_ERROR("Error destroying UVD (%d)!\n", r);
+ continue;
+ }
- ptr = rdev->uvd.cpu_addr;
- ptr += rdev->uvd_fw->size;
+ radeon_fence_wait(fence, false);
+ radeon_fence_unref(&fence);
- rdev->uvd.saved_bo = kmalloc(size, GFP_KERNEL);
- memcpy(rdev->uvd.saved_bo, ptr, size);
+ rdev->uvd.filp[i] = NULL;
+ atomic_set(&rdev->uvd.handles[i], 0);
+ }
+ }
return 0;
}
@@ -246,12 +250,7 @@ int radeon_uvd_resume(struct radeon_device *rdev)
ptr = rdev->uvd.cpu_addr;
ptr += rdev->uvd_fw->size;
- if (rdev->uvd.saved_bo != NULL) {
- memcpy(ptr, rdev->uvd.saved_bo, size);
- kfree(rdev->uvd.saved_bo);
- rdev->uvd.saved_bo = NULL;
- } else
- memset(ptr, 0, size);
+ memset(ptr, 0, size);
return 0;
}
@@ -396,6 +395,29 @@ static int radeon_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[])
return 0;
}
+static int radeon_uvd_validate_codec(struct radeon_cs_parser *p,
+ unsigned stream_type)
+{
+ switch (stream_type) {
+ case 0: /* H264 */
+ case 1: /* VC1 */
+ /* always supported */
+ return 0;
+
+ case 3: /* MPEG2 */
+ case 4: /* MPEG4 */
+ /* only since UVD 3 */
+ if (p->rdev->family >= CHIP_PALM)
+ return 0;
+
+ /* fall through */
+ default:
+ DRM_ERROR("UVD codec not supported by hardware %d!\n",
+ stream_type);
+ return -EINVAL;
+ }
+}
+
static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo,
unsigned offset, unsigned buf_sizes[])
{
@@ -436,50 +458,70 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo,
return -EINVAL;
}
- if (msg_type == 1) {
- /* it's a decode msg, calc buffer sizes */
- r = radeon_uvd_cs_msg_decode(msg, buf_sizes);
- /* calc image size (width * height) */
- img_size = msg[6] * msg[7];
+ switch (msg_type) {
+ case 0:
+ /* it's a create msg, calc image size (width * height) */
+ img_size = msg[7] * msg[8];
+
+ r = radeon_uvd_validate_codec(p, msg[4]);
radeon_bo_kunmap(bo);
if (r)
return r;
- } else if (msg_type == 2) {
+ /* try to alloc a new handle */
+ for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) {
+ if (atomic_read(&p->rdev->uvd.handles[i]) == handle) {
+ DRM_ERROR("Handle 0x%x already in use!\n", handle);
+ return -EINVAL;
+ }
+
+ if (!atomic_cmpxchg(&p->rdev->uvd.handles[i], 0, handle)) {
+ p->rdev->uvd.filp[i] = p->filp;
+ p->rdev->uvd.img_size[i] = img_size;
+ return 0;
+ }
+ }
+
+ DRM_ERROR("No more free UVD handles!\n");
+ return -EINVAL;
+
+ case 1:
+ /* it's a decode msg, validate codec and calc buffer sizes */
+ r = radeon_uvd_validate_codec(p, msg[4]);
+ if (!r)
+ r = radeon_uvd_cs_msg_decode(msg, buf_sizes);
+ radeon_bo_kunmap(bo);
+ if (r)
+ return r;
+
+ /* validate the handle */
+ for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) {
+ if (atomic_read(&p->rdev->uvd.handles[i]) == handle) {
+ if (p->rdev->uvd.filp[i] != p->filp) {
+ DRM_ERROR("UVD handle collision detected!\n");
+ return -EINVAL;
+ }
+ return 0;
+ }
+ }
+
+ DRM_ERROR("Invalid UVD handle 0x%x!\n", handle);
+ return -ENOENT;
+
+ case 2:
/* it's a destroy msg, free the handle */
for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i)
atomic_cmpxchg(&p->rdev->uvd.handles[i], handle, 0);
radeon_bo_kunmap(bo);
return 0;
- } else {
- /* it's a create msg, calc image size (width * height) */
- img_size = msg[7] * msg[8];
- radeon_bo_kunmap(bo);
- if (msg_type != 0) {
- DRM_ERROR("Illegal UVD message type (%d)!\n", msg_type);
- return -EINVAL;
- }
-
- /* it's a create msg, no special handling needed */
- }
-
- /* create or decode, validate the handle */
- for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) {
- if (atomic_read(&p->rdev->uvd.handles[i]) == handle)
- return 0;
- }
+ default:
- /* handle not found try to alloc a new one */
- for (i = 0; i < RADEON_MAX_UVD_HANDLES; ++i) {
- if (!atomic_cmpxchg(&p->rdev->uvd.handles[i], 0, handle)) {
- p->rdev->uvd.filp[i] = p->filp;
- p->rdev->uvd.img_size[i] = img_size;
- return 0;
- }
+ DRM_ERROR("Illegal UVD message type (%d)!\n", msg_type);
+ return -EINVAL;
}
- DRM_ERROR("No more free UVD handles!\n");
+ BUG();
return -EINVAL;
}
diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c
index 24f849f888bb..0de5711ac508 100644
--- a/drivers/gpu/drm/radeon/radeon_vce.c
+++ b/drivers/gpu/drm/radeon/radeon_vce.c
@@ -493,18 +493,27 @@ int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi,
*
* @p: parser context
* @handle: handle to validate
+ * @allocated: allocated a new handle?
*
* Validates the handle and return the found session index or -EINVAL
* we we don't have another free session index.
*/
-int radeon_vce_validate_handle(struct radeon_cs_parser *p, uint32_t handle)
+static int radeon_vce_validate_handle(struct radeon_cs_parser *p,
+ uint32_t handle, bool *allocated)
{
unsigned i;
+ *allocated = false;
+
/* validate the handle */
for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
- if (atomic_read(&p->rdev->vce.handles[i]) == handle)
+ if (atomic_read(&p->rdev->vce.handles[i]) == handle) {
+ if (p->rdev->vce.filp[i] != p->filp) {
+ DRM_ERROR("VCE handle collision detected!\n");
+ return -EINVAL;
+ }
return i;
+ }
}
/* handle not found try to alloc a new one */
@@ -512,6 +521,7 @@ int radeon_vce_validate_handle(struct radeon_cs_parser *p, uint32_t handle)
if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) {
p->rdev->vce.filp[i] = p->filp;
p->rdev->vce.img_size[i] = 0;
+ *allocated = true;
return i;
}
}
@@ -529,10 +539,10 @@ int radeon_vce_validate_handle(struct radeon_cs_parser *p, uint32_t handle)
int radeon_vce_cs_parse(struct radeon_cs_parser *p)
{
int session_idx = -1;
- bool destroyed = false;
+ bool destroyed = false, created = false, allocated = false;
uint32_t tmp, handle = 0;
uint32_t *size = &tmp;
- int i, r;
+ int i, r = 0;
while (p->idx < p->chunk_ib->length_dw) {
uint32_t len = radeon_get_ib_value(p, p->idx);
@@ -540,18 +550,21 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p)
if ((len < 8) || (len & 3)) {
DRM_ERROR("invalid VCE command length (%d)!\n", len);
- return -EINVAL;
+ r = -EINVAL;
+ goto out;
}
if (destroyed) {
DRM_ERROR("No other command allowed after destroy!\n");
- return -EINVAL;
+ r = -EINVAL;
+ goto out;
}
switch (cmd) {
case 0x00000001: // session
handle = radeon_get_ib_value(p, p->idx + 2);
- session_idx = radeon_vce_validate_handle(p, handle);
+ session_idx = radeon_vce_validate_handle(p, handle,
+ &allocated);
if (session_idx < 0)
return session_idx;
size = &p->rdev->vce.img_size[session_idx];
@@ -561,6 +574,13 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p)
break;
case 0x01000001: // create
+ created = true;
+ if (!allocated) {
+ DRM_ERROR("Handle already in use!\n");
+ r = -EINVAL;
+ goto out;
+ }
+
*size = radeon_get_ib_value(p, p->idx + 8) *
radeon_get_ib_value(p, p->idx + 10) *
8 * 3 / 2;
@@ -578,12 +598,12 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p)
r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9,
*size);
if (r)
- return r;
+ goto out;
r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11,
*size / 3);
if (r)
- return r;
+ goto out;
break;
case 0x02000001: // destroy
@@ -594,7 +614,7 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p)
r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2,
*size * 2);
if (r)
- return r;
+ goto out;
break;
case 0x05000004: // video bitstream buffer
@@ -602,36 +622,47 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p)
r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2,
tmp);
if (r)
- return r;
+ goto out;
break;
case 0x05000005: // feedback buffer
r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2,
4096);
if (r)
- return r;
+ goto out;
break;
default:
DRM_ERROR("invalid VCE command (0x%x)!\n", cmd);
- return -EINVAL;
+ r = -EINVAL;
+ goto out;
}
if (session_idx == -1) {
DRM_ERROR("no session command at start of IB\n");
- return -EINVAL;
+ r = -EINVAL;
+ goto out;
}
p->idx += len / 4;
}
- if (destroyed) {
- /* IB contains a destroy msg, free the handle */
+ if (allocated && !created) {
+ DRM_ERROR("New session without create command!\n");
+ r = -ENOENT;
+ }
+
+out:
+ if ((!r && destroyed) || (r && allocated)) {
+ /*
+ * IB contains a destroy msg or we have allocated an
+ * handle and got an error, anyway free the handle
+ */
for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i)
atomic_cmpxchg(&p->rdev->vce.handles[i], handle, 0);
}
- return 0;
+ return r;
}
/**
diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c
index 2a5a4a9e772d..9c3377ca17b7 100644
--- a/drivers/gpu/drm/radeon/radeon_vm.c
+++ b/drivers/gpu/drm/radeon/radeon_vm.c
@@ -458,14 +458,16 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
/* make sure object fit at this offset */
eoffset = soffset + size;
if (soffset >= eoffset) {
- return -EINVAL;
+ r = -EINVAL;
+ goto error_unreserve;
}
last_pfn = eoffset / RADEON_GPU_PAGE_SIZE;
if (last_pfn > rdev->vm_manager.max_pfn) {
dev_err(rdev->dev, "va above limit (0x%08X > 0x%08X)\n",
last_pfn, rdev->vm_manager.max_pfn);
- return -EINVAL;
+ r = -EINVAL;
+ goto error_unreserve;
}
} else {
@@ -473,6 +475,24 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
}
mutex_lock(&vm->mutex);
+ soffset /= RADEON_GPU_PAGE_SIZE;
+ eoffset /= RADEON_GPU_PAGE_SIZE;
+ if (soffset || eoffset) {
+ struct interval_tree_node *it;
+ it = interval_tree_iter_first(&vm->va, soffset, eoffset - 1);
+ if (it && it != &bo_va->it) {
+ struct radeon_bo_va *tmp;
+ tmp = container_of(it, struct radeon_bo_va, it);
+ /* bo and tmp overlap, invalid offset */
+ dev_err(rdev->dev, "bo %p va 0x%010Lx conflict with "
+ "(bo %p 0x%010lx 0x%010lx)\n", bo_va->bo,
+ soffset, tmp->bo, tmp->it.start, tmp->it.last);
+ mutex_unlock(&vm->mutex);
+ r = -EINVAL;
+ goto error_unreserve;
+ }
+ }
+
if (bo_va->it.start || bo_va->it.last) {
if (bo_va->addr) {
/* add a clone of the bo_va to clear the old address */
@@ -480,7 +500,8 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
tmp = kzalloc(sizeof(struct radeon_bo_va), GFP_KERNEL);
if (!tmp) {
mutex_unlock(&vm->mutex);
- return -ENOMEM;
+ r = -ENOMEM;
+ goto error_unreserve;
}
tmp->it.start = bo_va->it.start;
tmp->it.last = bo_va->it.last;
@@ -490,6 +511,8 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
spin_lock(&vm->status_lock);
list_add(&tmp->vm_status, &vm->freed);
spin_unlock(&vm->status_lock);
+
+ bo_va->addr = 0;
}
interval_tree_remove(&bo_va->it, &vm->va);
@@ -497,21 +520,7 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
bo_va->it.last = 0;
}
- soffset /= RADEON_GPU_PAGE_SIZE;
- eoffset /= RADEON_GPU_PAGE_SIZE;
if (soffset || eoffset) {
- struct interval_tree_node *it;
- it = interval_tree_iter_first(&vm->va, soffset, eoffset - 1);
- if (it) {
- struct radeon_bo_va *tmp;
- tmp = container_of(it, struct radeon_bo_va, it);
- /* bo and tmp overlap, invalid offset */
- dev_err(rdev->dev, "bo %p va 0x%010Lx conflict with "
- "(bo %p 0x%010lx 0x%010lx)\n", bo_va->bo,
- soffset, tmp->bo, tmp->it.start, tmp->it.last);
- mutex_unlock(&vm->mutex);
- return -EINVAL;
- }
bo_va->it.start = soffset;
bo_va->it.last = eoffset - 1;
interval_tree_insert(&bo_va->it, &vm->va);
@@ -550,7 +559,6 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
r = radeon_vm_clear_bo(rdev, pt);
if (r) {
radeon_bo_unref(&pt);
- radeon_bo_reserve(bo_va->bo, false);
return r;
}
@@ -570,6 +578,10 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
mutex_unlock(&vm->mutex);
return 0;
+
+error_unreserve:
+ radeon_bo_unreserve(bo_va->bo);
+ return r;
}
/**
@@ -1107,7 +1119,8 @@ void radeon_vm_bo_rmv(struct radeon_device *rdev,
list_del(&bo_va->bo_list);
mutex_lock(&vm->mutex);
- interval_tree_remove(&bo_va->it, &vm->va);
+ if (bo_va->it.start || bo_va->it.last)
+ interval_tree_remove(&bo_va->it, &vm->va);
spin_lock(&vm->status_lock);
list_del(&bo_va->vm_status);
diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h
index 3cf1e2921545..9ef2064b1c9c 100644
--- a/drivers/gpu/drm/radeon/rv770d.h
+++ b/drivers/gpu/drm/radeon/rv770d.h
@@ -989,6 +989,9 @@
((n) & 0x3FFF) << 16)
/* UVD */
+#define UVD_SEMA_ADDR_LOW 0xef00
+#define UVD_SEMA_ADDR_HIGH 0xef04
+#define UVD_SEMA_CMD 0xef08
#define UVD_GPCOM_VCPU_CMD 0xef0c
#define UVD_GPCOM_VCPU_DATA0 0xef10
#define UVD_GPCOM_VCPU_DATA1 0xef14
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index b1d74bc375d8..4c679b802bc8 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -4318,7 +4318,7 @@ static int si_pcie_gart_enable(struct radeon_device *rdev)
/* empty context1-15 */
/* set vm size, must be a multiple of 4 */
WREG32(VM_CONTEXT1_PAGE_TABLE_START_ADDR, 0);
- WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, rdev->vm_manager.max_pfn);
+ WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, rdev->vm_manager.max_pfn - 1);
/* Assign the pt base to something valid for now; the pts used for
* the VMs are determined by the application and setup and assigned
* on the fly in the vm part of radeon_gart.c
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index b35bccfeef79..ff8b83f5e929 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -2924,6 +2924,7 @@ struct si_dpm_quirk {
static struct si_dpm_quirk si_dpm_quirk_list[] = {
/* PITCAIRN - https://bugs.freedesktop.org/show_bug.cgi?id=76490 */
{ PCI_VENDOR_ID_ATI, 0x6810, 0x1462, 0x3036, 0, 120000 },
+ { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0xe271, 0, 120000 },
{ 0, 0, 0, 0 },
};
diff --git a/drivers/gpu/drm/radeon/uvd_v1_0.c b/drivers/gpu/drm/radeon/uvd_v1_0.c
index e72b3cb59358..c6b1cbca47fc 100644
--- a/drivers/gpu/drm/radeon/uvd_v1_0.c
+++ b/drivers/gpu/drm/radeon/uvd_v1_0.c
@@ -466,18 +466,8 @@ bool uvd_v1_0_semaphore_emit(struct radeon_device *rdev,
struct radeon_semaphore *semaphore,
bool emit_wait)
{
- uint64_t addr = semaphore->gpu_addr;
-
- radeon_ring_write(ring, PACKET0(UVD_SEMA_ADDR_LOW, 0));
- radeon_ring_write(ring, (addr >> 3) & 0x000FFFFF);
-
- radeon_ring_write(ring, PACKET0(UVD_SEMA_ADDR_HIGH, 0));
- radeon_ring_write(ring, (addr >> 23) & 0x000FFFFF);
-
- radeon_ring_write(ring, PACKET0(UVD_SEMA_CMD, 0));
- radeon_ring_write(ring, emit_wait ? 1 : 0);
-
- return true;
+ /* disable semaphores for UVD V1 hardware */
+ return false;
}
/**
diff --git a/drivers/gpu/drm/radeon/uvd_v2_2.c b/drivers/gpu/drm/radeon/uvd_v2_2.c
index 89193519f8a1..7ed778cec7c6 100644
--- a/drivers/gpu/drm/radeon/uvd_v2_2.c
+++ b/drivers/gpu/drm/radeon/uvd_v2_2.c
@@ -60,6 +60,35 @@ void uvd_v2_2_fence_emit(struct radeon_device *rdev,
}
/**
+ * uvd_v2_2_semaphore_emit - emit semaphore command
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon_ring pointer
+ * @semaphore: semaphore to emit commands for
+ * @emit_wait: true if we should emit a wait command
+ *
+ * Emit a semaphore command (either wait or signal) to the UVD ring.
+ */
+bool uvd_v2_2_semaphore_emit(struct radeon_device *rdev,
+ struct radeon_ring *ring,
+ struct radeon_semaphore *semaphore,
+ bool emit_wait)
+{
+ uint64_t addr = semaphore->gpu_addr;
+
+ radeon_ring_write(ring, PACKET0(UVD_SEMA_ADDR_LOW, 0));
+ radeon_ring_write(ring, (addr >> 3) & 0x000FFFFF);
+
+ radeon_ring_write(ring, PACKET0(UVD_SEMA_ADDR_HIGH, 0));
+ radeon_ring_write(ring, (addr >> 23) & 0x000FFFFF);
+
+ radeon_ring_write(ring, PACKET0(UVD_SEMA_CMD, 0));
+ radeon_ring_write(ring, emit_wait ? 1 : 0);
+
+ return true;
+}
+
+/**
* uvd_v2_2_resume - memory controller programming
*
* @rdev: radeon_device pointer
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index ccb0ce073ef2..4557f335a8a5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1409,7 +1409,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
struct vop *vop;
struct resource *res;
size_t alloc_size;
- int ret;
+ int ret, irq;
of_id = of_match_device(vop_driver_dt_match, dev);
vop_data = of_id->data;
@@ -1445,11 +1445,12 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
return ret;
}
- vop->irq = platform_get_irq(pdev, 0);
- if (vop->irq < 0) {
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
dev_err(dev, "cannot find irq for vop\n");
- return vop->irq;
+ return irq;
}
+ vop->irq = (unsigned int)irq;
spin_lock_init(&vop->reg_lock);
spin_lock_init(&vop->irq_lock);
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 1833abd7d3aa..bfad15a913a0 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -173,7 +173,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
drm->irq_enabled = true;
/* syncpoints are used for full 32-bit hardware VBLANK counters */
- drm->vblank_disable_immediate = true;
drm->max_vblank_count = 0xffffffff;
err = drm_vblank_init(drm, drm->mode_config.num_crtc);
diff --git a/drivers/gpu/drm/vgem/Makefile b/drivers/gpu/drm/vgem/Makefile
index 1055cb79096c..3f4c7b842028 100644
--- a/drivers/gpu/drm/vgem/Makefile
+++ b/drivers/gpu/drm/vgem/Makefile
@@ -1,4 +1,4 @@
ccflags-y := -Iinclude/drm
-vgem-y := vgem_drv.o vgem_dma_buf.o
+vgem-y := vgem_drv.o
obj-$(CONFIG_DRM_VGEM) += vgem.o
diff --git a/drivers/gpu/drm/vgem/vgem_dma_buf.c b/drivers/gpu/drm/vgem/vgem_dma_buf.c
deleted file mode 100644
index 0254438ad1a6..000000000000
--- a/drivers/gpu/drm/vgem/vgem_dma_buf.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright © 2012 Intel Corporation
- * Copyright © 2014 The Chromium OS Authors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- *
- * Authors:
- * Ben Widawsky <ben@bwidawsk.net>
- *
- */
-
-#include <linux/dma-buf.h>
-#include "vgem_drv.h"
-
-struct sg_table *vgem_gem_prime_get_sg_table(struct drm_gem_object *gobj)
-{
- struct drm_vgem_gem_object *obj = to_vgem_bo(gobj);
- BUG_ON(obj->pages == NULL);
-
- return drm_prime_pages_to_sg(obj->pages, obj->base.size / PAGE_SIZE);
-}
-
-int vgem_gem_prime_pin(struct drm_gem_object *gobj)
-{
- struct drm_vgem_gem_object *obj = to_vgem_bo(gobj);
- return vgem_gem_get_pages(obj);
-}
-
-void vgem_gem_prime_unpin(struct drm_gem_object *gobj)
-{
- struct drm_vgem_gem_object *obj = to_vgem_bo(gobj);
- vgem_gem_put_pages(obj);
-}
-
-void *vgem_gem_prime_vmap(struct drm_gem_object *gobj)
-{
- struct drm_vgem_gem_object *obj = to_vgem_bo(gobj);
- BUG_ON(obj->pages == NULL);
-
- return vmap(obj->pages, obj->base.size / PAGE_SIZE, 0, PAGE_KERNEL);
-}
-
-void vgem_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
-{
- vunmap(vaddr);
-}
-
-struct drm_gem_object *vgem_gem_prime_import(struct drm_device *dev,
- struct dma_buf *dma_buf)
-{
- struct drm_vgem_gem_object *obj = NULL;
- int ret;
-
- obj = kzalloc(sizeof(*obj), GFP_KERNEL);
- if (obj == NULL) {
- ret = -ENOMEM;
- goto fail;
- }
-
- ret = drm_gem_object_init(dev, &obj->base, dma_buf->size);
- if (ret) {
- ret = -ENOMEM;
- goto fail_free;
- }
-
- get_dma_buf(dma_buf);
-
- obj->base.dma_buf = dma_buf;
- obj->use_dma_buf = true;
-
- return &obj->base;
-
-fail_free:
- kfree(obj);
-fail:
- return ERR_PTR(ret);
-}
diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c
index cb3b43525b2d..7a207ca547be 100644
--- a/drivers/gpu/drm/vgem/vgem_drv.c
+++ b/drivers/gpu/drm/vgem/vgem_drv.c
@@ -302,22 +302,13 @@ static const struct file_operations vgem_driver_fops = {
};
static struct drm_driver vgem_driver = {
- .driver_features = DRIVER_GEM | DRIVER_PRIME,
+ .driver_features = DRIVER_GEM,
.gem_free_object = vgem_gem_free_object,
.gem_vm_ops = &vgem_gem_vm_ops,
.ioctls = vgem_ioctls,
.fops = &vgem_driver_fops,
.dumb_create = vgem_gem_dumb_create,
.dumb_map_offset = vgem_gem_dumb_map,
- .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
- .gem_prime_export = drm_gem_prime_export,
- .gem_prime_import = vgem_gem_prime_import,
- .gem_prime_pin = vgem_gem_prime_pin,
- .gem_prime_unpin = vgem_gem_prime_unpin,
- .gem_prime_get_sg_table = vgem_gem_prime_get_sg_table,
- .gem_prime_vmap = vgem_gem_prime_vmap,
- .gem_prime_vunmap = vgem_gem_prime_vunmap,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
diff --git a/drivers/gpu/drm/vgem/vgem_drv.h b/drivers/gpu/drm/vgem/vgem_drv.h
index 57ab4d8f41f9..e9f92f7ee275 100644
--- a/drivers/gpu/drm/vgem/vgem_drv.h
+++ b/drivers/gpu/drm/vgem/vgem_drv.h
@@ -43,15 +43,4 @@ struct drm_vgem_gem_object {
extern void vgem_gem_put_pages(struct drm_vgem_gem_object *obj);
extern int vgem_gem_get_pages(struct drm_vgem_gem_object *obj);
-/* vgem_dma_buf.c */
-extern struct sg_table *vgem_gem_prime_get_sg_table(
- struct drm_gem_object *gobj);
-extern int vgem_gem_prime_pin(struct drm_gem_object *gobj);
-extern void vgem_gem_prime_unpin(struct drm_gem_object *gobj);
-extern void *vgem_gem_prime_vmap(struct drm_gem_object *gobj);
-extern void vgem_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
-extern struct drm_gem_object *vgem_gem_prime_import(struct drm_device *dev,
- struct dma_buf *dma_buf);
-
-
#endif
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 67bab5c36056..6d2f39d36e44 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -1119,10 +1119,9 @@ static int ipu_irq_init(struct ipu_soc *ipu)
ct->regs.mask = IPU_INT_CTRL(i / 32);
}
- irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler);
- irq_set_handler_data(ipu->irq_sync, ipu);
- irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler);
- irq_set_handler_data(ipu->irq_err, ipu);
+ irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu);
+ irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler,
+ ipu);
return 0;
}
@@ -1131,10 +1130,8 @@ static void ipu_irq_exit(struct ipu_soc *ipu)
{
int i, irq;
- irq_set_chained_handler(ipu->irq_err, NULL);
- irq_set_handler_data(ipu->irq_err, NULL);
- irq_set_chained_handler(ipu->irq_sync, NULL);
- irq_set_handler_data(ipu->irq_sync, NULL);
+ irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL);
+ irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL);
/* TODO: remove irq_domain_generic_chips */
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 15338afdf7f9..cc4c6649d195 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -634,7 +634,12 @@ config HID_PLANTRONICS
tristate "Plantronics USB HID Driver"
depends on HID
---help---
- Provides HID support for Plantronics telephony devices.
+ Provides HID support for Plantronics USB audio devices.
+ Correctly maps vendor unique volume up/down HID usages to
+ KEY_VOLUMEUP and KEY_VOLUMEDOWN events and prevents core mapping
+ of other vendor unique HID usages to random mouse events.
+
+ Say M here if you may ever plug in a Plantronics USB audio device.
config HID_PRIMAX
tristate "Primax non-fully HID-compliant devices"
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index e4a21dfd7ef3..2f8a41dc3cc8 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -24,7 +24,7 @@ obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
obj-$(CONFIG_HID_ACRUX) += hid-axff.o
obj-$(CONFIG_HID_APPLE) += hid-apple.o
obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o
-obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
+obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
@@ -46,12 +46,12 @@ obj-$(CONFIG_HID_ICADE) += hid-icade.o
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
obj-$(CONFIG_HID_KYE) += hid-kye.o
-obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
+obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
-obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
+obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 722a925795a2..157c62775053 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -706,7 +706,8 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
(hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 ||
- hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3_JP) &&
+ hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3_JP ||
+ hid->product == USB_DEVICE_ID_MS_POWER_COVER) &&
hid->group == HID_GROUP_MULTITOUCH)
hid->group = HID_GROUP_GENERIC;
@@ -1061,13 +1062,13 @@ static u32 s32ton(__s32 value, unsigned n)
* Search linux-kernel and linux-usb-devel archives for "hid-core extract".
*/
-static __u32 extract(const struct hid_device *hid, __u8 *report,
+__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
unsigned offset, unsigned n)
{
u64 x;
if (n > 32)
- hid_warn(hid, "extract() called with n (%d) > 32! (%s)\n",
+ hid_warn(hid, "hid_field_extract() called with n (%d) > 32! (%s)\n",
n, current->comm);
report += offset >> 3; /* adjust byte index */
@@ -1076,6 +1077,7 @@ static __u32 extract(const struct hid_device *hid, __u8 *report,
x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */
return (u32) x;
}
+EXPORT_SYMBOL_GPL(hid_field_extract);
/*
* "implement" : set bits in a little endian bit stream.
@@ -1221,9 +1223,9 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
for (n = 0; n < count; n++) {
value[n] = min < 0 ?
- snto32(extract(hid, data, offset + n * size, size),
- size) :
- extract(hid, data, offset + n * size, size);
+ snto32(hid_field_extract(hid, data, offset + n * size,
+ size), size) :
+ hid_field_extract(hid, data, offset + n * size, size);
/* Ignore report if ErrorRollOver */
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
@@ -1851,6 +1853,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
@@ -1901,6 +1904,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
@@ -1959,9 +1963,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
@@ -1997,6 +2004,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) },
@@ -2265,14 +2273,6 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) },
@@ -2399,14 +2399,6 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WTP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) },
#endif
- { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
- { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
- { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
- { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
- { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
- { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) },
- { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
- { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
{ }
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
index c4ef3bc726e3..1b764d1745f3 100644
--- a/drivers/hid/hid-cypress.c
+++ b/drivers/hid/hid-cypress.c
@@ -41,13 +41,9 @@ static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
for (i = 0; i < *rsize - 4; i++)
if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
- __u8 tmp;
-
rdesc[i] = 0x19;
rdesc[i + 2] = 0x29;
- tmp = rdesc[i + 3];
- rdesc[i + 3] = rdesc[i + 1];
- rdesc[i + 1] = tmp;
+ swap(rdesc[i + 3], rdesc[i + 1]);
}
return rdesc;
}
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 41f167e4d75f..b04b0820d816 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -164,6 +164,7 @@
#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
+#define USB_DEVICE_ID_ATEN_CS682 0x2213
#define USB_VENDOR_ID_ATMEL 0x03eb
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
@@ -226,6 +227,7 @@
#define USB_DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418
#define USB_DEVICE_ID_CHICONY_MULTI_TOUCH 0xb19d
#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618
+#define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE 0x1053
#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123
#define USB_DEVICE_ID_CHICONY_AK1D 0x1125
@@ -362,16 +364,6 @@
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100
-#define USB_VENDOR_ID_GLAB 0x06c2
-#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
-#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
-#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
-#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
-#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
-#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
-#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
-#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
-
#define USB_VENDOR_ID_GOODTOUCH 0x1aad
#define USB_DEVICE_ID_GOODTOUCH_000f 0x000f
@@ -585,6 +577,7 @@
#define USB_DEVICE_ID_LENOVO_TPKBD 0x6009
#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047
#define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048
+#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
#define USB_VENDOR_ID_LG 0x1fd2
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
@@ -672,6 +665,7 @@
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07dc
#define USB_DEVICE_ID_MS_TYPE_COVER_3_JP 0x07dd
+#define USB_DEVICE_ID_MS_POWER_COVER 0x07da
#define USB_VENDOR_ID_MOJO 0x8282
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
@@ -851,6 +845,7 @@
#define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
#define USB_DEVICE_ID_SONY_PS4_CONTROLLER 0x05c4
+#define USB_DEVICE_ID_SONY_MOTION_CONTROLLER 0x03d5
#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
@@ -957,13 +952,6 @@
#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061
#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068
-#define USB_VENDOR_ID_VERNIER 0x08f7
-#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
-#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
-#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
-#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
-#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006
-
#define USB_VENDOR_ID_VTL 0x0306
#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f
@@ -982,9 +970,6 @@
#define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
-#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
-#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
-#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
#define USB_DEVICE_ID_SUPER_JOY_BOX_3 0x8888
#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800
#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
@@ -1038,4 +1023,11 @@
#define USB_VENDOR_ID_RISO_KAGAKU 0x1294 /* Riso Kagaku Corp. */
#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */
+#define USB_VENDOR_ID_MULTIPLE_1781 0x1781
+#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD 0x0a8d
+
+#define USB_VENDOR_ID_DRACAL_RAPHNET 0x289b
+#define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002
+#define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003
+
#endif
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 008e89bf6f3c..3511bbaba505 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1157,7 +1157,8 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
return;
/* report the usage code as scancode if the key status has changed */
- if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
+ if (usage->type == EV_KEY &&
+ (!test_bit(usage->code, input->key)) == value)
input_event(input, EV_MSC, MSC_SCAN, usage->hid);
input_event(input, usage->type, usage->code, value);
diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c
index c4c3f0952521..4f59bffd0205 100644
--- a/drivers/hid/hid-lenovo.c
+++ b/drivers/hid/hid-lenovo.c
@@ -43,6 +43,35 @@ struct lenovo_drvdata_cptkbd {
#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
+static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
+ 0x05, 0x88, /* Usage Page (Vendor Usage Page 0x88) */
+ 0x09, 0x01, /* Usage (Vendor Usage 0x01) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x85, 0x04, /* Report ID (4) */
+ 0x19, 0x00, /* Usage Minimum (0) */
+ 0x2a, 0xff, 0xff, /* Usage Maximum (65535) */
+};
+
+static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ switch (hdev->product) {
+ case USB_DEVICE_ID_LENOVO_TPPRODOCK:
+ /* the fixups that need to be done:
+ * - get a reasonable usage max for the vendor collection
+ * 0x8801 from the report ID 4
+ */
+ if (*rsize >= 153 &&
+ memcmp(&rdesc[140], lenovo_pro_dock_need_fixup_collection,
+ sizeof(lenovo_pro_dock_need_fixup_collection)) == 0) {
+ rdesc[151] = 0x01;
+ rdesc[152] = 0x00;
+ }
+ break;
+ }
+ return rdesc;
+}
+
static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
struct hid_input *hi, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max)
@@ -599,7 +628,8 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
GFP_KERNEL);
if (data_pointer == NULL) {
hid_err(hdev, "Could not allocate memory for driver data\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err;
}
// set same default values as windows driver
@@ -610,7 +640,8 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
if (name_mute == NULL || name_micmute == NULL) {
hid_err(hdev, "Could not allocate memory for led data\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err;
}
snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
@@ -634,6 +665,9 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
lenovo_features_set_tpkbd(hdev);
return 0;
+err:
+ sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd);
+ return ret;
}
static int lenovo_probe_cptkbd(struct hid_device *hdev)
@@ -762,10 +796,29 @@ static void lenovo_remove(struct hid_device *hdev)
hid_hw_stop(hdev);
}
+static void lenovo_input_configured(struct hid_device *hdev,
+ struct hid_input *hi)
+{
+ switch (hdev->product) {
+ case USB_DEVICE_ID_LENOVO_TPKBD:
+ case USB_DEVICE_ID_LENOVO_CUSBKBD:
+ case USB_DEVICE_ID_LENOVO_CBTKBD:
+ if (test_bit(EV_REL, hi->input->evbit)) {
+ /* set only for trackpoint device */
+ __set_bit(INPUT_PROP_POINTER, hi->input->propbit);
+ __set_bit(INPUT_PROP_POINTING_STICK,
+ hi->input->propbit);
+ }
+ break;
+ }
+}
+
+
static const struct hid_device_id lenovo_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
{ }
};
@@ -774,10 +827,12 @@ MODULE_DEVICE_TABLE(hid, lenovo_devices);
static struct hid_driver lenovo_driver = {
.name = "lenovo",
.id_table = lenovo_devices,
+ .input_configured = lenovo_input_configured,
.input_mapping = lenovo_input_mapping,
.probe = lenovo_probe,
.remove = lenovo_remove,
.raw_event = lenovo_raw_event,
+ .report_fixup = lenovo_report_fixup,
};
module_hid_driver(lenovo_driver);
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index b86c18e651ed..429340d809b5 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -700,7 +700,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
/* insert a little delay of 10 jiffies ~ 40ms */
wait_queue_head_t wait;
init_waitqueue_head (&wait);
- wait_event_interruptible_timeout(wait, 0, 10);
+ wait_event_interruptible_timeout(wait, 0,
+ msecs_to_jiffies(40));
/* Select random Address */
buf[1] = 0xB2;
@@ -712,13 +713,16 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
if (drv_data->quirks & LG_FF)
- lgff_init(hdev);
- if (drv_data->quirks & LG_FF2)
- lg2ff_init(hdev);
- if (drv_data->quirks & LG_FF3)
- lg3ff_init(hdev);
- if (drv_data->quirks & LG_FF4)
- lg4ff_init(hdev);
+ ret = lgff_init(hdev);
+ else if (drv_data->quirks & LG_FF2)
+ ret = lg2ff_init(hdev);
+ else if (drv_data->quirks & LG_FF3)
+ ret = lg3ff_init(hdev);
+ else if (drv_data->quirks & LG_FF4)
+ ret = lg4ff_init(hdev);
+
+ if (ret)
+ goto err_free;
return 0;
err_free:
@@ -731,8 +735,8 @@ static void lg_remove(struct hid_device *hdev)
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if (drv_data->quirks & LG_FF4)
lg4ff_deinit(hdev);
-
- hid_hw_stop(hdev);
+ else
+ hid_hw_stop(hdev);
kfree(drv_data);
}
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 1232210b1cc5..02cec83caac3 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -68,26 +68,32 @@
#define LG4FF_FFEX_REV_MAJ 0x21
#define LG4FF_FFEX_REV_MIN 0x00
-static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
-static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
-
-struct lg4ff_device_entry {
- __u32 product_id;
- __u16 range;
- __u16 min_range;
- __u16 max_range;
+static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
+static void lg4ff_set_range_g25(struct hid_device *hid, u16 range);
+
+struct lg4ff_wheel_data {
+ const u32 product_id;
+ u16 range;
+ const u16 min_range;
+ const u16 max_range;
#ifdef CONFIG_LEDS_CLASS
- __u8 led_state;
+ u8 led_state;
struct led_classdev *led[5];
#endif
- u32 alternate_modes;
- const char *real_tag;
- const char *real_name;
- u16 real_product_id;
- struct list_head list;
+ const u32 alternate_modes;
+ const char * const real_tag;
+ const char * const real_name;
+ const u16 real_product_id;
+
void (*set_range)(struct hid_device *hid, u16 range);
};
+struct lg4ff_device_entry {
+ spinlock_t report_lock; /* Protect output HID report */
+ struct hid_report *report;
+ struct lg4ff_wheel_data wdata;
+};
+
static const signed short lg4ff_wheel_effects[] = {
FF_CONSTANT,
FF_AUTOCENTER,
@@ -95,16 +101,16 @@ static const signed short lg4ff_wheel_effects[] = {
};
struct lg4ff_wheel {
- const __u32 product_id;
+ const u32 product_id;
const signed short *ff_effects;
- const __u16 min_range;
- const __u16 max_range;
+ const u16 min_range;
+ const u16 max_range;
void (*set_range)(struct hid_device *hid, u16 range);
};
struct lg4ff_compat_mode_switch {
- const __u8 cmd_count; /* Number of commands to send */
- const __u8 cmd[];
+ const u8 cmd_count; /* Number of commands to send */
+ const u8 cmd[];
};
struct lg4ff_wheel_ident_info {
@@ -134,10 +140,10 @@ struct lg4ff_alternate_mode {
static const struct lg4ff_wheel lg4ff_devices[] = {
{USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
- {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_dfp},
- {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
- {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
- {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
+ {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_dfp},
+ {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
+ {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
+ {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL},
{USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}
};
@@ -245,10 +251,10 @@ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {
};
/* Recalculates X axis value accordingly to currently selected range */
-static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
+static s32 lg4ff_adjust_dfp_x_axis(s32 value, u16 range)
{
- __u16 max_range;
- __s32 new_value;
+ u16 max_range;
+ s32 new_value;
if (range == 900)
return value;
@@ -269,21 +275,21 @@ static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
}
int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data)
+ struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data)
{
struct lg4ff_device_entry *entry = drv_data->device_props;
- __s32 new_value = 0;
+ s32 new_value = 0;
if (!entry) {
hid_err(hid, "Device properties not found");
return 0;
}
- switch (entry->product_id) {
+ switch (entry->wdata.product_id) {
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
switch (usage->code) {
case ABS_X:
- new_value = lg4ff_adjust_dfp_x_axis(value, entry->range);
+ new_value = lg4ff_adjust_dfp_x_axis(value, entry->wdata.range);
input_event(field->hidinput->input, usage->type, usage->code, new_value);
return 1;
default:
@@ -294,14 +300,56 @@ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
}
}
-static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const struct lg4ff_wheel *wheel,
+ const struct lg4ff_multimode_wheel *mmode_wheel,
+ const u16 real_product_id)
+{
+ u32 alternate_modes = 0;
+ const char *real_tag = NULL;
+ const char *real_name = NULL;
+
+ if (mmode_wheel) {
+ alternate_modes = mmode_wheel->alternate_modes;
+ real_tag = mmode_wheel->real_tag;
+ real_name = mmode_wheel->real_name;
+ }
+
+ {
+ struct lg4ff_wheel_data t_wdata = { .product_id = wheel->product_id,
+ .real_product_id = real_product_id,
+ .min_range = wheel->min_range,
+ .max_range = wheel->max_range,
+ .set_range = wheel->set_range,
+ .alternate_modes = alternate_modes,
+ .real_tag = real_tag,
+ .real_name = real_name };
+
+ memcpy(wdata, &t_wdata, sizeof(t_wdata));
+ }
+}
+
+static int lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
+ s32 *value;
int x;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return -EINVAL;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return -EINVAL;
+ }
+ value = entry->report->field[0]->value;
+
#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
switch (effect->type) {
@@ -309,6 +357,7 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
CLAMP(x);
+ spin_lock_irqsave(&entry->report_lock, flags);
if (x == 0x80) {
/* De-activate force in slot-1*/
value[0] = 0x13;
@@ -319,7 +368,8 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
return 0;
}
@@ -331,7 +381,8 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
break;
}
return 0;
@@ -339,15 +390,16 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
/* Sends default autocentering command compatible with
* all wheels except Formula Force EX */
-static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
+static void lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
{
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
- __u32 expand_a, expand_b;
+ s32 *value = report->field[0]->value;
+ u32 expand_a, expand_b;
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
+ unsigned long flags;
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
@@ -360,8 +412,10 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
hid_err(hid, "Device properties not found!\n");
return;
}
+ value = entry->report->field[0]->value;
/* De-activate Auto-Center */
+ spin_lock_irqsave(&entry->report_lock, flags);
if (magnitude == 0) {
value[0] = 0xf5;
value[1] = 0x00;
@@ -371,7 +425,8 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
return;
}
@@ -384,7 +439,7 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
}
/* Adjust for non-MOMO wheels */
- switch (entry->product_id) {
+ switch (entry->wdata.product_id) {
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
break;
@@ -401,7 +456,7 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
/* Activate Auto-Center */
value[0] = 0x14;
@@ -412,18 +467,34 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
/* Sends autocentering command compatible with Formula Force EX */
-static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
+static void lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
{
struct hid_device *hid = input_get_drvdata(dev);
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
+ s32 *value;
magnitude = magnitude * 90 / 65535;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+ value = entry->report->field[0]->value;
+
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xfe;
value[1] = 0x03;
value[2] = magnitude >> 14;
@@ -432,18 +503,33 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
/* Sends command to set range compatible with G25/G27/Driving Force GT */
-static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
+static void lg4ff_set_range_g25(struct hid_device *hid, u16 range)
{
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
+ s32 *value;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+ value = entry->report->field[0]->value;
dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xf8;
value[1] = 0x81;
value[2] = range & 0x00ff;
@@ -452,20 +538,35 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
/* Sends commands to set range compatible with Driving Force Pro wheel */
-static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
+static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range)
{
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
int start_left, start_right, full_range;
- __s32 *value = report->field[0]->value;
+ s32 *value;
+
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+ value = entry->report->field[0]->value;
dbg_hid("Driving Force Pro: setting range to %u\n", range);
/* Prepare "coarse" limit command */
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xf8;
value[1] = 0x00; /* Set later */
value[2] = 0x00;
@@ -475,13 +576,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
value[6] = 0x00;
if (range > 200) {
- report->field[0]->value[1] = 0x03;
+ value[1] = 0x03;
full_range = 900;
} else {
- report->field[0]->value[1] = 0x02;
+ value[1] = 0x02;
full_range = 200;
}
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
/* Prepare "fine" limit command */
value[0] = 0x81;
@@ -493,7 +594,8 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
value[6] = 0x00;
if (range == 200 || range == 900) { /* Do not apply any fine limit */
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
return;
}
@@ -507,7 +609,8 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
value[6] = 0xff;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(const u16 real_product_id, const u16 target_product_id)
@@ -569,19 +672,35 @@ static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(cons
static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s)
{
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ struct lg4ff_device_entry *entry;
+ struct lg_drv_data *drv_data;
+ unsigned long flags;
+ s32 *value;
u8 i;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return -EINVAL;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return -EINVAL;
+ }
+ value = entry->report->field[0]->value;
+
+ spin_lock_irqsave(&entry->report_lock, flags);
for (i = 0; i < s->cmd_count; i++) {
u8 j;
for (j = 0; j < 7; j++)
value[j] = s->cmd[j + (7*i)];
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
}
+ spin_unlock_irqrestore(&entry->report_lock, flags);
hid_hw_wait(hid);
return 0;
}
@@ -606,23 +725,23 @@ static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attr
return 0;
}
- if (!entry->real_name) {
+ if (!entry->wdata.real_name) {
hid_err(hid, "NULL pointer to string\n");
return 0;
}
for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) {
- if (entry->alternate_modes & BIT(i)) {
+ if (entry->wdata.alternate_modes & BIT(i)) {
/* Print tag and full name */
count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s",
lg4ff_alternate_modes[i].tag,
- !lg4ff_alternate_modes[i].product_id ? entry->real_name : lg4ff_alternate_modes[i].name);
+ !lg4ff_alternate_modes[i].product_id ? entry->wdata.real_name : lg4ff_alternate_modes[i].name);
if (count >= PAGE_SIZE - 1)
return count;
/* Mark the currently active mode with an asterisk */
- if (lg4ff_alternate_modes[i].product_id == entry->product_id ||
- (lg4ff_alternate_modes[i].product_id == 0 && entry->product_id == entry->real_product_id))
+ if (lg4ff_alternate_modes[i].product_id == entry->wdata.product_id ||
+ (lg4ff_alternate_modes[i].product_id == 0 && entry->wdata.product_id == entry->wdata.real_product_id))
count += scnprintf(buf + count, PAGE_SIZE - count, " *\n");
else
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
@@ -675,10 +794,10 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
const u16 mode_product_id = lg4ff_alternate_modes[i].product_id;
const char *tag = lg4ff_alternate_modes[i].tag;
- if (entry->alternate_modes & BIT(i)) {
+ if (entry->wdata.alternate_modes & BIT(i)) {
if (!strcmp(tag, lbuf)) {
if (!mode_product_id)
- target_product_id = entry->real_product_id;
+ target_product_id = entry->wdata.real_product_id;
else
target_product_id = mode_product_id;
break;
@@ -693,24 +812,24 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
}
kfree(lbuf); /* Not needed anymore */
- if (target_product_id == entry->product_id) /* Nothing to do */
+ if (target_product_id == entry->wdata.product_id) /* Nothing to do */
return count;
/* Automatic switching has to be disabled for the switch to DF-EX mode to work correctly */
if (target_product_id == USB_DEVICE_ID_LOGITECH_WHEEL && !lg4ff_no_autoswitch) {
hid_info(hid, "\"%s\" cannot be switched to \"DF-EX\" mode. Load the \"hid_logitech\" module with \"lg4ff_no_autoswitch=1\" parameter set and try again\n",
- entry->real_name);
+ entry->wdata.real_name);
return -EINVAL;
}
/* Take care of hardware limitations */
- if ((entry->real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL) &&
- entry->product_id > target_product_id) {
- hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->real_name, lg4ff_alternate_modes[i].name);
+ if ((entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL) &&
+ entry->wdata.product_id > target_product_id) {
+ hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->wdata.real_name, lg4ff_alternate_modes[i].name);
return -EINVAL;
}
- s = lg4ff_get_mode_switch_command(entry->real_product_id, target_product_id);
+ s = lg4ff_get_mode_switch_command(entry->wdata.real_product_id, target_product_id);
if (!s) {
hid_err(hid, "Invalid target product ID %X\n", target_product_id);
return -EINVAL;
@@ -721,9 +840,9 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
}
static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store);
-/* Read current range and display it in terminal */
-static ssize_t range_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+/* Export the currently set range of the wheel */
+static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct hid_device *hid = to_hid_device(dev);
struct lg4ff_device_entry *entry;
@@ -742,19 +861,19 @@ static ssize_t range_show(struct device *dev, struct device_attribute *attr,
return 0;
}
- count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->range);
+ count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.range);
return count;
}
/* Set range to user specified value, call appropriate function
* according to the type of the wheel */
-static ssize_t range_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct hid_device *hid = to_hid_device(dev);
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
- __u16 range = simple_strtoul(buf, NULL, 10);
+ u16 range = simple_strtoul(buf, NULL, 10);
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
@@ -769,18 +888,18 @@ static ssize_t range_store(struct device *dev, struct device_attribute *attr,
}
if (range == 0)
- range = entry->max_range;
+ range = entry->wdata.max_range;
/* Check if the wheel supports range setting
* and that the range is within limits for the wheel */
- if (entry->set_range != NULL && range >= entry->min_range && range <= entry->max_range) {
- entry->set_range(hid, range);
- entry->range = range;
+ if (entry->wdata.set_range && range >= entry->wdata.min_range && range <= entry->wdata.max_range) {
+ entry->wdata.set_range(hid, range);
+ entry->wdata.range = range;
}
return count;
}
-static DEVICE_ATTR_RW(range);
+static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_range_show, lg4ff_range_store);
static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -801,12 +920,12 @@ static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *a
return 0;
}
- if (!entry->real_tag || !entry->real_name) {
+ if (!entry->wdata.real_tag || !entry->wdata.real_name) {
hid_err(hid, "NULL pointer to string\n");
return 0;
}
- count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->real_tag, entry->real_name);
+ count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->wdata.real_tag, entry->wdata.real_name);
return count;
}
@@ -818,12 +937,27 @@ static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *
static DEVICE_ATTR(real_id, S_IRUGO, lg4ff_real_id_show, lg4ff_real_id_store);
#ifdef CONFIG_LEDS_CLASS
-static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
+static void lg4ff_set_leds(struct hid_device *hid, u8 leds)
{
- struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ struct lg_drv_data *drv_data;
+ struct lg4ff_device_entry *entry;
+ unsigned long flags;
+ s32 *value;
+
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Private driver data not found!\n");
+ return;
+ }
+
+ entry = drv_data->device_props;
+ if (!entry) {
+ hid_err(hid, "Device properties not found!\n");
+ return;
+ }
+ value = entry->report->field[0]->value;
+ spin_lock_irqsave(&entry->report_lock, flags);
value[0] = 0xf8;
value[1] = 0x12;
value[2] = leds;
@@ -831,7 +965,8 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
value[4] = 0x00;
value[5] = 0x00;
value[6] = 0x00;
- hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+ hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+ spin_unlock_irqrestore(&entry->report_lock, flags);
}
static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
@@ -848,7 +983,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
return;
}
- entry = (struct lg4ff_device_entry *)drv_data->device_props;
+ entry = drv_data->device_props;
if (!entry) {
hid_err(hid, "Device properties not found.");
@@ -856,15 +991,15 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
}
for (i = 0; i < 5; i++) {
- if (led_cdev != entry->led[i])
+ if (led_cdev != entry->wdata.led[i])
continue;
- state = (entry->led_state >> i) & 1;
+ state = (entry->wdata.led_state >> i) & 1;
if (value == LED_OFF && state) {
- entry->led_state &= ~(1 << i);
- lg4ff_set_leds(hid, entry->led_state);
+ entry->wdata.led_state &= ~(1 << i);
+ lg4ff_set_leds(hid, entry->wdata.led_state);
} else if (value != LED_OFF && !state) {
- entry->led_state |= 1 << i;
- lg4ff_set_leds(hid, entry->led_state);
+ entry->wdata.led_state |= 1 << i;
+ lg4ff_set_leds(hid, entry->wdata.led_state);
}
break;
}
@@ -883,7 +1018,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
return LED_OFF;
}
- entry = (struct lg4ff_device_entry *)drv_data->device_props;
+ entry = drv_data->device_props;
if (!entry) {
hid_err(hid, "Device properties not found.");
@@ -891,8 +1026,8 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
}
for (i = 0; i < 5; i++)
- if (led_cdev == entry->led[i]) {
- value = (entry->led_state >> i) & 1;
+ if (led_cdev == entry->wdata.led[i]) {
+ value = (entry->wdata.led_state >> i) & 1;
break;
}
@@ -991,8 +1126,11 @@ int lg4ff_init(struct hid_device *hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
struct input_dev *dev = hidinput->input;
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
+ const struct lg4ff_multimode_wheel *mmode_wheel = NULL;
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
int error, i, j;
@@ -1003,6 +1141,18 @@ int lg4ff_init(struct hid_device *hid)
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
return -1;
+ drv_data = hid_get_drvdata(hid);
+ if (!drv_data) {
+ hid_err(hid, "Cannot add device, private driver data not allocated\n");
+ return -1;
+ }
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+ spin_lock_init(&entry->report_lock);
+ entry->report = report;
+ drv_data->device_props = entry;
+
/* Check if a multimode wheel has been connected and
* handle it appropriately */
mmode_ret = lg4ff_handle_multimode_wheel(hid, &real_product_id, bcdDevice);
@@ -1012,6 +1162,11 @@ int lg4ff_init(struct hid_device *hid)
*/
if (mmode_ret == LG4FF_MMODE_SWITCHED)
return 0;
+ else if (mmode_ret < 0) {
+ hid_err(hid, "Unable to switch device mode during initialization, errno %d\n", mmode_ret);
+ error = mmode_ret;
+ goto err_init;
+ }
/* Check what wheel has been connected */
for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
@@ -1022,9 +1177,11 @@ int lg4ff_init(struct hid_device *hid)
}
if (i == ARRAY_SIZE(lg4ff_devices)) {
- hid_err(hid, "Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to"
- "LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>\n");
- return -1;
+ hid_err(hid, "This device is flagged to be handled by the lg4ff module but this module does not know how to handle it. "
+ "Please report this as a bug to LKML, Simon Wood <simon@mungewell.org> or "
+ "Michal Maly <madcatxster@devoid-pointer.net>\n");
+ error = -1;
+ goto err_init;
}
if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
@@ -1035,7 +1192,8 @@ int lg4ff_init(struct hid_device *hid)
if (mmode_idx == ARRAY_SIZE(lg4ff_multimode_wheels)) {
hid_err(hid, "Device product ID %X is not listed as a multimode wheel", real_product_id);
- return -1;
+ error = -1;
+ goto err_init;
}
}
@@ -1043,37 +1201,17 @@ int lg4ff_init(struct hid_device *hid)
for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
- error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
+ error = input_ff_create_memless(dev, NULL, lg4ff_play);
if (error)
- return error;
-
- /* Get private driver data */
- drv_data = hid_get_drvdata(hid);
- if (!drv_data) {
- hid_err(hid, "Cannot add device, private driver data not allocated\n");
- return -1;
- }
+ goto err_init;
/* Initialize device properties */
- entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
- if (!entry) {
- hid_err(hid, "Cannot add device, insufficient memory to allocate device properties.\n");
- return -ENOMEM;
- }
- drv_data->device_props = entry;
-
- entry->product_id = lg4ff_devices[i].product_id;
- entry->real_product_id = real_product_id;
- entry->min_range = lg4ff_devices[i].min_range;
- entry->max_range = lg4ff_devices[i].max_range;
- entry->set_range = lg4ff_devices[i].set_range;
if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
BUG_ON(mmode_idx == -1);
- entry->alternate_modes = lg4ff_multimode_wheels[mmode_idx].alternate_modes;
- entry->real_tag = lg4ff_multimode_wheels[mmode_idx].real_tag;
- entry->real_name = lg4ff_multimode_wheels[mmode_idx].real_name;
+ mmode_wheel = &lg4ff_multimode_wheels[mmode_idx];
}
+ lg4ff_init_wheel_data(&entry->wdata, &lg4ff_devices[i], mmode_wheel, real_product_id);
/* Check if autocentering is available and
* set the centering force to zero by default */
@@ -1081,9 +1219,9 @@ int lg4ff_init(struct hid_device *hid)
/* Formula Force EX expects different autocentering command */
if ((bcdDevice >> 8) == LG4FF_FFEX_REV_MAJ &&
(bcdDevice & 0xff) == LG4FF_FFEX_REV_MIN)
- dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
+ dev->ff->set_autocenter = lg4ff_set_autocenter_ffex;
else
- dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
+ dev->ff->set_autocenter = lg4ff_set_autocenter_default;
dev->ff->set_autocenter(dev, 0);
}
@@ -1091,27 +1229,27 @@ int lg4ff_init(struct hid_device *hid)
/* Create sysfs interface */
error = device_create_file(&hid->dev, &dev_attr_range);
if (error)
- return error;
+ hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error);
if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
error = device_create_file(&hid->dev, &dev_attr_real_id);
if (error)
- return error;
+ hid_warn(hid, "Unable to create sysfs interface for \"real_id\", errno %d\n", error);
error = device_create_file(&hid->dev, &dev_attr_alternate_modes);
if (error)
- return error;
+ hid_warn(hid, "Unable to create sysfs interface for \"alternate_modes\", errno %d\n", error);
}
dbg_hid("sysfs interface created\n");
/* Set the maximum range to start with */
- entry->range = entry->max_range;
- if (entry->set_range != NULL)
- entry->set_range(hid, entry->range);
+ entry->wdata.range = entry->wdata.max_range;
+ if (entry->wdata.set_range)
+ entry->wdata.set_range(hid, entry->wdata.range);
#ifdef CONFIG_LEDS_CLASS
/* register led subsystem - G27 only */
- entry->led_state = 0;
+ entry->wdata.led_state = 0;
for (j = 0; j < 5; j++)
- entry->led[j] = NULL;
+ entry->wdata.led[j] = NULL;
if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) {
struct led_classdev *led;
@@ -1126,7 +1264,7 @@ int lg4ff_init(struct hid_device *hid)
led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
if (!led) {
hid_err(hid, "can't allocate memory for LED %d\n", j);
- goto err;
+ goto err_leds;
}
name = (void *)(&led[1]);
@@ -1137,16 +1275,16 @@ int lg4ff_init(struct hid_device *hid)
led->brightness_get = lg4ff_led_get_brightness;
led->brightness_set = lg4ff_led_set_brightness;
- entry->led[j] = led;
+ entry->wdata.led[j] = led;
error = led_classdev_register(&hid->dev, led);
if (error) {
hid_err(hid, "failed to register LED %d. Aborting.\n", j);
-err:
+err_leds:
/* Deregister LEDs (if any) */
for (j = 0; j < 5; j++) {
- led = entry->led[j];
- entry->led[j] = NULL;
+ led = entry->wdata.led[j];
+ entry->wdata.led[j] = NULL;
if (!led)
continue;
led_classdev_unregister(led);
@@ -1160,6 +1298,11 @@ out:
#endif
hid_info(hid, "Force feedback support for Logitech Gaming Wheels\n");
return 0;
+
+err_init:
+ drv_data->device_props = NULL;
+ kfree(entry);
+ return error;
}
int lg4ff_deinit(struct hid_device *hid)
@@ -1176,14 +1319,13 @@ int lg4ff_deinit(struct hid_device *hid)
if (!entry)
goto out; /* Nothing more to do */
- device_remove_file(&hid->dev, &dev_attr_range);
-
/* Multimode devices will have at least the "MODE_NATIVE" bit set */
- if (entry->alternate_modes) {
+ if (entry->wdata.alternate_modes) {
device_remove_file(&hid->dev, &dev_attr_real_id);
device_remove_file(&hid->dev, &dev_attr_alternate_modes);
}
+ device_remove_file(&hid->dev, &dev_attr_range);
#ifdef CONFIG_LEDS_CLASS
{
int j;
@@ -1192,8 +1334,8 @@ int lg4ff_deinit(struct hid_device *hid)
/* Deregister LEDs (if any) */
for (j = 0; j < 5; j++) {
- led = entry->led[j];
- entry->led[j] = NULL;
+ led = entry->wdata.led[j];
+ entry->wdata.led[j] = NULL;
if (!led)
continue;
led_classdev_unregister(led);
@@ -1201,10 +1343,10 @@ int lg4ff_deinit(struct hid_device *hid)
}
}
#endif
+ hid_hw_stop(hid);
+ drv_data->device_props = NULL;
- /* Deallocate memory */
kfree(entry);
-
out:
dbg_hid("Device successfully unregistered\n");
return 0;
diff --git a/drivers/hid/hid-lg4ff.h b/drivers/hid/hid-lg4ff.h
index 5b6a5086c47f..66201af44da3 100644
--- a/drivers/hid/hid-lg4ff.h
+++ b/drivers/hid/hid-lg4ff.h
@@ -5,12 +5,12 @@
extern int lg4ff_no_autoswitch; /* From hid-lg.c */
int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
+ struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data);
int lg4ff_init(struct hid_device *hdev);
int lg4ff_deinit(struct hid_device *hdev);
#else
static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
+ struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data) { return 0; }
static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
#endif
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index b3cf6fd4be96..484196459305 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -40,11 +40,11 @@ MODULE_PARM_DESC(disable_raw_mode,
#define HIDPP_REPORT_LONG_LENGTH 20
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
+#define HIDPP_QUIRK_CLASS_M560 BIT(1)
-/* bits 1..20 are reserved for classes */
+/* bits 2..20 are reserved for classes */
#define HIDPP_QUIRK_DELAYED_INIT BIT(21)
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
-#define HIDPP_QUIRK_MULTI_INPUT BIT(23)
/*
* There are two hidpp protocols in use, the first version hidpp10 is known
@@ -706,12 +706,6 @@ static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- struct hidpp_device *hidpp = hid_get_drvdata(hdev);
-
- if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) &&
- (field->application == HID_GD_KEYBOARD))
- return 0;
-
return -1;
}
@@ -720,10 +714,6 @@ static void wtp_populate_input(struct hidpp_device *hidpp,
{
struct wtp_data *wd = hidpp->private_data;
- if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) && origin_is_hid_core)
- /* this is the generic hid-input call */
- return;
-
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
__clear_bit(EV_REL, input_dev->evbit);
@@ -941,6 +931,207 @@ static int wtp_connect(struct hid_device *hdev, bool connected)
true, true);
}
+/* ------------------------------------------------------------------------- */
+/* Logitech M560 devices */
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Logitech M560 protocol overview
+ *
+ * The Logitech M560 mouse, is designed for windows 8. When the middle and/or
+ * the sides buttons are pressed, it sends some keyboard keys events
+ * instead of buttons ones.
+ * To complicate things further, the middle button keys sequence
+ * is different from the odd press and the even press.
+ *
+ * forward button -> Super_R
+ * backward button -> Super_L+'d' (press only)
+ * middle button -> 1st time: Alt_L+SuperL+XF86TouchpadOff (press only)
+ * 2nd time: left-click (press only)
+ * NB: press-only means that when the button is pressed, the
+ * KeyPress/ButtonPress and KeyRelease/ButtonRelease events are generated
+ * together sequentially; instead when the button is released, no event is
+ * generated !
+ *
+ * With the command
+ * 10<xx>0a 3500af03 (where <xx> is the mouse id),
+ * the mouse reacts differently:
+ * - it never sends a keyboard key event
+ * - for the three mouse button it sends:
+ * middle button press 11<xx>0a 3500af00...
+ * side 1 button (forward) press 11<xx>0a 3500b000...
+ * side 2 button (backward) press 11<xx>0a 3500ae00...
+ * middle/side1/side2 button release 11<xx>0a 35000000...
+ */
+
+static const u8 m560_config_parameter[] = {0x00, 0xaf, 0x03};
+
+struct m560_private_data {
+ struct input_dev *input;
+};
+
+/* how buttons are mapped in the report */
+#define M560_MOUSE_BTN_LEFT 0x01
+#define M560_MOUSE_BTN_RIGHT 0x02
+#define M560_MOUSE_BTN_WHEEL_LEFT 0x08
+#define M560_MOUSE_BTN_WHEEL_RIGHT 0x10
+
+#define M560_SUB_ID 0x0a
+#define M560_BUTTON_MODE_REGISTER 0x35
+
+static int m560_send_config_command(struct hid_device *hdev, bool connected)
+{
+ struct hidpp_report response;
+ struct hidpp_device *hidpp_dev;
+
+ hidpp_dev = hid_get_drvdata(hdev);
+
+ if (!connected)
+ return -ENODEV;
+
+ return hidpp_send_rap_command_sync(
+ hidpp_dev,
+ REPORT_ID_HIDPP_SHORT,
+ M560_SUB_ID,
+ M560_BUTTON_MODE_REGISTER,
+ (u8 *)m560_config_parameter,
+ sizeof(m560_config_parameter),
+ &response
+ );
+}
+
+static int m560_allocate(struct hid_device *hdev)
+{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ struct m560_private_data *d;
+
+ d = devm_kzalloc(&hdev->dev, sizeof(struct m560_private_data),
+ GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ hidpp->private_data = d;
+
+ return 0;
+};
+
+static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
+{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ struct m560_private_data *mydata = hidpp->private_data;
+
+ /* sanity check */
+ if (!mydata || !mydata->input) {
+ hid_err(hdev, "error in parameter\n");
+ return -EINVAL;
+ }
+
+ if (size < 7) {
+ hid_err(hdev, "error in report\n");
+ return 0;
+ }
+
+ if (data[0] == REPORT_ID_HIDPP_LONG &&
+ data[2] == M560_SUB_ID && data[6] == 0x00) {
+ /*
+ * m560 mouse report for middle, forward and backward button
+ *
+ * data[0] = 0x11
+ * data[1] = device-id
+ * data[2] = 0x0a
+ * data[5] = 0xaf -> middle
+ * 0xb0 -> forward
+ * 0xae -> backward
+ * 0x00 -> release all
+ * data[6] = 0x00
+ */
+
+ switch (data[5]) {
+ case 0xaf:
+ input_report_key(mydata->input, BTN_MIDDLE, 1);
+ break;
+ case 0xb0:
+ input_report_key(mydata->input, BTN_FORWARD, 1);
+ break;
+ case 0xae:
+ input_report_key(mydata->input, BTN_BACK, 1);
+ break;
+ case 0x00:
+ input_report_key(mydata->input, BTN_BACK, 0);
+ input_report_key(mydata->input, BTN_FORWARD, 0);
+ input_report_key(mydata->input, BTN_MIDDLE, 0);
+ break;
+ default:
+ hid_err(hdev, "error in report\n");
+ return 0;
+ }
+ input_sync(mydata->input);
+
+ } else if (data[0] == 0x02) {
+ /*
+ * Logitech M560 mouse report
+ *
+ * data[0] = type (0x02)
+ * data[1..2] = buttons
+ * data[3..5] = xy
+ * data[6] = wheel
+ */
+
+ int v;
+
+ input_report_key(mydata->input, BTN_LEFT,
+ !!(data[1] & M560_MOUSE_BTN_LEFT));
+ input_report_key(mydata->input, BTN_RIGHT,
+ !!(data[1] & M560_MOUSE_BTN_RIGHT));
+
+ if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT)
+ input_report_rel(mydata->input, REL_HWHEEL, -1);
+ else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT)
+ input_report_rel(mydata->input, REL_HWHEEL, 1);
+
+ v = hid_snto32(hid_field_extract(hdev, data+3, 0, 12), 12);
+ input_report_rel(mydata->input, REL_X, v);
+
+ v = hid_snto32(hid_field_extract(hdev, data+3, 12, 12), 12);
+ input_report_rel(mydata->input, REL_Y, v);
+
+ v = hid_snto32(data[6], 8);
+ input_report_rel(mydata->input, REL_WHEEL, v);
+
+ input_sync(mydata->input);
+ }
+
+ return 1;
+}
+
+static void m560_populate_input(struct hidpp_device *hidpp,
+ struct input_dev *input_dev, bool origin_is_hid_core)
+{
+ struct m560_private_data *mydata = hidpp->private_data;
+
+ mydata->input = input_dev;
+
+ __set_bit(EV_KEY, mydata->input->evbit);
+ __set_bit(BTN_MIDDLE, mydata->input->keybit);
+ __set_bit(BTN_RIGHT, mydata->input->keybit);
+ __set_bit(BTN_LEFT, mydata->input->keybit);
+ __set_bit(BTN_BACK, mydata->input->keybit);
+ __set_bit(BTN_FORWARD, mydata->input->keybit);
+
+ __set_bit(EV_REL, mydata->input->evbit);
+ __set_bit(REL_X, mydata->input->relbit);
+ __set_bit(REL_Y, mydata->input->relbit);
+ __set_bit(REL_WHEEL, mydata->input->relbit);
+ __set_bit(REL_HWHEEL, mydata->input->relbit);
+}
+
+static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ return -1;
+}
+
/* -------------------------------------------------------------------------- */
/* Generic HID++ devices */
/* -------------------------------------------------------------------------- */
@@ -953,6 +1144,9 @@ static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
return wtp_input_mapping(hdev, hi, field, usage, bit, max);
+ else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560 &&
+ field->application != HID_GD_MOUSE)
+ return m560_input_mapping(hdev, hi, field, usage, bit, max);
return 0;
}
@@ -962,6 +1156,8 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
{
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
wtp_populate_input(hidpp, input, origin_is_hid_core);
+ else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
+ m560_populate_input(hidpp, input, origin_is_hid_core);
}
static void hidpp_input_configured(struct hid_device *hdev,
@@ -1049,6 +1245,8 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
return wtp_raw_event(hdev, data, size);
+ else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
+ return m560_raw_event(hdev, data, size);
return 0;
}
@@ -1126,6 +1324,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
ret = wtp_connect(hdev, connected);
if (ret)
return;
+ } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
+ ret = m560_send_config_command(hdev, connected);
+ if (ret)
+ return;
}
if (!connected || hidpp->delayed_input)
@@ -1201,7 +1403,11 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
ret = wtp_allocate(hdev, id);
if (ret)
- goto wtp_allocate_fail;
+ goto allocate_fail;
+ } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
+ ret = m560_allocate(hdev);
+ if (ret)
+ goto allocate_fail;
}
INIT_WORK(&hidpp->work, delayed_work_cb);
@@ -1245,10 +1451,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
connect_mask &= ~HID_CONNECT_HIDINPUT;
- /* Re-enable hidinput for multi-input devices */
- if (hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT)
- connect_mask |= HID_CONNECT_HIDINPUT;
-
ret = hid_hw_start(hdev, connect_mask);
if (ret) {
hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
@@ -1268,7 +1470,7 @@ hid_hw_start_fail:
hid_parse_fail:
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
-wtp_allocate_fail:
+allocate_fail:
hid_set_drvdata(hdev, NULL);
return ret;
}
@@ -1296,11 +1498,10 @@ static const struct hid_device_id hidpp_devices[] = {
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_T651),
.driver_data = HIDPP_QUIRK_CLASS_WTP },
- { /* Keyboard TK820 */
+ { /* Mouse logitech M560 */
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
- USB_VENDOR_ID_LOGITECH, 0x4102),
- .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_MULTI_INPUT |
- HIDPP_QUIRK_CLASS_WTP },
+ USB_VENDOR_ID_LOGITECH, 0x402d),
+ .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index af935eb198c9..32a596f554af 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -280,6 +280,8 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP),
.driver_data = MS_HIDINPUT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
+ .driver_data = MS_HIDINPUT },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
.driver_data = MS_PRESENTER },
diff --git a/drivers/hid/hid-plantronics.c b/drivers/hid/hid-plantronics.c
index 2180e0789b76..febb21ee190e 100644
--- a/drivers/hid/hid-plantronics.c
+++ b/drivers/hid/hid-plantronics.c
@@ -2,7 +2,7 @@
* Plantronics USB HID Driver
*
* Copyright (c) 2014 JD Cole <jd.cole@plantronics.com>
- * Copyright (c) 2014 Terry Junge <terry.junge@plantronics.com>
+ * Copyright (c) 2015 Terry Junge <terry.junge@plantronics.com>
*/
/*
@@ -17,23 +17,138 @@
#include <linux/hid.h>
#include <linux/module.h>
+#define PLT_HID_1_0_PAGE 0xffa00000
+#define PLT_HID_2_0_PAGE 0xffa20000
+
+#define PLT_BASIC_TELEPHONY 0x0003
+#define PLT_BASIC_EXCEPTION 0x0005
+
+#define PLT_VOL_UP 0x00b1
+#define PLT_VOL_DOWN 0x00b2
+
+#define PLT1_VOL_UP (PLT_HID_1_0_PAGE | PLT_VOL_UP)
+#define PLT1_VOL_DOWN (PLT_HID_1_0_PAGE | PLT_VOL_DOWN)
+#define PLT2_VOL_UP (PLT_HID_2_0_PAGE | PLT_VOL_UP)
+#define PLT2_VOL_DOWN (PLT_HID_2_0_PAGE | PLT_VOL_DOWN)
+
+#define PLT_DA60 0xda60
+#define PLT_BT300_MIN 0x0413
+#define PLT_BT300_MAX 0x0418
+
+
+#define PLT_ALLOW_CONSUMER (field->application == HID_CP_CONSUMERCONTROL && \
+ (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER)
+
static int plantronics_input_mapping(struct hid_device *hdev,
struct hid_input *hi,
struct hid_field *field,
struct hid_usage *usage,
unsigned long **bit, int *max)
{
- if (field->application == HID_CP_CONSUMERCONTROL
- && (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
- hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
- usage->hid, field->application);
- return 0;
+ unsigned short mapped_key;
+ unsigned long plt_type = (unsigned long)hid_get_drvdata(hdev);
+
+ /* handle volume up/down mapping */
+ /* non-standard types or multi-HID interfaces - plt_type is PID */
+ if (!(plt_type & HID_USAGE_PAGE)) {
+ switch (plt_type) {
+ case PLT_DA60:
+ if (PLT_ALLOW_CONSUMER)
+ goto defaulted;
+ goto ignored;
+ default:
+ if (PLT_ALLOW_CONSUMER)
+ goto defaulted;
+ }
+ }
+ /* handle standard types - plt_type is 0xffa0uuuu or 0xffa2uuuu */
+ /* 'basic telephony compliant' - allow default consumer page map */
+ else if ((plt_type & HID_USAGE) >= PLT_BASIC_TELEPHONY &&
+ (plt_type & HID_USAGE) != PLT_BASIC_EXCEPTION) {
+ if (PLT_ALLOW_CONSUMER)
+ goto defaulted;
+ }
+ /* not 'basic telephony' - apply legacy mapping */
+ /* only map if the field is in the device's primary vendor page */
+ else if (!((field->application ^ plt_type) & HID_USAGE_PAGE)) {
+ switch (usage->hid) {
+ case PLT1_VOL_UP:
+ case PLT2_VOL_UP:
+ mapped_key = KEY_VOLUMEUP;
+ goto mapped;
+ case PLT1_VOL_DOWN:
+ case PLT2_VOL_DOWN:
+ mapped_key = KEY_VOLUMEDOWN;
+ goto mapped;
+ }
}
- hid_dbg(hdev, "usage: %08x (appl: %08x) - ignored\n",
- usage->hid, field->application);
+/*
+ * Future mapping of call control or other usages,
+ * if and when keys are defined would go here
+ * otherwise, ignore everything else that was not mapped
+ */
+ignored:
return -1;
+
+defaulted:
+ hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
+ usage->hid, field->application);
+ return 0;
+
+mapped:
+ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, mapped_key);
+ hid_dbg(hdev, "usage: %08x (appl: %08x) - mapped to key %d\n",
+ usage->hid, field->application, mapped_key);
+ return 1;
+}
+
+static unsigned long plantronics_device_type(struct hid_device *hdev)
+{
+ unsigned i, col_page;
+ unsigned long plt_type = hdev->product;
+
+ /* multi-HID interfaces? - plt_type is PID */
+ if (plt_type >= PLT_BT300_MIN && plt_type <= PLT_BT300_MAX)
+ goto exit;
+
+ /* determine primary vendor page */
+ for (i = 0; i < hdev->maxcollection; i++) {
+ col_page = hdev->collection[i].usage & HID_USAGE_PAGE;
+ if (col_page == PLT_HID_2_0_PAGE) {
+ plt_type = hdev->collection[i].usage;
+ break;
+ }
+ if (col_page == PLT_HID_1_0_PAGE)
+ plt_type = hdev->collection[i].usage;
+ }
+
+exit:
+ hid_dbg(hdev, "plt_type decoded as: %08lx\n", plt_type);
+ return plt_type;
+}
+
+static int plantronics_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret;
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "parse failed\n");
+ goto err;
+ }
+
+ hid_set_drvdata(hdev, (void *)plantronics_device_type(hdev));
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
+ HID_CONNECT_HIDINPUT_FORCE | HID_CONNECT_HIDDEV_FORCE);
+ if (ret)
+ hid_err(hdev, "hw start failed\n");
+
+err:
+ return ret;
}
static const struct hid_device_id plantronics_devices[] = {
@@ -46,6 +161,7 @@ static struct hid_driver plantronics_driver = {
.name = "plantronics",
.id_table = plantronics_devices,
.input_mapping = plantronics_input_mapping,
+ .probe = plantronics_probe,
};
module_hid_driver(plantronics_driver);
diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c
index 91fab975063c..e3e98ccf137b 100644
--- a/drivers/hid/hid-prodikeys.c
+++ b/drivers/hid/hid-prodikeys.c
@@ -395,11 +395,10 @@ static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)
/* break keys */
for (bit_index = 0; bit_index < 24; bit_index++) {
- key = pm->last_key[bit_index];
if (!((0x01 << bit_index) & bit_mask)) {
input_event(pm->input_ep82, EV_KEY,
pm->last_key[bit_index], 0);
- pm->last_key[bit_index] = 0;
+ pm->last_key[bit_index] = 0;
}
}
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index 368ffdf2c0a3..4cf80bb276dc 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -29,9 +29,9 @@
#define RMI_SET_RMI_MODE_REPORT_ID 0x0f /* Feature Report */
/* flags */
-#define RMI_READ_REQUEST_PENDING BIT(0)
-#define RMI_READ_DATA_PENDING BIT(1)
-#define RMI_STARTED BIT(2)
+#define RMI_READ_REQUEST_PENDING 0
+#define RMI_READ_DATA_PENDING 1
+#define RMI_STARTED 2
/* device flags */
#define RMI_DEVICE BIT(0)
@@ -1013,6 +1013,7 @@ static int rmi_populate_f30(struct hid_device *hdev)
static int rmi_populate(struct hid_device *hdev)
{
+ struct rmi_data *data = hid_get_drvdata(hdev);
int ret;
ret = rmi_scan_pdt(hdev);
@@ -1033,9 +1034,11 @@ static int rmi_populate(struct hid_device *hdev)
return ret;
}
- ret = rmi_populate_f30(hdev);
- if (ret)
- hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
+ if (!(data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)) {
+ ret = rmi_populate_f30(hdev);
+ if (ret)
+ hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
+ }
return 0;
}
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index c3f6f1e311ea..090a1ba0abb6 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -294,7 +294,7 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
if (!report)
return -EINVAL;
- mutex_lock(&hsdev->mutex);
+ mutex_lock(hsdev->mutex_ptr);
if (flag == SENSOR_HUB_SYNC) {
memset(&hsdev->pending, 0, sizeof(hsdev->pending));
init_completion(&hsdev->pending.ready);
@@ -328,7 +328,7 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
kfree(hsdev->pending.raw_data);
hsdev->pending.status = false;
}
- mutex_unlock(&hsdev->mutex);
+ mutex_unlock(hsdev->mutex_ptr);
return ret_val;
}
@@ -667,7 +667,14 @@ static int sensor_hub_probe(struct hid_device *hdev,
hsdev->vendor_id = hdev->vendor;
hsdev->product_id = hdev->product;
hsdev->usage = collection->usage;
- mutex_init(&hsdev->mutex);
+ hsdev->mutex_ptr = devm_kzalloc(&hdev->dev,
+ sizeof(struct mutex),
+ GFP_KERNEL);
+ if (!hsdev->mutex_ptr) {
+ ret = -ENOMEM;
+ goto err_stop_hw;
+ }
+ mutex_init(hsdev->mutex_ptr);
hsdev->start_collection_index = i;
if (last_hsdev)
last_hsdev->end_collection_index = i;
diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c
index 37845eccddb5..36b6470af947 100644
--- a/drivers/hid/hid-sjoy.c
+++ b/drivers/hid/hid-sjoy.c
@@ -166,6 +166,9 @@ static const struct hid_device_id sjoy_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD),
.driver_data = HID_QUIRK_MULTI_INPUT |
HID_QUIRK_SKIP_OUTPUT_REPORTS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII),
+ .driver_data = HID_QUIRK_MULTI_INPUT |
+ HID_QUIRK_SKIP_OUTPUT_REPORTS },
{ }
};
MODULE_DEVICE_TABLE(hid, sjoy_devices);
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 6ca96cebb44c..ed2f008f8403 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -46,20 +46,37 @@
#define PS3REMOTE BIT(4)
#define DUALSHOCK4_CONTROLLER_USB BIT(5)
#define DUALSHOCK4_CONTROLLER_BT BIT(6)
+#define MOTION_CONTROLLER_USB BIT(7)
+#define MOTION_CONTROLLER_BT BIT(8)
+#define NAVIGATION_CONTROLLER_USB BIT(9)
+#define NAVIGATION_CONTROLLER_BT BIT(10)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
+#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
+#define NAVIGATION_CONTROLLER (NAVIGATION_CONTROLLER_USB |\
+ NAVIGATION_CONTROLLER_BT)
#define DUALSHOCK4_CONTROLLER (DUALSHOCK4_CONTROLLER_USB |\
DUALSHOCK4_CONTROLLER_BT)
#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\
- DUALSHOCK4_CONTROLLER)
-#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
-#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
+ DUALSHOCK4_CONTROLLER | MOTION_CONTROLLER |\
+ NAVIGATION_CONTROLLER)
+#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
+ MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER)
+#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
+ MOTION_CONTROLLER)
#define MAX_LEDS 4
+/*
+ * The Sixaxis reports both digital and analog values for each button on the
+ * controller except for Start, Select and the PS button. The controller ends
+ * up reporting 27 axes which causes them to spill over into the multi-touch
+ * axis values. Additionally, the controller only has 20 actual, physical axes
+ * so there are several unused axes in between the used ones.
+ */
static __u8 sixaxis_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
- 0x09, 0x04, /* Usage (Joystik), */
+ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x85, 0x01, /* Report ID (1), */
@@ -134,6 +151,186 @@ static __u8 sixaxis_rdesc[] = {
0xC0 /* End Collection */
};
+/* PS/3 Motion controller */
+static __u8 motion_rdesc[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x04, /* Usage (Joystick), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0x01, /* Report ID (1), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x15, /* Report Count (21), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x35, 0x00, /* Physical Minimum (0), */
+ 0x45, 0x01, /* Physical Maximum (1), */
+ 0x05, 0x09, /* Usage Page (Button), */
+ 0x19, 0x01, /* Usage Minimum (01h), */
+ 0x29, 0x15, /* Usage Maximum (15h), */
+ 0x81, 0x02, /* Input (Variable), * Buttons */
+ 0x95, 0x0B, /* Report Count (11), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x81, 0x03, /* Input (Constant, Variable), * Padding */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0xA1, 0x00, /* Collection (Physical), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x35, 0x00, /* Physical Minimum (0), */
+ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x81, 0x02, /* Input (Variable), * Trigger */
+ 0xC0, /* End Collection, */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x07, /* Report Count (7), * skip 7 bytes */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x46, 0xFF, 0xFF, /* Physical Maximum (65535), */
+ 0x27, 0xFF, 0xFF, 0x00, 0x00, /* Logical Maximum (65535), */
+ 0x95, 0x03, /* Report Count (3), * 3x Accels */
+ 0x09, 0x33, /* Usage (rX), */
+ 0x09, 0x34, /* Usage (rY), */
+ 0x09, 0x35, /* Usage (rZ), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x95, 0x03, /* Report Count (3), * Skip Accels 2nd frame */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0x95, 0x03, /* Report Count (3), * 3x Gyros */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x95, 0x03, /* Report Count (3), * Skip Gyros 2nd frame */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x0C, /* Report Size (12), */
+ 0x46, 0xFF, 0x0F, /* Physical Maximum (4095), */
+ 0x26, 0xFF, 0x0F, /* Logical Maximum (4095), */
+ 0x95, 0x04, /* Report Count (4), * Skip Temp and Magnetometers */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x95, 0x06, /* Report Count (6), * Skip Timestamp and Extension Bytes */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0x02, /* Report ID (2), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0xEE, /* Report ID (238), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0xEF, /* Report ID (239), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xC0 /* End Collection */
+};
+
+/* PS/3 Navigation controller */
+static __u8 navigation_rdesc[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x04, /* Usage (Joystik), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0x01, /* Report ID (1), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x13, /* Report Count (19), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x35, 0x00, /* Physical Minimum (0), */
+ 0x45, 0x01, /* Physical Maximum (1), */
+ 0x05, 0x09, /* Usage Page (Button), */
+ 0x19, 0x01, /* Usage Minimum (01h), */
+ 0x29, 0x13, /* Usage Maximum (13h), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x0D, /* Report Count (13), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+ 0x15, 0x00, /* Logical Minimum (0), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xA1, 0x00, /* Collection (Physical), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x02, /* Report Count (2), */
+ 0x35, 0x00, /* Physical Minimum (0), */
+ 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x09, 0x31, /* Usage (Y), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xC0, /* End Collection, */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x95, 0x06, /* Report Count (6), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x05, /* Report Count (5), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
+ 0x95, 0x20, /* Report Count (26), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0x91, 0x02, /* Output (Variable), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0x02, /* Report ID (2), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0xEE, /* Report ID (238), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xA1, 0x02, /* Collection (Logical), */
+ 0x85, 0xEF, /* Report ID (239), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x30, /* Report Count (48), */
+ 0x09, 0x01, /* Usage (Pointer), */
+ 0xB1, 0x02, /* Feature (Variable), */
+ 0xC0, /* End Collection, */
+ 0xC0 /* End Collection */
+};
+
/*
* The default descriptor doesn't provide mapping for the accelerometers
* or orientation sensors. This fixed descriptor maps the accelerometers
@@ -798,12 +995,20 @@ union sixaxis_output_report_01 {
__u8 buf[36];
};
+struct motion_output_report_02 {
+ u8 type, zero;
+ u8 r, g, b;
+ u8 zero2;
+ u8 rumble;
+};
+
#define DS4_REPORT_0x02_SIZE 37
#define DS4_REPORT_0x05_SIZE 32
#define DS4_REPORT_0x11_SIZE 78
#define DS4_REPORT_0x81_SIZE 7
#define SIXAXIS_REPORT_0xF2_SIZE 17
#define SIXAXIS_REPORT_0xF5_SIZE 8
+#define MOTION_REPORT_0x02_SIZE 49
static DEFINE_SPINLOCK(sony_dev_list_lock);
static LIST_HEAD(sony_device_list);
@@ -844,6 +1049,20 @@ static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
return sixaxis_rdesc;
}
+static u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc,
+ unsigned int *rsize)
+{
+ *rsize = sizeof(motion_rdesc);
+ return motion_rdesc;
+}
+
+static u8 *navigation_fixup(struct hid_device *hdev, u8 *rdesc,
+ unsigned int *rsize)
+{
+ *rsize = sizeof(navigation_rdesc);
+ return navigation_rdesc;
+}
+
static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@@ -924,6 +1143,12 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
if (sc->quirks & SIXAXIS_CONTROLLER)
return sixaxis_fixup(hdev, rdesc, rsize);
+ if (sc->quirks & MOTION_CONTROLLER)
+ return motion_fixup(hdev, rdesc, rsize);
+
+ if (sc->quirks & NAVIGATION_CONTROLLER)
+ return navigation_fixup(hdev, rdesc, rsize);
+
if (sc->quirks & PS3REMOTE)
return ps3remote_fixup(hdev, rdesc, rsize);
@@ -934,6 +1159,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
{
static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
unsigned long flags;
+ int offset;
__u8 cable_state, battery_capacity, battery_charging;
/*
@@ -942,12 +1168,14 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
* It does not report the actual level while charging so it
* is set to 100% while charging is in progress.
*/
- if (rd[30] >= 0xee) {
+ offset = (sc->quirks & MOTION_CONTROLLER) ? 12 : 30;
+
+ if (rd[offset] >= 0xee) {
battery_capacity = 100;
- battery_charging = !(rd[30] & 0x01);
+ battery_charging = !(rd[offset] & 0x01);
cable_state = 1;
} else {
- __u8 index = rd[30] <= 5 ? rd[30] : 5;
+ __u8 index = rd[offset] <= 5 ? rd[offset] : 5;
battery_capacity = sixaxis_battery_capacity[index];
battery_charging = 0;
cable_state = 0;
@@ -1048,6 +1276,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
swap(rd[47], rd[48]);
sixaxis_parse_report(sc, rd, size);
+ } else if ((sc->quirks & MOTION_CONTROLLER_BT) && rd[0] == 0x01 && size == 49) {
+ sixaxis_parse_report(sc, rd, size);
+ } else if ((sc->quirks & NAVIGATION_CONTROLLER) && rd[0] == 0x01 &&
+ size == 49) {
+ sixaxis_parse_report(sc, rd, size);
} else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
&& rd[0] == 0x11 && size == 78)) {
@@ -1208,7 +1441,7 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
return ret;
}
-static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+static void sixaxis_set_leds_from_id(struct sony_sc *sc)
{
static const __u8 sixaxis_leds[10][4] = {
{ 0x01, 0x00, 0x00, 0x00 },
@@ -1223,16 +1456,18 @@ static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
{ 0x01, 0x01, 0x01, 0x01 }
};
- BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
+ int id = sc->device_id;
+
+ BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
if (id < 0)
return;
id %= 10;
- memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
+ memcpy(sc->led_state, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
}
-static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+static void dualshock4_set_leds_from_id(struct sony_sc *sc)
{
/* The first 4 color/index entries match what the PS4 assigns */
static const __u8 color_code[7][3] = {
@@ -1245,46 +1480,44 @@ static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
/* White */ { 0x01, 0x01, 0x01 }
};
- BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
+ int id = sc->device_id;
+
+ BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
if (id < 0)
return;
id %= 7;
- memcpy(values, color_code[id], sizeof(color_code[id]));
+ memcpy(sc->led_state, color_code[id], sizeof(color_code[id]));
}
-static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
+static void buzz_set_leds(struct sony_sc *sc)
{
+ struct hid_device *hdev = sc->hdev;
struct list_head *report_list =
&hdev->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next,
struct hid_report, list);
__s32 *value = report->field[0]->value;
+ BUILD_BUG_ON(MAX_LEDS < 4);
+
value[0] = 0x00;
- value[1] = leds[0] ? 0xff : 0x00;
- value[2] = leds[1] ? 0xff : 0x00;
- value[3] = leds[2] ? 0xff : 0x00;
- value[4] = leds[3] ? 0xff : 0x00;
+ value[1] = sc->led_state[0] ? 0xff : 0x00;
+ value[2] = sc->led_state[1] ? 0xff : 0x00;
+ value[3] = sc->led_state[2] ? 0xff : 0x00;
+ value[4] = sc->led_state[3] ? 0xff : 0x00;
value[5] = 0x00;
value[6] = 0x00;
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
}
-static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)
+static void sony_set_leds(struct sony_sc *sc)
{
- int n;
-
- BUG_ON(count > MAX_LEDS);
-
- if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
- buzz_set_leds(sc->hdev, leds);
- } else {
- for (n = 0; n < count; n++)
- sc->led_state[n] = leds[n];
+ if (!(sc->quirks & BUZZ_CONTROLLER))
schedule_work(&sc->state_worker);
- }
+ else
+ buzz_set_leds(sc);
}
static void sony_led_set_brightness(struct led_classdev *led,
@@ -1324,8 +1557,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
drv_data->led_delay_on[n] = 0;
drv_data->led_delay_off[n] = 0;
- sony_set_leds(drv_data, drv_data->led_state,
- drv_data->led_count);
+ sony_set_leds(drv_data);
break;
}
}
@@ -1431,7 +1663,6 @@ static int sony_leds_init(struct sony_sc *sc)
const char *name_fmt;
static const char * const ds4_name_str[] = { "red", "green", "blue",
"global" };
- __u8 initial_values[MAX_LEDS] = { 0 };
__u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
__u8 use_hw_blink[MAX_LEDS] = { 0 };
@@ -1446,16 +1677,31 @@ static int sony_leds_init(struct sony_sc *sc)
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
return -ENODEV;
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
- dualshock4_set_leds_from_id(sc->device_id, initial_values);
- initial_values[3] = 1;
+ dualshock4_set_leds_from_id(sc);
+ sc->led_state[3] = 1;
sc->led_count = 4;
memset(max_brightness, 255, 3);
use_hw_blink[3] = 1;
use_ds4_names = 1;
name_len = 0;
name_fmt = "%s:%s";
+ } else if (sc->quirks & MOTION_CONTROLLER) {
+ sc->led_count = 3;
+ memset(max_brightness, 255, 3);
+ use_ds4_names = 1;
+ name_len = 0;
+ name_fmt = "%s:%s";
+ } else if (sc->quirks & NAVIGATION_CONTROLLER) {
+ static const __u8 navigation_leds[4] = {0x01, 0x00, 0x00, 0x00};
+
+ memcpy(sc->led_state, navigation_leds, sizeof(navigation_leds));
+ sc->led_count = 1;
+ memset(use_hw_blink, 1, 4);
+ use_ds4_names = 0;
+ name_len = strlen("::sony#");
+ name_fmt = "%s::sony%d";
} else {
- sixaxis_set_leds_from_id(sc->device_id, initial_values);
+ sixaxis_set_leds_from_id(sc);
sc->led_count = 4;
memset(use_hw_blink, 1, 4);
use_ds4_names = 0;
@@ -1468,7 +1714,7 @@ static int sony_leds_init(struct sony_sc *sc)
* only relevant if the driver is loaded after somebody actively set the
* LEDs to on
*/
- sony_set_leds(sc, initial_values, sc->led_count);
+ sony_set_leds(sc);
name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
@@ -1491,7 +1737,7 @@ static int sony_leds_init(struct sony_sc *sc)
else
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
led->name = name;
- led->brightness = initial_values[n];
+ led->brightness = sc->led_state[n];
led->max_brightness = max_brightness[n];
led->brightness_get = sony_led_get_brightness;
led->brightness_set = sony_led_set_brightness;
@@ -1622,9 +1868,31 @@ static void dualshock4_state_worker(struct work_struct *work)
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
+static void motion_state_worker(struct work_struct *work)
+{
+ struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
+ struct hid_device *hdev = sc->hdev;
+ struct motion_output_report_02 *report =
+ (struct motion_output_report_02 *)sc->output_report_dmabuf;
+
+ memset(report, 0, MOTION_REPORT_0x02_SIZE);
+
+ report->type = 0x02; /* set leds */
+ report->r = sc->led_state[0];
+ report->g = sc->led_state[1];
+ report->b = sc->led_state[2];
+
+#ifdef CONFIG_SONY_FF
+ report->rumble = max(sc->right, sc->left);
+#endif
+
+ hid_hw_output_report(hdev, (__u8 *)report, MOTION_REPORT_0x02_SIZE);
+}
+
static int sony_allocate_output_report(struct sony_sc *sc)
{
- if (sc->quirks & SIXAXIS_CONTROLLER)
+ if ((sc->quirks & SIXAXIS_CONTROLLER) ||
+ (sc->quirks & NAVIGATION_CONTROLLER))
sc->output_report_dmabuf =
kmalloc(sizeof(union sixaxis_output_report_01),
GFP_KERNEL);
@@ -1634,6 +1902,9 @@ static int sony_allocate_output_report(struct sony_sc *sc)
else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x05_SIZE,
GFP_KERNEL);
+ else if (sc->quirks & MOTION_CONTROLLER)
+ sc->output_report_dmabuf = kmalloc(MOTION_REPORT_0x02_SIZE,
+ GFP_KERNEL);
else
return 0;
@@ -1839,6 +2110,8 @@ static int sony_check_add(struct sony_sc *sc)
int n, ret;
if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
+ (sc->quirks & MOTION_CONTROLLER_BT) ||
+ (sc->quirks & NAVIGATION_CONTROLLER_BT) ||
(sc->quirks & SIXAXIS_CONTROLLER_BT)) {
/*
* sony_get_bt_devaddr() attempts to parse the Bluetooth MAC
@@ -1871,7 +2144,8 @@ static int sony_check_add(struct sony_sc *sc)
}
memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
- } else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+ } else if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
+ (sc->quirks & NAVIGATION_CONTROLLER_USB)) {
buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -1993,19 +2267,20 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
- ret = sony_allocate_output_report(sc);
+ ret = sony_set_device_id(sc);
if (ret < 0) {
- hid_err(hdev, "failed to allocate the output report buffer\n");
+ hid_err(hdev, "failed to allocate the device id\n");
goto err_stop;
}
- ret = sony_set_device_id(sc);
+ ret = sony_allocate_output_report(sc);
if (ret < 0) {
- hid_err(hdev, "failed to allocate the device id\n");
+ hid_err(hdev, "failed to allocate the output report buffer\n");
goto err_stop;
}
- if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+ if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
+ (sc->quirks & NAVIGATION_CONTROLLER_USB)) {
/*
* The Sony Sixaxis does not handle HID Output Reports on the
* Interrupt EP like it could, so we need to force HID Output
@@ -2020,7 +2295,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
ret = sixaxis_set_operational_usb(hdev);
sony_init_work(sc, sixaxis_state_worker);
- } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
+ } else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) ||
+ (sc->quirks & NAVIGATION_CONTROLLER_BT)) {
/*
* The Sixaxis wants output reports sent on the ctrl endpoint
* when connected via Bluetooth.
@@ -2043,6 +2319,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
sony_init_work(sc, dualshock4_state_worker);
+ } else if (sc->quirks & MOTION_CONTROLLER) {
+ sony_init_work(sc, motion_state_worker);
} else {
ret = 0;
}
@@ -2122,7 +2400,13 @@ static const struct hid_device_id sony_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
.driver_data = SIXAXIS_CONTROLLER_USB },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER),
- .driver_data = SIXAXIS_CONTROLLER_USB },
+ .driver_data = NAVIGATION_CONTROLLER_USB },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER),
+ .driver_data = NAVIGATION_CONTROLLER_BT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER),
+ .driver_data = MOTION_CONTROLLER_USB },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER),
+ .driver_data = MOTION_CONTROLLER_BT },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
.driver_data = SIXAXIS_CONTROLLER_BT },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE),
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index ab4dd952b6ba..f77469d4edfb 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -42,9 +42,9 @@
#include <linux/i2c/i2c-hid.h>
/* flags */
-#define I2C_HID_STARTED (1 << 0)
-#define I2C_HID_RESET_PENDING (1 << 1)
-#define I2C_HID_READ_PENDING (1 << 2)
+#define I2C_HID_STARTED 0
+#define I2C_HID_RESET_PENDING 1
+#define I2C_HID_READ_PENDING 2
#define I2C_HID_PWR_ON 0x00
#define I2C_HID_PWR_SLEEP 0x01
@@ -862,6 +862,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
union acpi_object *obj;
struct acpi_device *adev;
acpi_handle handle;
+ int ret;
handle = ACPI_HANDLE(&client->dev);
if (!handle || acpi_bus_get_device(handle, &adev))
@@ -877,7 +878,9 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
pdata->hid_descriptor_address = obj->integer.value;
ACPI_FREE(obj);
- return acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios);
+ /* GPIOs are optional */
+ ret = acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios);
+ return ret < 0 && ret != -ENXIO ? ret : 0;
}
static const struct acpi_device_id i2c_hid_acpi_match[] = {
@@ -1016,7 +1019,6 @@ static int i2c_hid_probe(struct i2c_client *client,
hid->driver_data = client;
hid->ll_driver = &i2c_hid_ll_driver;
hid->dev.parent = &client->dev;
- ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
hid->bus = BUS_I2C;
hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index a775143e6265..53e7de7cb9e2 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -52,7 +52,6 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH_2968, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
- { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET },
@@ -61,6 +60,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS682, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FIGHTERSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE, HID_QUIRK_NOGET },
@@ -69,6 +69,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL },
@@ -88,6 +89,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
@@ -140,6 +142,9 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_INPUT_REPORTS },
+ { USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES, HID_QUIRK_MULTI_INPUT },
{ 0, 0 }
};
diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h
index 024f4d89d579..a533787a6d85 100644
--- a/drivers/hid/wacom.h
+++ b/drivers/hid/wacom.h
@@ -134,8 +134,10 @@ static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
extern const struct hid_device_id wacom_ids[];
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
-void wacom_setup_device_quirks(struct wacom_features *features);
-int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
+void wacom_setup_device_quirks(struct wacom *wacom);
+int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
+ struct wacom_wac *wacom_wac);
+int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac);
int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac);
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index e8607d096138..4c0ffca97bef 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -35,7 +35,11 @@ static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf,
do {
retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
HID_REQ_GET_REPORT);
- } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+ } while ((retval == -ETIMEDOUT || retval == -EAGAIN) && --retries);
+
+ if (retval < 0)
+ hid_err(hdev, "wacom_get_report: ran out of retries "
+ "(last error = %d)\n", retval);
return retval;
}
@@ -48,7 +52,11 @@ static int wacom_set_report(struct hid_device *hdev, u8 type, u8 *buf,
do {
retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
HID_REQ_SET_REPORT);
- } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+ } while ((retval == -ETIMEDOUT || retval == -EAGAIN) && --retries);
+
+ if (retval < 0)
+ hid_err(hdev, "wacom_set_report: ran out of retries "
+ "(last error = %d)\n", retval);
return retval;
}
@@ -117,9 +125,16 @@ static void wacom_feature_mapping(struct hid_device *hdev,
break;
data[0] = field->report->id;
ret = wacom_get_report(hdev, HID_FEATURE_REPORT,
- data, 2, 0);
- if (ret == 2)
+ data, 2, WAC_CMD_RETRIES);
+ if (ret == 2) {
features->touch_max = data[1];
+ } else {
+ features->touch_max = 16;
+ hid_warn(hdev, "wacom_feature_mapping: "
+ "could not get HID_DG_CONTACTMAX, "
+ "defaulting to %d\n",
+ features->touch_max);
+ }
kfree(data);
}
break;
@@ -181,7 +196,11 @@ static void wacom_usage_mapping(struct hid_device *hdev,
* X/Y values and some cases of invalid Digitizer X/Y
* values commonly reported.
*/
- if (!pen && !finger)
+ if (pen)
+ features->device_type |= WACOM_DEVICETYPE_PEN;
+ else if (finger)
+ features->device_type |= WACOM_DEVICETYPE_TOUCH;
+ else
return;
/*
@@ -198,14 +217,11 @@ static void wacom_usage_mapping(struct hid_device *hdev,
case HID_GD_X:
features->x_max = field->logical_maximum;
if (finger) {
- features->device_type = BTN_TOOL_FINGER;
features->x_phy = field->physical_maximum;
if (features->type != BAMBOO_PT) {
features->unit = field->unit;
features->unitExpo = field->unit_exponent;
}
- } else {
- features->device_type = BTN_TOOL_PEN;
}
break;
case HID_GD_Y:
@@ -237,7 +253,7 @@ static void wacom_post_parse_hid(struct hid_device *hdev,
if (features->type == HID_GENERIC) {
/* Any last-minute generic device setup */
if (features->touch_max > 1) {
- input_mt_init_slots(wacom_wac->input, wacom_wac->features.touch_max,
+ input_mt_init_slots(wacom_wac->touch_input, wacom_wac->features.touch_max,
INPUT_MT_DIRECT);
}
}
@@ -395,7 +411,7 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
if (features->type == HID_GENERIC)
return wacom_hid_set_device_mode(hdev);
- if (features->device_type == BTN_TOOL_FINGER) {
+ if (features->device_type & WACOM_DEVICETYPE_TOUCH) {
if (features->type > TABLETPC) {
/* MT Tablet PC touch */
return wacom_set_device_mode(hdev, 3, 4, 4);
@@ -409,7 +425,7 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
else if (features->type == BAMBOO_PAD) {
return wacom_set_device_mode(hdev, 2, 2, 2);
}
- } else if (features->device_type == BTN_TOOL_PEN) {
+ } else if (features->device_type & WACOM_DEVICETYPE_PEN) {
if (features->type <= BAMBOO_PT && features->type != WIRELESS) {
return wacom_set_device_mode(hdev, 2, 2, 2);
}
@@ -425,7 +441,6 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
struct usb_interface *intf = wacom->intf;
/* default features */
- features->device_type = BTN_TOOL_PEN;
features->x_fuzz = 4;
features->y_fuzz = 4;
features->pressure_fuzz = 0;
@@ -439,17 +454,13 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
*/
if (features->type == WIRELESS) {
if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
- features->device_type = 0;
+ features->device_type = WACOM_DEVICETYPE_NONE;
} else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) {
- features->device_type = BTN_TOOL_FINGER;
+ features->device_type |= WACOM_DEVICETYPE_TOUCH;
features->pktlen = WACOM_PKGLEN_BBTOUCH3;
}
}
- /* only devices that support touch need to retrieve the info */
- if (features->type < BAMBOO_PT)
- return;
-
wacom_parse_hid(hdev, features);
}
@@ -527,9 +538,9 @@ static int wacom_add_shared_data(struct hid_device *hdev)
wacom_wac->shared = &data->shared;
- if (wacom_wac->features.device_type == BTN_TOOL_FINGER)
+ if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)
wacom_wac->shared->touch = hdev;
- else if (wacom_wac->features.device_type == BTN_TOOL_PEN)
+ else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN)
wacom_wac->shared->pen = hdev;
out:
@@ -848,6 +859,9 @@ static int wacom_initialize_leds(struct wacom *wacom)
{
int error;
+ if (!(wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD))
+ return 0;
+
/* Initialize default values */
switch (wacom->wacom_wac.features.type) {
case INTUOS4S:
@@ -881,17 +895,14 @@ static int wacom_initialize_leds(struct wacom *wacom)
case INTUOSPS:
case INTUOSPM:
case INTUOSPL:
- if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) {
- wacom->led.select[0] = 0;
- wacom->led.select[1] = 0;
- wacom->led.llv = 32;
- wacom->led.hlv = 0;
- wacom->led.img_lum = 0;
-
- error = sysfs_create_group(&wacom->hdev->dev.kobj,
- &intuos5_led_attr_group);
- } else
- return 0;
+ wacom->led.select[0] = 0;
+ wacom->led.select[1] = 0;
+ wacom->led.llv = 32;
+ wacom->led.hlv = 0;
+ wacom->led.img_lum = 0;
+
+ error = sysfs_create_group(&wacom->hdev->dev.kobj,
+ &intuos5_led_attr_group);
break;
default:
@@ -914,6 +925,9 @@ static void wacom_destroy_leds(struct wacom *wacom)
if (!wacom->led_initialized)
return;
+ if (!(wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD))
+ return;
+
wacom->led_initialized = false;
switch (wacom->wacom_wac.features.type) {
@@ -937,9 +951,8 @@ static void wacom_destroy_leds(struct wacom *wacom)
case INTUOSPS:
case INTUOSPM:
case INTUOSPL:
- if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN)
- sysfs_remove_group(&wacom->hdev->dev.kobj,
- &intuos5_led_attr_group);
+ sysfs_remove_group(&wacom->hdev->dev.kobj,
+ &intuos5_led_attr_group);
break;
}
}
@@ -1117,7 +1130,7 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom)
if (!input_dev)
return NULL;
- input_dev->name = wacom_wac->name;
+ input_dev->name = wacom_wac->pen_name;
input_dev->phys = hdev->phys;
input_dev->dev.parent = &hdev->dev;
input_dev->open = wacom_open;
@@ -1136,27 +1149,33 @@ static void wacom_free_inputs(struct wacom *wacom)
{
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
- if (wacom_wac->input)
- input_free_device(wacom_wac->input);
+ if (wacom_wac->pen_input)
+ input_free_device(wacom_wac->pen_input);
+ if (wacom_wac->touch_input)
+ input_free_device(wacom_wac->touch_input);
if (wacom_wac->pad_input)
input_free_device(wacom_wac->pad_input);
- wacom_wac->input = NULL;
+ wacom_wac->pen_input = NULL;
+ wacom_wac->touch_input = NULL;
wacom_wac->pad_input = NULL;
}
static int wacom_allocate_inputs(struct wacom *wacom)
{
- struct input_dev *input_dev, *pad_input_dev;
+ struct input_dev *pen_input_dev, *touch_input_dev, *pad_input_dev;
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
- input_dev = wacom_allocate_input(wacom);
+ pen_input_dev = wacom_allocate_input(wacom);
+ touch_input_dev = wacom_allocate_input(wacom);
pad_input_dev = wacom_allocate_input(wacom);
- if (!input_dev || !pad_input_dev) {
+ if (!pen_input_dev || !touch_input_dev || !pad_input_dev) {
wacom_free_inputs(wacom);
return -ENOMEM;
}
- wacom_wac->input = input_dev;
+ wacom_wac->pen_input = pen_input_dev;
+ wacom_wac->touch_input = touch_input_dev;
+ wacom_wac->touch_input->name = wacom_wac->touch_name;
wacom_wac->pad_input = pad_input_dev;
wacom_wac->pad_input->name = wacom_wac->pad_name;
@@ -1165,11 +1184,17 @@ static int wacom_allocate_inputs(struct wacom *wacom)
static void wacom_clean_inputs(struct wacom *wacom)
{
- if (wacom->wacom_wac.input) {
- if (wacom->wacom_wac.input_registered)
- input_unregister_device(wacom->wacom_wac.input);
+ if (wacom->wacom_wac.pen_input) {
+ if (wacom->wacom_wac.pen_registered)
+ input_unregister_device(wacom->wacom_wac.pen_input);
else
- input_free_device(wacom->wacom_wac.input);
+ input_free_device(wacom->wacom_wac.pen_input);
+ }
+ if (wacom->wacom_wac.touch_input) {
+ if (wacom->wacom_wac.touch_registered)
+ input_unregister_device(wacom->wacom_wac.touch_input);
+ else
+ input_free_device(wacom->wacom_wac.touch_input);
}
if (wacom->wacom_wac.pad_input) {
if (wacom->wacom_wac.pad_registered)
@@ -1177,29 +1202,49 @@ static void wacom_clean_inputs(struct wacom *wacom)
else
input_free_device(wacom->wacom_wac.pad_input);
}
- wacom->wacom_wac.input = NULL;
+ wacom->wacom_wac.pen_input = NULL;
+ wacom->wacom_wac.touch_input = NULL;
wacom->wacom_wac.pad_input = NULL;
wacom_destroy_leds(wacom);
}
static int wacom_register_inputs(struct wacom *wacom)
{
- struct input_dev *input_dev, *pad_input_dev;
+ struct input_dev *pen_input_dev, *touch_input_dev, *pad_input_dev;
struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
- int error;
+ int error = 0;
- input_dev = wacom_wac->input;
+ pen_input_dev = wacom_wac->pen_input;
+ touch_input_dev = wacom_wac->touch_input;
pad_input_dev = wacom_wac->pad_input;
- if (!input_dev || !pad_input_dev)
+ if (!pen_input_dev || !touch_input_dev || !pad_input_dev)
return -EINVAL;
- error = wacom_setup_pentouch_input_capabilities(input_dev, wacom_wac);
- if (!error) {
- error = input_register_device(input_dev);
+ error = wacom_setup_pen_input_capabilities(pen_input_dev, wacom_wac);
+ if (error) {
+ /* no pen in use on this interface */
+ input_free_device(pen_input_dev);
+ wacom_wac->pen_input = NULL;
+ pen_input_dev = NULL;
+ } else {
+ error = input_register_device(pen_input_dev);
+ if (error)
+ goto fail_register_pen_input;
+ wacom_wac->pen_registered = true;
+ }
+
+ error = wacom_setup_touch_input_capabilities(touch_input_dev, wacom_wac);
+ if (error) {
+ /* no touch in use on this interface */
+ input_free_device(touch_input_dev);
+ wacom_wac->touch_input = NULL;
+ touch_input_dev = NULL;
+ } else {
+ error = input_register_device(touch_input_dev);
if (error)
- return error;
- wacom_wac->input_registered = true;
+ goto fail_register_touch_input;
+ wacom_wac->touch_registered = true;
}
error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
@@ -1226,9 +1271,14 @@ fail_leds:
pad_input_dev = NULL;
wacom_wac->pad_registered = false;
fail_register_pad_input:
- input_unregister_device(input_dev);
- wacom_wac->input = NULL;
- wacom_wac->input_registered = false;
+ input_unregister_device(touch_input_dev);
+ wacom_wac->touch_input = NULL;
+ wacom_wac->touch_registered = false;
+fail_register_touch_input:
+ input_unregister_device(pen_input_dev);
+ wacom_wac->pen_input = NULL;
+ wacom_wac->pen_registered = false;
+fail_register_pen_input:
return error;
}
@@ -1285,8 +1335,11 @@ static void wacom_wireless_work(struct work_struct *work)
/* Stylus interface */
wacom_wac1->features =
*((struct wacom_features *)id->driver_data);
- wacom_wac1->features.device_type = BTN_TOOL_PEN;
- snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen",
+ wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PEN;
+ if (wacom_wac1->features.type != INTUOSHT &&
+ wacom_wac1->features.type != BAMBOO_PT)
+ wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD;
+ snprintf(wacom_wac1->pen_name, WACOM_NAME_MAX, "%s (WL) Pen",
wacom_wac1->features.name);
snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
wacom_wac1->features.name);
@@ -1304,16 +1357,16 @@ static void wacom_wireless_work(struct work_struct *work)
wacom_wac2->features =
*((struct wacom_features *)id->driver_data);
wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
- wacom_wac2->features.device_type = BTN_TOOL_FINGER;
wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
- if (wacom_wac2->features.touch_max)
- snprintf(wacom_wac2->name, WACOM_NAME_MAX,
- "%s (WL) Finger",wacom_wac2->features.name);
- else
- snprintf(wacom_wac2->name, WACOM_NAME_MAX,
- "%s (WL) Pad",wacom_wac2->features.name);
+ snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX,
+ "%s (WL) Finger",wacom_wac2->features.name);
snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
- "%s (WL) Pad", wacom_wac2->features.name);
+ "%s (WL) Pad",wacom_wac2->features.name);
+ if (wacom_wac1->features.touch_max)
+ wacom_wac2->features.device_type |= WACOM_DEVICETYPE_TOUCH;
+ if (wacom_wac1->features.type == INTUOSHT ||
+ wacom_wac1->features.type == BAMBOO_PT)
+ wacom_wac2->features.device_type |= WACOM_DEVICETYPE_PAD;
wacom_wac2->pid = wacom_wac->pid;
error = wacom_allocate_inputs(wacom2) ||
wacom_register_inputs(wacom2);
@@ -1322,7 +1375,7 @@ static void wacom_wireless_work(struct work_struct *work)
if (wacom_wac1->features.type == INTUOSHT &&
wacom_wac1->features.touch_max)
- wacom_wac->shared->touch_input = wacom_wac2->input;
+ wacom_wac->shared->touch_input = wacom_wac2->touch_input;
}
error = wacom_initialize_battery(wacom);
@@ -1369,6 +1422,12 @@ static void wacom_set_default_phy(struct wacom_features *features)
static void wacom_calculate_res(struct wacom_features *features)
{
+ /* set unit to "100th of a mm" for devices not reported by HID */
+ if (!features->unit) {
+ features->unit = 0x11;
+ features->unitExpo = -3;
+ }
+
features->x_resolution = wacom_calc_hid_res(features->x_max,
features->x_phy,
features->unit,
@@ -1396,6 +1455,49 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev)
return size;
}
+static void wacom_update_name(struct wacom *wacom)
+{
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct wacom_features *features = &wacom_wac->features;
+ char name[WACOM_NAME_MAX];
+
+ /* Generic devices name unspecified */
+ if ((features->type == HID_GENERIC) && !strcmp("Wacom HID", features->name)) {
+ if (strstr(wacom->hdev->name, "Wacom") ||
+ strstr(wacom->hdev->name, "wacom") ||
+ strstr(wacom->hdev->name, "WACOM")) {
+ /* name is in HID descriptor, use it */
+ strlcpy(name, wacom->hdev->name, sizeof(name));
+
+ /* strip out excess whitespaces */
+ while (1) {
+ char *gap = strstr(name, " ");
+ if (gap == NULL)
+ break;
+ /* shift everything including the terminator */
+ memmove(gap, gap+1, strlen(gap));
+ }
+ /* get rid of trailing whitespace */
+ if (name[strlen(name)-1] == ' ')
+ name[strlen(name)-1] = '\0';
+ } else {
+ /* no meaningful name retrieved. use product ID */
+ snprintf(name, sizeof(name),
+ "%s %X", features->name, wacom->hdev->product);
+ }
+ } else {
+ strlcpy(name, features->name, sizeof(name));
+ }
+
+ /* Append the device type to the name */
+ snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
+ "%s Pen", name);
+ snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name),
+ "%s Finger", name);
+ snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
+ "%s Pad", name);
+}
+
static int wacom_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
@@ -1474,64 +1576,25 @@ static int wacom_probe(struct hid_device *hdev,
/* Retrieve the physical and logical size for touch devices */
wacom_retrieve_hid_descriptor(hdev, features);
+ wacom_setup_device_quirks(wacom);
- /*
- * Intuos5 has no useful data about its touch interface in its
- * HID descriptor. If this is the touch interface (PacketSize
- * of WACOM_PKGLEN_BBTOUCH3), override the table values.
- */
- if (features->type >= INTUOS5S && features->type <= INTUOSHT) {
- if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
- features->device_type = BTN_TOOL_FINGER;
+ if (features->device_type == WACOM_DEVICETYPE_NONE &&
+ features->type != WIRELESS) {
+ error = features->type == HID_GENERIC ? -ENODEV : 0;
- features->x_max = 4096;
- features->y_max = 4096;
- } else {
- features->device_type = BTN_TOOL_PEN;
- }
- }
+ dev_warn(&hdev->dev, "Unknown device_type for '%s'. %s.",
+ hdev->name,
+ error ? "Ignoring" : "Assuming pen");
- /*
- * Same thing for Bamboo 3rd gen.
- */
- if ((features->type == BAMBOO_PT) &&
- (features->pktlen == WACOM_PKGLEN_BBTOUCH3) &&
- (features->device_type == BTN_TOOL_PEN)) {
- features->device_type = BTN_TOOL_FINGER;
+ if (error)
+ goto fail_shared_data;
- features->x_max = 4096;
- features->y_max = 4096;
+ features->device_type |= WACOM_DEVICETYPE_PEN;
}
- /*
- * Same thing for Bamboo PAD
- */
- if (features->type == BAMBOO_PAD)
- features->device_type = BTN_TOOL_FINGER;
-
- if (hdev->bus == BUS_BLUETOOTH)
- features->quirks |= WACOM_QUIRK_BATTERY;
-
- wacom_setup_device_quirks(features);
-
- /* set unit to "100th of a mm" for devices not reported by HID */
- if (!features->unit) {
- features->unit = 0x11;
- features->unitExpo = -3;
- }
wacom_calculate_res(features);
- strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
- snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
- "%s Pad", features->name);
-
- /* Append the device type to the name */
- if (features->device_type != BTN_TOOL_FINGER)
- strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX);
- else if (features->touch_max)
- strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX);
- else
- strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX);
+ wacom_update_name(wacom);
error = wacom_add_shared_data(hdev);
if (error)
@@ -1574,9 +1637,9 @@ static int wacom_probe(struct hid_device *hdev,
if (features->quirks & WACOM_QUIRK_MONITOR)
error = hid_hw_open(hdev);
- if (wacom_wac->features.type == INTUOSHT && wacom_wac->features.touch_max) {
- if (wacom_wac->features.device_type == BTN_TOOL_FINGER)
- wacom_wac->shared->touch_input = wacom_wac->input;
+ if (wacom_wac->features.type == INTUOSHT &&
+ wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) {
+ wacom_wac->shared->touch_input = wacom_wac->touch_input;
}
return 0;
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index fa54d3290659..232da89f4e88 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -69,7 +69,7 @@ static void wacom_notify_battery(struct wacom_wac *wacom_wac,
static int wacom_penpartner_irq(struct wacom_wac *wacom)
{
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
switch (data[0]) {
case 1:
@@ -114,7 +114,7 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
int prox, pressure;
if (data[0] != WACOM_REPORT_PENABLED) {
@@ -186,7 +186,7 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
static int wacom_ptu_irq(struct wacom_wac *wacom)
{
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
if (data[0] != WACOM_REPORT_PENABLED) {
dev_dbg(input->dev.parent,
@@ -215,7 +215,7 @@ static int wacom_ptu_irq(struct wacom_wac *wacom)
static int wacom_dtu_irq(struct wacom_wac *wacom)
{
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
int prox = data[1] & 0x20;
dev_dbg(input->dev.parent,
@@ -245,7 +245,7 @@ static int wacom_dtu_irq(struct wacom_wac *wacom)
static int wacom_dtus_irq(struct wacom_wac *wacom)
{
char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
unsigned short prox, pressure = 0;
if (data[0] != WACOM_REPORT_DTUS && data[0] != WACOM_REPORT_DTUSPAD) {
@@ -297,7 +297,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
struct input_dev *pad_input = wacom->pad_input;
int battery_capacity, ps_connected;
int prox;
@@ -464,7 +464,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
int idx = 0;
/* tool number */
@@ -649,7 +649,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
unsigned int t;
/* general pen packet */
@@ -681,7 +681,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
unsigned int t;
int idx = 0, result;
@@ -1025,7 +1025,7 @@ static void wacom_intuos_bt_process_data(struct wacom_wac *wacom,
memcpy(wacom->data, data, 10);
wacom_intuos_irq(wacom);
- input_sync(wacom->input);
+ input_sync(wacom->pen_input);
if (wacom->pad_input)
input_sync(wacom->pad_input);
}
@@ -1057,7 +1057,7 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
ps_connected);
break;
default:
- dev_dbg(wacom->input->dev.parent,
+ dev_dbg(wacom->pen_input->dev.parent,
"Unknown report: %d,%d size:%zu\n",
data[0], data[1], len);
return 0;
@@ -1067,14 +1067,16 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
{
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
unsigned touch_max = wacom->features.touch_max;
int count = 0;
int i;
- /* non-HID_GENERIC single touch input doesn't call this routine */
- if ((touch_max == 1) && (wacom->features.type == HID_GENERIC))
- return wacom->hid_data.tipswitch &&
+ if (!touch_max)
+ return 0;
+
+ if (touch_max == 1)
+ return test_bit(BTN_TOUCH, input->key) &&
!wacom->shared->stylus_in_proximity;
for (i = 0; i < input->mt->num_slots; i++) {
@@ -1089,7 +1091,7 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
static int wacom_24hdt_irq(struct wacom_wac *wacom)
{
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
unsigned char *data = wacom->data;
int i;
int current_num_contacts = data[61];
@@ -1157,7 +1159,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
static int wacom_mt_touch(struct wacom_wac *wacom)
{
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
unsigned char *data = wacom->data;
int i;
int current_num_contacts = data[2];
@@ -1208,7 +1210,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
{
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
unsigned char *data = wacom->data;
int i;
@@ -1237,7 +1239,7 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
{
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
bool prox = !wacom->shared->stylus_in_proximity;
int x = 0, y = 0;
@@ -1273,7 +1275,7 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
static int wacom_tpc_pen(struct wacom_wac *wacom)
{
unsigned char *data = wacom->data;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
bool prox = data[1] & 0x20;
if (!wacom->shared->stylus_in_proximity) /* first in prox */
@@ -1302,8 +1304,12 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
{
unsigned char *data = wacom->data;
- dev_dbg(wacom->input->dev.parent,
- "%s: received report #%d\n", __func__, data[0]);
+ if (wacom->pen_input)
+ dev_dbg(wacom->pen_input->dev.parent,
+ "%s: received report #%d\n", __func__, data[0]);
+ else if (wacom->touch_input)
+ dev_dbg(wacom->touch_input->dev.parent,
+ "%s: received report #%d\n", __func__, data[0]);
switch (len) {
case WACOM_PKGLEN_TPC1FG:
@@ -1335,11 +1341,9 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
return 0;
}
-static void wacom_map_usage(struct wacom *wacom, struct hid_usage *usage,
+static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
struct hid_field *field, __u8 type, __u16 code, int fuzz)
{
- struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct input_dev *input = wacom_wac->input;
int fmin = field->logical_minimum;
int fmax = field->logical_maximum;
@@ -1367,36 +1371,38 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage)
{
struct wacom *wacom = hid_get_drvdata(hdev);
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct input_dev *input = wacom_wac->pen_input;
switch (usage->hid) {
case HID_GD_X:
- wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4);
break;
case HID_GD_Y:
- wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4);
break;
case HID_DG_TIPPRESSURE:
- wacom_map_usage(wacom, usage, field, EV_ABS, ABS_PRESSURE, 0);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_PRESSURE, 0);
break;
case HID_DG_INRANGE:
- wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
+ wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
break;
case HID_DG_INVERT:
- wacom_map_usage(wacom, usage, field, EV_KEY,
+ wacom_map_usage(input, usage, field, EV_KEY,
BTN_TOOL_RUBBER, 0);
break;
case HID_DG_ERASER:
case HID_DG_TIPSWITCH:
- wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
+ wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0);
break;
case HID_DG_BARRELSWITCH:
- wacom_map_usage(wacom, usage, field, EV_KEY, BTN_STYLUS, 0);
+ wacom_map_usage(input, usage, field, EV_KEY, BTN_STYLUS, 0);
break;
case HID_DG_BARRELSWITCH2:
- wacom_map_usage(wacom, usage, field, EV_KEY, BTN_STYLUS2, 0);
+ wacom_map_usage(input, usage, field, EV_KEY, BTN_STYLUS2, 0);
break;
case HID_DG_TOOLSERIALNUMBER:
- wacom_map_usage(wacom, usage, field, EV_MSC, MSC_SERIAL, 0);
+ wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0);
break;
}
}
@@ -1406,7 +1412,7 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field,
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct input_dev *input = wacom_wac->input;
+ struct input_dev *input = wacom_wac->pen_input;
/* checking which Tool / tip switch to send */
switch (usage->hid) {
@@ -1436,7 +1442,7 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct input_dev *input = wacom_wac->input;
+ struct input_dev *input = wacom_wac->pen_input;
bool prox = wacom_wac->hid_data.inrange_state;
if (!wacom_wac->shared->stylus_in_proximity) /* first in prox */
@@ -1465,23 +1471,24 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features;
+ struct input_dev *input = wacom_wac->touch_input;
unsigned touch_max = wacom_wac->features.touch_max;
switch (usage->hid) {
case HID_GD_X:
features->last_slot_field = usage->hid;
if (touch_max == 1)
- wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4);
else
- wacom_map_usage(wacom, usage, field, EV_ABS,
+ wacom_map_usage(input, usage, field, EV_ABS,
ABS_MT_POSITION_X, 4);
break;
case HID_GD_Y:
features->last_slot_field = usage->hid;
if (touch_max == 1)
- wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
+ wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4);
else
- wacom_map_usage(wacom, usage, field, EV_ABS,
+ wacom_map_usage(input, usage, field, EV_ABS,
ABS_MT_POSITION_Y, 4);
break;
case HID_DG_CONTACTID:
@@ -1495,7 +1502,7 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
break;
case HID_DG_TIPSWITCH:
features->last_slot_field = usage->hid;
- wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
+ wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0);
break;
}
}
@@ -1551,7 +1558,7 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
if (usage->usage_index + 1 == field->report_count) {
if (usage->hid == wacom_wac->features.last_slot_field)
- wacom_wac_finger_slot(wacom_wac, wacom_wac->input);
+ wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
}
return 0;
@@ -1562,7 +1569,7 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct input_dev *input = wacom_wac->input;
+ struct input_dev *input = wacom_wac->touch_input;
unsigned touch_max = wacom_wac->features.touch_max;
if (touch_max > 1)
@@ -1579,10 +1586,10 @@ void wacom_wac_usage_mapping(struct hid_device *hdev,
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct input_dev *input = wacom_wac->input;
/* currently, only direct devices have proper hid report descriptors */
- __set_bit(INPUT_PROP_DIRECT, input->propbit);
+ __set_bit(INPUT_PROP_DIRECT, wacom_wac->pen_input->propbit);
+ __set_bit(INPUT_PROP_DIRECT, wacom_wac->touch_input->propbit);
if (WACOM_PEN_FIELD(field))
return wacom_wac_pen_usage_mapping(hdev, field, usage);
@@ -1627,7 +1634,7 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
static int wacom_bpt_touch(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
struct input_dev *pad_input = wacom->pad_input;
unsigned char *data = wacom->data;
int i;
@@ -1675,7 +1682,7 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
{
struct wacom_features *features = &wacom->features;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
bool touch = data[1] & 0x80;
int slot = input_mt_get_slot_by_key(input, data[0]);
@@ -1733,7 +1740,6 @@ static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
static int wacom_bpt3_touch(struct wacom_wac *wacom)
{
- struct input_dev *input = wacom->input;
unsigned char *data = wacom->data;
int count = data[1] & 0x07;
int i;
@@ -1752,8 +1758,12 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
wacom_bpt3_button_msg(wacom, data + offset);
}
- input_mt_sync_frame(input);
- wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
+
+ /* only update the touch if we actually have a touchpad */
+ if (wacom->touch_registered) {
+ input_mt_sync_frame(wacom->touch_input);
+ wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
+ }
return 1;
}
@@ -1761,7 +1771,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
static int wacom_bpt_pen(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->pen_input;
unsigned char *data = wacom->data;
int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
@@ -1870,7 +1880,7 @@ static void wacom_bamboo_pad_pen_event(struct wacom_wac *wacom,
static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom,
unsigned char *data)
{
- struct input_dev *input = wacom->input;
+ struct input_dev *input = wacom->touch_input;
unsigned char *finger_data, prefix;
unsigned id;
int x, y;
@@ -2114,7 +2124,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
}
if (sync) {
- input_sync(wacom_wac->input);
+ if (wacom_wac->pen_input)
+ input_sync(wacom_wac->pen_input);
+ if (wacom_wac->touch_input)
+ input_sync(wacom_wac->touch_input);
if (wacom_wac->pad_input)
input_sync(wacom_wac->pad_input);
}
@@ -2122,7 +2135,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
{
- struct input_dev *input_dev = wacom_wac->input;
+ struct input_dev *input_dev = wacom_wac->pen_input;
input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
@@ -2145,7 +2158,7 @@ static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
{
- struct input_dev *input_dev = wacom_wac->input;
+ struct input_dev *input_dev = wacom_wac->pen_input;
input_set_capability(input_dev, EV_REL, REL_WHEEL);
@@ -2164,15 +2177,57 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
}
-void wacom_setup_device_quirks(struct wacom_features *features)
+void wacom_setup_device_quirks(struct wacom *wacom)
{
+ struct wacom_features *features = &wacom->wacom_wac.features;
+
+ /* The pen and pad share the same interface on most devices */
+ if (features->type == GRAPHIRE_BT || features->type == WACOM_G4 ||
+ features->type == DTUS || features->type == WACOM_MO ||
+ (features->type >= INTUOS3S && features->type <= WACOM_13HD &&
+ features->type != INTUOSHT)) {
+ if (features->device_type & WACOM_DEVICETYPE_PEN)
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+ }
/* touch device found but size is not defined. use default */
- if (features->device_type == BTN_TOOL_FINGER && !features->x_max) {
+ if (features->device_type & WACOM_DEVICETYPE_TOUCH && !features->x_max) {
features->x_max = 1023;
features->y_max = 1023;
}
+ /*
+ * Intuos5/Pro and Bamboo 3rd gen have no useful data about its
+ * touch interface in its HID descriptor. If this is the touch
+ * interface (PacketSize of WACOM_PKGLEN_BBTOUCH3), override the
+ * tablet values.
+ */
+ if ((features->type >= INTUOS5S && features->type <= INTUOSHT) ||
+ (features->type == BAMBOO_PT)) {
+ if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
+ if (features->touch_max)
+ features->device_type |= WACOM_DEVICETYPE_TOUCH;
+ if (features->type == BAMBOO_PT || features->type == INTUOSHT)
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+
+ features->x_max = 4096;
+ features->y_max = 4096;
+ }
+ }
+
+ /*
+ * Raw Wacom-mode pen and touch events both come from interface
+ * 0, whose HID descriptor has an application usage of 0xFF0D
+ * (i.e., WACOM_VENDORDEFINED_PEN). We route pen packets back
+ * out through the HID_GENERIC device created for interface 1,
+ * so rewrite this one to be of type BTN_TOOL_FINGER.
+ */
+ if (features->type == BAMBOO_PAD)
+ features->device_type |= WACOM_DEVICETYPE_TOUCH;
+
+ if (wacom->hdev->bus == BUS_BLUETOOTH)
+ features->quirks |= WACOM_QUIRK_BATTERY;
+
/* quirk for bamboo touch with 2 low res touches */
if (features->type == BAMBOO_PT &&
features->pktlen == WACOM_PKGLEN_BBTOUCH) {
@@ -2189,61 +2244,23 @@ void wacom_setup_device_quirks(struct wacom_features *features)
features->quirks |= WACOM_QUIRK_NO_INPUT;
/* must be monitor interface if no device_type set */
- if (!features->device_type) {
+ if (features->device_type == WACOM_DEVICETYPE_NONE) {
features->quirks |= WACOM_QUIRK_MONITOR;
features->quirks |= WACOM_QUIRK_BATTERY;
}
}
}
-static void wacom_abs_set_axis(struct input_dev *input_dev,
- struct wacom_wac *wacom_wac)
-{
- struct wacom_features *features = &wacom_wac->features;
-
- if (features->device_type == BTN_TOOL_PEN) {
- input_set_abs_params(input_dev, ABS_X, features->x_min,
- features->x_max, features->x_fuzz, 0);
- input_set_abs_params(input_dev, ABS_Y, features->y_min,
- features->y_max, features->y_fuzz, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0,
- features->pressure_max, features->pressure_fuzz, 0);
-
- /* penabled devices have fixed resolution for each model */
- input_abs_set_res(input_dev, ABS_X, features->x_resolution);
- input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
- } else {
- if (features->touch_max == 1) {
- input_set_abs_params(input_dev, ABS_X, 0,
- features->x_max, features->x_fuzz, 0);
- input_set_abs_params(input_dev, ABS_Y, 0,
- features->y_max, features->y_fuzz, 0);
- input_abs_set_res(input_dev, ABS_X,
- features->x_resolution);
- input_abs_set_res(input_dev, ABS_Y,
- features->y_resolution);
- }
-
- if (features->touch_max > 1) {
- input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
- features->x_max, features->x_fuzz, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
- features->y_max, features->y_fuzz, 0);
- input_abs_set_res(input_dev, ABS_MT_POSITION_X,
- features->x_resolution);
- input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
- features->y_resolution);
- }
- }
-}
-
-int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
+int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac)
{
struct wacom_features *features = &wacom_wac->features;
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ if (!(features->device_type & WACOM_DEVICETYPE_PEN))
+ return -ENODEV;
+
if (features->type == HID_GENERIC)
/* setup has already been done */
return 0;
@@ -2251,7 +2268,17 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(ABS_MISC, input_dev->absbit);
- wacom_abs_set_axis(input_dev, wacom_wac);
+ input_set_abs_params(input_dev, ABS_X, features->x_min,
+ features->x_max, features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_Y, features->y_min,
+ features->y_max, features->y_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0,
+ features->pressure_max, features->pressure_fuzz, 0);
+
+ /* penabled devices have fixed resolution for each model */
+ input_abs_set_res(input_dev, ABS_X, features->x_resolution);
+ input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
+
switch (features->type) {
case GRAPHIRE_BT:
@@ -2320,53 +2347,25 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
case INTUOSPS:
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
- if (features->device_type == BTN_TOOL_PEN) {
- input_set_abs_params(input_dev, ABS_DISTANCE, 0,
- features->distance_max,
- 0, 0);
-
- input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
- input_abs_set_res(input_dev, ABS_Z, 287);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+ features->distance_max,
+ 0, 0);
- wacom_setup_intuos(wacom_wac);
- } else if (features->device_type == BTN_TOOL_FINGER) {
- __clear_bit(ABS_MISC, input_dev->absbit);
+ input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+ input_abs_set_res(input_dev, ABS_Z, 287);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- 0, features->x_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
- 0, features->y_max, 0, 0);
- input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
- }
+ wacom_setup_intuos(wacom_wac);
break;
case WACOM_24HDT:
- if (features->device_type == BTN_TOOL_FINGER) {
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, features->x_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, 0, features->y_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
- }
- /* fall through */
-
case WACOM_27QHDT:
case MTSCREEN:
case MTTPC:
case MTTPC_B:
case TABLETPC2FG:
- if (features->device_type == BTN_TOOL_FINGER && features->touch_max > 1)
- input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_DIRECT);
- /* fall through */
-
case TABLETPC:
case TABLETPCE:
__clear_bit(ABS_MISC, input_dev->absbit);
-
- __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
-
- if (features->device_type != BTN_TOOL_PEN)
- break; /* no need to process stylus stuff */
-
/* fall through */
case DTUS:
@@ -2394,50 +2393,114 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
break;
case INTUOSHT:
- if (features->touch_max &&
- features->device_type == BTN_TOOL_FINGER) {
- input_dev->evbit[0] |= BIT_MASK(EV_SW);
- __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
- }
- /* fall through */
-
case BAMBOO_PT:
__clear_bit(ABS_MISC, input_dev->absbit);
- if (features->device_type == BTN_TOOL_FINGER) {
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+ __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+ __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+ __set_bit(BTN_STYLUS, input_dev->keybit);
+ __set_bit(BTN_STYLUS2, input_dev->keybit);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+ features->distance_max,
+ 0, 0);
+ break;
+ case BAMBOO_PAD:
+ __clear_bit(ABS_MISC, input_dev->absbit);
+ break;
+ }
+ return 0;
+}
- if (features->touch_max) {
- if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
- input_set_abs_params(input_dev,
- ABS_MT_TOUCH_MAJOR,
- 0, features->x_max, 0, 0);
- input_set_abs_params(input_dev,
- ABS_MT_TOUCH_MINOR,
- 0, features->y_max, 0, 0);
- }
- input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
- } else {
- /* buttons/keys only interface */
- __clear_bit(ABS_X, input_dev->absbit);
- __clear_bit(ABS_Y, input_dev->absbit);
- __clear_bit(BTN_TOUCH, input_dev->keybit);
+int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
+ struct wacom_wac *wacom_wac)
+{
+ struct wacom_features *features = &wacom_wac->features;
- /* PAD is setup by wacom_setup_pad_input_capabilities later */
- return 1;
- }
- } else if (features->device_type == BTN_TOOL_PEN) {
- __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
- __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
- __set_bit(BTN_TOOL_PEN, input_dev->keybit);
- __set_bit(BTN_STYLUS, input_dev->keybit);
- __set_bit(BTN_STYLUS2, input_dev->keybit);
- input_set_abs_params(input_dev, ABS_DISTANCE, 0,
- features->distance_max,
- 0, 0);
+ input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ if (!(features->device_type & WACOM_DEVICETYPE_TOUCH))
+ return -ENODEV;
+
+ if (features->type == HID_GENERIC)
+ /* setup has already been done */
+ return 0;
+
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ if (features->touch_max == 1) {
+ input_set_abs_params(input_dev, ABS_X, 0,
+ features->x_max, features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0,
+ features->y_max, features->y_fuzz, 0);
+ input_abs_set_res(input_dev, ABS_X,
+ features->x_resolution);
+ input_abs_set_res(input_dev, ABS_Y,
+ features->y_resolution);
+ }
+ else if (features->touch_max > 1) {
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ features->x_max, features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ features->y_max, features->y_fuzz, 0);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+ features->x_resolution);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+ features->y_resolution);
+ }
+
+ switch (features->type) {
+ case INTUOS5:
+ case INTUOS5L:
+ case INTUOSPM:
+ case INTUOSPL:
+ case INTUOS5S:
+ case INTUOSPS:
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, features->y_max, 0, 0);
+ input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
+ break;
+
+ case WACOM_24HDT:
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, 0, features->y_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+ /* fall through */
+
+ case WACOM_27QHDT:
+ case MTSCREEN:
+ case MTTPC:
+ case MTTPC_B:
+ case TABLETPC2FG:
+ input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_DIRECT);
+ /*fall through */
+
+ case TABLETPC:
+ case TABLETPCE:
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+ break;
+
+ case INTUOSHT:
+ input_dev->evbit[0] |= BIT_MASK(EV_SW);
+ __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
+ /* fall through */
+
+ case BAMBOO_PT:
+ if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
+ input_set_abs_params(input_dev,
+ ABS_MT_TOUCH_MAJOR,
+ 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev,
+ ABS_MT_TOUCH_MINOR,
+ 0, features->y_max, 0, 0);
}
+ input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
break;
+
case BAMBOO_PAD:
- __clear_bit(ABS_MISC, input_dev->absbit);
input_mt_init_slots(input_dev, features->touch_max,
INPUT_MT_POINTER);
__set_bit(BTN_LEFT, input_dev->keybit);
@@ -2453,6 +2516,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
struct wacom_features *features = &wacom_wac->features;
int i;
+ if (!(features->device_type & WACOM_DEVICETYPE_PAD))
+ return -ENODEV;
+
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
/* kept for making legacy xf86-input-wacom working with the wheels */
@@ -2589,10 +2655,6 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
case INTUOS5S:
case INTUOSPS:
- /* touch interface does not have the pad device */
- if (features->device_type != BTN_TOOL_PEN)
- return -ENODEV;
-
for (i = 0; i < 7; i++)
__set_bit(BTN_0 + i, input_dev->keybit);
@@ -2634,12 +2696,6 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
case INTUOSHT:
case BAMBOO_PT:
- /* pad device is on the touch interface */
- if ((features->device_type != BTN_TOOL_FINGER) ||
- /* Bamboo Pen only tablet does not have pad */
- ((features->type == BAMBOO_PT) && !features->touch_max))
- return -ENODEV;
-
__clear_bit(ABS_MISC, input_dev->absbit);
__set_bit(BTN_LEFT, input_dev->keybit);
@@ -2919,6 +2975,9 @@ static const struct wacom_features wacom_features_0x32F =
{ "Wacom DTU1031X", 22472, 12728, 511, 0,
DTUSX, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
+static const struct wacom_features wacom_features_0x336 =
+ { "Wacom DTU1141", 23472, 13203, 1023, 0,
+ DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x57 =
{ "Wacom DTK2241", 95640, 54060, 2047, 63,
DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
@@ -3272,6 +3331,7 @@ const struct hid_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x32F) },
{ USB_DEVICE_WACOM(0x333) },
{ USB_DEVICE_WACOM(0x335) },
+ { USB_DEVICE_WACOM(0x336) },
{ USB_DEVICE_WACOM(0x4001) },
{ USB_DEVICE_WACOM(0x4004) },
{ USB_DEVICE_WACOM(0x5000) },
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 4700ac994a3b..2978c303909d 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -18,10 +18,7 @@
#define WACOM_NAME_MAX 64
/* packet length for individual models */
-#define WACOM_PKGLEN_PENPRTN 7
-#define WACOM_PKGLEN_GRAPHIRE 8
#define WACOM_PKGLEN_BBFUN 9
-#define WACOM_PKGLEN_INTUOS 10
#define WACOM_PKGLEN_TPC1FG 5
#define WACOM_PKGLEN_TPC1FG_B 10
#define WACOM_PKGLEN_TPC2FG 14
@@ -29,9 +26,6 @@
#define WACOM_PKGLEN_BBTOUCH3 64
#define WACOM_PKGLEN_BBPEN 10
#define WACOM_PKGLEN_WIRELESS 32
-#define WACOM_PKGLEN_MTOUCH 62
-#define WACOM_PKGLEN_MTTPC 40
-#define WACOM_PKGLEN_DTUS 68
#define WACOM_PKGLEN_PENABLED 8
#define WACOM_PKGLEN_BPAD_TOUCH 32
#define WACOM_PKGLEN_BPAD_TOUCH_USB 64
@@ -78,10 +72,20 @@
#define WACOM_QUIRK_MONITOR 0x0004
#define WACOM_QUIRK_BATTERY 0x0008
+/* device types */
+#define WACOM_DEVICETYPE_NONE 0x0000
+#define WACOM_DEVICETYPE_PEN 0x0001
+#define WACOM_DEVICETYPE_TOUCH 0x0002
+#define WACOM_DEVICETYPE_PAD 0x0004
+
+#define WACOM_VENDORDEFINED_PEN 0xff0d0001
+
#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \
((f)->physical == HID_DG_STYLUS) || \
((f)->physical == HID_DG_PEN) || \
- ((f)->application == HID_DG_PEN))
+ ((f)->application == HID_DG_PEN) || \
+ ((f)->application == HID_DG_DIGITIZER) || \
+ ((f)->application == WACOM_VENDORDEFINED_PEN))
#define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \
((f)->physical == HID_DG_FINGER) || \
((f)->application == HID_DG_TOUCHSCREEN))
@@ -192,7 +196,8 @@ struct hid_data {
};
struct wacom_wac {
- char name[WACOM_NAME_MAX];
+ char pen_name[WACOM_NAME_MAX];
+ char touch_name[WACOM_NAME_MAX];
char pad_name[WACOM_NAME_MAX];
char bat_name[WACOM_NAME_MAX];
char ac_name[WACOM_NAME_MAX];
@@ -203,9 +208,11 @@ struct wacom_wac {
bool reporting_data;
struct wacom_features features;
struct wacom_shared *shared;
- struct input_dev *input;
+ struct input_dev *pen_input;
+ struct input_dev *touch_input;
struct input_dev *pad_input;
- bool input_registered;
+ bool pen_registered;
+ bool touch_registered;
bool pad_registered;
int pid;
int battery_capacity;
diff --git a/drivers/hsi/clients/cmt_speech.c b/drivers/hsi/clients/cmt_speech.c
index 4983529a9c6c..d04643f9548b 100644
--- a/drivers/hsi/clients/cmt_speech.c
+++ b/drivers/hsi/clients/cmt_speech.c
@@ -451,9 +451,14 @@ static void cs_hsi_read_on_control_complete(struct hsi_msg *msg)
dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd);
cs_release_cmd(msg);
if (hi->flags & CS_FEAT_TSTAMP_RX_CTRL) {
- struct timespec *tstamp =
+ struct timespec tspec;
+ struct cs_timestamp *tstamp =
&hi->mmap_cfg->tstamp_rx_ctrl;
- do_posix_clock_monotonic_gettime(tstamp);
+
+ ktime_get_ts(&tspec);
+
+ tstamp->tv_sec = (__u32) tspec.tv_sec;
+ tstamp->tv_nsec = (__u32) tspec.tv_nsec;
}
spin_unlock(&hi->lock);
diff --git a/drivers/hsi/clients/nokia-modem.c b/drivers/hsi/clients/nokia-modem.c
index bbb19231fa82..7f82c911ad74 100644
--- a/drivers/hsi/clients/nokia-modem.c
+++ b/drivers/hsi/clients/nokia-modem.c
@@ -112,7 +112,8 @@ static int nokia_modem_gpio_probe(struct device *dev)
modem->gpio_amount = gpio_count;
for (i = 0; i < gpio_count; i++) {
- modem->gpios[i].gpio = devm_gpiod_get_index(dev, NULL, i);
+ modem->gpios[i].gpio = devm_gpiod_get_index(dev, NULL, i,
+ GPIOD_OUT_LOW);
if (IS_ERR(modem->gpios[i].gpio)) {
dev_err(dev, "Could not get gpio %d\n", i);
return PTR_ERR(modem->gpios[i].gpio);
@@ -125,10 +126,6 @@ static int nokia_modem_gpio_probe(struct device *dev)
return err;
}
- err = gpiod_direction_output(modem->gpios[i].gpio, 0);
- if (err)
- return err;
-
err = gpiod_export(modem->gpios[i].gpio, 0);
if (err)
return err;
@@ -208,7 +205,7 @@ static int nokia_modem_probe(struct device *dev)
err = device_attach(&modem->ssi_protocol->device);
if (err == 0) {
- dev_err(dev, "Missing ssi-protocol driver\n");
+ dev_dbg(dev, "Missing ssi-protocol driver\n");
err = -EPROBE_DEFER;
goto error3;
} else if (err < 0) {
@@ -231,7 +228,7 @@ static int nokia_modem_probe(struct device *dev)
err = device_attach(&modem->cmt_speech->device);
if (err == 0) {
- dev_err(dev, "Missing cmt-speech driver\n");
+ dev_dbg(dev, "Missing cmt-speech driver\n");
err = -EPROBE_DEFER;
goto error4;
} else if (err < 0) {
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 25d9e72627e9..54075a07d2a1 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -509,7 +509,7 @@ config SENSORS_G762
config SENSORS_GPIO_FAN
tristate "GPIO fan"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
depends on THERMAL || THERMAL=n
help
If you say yes here you get support for fans connected to GPIO lines.
@@ -1106,8 +1106,8 @@ config SENSORS_NTC_THERMISTOR
send notifications about the temperature.
Currently, this driver supports
- NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333
- from Murata and B57330V2103 from EPCOS.
+ NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, NCP15WL333,
+ and NCP03WF104 from Murata and B57330V2103 from EPCOS.
This driver can also be built as a module. If so, the module
will be called ntc-thermistor.
@@ -1186,7 +1186,7 @@ config SENSORS_PWM_FAN
config SENSORS_SHT15
tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
If you say yes here you get support for the Sensiron SHT10, SHT11,
SHT15, SHT71, SHT75 humidity and temperature sensors.
@@ -1452,6 +1452,16 @@ config SENSORS_INA2XX
This driver can also be built as a module. If so, the module
will be called ina2xx.
+config SENSORS_TC74
+ tristate "Microchip TC74"
+ depends on I2C
+ help
+ If you say yes here you get support for Microchip TC74 single
+ input temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called tc74.
+
config SENSORS_THMC50
tristate "Texas Instruments THMC50 / Analog Devices ADM1022"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b4a40f17e2aa..ab904027f074 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -140,6 +140,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
+obj-$(CONFIG_SENSORS_TC74) += tc74.o
obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
obj-$(CONFIG_SENSORS_TMP102) += tmp102.o
obj-$(CONFIG_SENSORS_TMP103) += tmp103.o
diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c
index 4c829bb2f9db..f2f2f2fc755a 100644
--- a/drivers/hwmon/atxp1.c
+++ b/drivers/hwmon/atxp1.c
@@ -12,10 +12,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
+ * The ATXP1 can reside on I2C addresses 0x37 or 0x4e. The chip is
+ * not auto-detected by the driver and must be instantiated explicitly.
+ * See Documentation/i2c/instantiating-devices for more information.
*/
#include <linux/kernel.h>
@@ -43,8 +42,6 @@ MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
#define ATXP1_VIDMASK 0x1f
#define ATXP1_GPIO1MASK 0x0f
-static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
-
struct atxp1_data {
struct i2c_client *client;
struct mutex update_lock;
@@ -259,48 +256,6 @@ static struct attribute *atxp1_attrs[] = {
};
ATTRIBUTE_GROUPS(atxp1);
-/* Return 0 if detection is successful, -ENODEV otherwise */
-static int atxp1_detect(struct i2c_client *new_client,
- struct i2c_board_info *info)
-{
- struct i2c_adapter *adapter = new_client->adapter;
-
- u8 temp;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -ENODEV;
-
- /* Detect ATXP1, checking if vendor ID registers are all zero */
- if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
- (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
- (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
- (i2c_smbus_read_byte_data(new_client, 0xff) == 0)))
- return -ENODEV;
-
- /*
- * No vendor ID, now checking if registers 0x10,0x11 (non-existent)
- * showing the same as register 0x00
- */
- temp = i2c_smbus_read_byte_data(new_client, 0x00);
-
- if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
- (i2c_smbus_read_byte_data(new_client, 0x11) == temp)))
- return -ENODEV;
-
- /* Get VRM */
- temp = vid_which_vrm();
-
- if ((temp != 90) && (temp != 91)) {
- dev_err(&adapter->dev, "atxp1: Not supporting VRM %d.%d\n",
- temp / 10, temp % 10);
- return -ENODEV;
- }
-
- strlcpy(info->type, "atxp1", I2C_NAME_SIZE);
-
- return 0;
-}
-
static int atxp1_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -314,6 +269,11 @@ static int atxp1_probe(struct i2c_client *client,
/* Get VRM */
data->vrm = vid_which_vrm();
+ if (data->vrm != 90 && data->vrm != 91) {
+ dev_err(dev, "atxp1: Not supporting VRM %d.%d\n",
+ data->vrm / 10, data->vrm % 10);
+ return -ENODEV;
+ }
data->client = client;
mutex_init(&data->update_lock);
@@ -342,8 +302,6 @@ static struct i2c_driver atxp1_driver = {
},
.probe = atxp1_probe,
.id_table = atxp1_id,
- .detect = atxp1_detect,
- .address_list = normal_i2c,
};
module_i2c_driver(atxp1_driver);
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index ed303ba3a593..3e03379e7c5d 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -63,7 +63,8 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO)
#ifdef CONFIG_SMP
-#define for_each_sibling(i, cpu) for_each_cpu(i, cpu_sibling_mask(cpu))
+#define for_each_sibling(i, cpu) \
+ for_each_cpu(i, topology_sibling_cpumask(cpu))
#else
#define for_each_sibling(i, cpu) for (i = 0; false; )
#endif
diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c
index cb0dcfda958c..07628569547a 100644
--- a/drivers/hwmon/max197.c
+++ b/drivers/hwmon/max197.c
@@ -324,7 +324,7 @@ static int max197_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id max197_device_ids[] = {
+static const struct platform_device_id max197_device_ids[] = {
{ "max197", max197 },
{ "max199", max199 },
{ }
diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c
index f3830db02d46..37f01702d081 100644
--- a/drivers/hwmon/nct6683.c
+++ b/drivers/hwmon/nct6683.c
@@ -439,6 +439,7 @@ nct6683_create_attr_group(struct device *dev, struct sensor_template_group *tg,
(*t)->dev_attr.attr.name, tg->base + i);
if ((*t)->s2) {
a2 = &su->u.a2;
+ sysfs_attr_init(&a2->dev_attr.attr);
a2->dev_attr.attr.name = su->name;
a2->nr = (*t)->u.s.nr + i;
a2->index = (*t)->u.s.index;
@@ -449,6 +450,7 @@ nct6683_create_attr_group(struct device *dev, struct sensor_template_group *tg,
*attrs = &a2->dev_attr.attr;
} else {
a = &su->u.a1;
+ sysfs_attr_init(&a->dev_attr.attr);
a->dev_attr.attr.name = su->name;
a->index = (*t)->u.index + i;
a->dev_attr.attr.mode =
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
index 4fcb48103299..bd1c99deac71 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775.c
@@ -995,6 +995,7 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg,
(*t)->dev_attr.attr.name, tg->base + i);
if ((*t)->s2) {
a2 = &su->u.a2;
+ sysfs_attr_init(&a2->dev_attr.attr);
a2->dev_attr.attr.name = su->name;
a2->nr = (*t)->u.s.nr + i;
a2->index = (*t)->u.s.index;
@@ -1005,6 +1006,7 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg,
*attrs = &a2->dev_attr.attr;
} else {
a = &su->u.a1;
+ sysfs_attr_init(&a->dev_attr.attr);
a->dev_attr.attr.name = su->name;
a->index = (*t)->u.index + i;
a->dev_attr.attr.mode =
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
index 112e4d45e4a0..dc0b76c5e302 100644
--- a/drivers/hwmon/ntc_thermistor.c
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -53,6 +53,7 @@ static const struct platform_device_id ntc_thermistor_id[] = {
{ "ncp03wb473", TYPE_NCPXXWB473 },
{ "ncp15wl333", TYPE_NCPXXWL333 },
{ "b57330v2103", TYPE_B57330V2103},
+ { "ncp03wf104", TYPE_NCPXXWF104 },
{ },
};
@@ -135,6 +136,43 @@ static const struct ntc_compensation ncpXXwl333[] = {
{ .temp_c = 125, .ohm = 707 },
};
+static const struct ntc_compensation ncpXXwf104[] = {
+ { .temp_c = -40, .ohm = 4397119 },
+ { .temp_c = -35, .ohm = 3088599 },
+ { .temp_c = -30, .ohm = 2197225 },
+ { .temp_c = -25, .ohm = 1581881 },
+ { .temp_c = -20, .ohm = 1151037 },
+ { .temp_c = -15, .ohm = 846579 },
+ { .temp_c = -10, .ohm = 628988 },
+ { .temp_c = -5, .ohm = 471632 },
+ { .temp_c = 0, .ohm = 357012 },
+ { .temp_c = 5, .ohm = 272500 },
+ { .temp_c = 10, .ohm = 209710 },
+ { .temp_c = 15, .ohm = 162651 },
+ { .temp_c = 20, .ohm = 127080 },
+ { .temp_c = 25, .ohm = 100000 },
+ { .temp_c = 30, .ohm = 79222 },
+ { .temp_c = 35, .ohm = 63167 },
+ { .temp_c = 40, .ohm = 50677 },
+ { .temp_c = 45, .ohm = 40904 },
+ { .temp_c = 50, .ohm = 33195 },
+ { .temp_c = 55, .ohm = 27091 },
+ { .temp_c = 60, .ohm = 22224 },
+ { .temp_c = 65, .ohm = 18323 },
+ { .temp_c = 70, .ohm = 15184 },
+ { .temp_c = 75, .ohm = 12635 },
+ { .temp_c = 80, .ohm = 10566 },
+ { .temp_c = 85, .ohm = 8873 },
+ { .temp_c = 90, .ohm = 7481 },
+ { .temp_c = 95, .ohm = 6337 },
+ { .temp_c = 100, .ohm = 5384 },
+ { .temp_c = 105, .ohm = 4594 },
+ { .temp_c = 110, .ohm = 3934 },
+ { .temp_c = 115, .ohm = 3380 },
+ { .temp_c = 120, .ohm = 2916 },
+ { .temp_c = 125, .ohm = 2522 },
+};
+
/*
* The following compensation table is from the specification of EPCOS NTC
* Thermistors Datasheet
@@ -190,20 +228,21 @@ struct ntc_data {
static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata)
{
struct iio_channel *channel = pdata->chan;
- s64 result;
- int val, ret;
+ int raw, uv, ret;
- ret = iio_read_channel_raw(channel, &val);
+ ret = iio_read_channel_raw(channel, &raw);
if (ret < 0) {
pr_err("read channel() error: %d\n", ret);
return ret;
}
- /* unit: mV */
- result = pdata->pullup_uv * (s64) val;
- result >>= 12;
+ ret = iio_convert_raw_to_processed(channel, raw, &uv, 1000);
+ if (ret < 0) {
+ /* Assume 12 bit ADC with vref at pullup_uv */
+ uv = (pdata->pullup_uv * (s64)raw) >> 12;
+ }
- return (int)result;
+ return uv;
}
static const struct of_device_id ntc_match[] = {
@@ -219,6 +258,8 @@ static const struct of_device_id ntc_match[] = {
.data = &ntc_thermistor_id[4] },
{ .compatible = "epcos,b57330v2103",
.data = &ntc_thermistor_id[5]},
+ { .compatible = "murata,ncp03wf104",
+ .data = &ntc_thermistor_id[6] },
/* Usage of vendor name "ntc" is deprecated */
{ .compatible = "ntc,ncp15wb473",
@@ -239,8 +280,10 @@ static struct ntc_thermistor_platform_data *
ntc_thermistor_parse_dt(struct platform_device *pdev)
{
struct iio_channel *chan;
+ enum iio_chan_type type;
struct device_node *np = pdev->dev.of_node;
struct ntc_thermistor_platform_data *pdata;
+ int ret;
if (!np)
return NULL;
@@ -253,6 +296,13 @@ ntc_thermistor_parse_dt(struct platform_device *pdev)
if (IS_ERR(chan))
return ERR_CAST(chan);
+ ret = iio_get_channel_type(chan, &type);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ if (type != IIO_VOLTAGE)
+ return ERR_PTR(-EINVAL);
+
if (of_property_read_u32(np, "pullup-uv", &pdata->pullup_uv))
return ERR_PTR(-ENODEV);
if (of_property_read_u32(np, "pullup-ohm", &pdata->pullup_ohm))
@@ -300,30 +350,27 @@ static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
{
struct ntc_thermistor_platform_data *pdata = data->pdata;
- u64 mv = uv / 1000;
- u64 pmv = pdata->pullup_uv / 1000;
+ u32 puv = pdata->pullup_uv;
u64 n, puo, pdo;
puo = pdata->pullup_ohm;
pdo = pdata->pulldown_ohm;
- if (mv == 0) {
- if (pdata->connect == NTC_CONNECTED_POSITIVE)
- return INT_MAX;
- return 0;
- }
- if (mv >= pmv)
+ if (uv == 0)
+ return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
+ INT_MAX : 0;
+ if (uv >= puv)
return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
0 : INT_MAX;
if (pdata->connect == NTC_CONNECTED_POSITIVE && puo == 0)
- n = div64_u64_safe(pdo * (pmv - mv), mv);
+ n = div_u64(pdo * (puv - uv), uv);
else if (pdata->connect == NTC_CONNECTED_GROUND && pdo == 0)
- n = div64_u64_safe(puo * mv, pmv - mv);
+ n = div_u64(puo * uv, puv - uv);
else if (pdata->connect == NTC_CONNECTED_POSITIVE)
- n = div64_u64_safe(pdo * puo * (pmv - mv),
- puo * mv - pdo * (pmv - mv));
+ n = div64_u64_safe(pdo * puo * (puv - uv),
+ puo * uv - pdo * (puv - uv));
else
- n = div64_u64_safe(pdo * puo * mv, pdo * (pmv - mv) - puo * mv);
+ n = div64_u64_safe(pdo * puo * uv, pdo * (puv - uv) - puo * uv);
if (n > INT_MAX)
n = INT_MAX;
@@ -558,6 +605,10 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
data->comp = b57330v2103;
data->n_comp = ARRAY_SIZE(b57330v2103);
break;
+ case TYPE_NCPXXWF104:
+ data->comp = ncpXXwf104;
+ data->n_comp = ARRAY_SIZE(ncpXXwf104);
+ break;
default:
dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
pdev_id->driver_data, pdev_id->name);
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index d4f0935daaa1..497a7f822a12 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -1074,7 +1074,7 @@ static int sht15_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id sht15_device_ids[] = {
+static const struct platform_device_id sht15_device_ids[] = {
{ "sht10", sht10 },
{ "sht11", sht11 },
{ "sht15", sht15 },
diff --git a/drivers/hwmon/tc74.c b/drivers/hwmon/tc74.c
new file mode 100644
index 000000000000..d95165158800
--- /dev/null
+++ b/drivers/hwmon/tc74.c
@@ -0,0 +1,177 @@
+/*
+ * An hwmon driver for the Microchip TC74
+ *
+ * Copyright 2015 Maciej Szmigiero <mail@maciej.szmigiero.name>
+ *
+ * Based on ad7414.c:
+ * Copyright 2006 Stefan Roese, DENX Software Engineering
+ * Copyright 2008 Sean MacLennan, PIKA Technologies
+ * Copyright 2008 Frank Edelhaeuser, Spansion Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+/* TC74 registers */
+#define TC74_REG_TEMP 0x00
+#define TC74_REG_CONFIG 0x01
+
+struct tc74_data {
+ struct i2c_client *client;
+ struct mutex lock; /* atomic read data updates */
+ bool valid; /* validity of fields below */
+ unsigned long next_update; /* In jiffies */
+ s8 temp_input; /* Temp value in dC */
+};
+
+static int tc74_update_device(struct device *dev)
+{
+ struct tc74_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret;
+
+ ret = mutex_lock_interruptible(&data->lock);
+ if (ret)
+ return ret;
+
+ if (time_after(jiffies, data->next_update) || !data->valid) {
+ s32 value;
+
+ value = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
+ if (value < 0) {
+ dev_dbg(&client->dev, "TC74_REG_CONFIG read err %d\n",
+ (int)value);
+
+ ret = value;
+ goto ret_unlock;
+ }
+
+ if (!(value & BIT(6))) {
+ /* not ready yet */
+
+ ret = -EAGAIN;
+ goto ret_unlock;
+ }
+
+ value = i2c_smbus_read_byte_data(client, TC74_REG_TEMP);
+ if (value < 0) {
+ dev_dbg(&client->dev, "TC74_REG_TEMP read err %d\n",
+ (int)value);
+
+ ret = value;
+ goto ret_unlock;
+ }
+
+ data->temp_input = value;
+ data->next_update = jiffies + HZ / 4;
+ data->valid = true;
+ }
+
+ret_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static ssize_t show_temp_input(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tc74_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = tc74_update_device(dev);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d\n", data->temp_input * 1000);
+}
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
+
+static struct attribute *tc74_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(tc74);
+
+static int tc74_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ struct device *dev = &client->dev;
+ struct tc74_data *data;
+ struct device *hwmon_dev;
+ s32 conf;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EOPNOTSUPP;
+
+ data = devm_kzalloc(dev, sizeof(struct tc74_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->lock);
+
+ /* Make sure the chip is powered up. */
+ conf = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
+ if (conf < 0) {
+ dev_err(dev, "unable to read config register\n");
+
+ return conf;
+ }
+
+ if (conf & 0x3f) {
+ dev_err(dev, "invalid config register value\n");
+
+ return -ENODEV;
+ }
+
+ if (conf & BIT(7)) {
+ s32 ret;
+
+ conf &= ~BIT(7);
+
+ ret = i2c_smbus_write_byte_data(client, TC74_REG_CONFIG, conf);
+ if (ret)
+ dev_warn(dev, "unable to disable STANDBY\n");
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ client->name,
+ data, tc74_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id tc74_id[] = {
+ { "tc74", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tc74_id);
+
+static struct i2c_driver tc74_driver = {
+ .driver = {
+ .name = "tc74",
+ },
+ .probe = tc74_probe,
+ .id_table = tc74_id,
+};
+
+module_i2c_driver(tc74_driver);
+
+MODULE_AUTHOR("Maciej Szmigiero <mail@maciej.szmigiero.name>");
+
+MODULE_DESCRIPTION("TC74 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index 99664ebc738d..ccf4cffe0ee1 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -44,7 +44,7 @@
#include <linux/sysfs.h>
/* Addresses to scan */
-static const unsigned short normal_i2c[] = { 0x37, 0x48, 0x49, 0x4a, 0x4c, 0x4d,
+static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c, 0x4d,
0x4e, 0x4f, I2C_CLIENT_END };
enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 };
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 2255af23b9c7..5f1c1c4f5d87 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1103,7 +1103,7 @@ config I2C_SIBYTE
config I2C_CROS_EC_TUNNEL
tristate "ChromeOS EC tunnel I2C bus"
- depends on MFD_CROS_EC
+ depends on CROS_EC_PROTO
help
If you say yes here you get an I2C bus that will tunnel i2c commands
through to the other side of the ChromeOS EC to the i2c bus
diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
index fa8dedd8c3a2..a0d95ff682ae 100644
--- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c
+++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
@@ -182,8 +182,9 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
const u16 bus_num = bus->remote_bus;
int request_len;
int response_len;
+ int alloc_size;
int result;
- struct cros_ec_command msg = { };
+ struct cros_ec_command *msg;
request_len = ec_i2c_count_message(i2c_msgs, num);
if (request_len < 0) {
@@ -198,25 +199,39 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
return response_len;
}
- result = ec_i2c_construct_message(msg.outdata, i2c_msgs, num, bus_num);
- if (result)
- return result;
+ alloc_size = max(request_len, response_len);
+ msg = kmalloc(sizeof(*msg) + alloc_size, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
- msg.version = 0;
- msg.command = EC_CMD_I2C_PASSTHRU;
- msg.outsize = request_len;
- msg.insize = response_len;
+ result = ec_i2c_construct_message(msg->data, i2c_msgs, num, bus_num);
+ if (result) {
+ dev_err(dev, "Error constructing EC i2c message %d\n", result);
+ goto exit;
+ }
- result = cros_ec_cmd_xfer(bus->ec, &msg);
- if (result < 0)
- return result;
+ msg->version = 0;
+ msg->command = EC_CMD_I2C_PASSTHRU;
+ msg->outsize = request_len;
+ msg->insize = response_len;
- result = ec_i2c_parse_response(msg.indata, i2c_msgs, &num);
- if (result < 0)
- return result;
+ result = cros_ec_cmd_xfer(bus->ec, msg);
+ if (result < 0) {
+ dev_err(dev, "Error transferring EC i2c message %d\n", result);
+ goto exit;
+ }
+
+ result = ec_i2c_parse_response(msg->data, i2c_msgs, &num);
+ if (result < 0) {
+ dev_err(dev, "Error parsing EC i2c message %d\n", result);
+ goto exit;
+ }
/* Indicate success by saying how many messages were sent */
- return num;
+ result = num;
+exit:
+ kfree(msg);
+ return result;
}
static u32 ec_i2c_functionality(struct i2c_adapter *adap)
diff --git a/drivers/i2c/busses/i2c-hix5hd2.c b/drivers/i2c/busses/i2c-hix5hd2.c
index 8fe78d08e01c..7c6966434ee7 100644
--- a/drivers/i2c/busses/i2c-hix5hd2.c
+++ b/drivers/i2c/busses/i2c-hix5hd2.c
@@ -554,4 +554,4 @@ module_platform_driver(hix5hd2_i2c_driver);
MODULE_DESCRIPTION("Hix5hd2 I2C Bus driver");
MODULE_AUTHOR("Wei Yan <sledge.yanwei@huawei.com>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:i2c-hix5hd2");
+MODULE_ALIAS("platform:hix5hd2-i2c");
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 67cbec6796a0..630bce68bf38 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -245,7 +245,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
PIIX4_dev->revision >= 0x41) ||
(PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
- PIIX4_dev->device == 0x790b &&
+ PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
PIIX4_dev->revision >= 0x49))
smb_en = 0x00;
else
@@ -545,7 +545,7 @@ static const struct pci_device_id piix4_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) },
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x790b) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
PCI_DEVICE_ID_SERVERWORKS_OSB4) },
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 958c8db4ec30..297e9c9ac943 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -1143,6 +1143,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
return -ENOMEM;
i2c->quirks = s3c24xx_get_device_quirks(pdev);
+ i2c->sysreg = ERR_PTR(-ENOENT);
if (pdata)
memcpy(i2c->pdata, pdata, sizeof(*pdata));
else
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 987c124432c5..fc2ee8213fb6 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -107,7 +107,7 @@ static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data)
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
info->flags |= I2C_CLIENT_TEN;
}
- } else if (info->irq < 0) {
+ } else if (!info->irq) {
struct resource r;
if (acpi_dev_resource_interrupt(ares, 0, &r))
@@ -134,7 +134,6 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
memset(&info, 0, sizeof(info));
info.fwnode = acpi_fwnode_handle(adev);
- info.irq = -1;
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list,
@@ -632,8 +631,13 @@ static int i2c_device_probe(struct device *dev)
if (!client)
return 0;
- if (!client->irq && dev->of_node) {
- int irq = of_irq_get(dev->of_node, 0);
+ if (!client->irq) {
+ int irq = -ENOENT;
+
+ if (dev->of_node)
+ irq = of_irq_get(dev->of_node, 0);
+ else if (ACPI_COMPANION(dev))
+ irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
if (irq == -EPROBE_DEFER)
return irq;
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index a04c49f2a011..39ea67f9b066 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -643,15 +643,6 @@ config BLK_DEV_TC86C001
help
This driver adds support for Toshiba TC86C001 GOKU-S chip.
-config BLK_DEV_CELLEB
- tristate "Toshiba's Cell Reference Set IDE support"
- depends on PPC_CELLEB
- select BLK_DEV_IDEDMA_PCI
- help
- This driver provides support for the on-board IDE controller on
- Toshiba Cell Reference Board.
- If unsure, say Y.
-
endif
# TODO: BLK_DEV_IDEDMA_PCI -> BLK_DEV_IDEDMA_SFF
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index a04ee82f1c8f..2a8c417d4081 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -38,7 +38,6 @@ obj-$(CONFIG_BLK_DEV_AEC62XX) += aec62xx.o
obj-$(CONFIG_BLK_DEV_ALI15X3) += alim15x3.o
obj-$(CONFIG_BLK_DEV_AMD74XX) += amd74xx.o
obj-$(CONFIG_BLK_DEV_ATIIXP) += atiixp.o
-obj-$(CONFIG_BLK_DEV_CELLEB) += scc_pata.o
obj-$(CONFIG_BLK_DEV_CMD64X) += cmd64x.o
obj-$(CONFIG_BLK_DEV_CS5520) += cs5520.o
obj-$(CONFIG_BLK_DEV_CS5530) += cs5530.o
diff --git a/drivers/ide/scc_pata.c b/drivers/ide/scc_pata.c
deleted file mode 100644
index 2a2d188b5d5b..000000000000
--- a/drivers/ide/scc_pata.c
+++ /dev/null
@@ -1,887 +0,0 @@
-/*
- * Support for IDE interfaces on Celleb platform
- *
- * (C) Copyright 2006 TOSHIBA CORPORATION
- *
- * This code is based on drivers/ide/pci/siimage.c:
- * Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org>
- * Copyright (C) 2003 Red Hat
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/ide.h>
-#include <linux/init.h>
-
-#define PCI_DEVICE_ID_TOSHIBA_SCC_ATA 0x01b4
-
-#define SCC_PATA_NAME "scc IDE"
-
-#define TDVHSEL_MASTER 0x00000001
-#define TDVHSEL_SLAVE 0x00000004
-
-#define MODE_JCUSFEN 0x00000080
-
-#define CCKCTRL_ATARESET 0x00040000
-#define CCKCTRL_BUFCNT 0x00020000
-#define CCKCTRL_CRST 0x00010000
-#define CCKCTRL_OCLKEN 0x00000100
-#define CCKCTRL_ATACLKOEN 0x00000002
-#define CCKCTRL_LCLKEN 0x00000001
-
-#define QCHCD_IOS_SS 0x00000001
-
-#define QCHSD_STPDIAG 0x00020000
-
-#define INTMASK_MSK 0xD1000012
-#define INTSTS_SERROR 0x80000000
-#define INTSTS_PRERR 0x40000000
-#define INTSTS_RERR 0x10000000
-#define INTSTS_ICERR 0x01000000
-#define INTSTS_BMSINT 0x00000010
-#define INTSTS_BMHE 0x00000008
-#define INTSTS_IOIRQS 0x00000004
-#define INTSTS_INTRQ 0x00000002
-#define INTSTS_ACTEINT 0x00000001
-
-#define ECMODE_VALUE 0x01
-
-static struct scc_ports {
- unsigned long ctl, dma;
- struct ide_host *host; /* for removing port from system */
-} scc_ports[MAX_HWIFS];
-
-/* PIO transfer mode table */
-/* JCHST */
-static unsigned long JCHSTtbl[2][7] = {
- {0x0E, 0x05, 0x02, 0x03, 0x02, 0x00, 0x00}, /* 100MHz */
- {0x13, 0x07, 0x04, 0x04, 0x03, 0x00, 0x00} /* 133MHz */
-};
-
-/* JCHHT */
-static unsigned long JCHHTtbl[2][7] = {
- {0x0E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00}, /* 100MHz */
- {0x13, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00} /* 133MHz */
-};
-
-/* JCHCT */
-static unsigned long JCHCTtbl[2][7] = {
- {0x1D, 0x1D, 0x1C, 0x0B, 0x06, 0x00, 0x00}, /* 100MHz */
- {0x27, 0x26, 0x26, 0x0E, 0x09, 0x00, 0x00} /* 133MHz */
-};
-
-
-/* DMA transfer mode table */
-/* JCHDCTM/JCHDCTS */
-static unsigned long JCHDCTxtbl[2][7] = {
- {0x0A, 0x06, 0x04, 0x03, 0x01, 0x00, 0x00}, /* 100MHz */
- {0x0E, 0x09, 0x06, 0x04, 0x02, 0x01, 0x00} /* 133MHz */
-};
-
-/* JCSTWTM/JCSTWTS */
-static unsigned long JCSTWTxtbl[2][7] = {
- {0x06, 0x04, 0x03, 0x02, 0x02, 0x02, 0x00}, /* 100MHz */
- {0x09, 0x06, 0x04, 0x02, 0x02, 0x02, 0x02} /* 133MHz */
-};
-
-/* JCTSS */
-static unsigned long JCTSStbl[2][7] = {
- {0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}, /* 100MHz */
- {0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05} /* 133MHz */
-};
-
-/* JCENVT */
-static unsigned long JCENVTtbl[2][7] = {
- {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}, /* 100MHz */
- {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02} /* 133MHz */
-};
-
-/* JCACTSELS/JCACTSELM */
-static unsigned long JCACTSELtbl[2][7] = {
- {0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}, /* 100MHz */
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} /* 133MHz */
-};
-
-
-static u8 scc_ide_inb(unsigned long port)
-{
- u32 data = in_be32((void*)port);
- return (u8)data;
-}
-
-static void scc_exec_command(ide_hwif_t *hwif, u8 cmd)
-{
- out_be32((void *)hwif->io_ports.command_addr, cmd);
- eieio();
- in_be32((void *)(hwif->dma_base + 0x01c));
- eieio();
-}
-
-static u8 scc_read_status(ide_hwif_t *hwif)
-{
- return (u8)in_be32((void *)hwif->io_ports.status_addr);
-}
-
-static u8 scc_read_altstatus(ide_hwif_t *hwif)
-{
- return (u8)in_be32((void *)hwif->io_ports.ctl_addr);
-}
-
-static u8 scc_dma_sff_read_status(ide_hwif_t *hwif)
-{
- return (u8)in_be32((void *)(hwif->dma_base + 4));
-}
-
-static void scc_write_devctl(ide_hwif_t *hwif, u8 ctl)
-{
- out_be32((void *)hwif->io_ports.ctl_addr, ctl);
- eieio();
- in_be32((void *)(hwif->dma_base + 0x01c));
- eieio();
-}
-
-static void scc_ide_insw(unsigned long port, void *addr, u32 count)
-{
- u16 *ptr = (u16 *)addr;
- while (count--) {
- *ptr++ = le16_to_cpu(in_be32((void*)port));
- }
-}
-
-static void scc_ide_insl(unsigned long port, void *addr, u32 count)
-{
- u16 *ptr = (u16 *)addr;
- while (count--) {
- *ptr++ = le16_to_cpu(in_be32((void*)port));
- *ptr++ = le16_to_cpu(in_be32((void*)port));
- }
-}
-
-static void scc_ide_outb(u8 addr, unsigned long port)
-{
- out_be32((void*)port, addr);
-}
-
-static void
-scc_ide_outsw(unsigned long port, void *addr, u32 count)
-{
- u16 *ptr = (u16 *)addr;
- while (count--) {
- out_be32((void*)port, cpu_to_le16(*ptr++));
- }
-}
-
-static void
-scc_ide_outsl(unsigned long port, void *addr, u32 count)
-{
- u16 *ptr = (u16 *)addr;
- while (count--) {
- out_be32((void*)port, cpu_to_le16(*ptr++));
- out_be32((void*)port, cpu_to_le16(*ptr++));
- }
-}
-
-/**
- * scc_set_pio_mode - set host controller for PIO mode
- * @hwif: port
- * @drive: drive
- *
- * Load the timing settings for this device mode into the
- * controller.
- */
-
-static void scc_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
-{
- struct scc_ports *ports = ide_get_hwifdata(hwif);
- unsigned long ctl_base = ports->ctl;
- unsigned long cckctrl_port = ctl_base + 0xff0;
- unsigned long piosht_port = ctl_base + 0x000;
- unsigned long pioct_port = ctl_base + 0x004;
- unsigned long reg;
- int offset;
- const u8 pio = drive->pio_mode - XFER_PIO_0;
-
- reg = in_be32((void __iomem *)cckctrl_port);
- if (reg & CCKCTRL_ATACLKOEN) {
- offset = 1; /* 133MHz */
- } else {
- offset = 0; /* 100MHz */
- }
- reg = JCHSTtbl[offset][pio] << 16 | JCHHTtbl[offset][pio];
- out_be32((void __iomem *)piosht_port, reg);
- reg = JCHCTtbl[offset][pio];
- out_be32((void __iomem *)pioct_port, reg);
-}
-
-/**
- * scc_set_dma_mode - set host controller for DMA mode
- * @hwif: port
- * @drive: drive
- *
- * Load the timing settings for this device mode into the
- * controller.
- */
-
-static void scc_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
-{
- struct scc_ports *ports = ide_get_hwifdata(hwif);
- unsigned long ctl_base = ports->ctl;
- unsigned long cckctrl_port = ctl_base + 0xff0;
- unsigned long mdmact_port = ctl_base + 0x008;
- unsigned long mcrcst_port = ctl_base + 0x00c;
- unsigned long sdmact_port = ctl_base + 0x010;
- unsigned long scrcst_port = ctl_base + 0x014;
- unsigned long udenvt_port = ctl_base + 0x018;
- unsigned long tdvhsel_port = ctl_base + 0x020;
- int is_slave = drive->dn & 1;
- int offset, idx;
- unsigned long reg;
- unsigned long jcactsel;
- const u8 speed = drive->dma_mode;
-
- reg = in_be32((void __iomem *)cckctrl_port);
- if (reg & CCKCTRL_ATACLKOEN) {
- offset = 1; /* 133MHz */
- } else {
- offset = 0; /* 100MHz */
- }
-
- idx = speed - XFER_UDMA_0;
-
- jcactsel = JCACTSELtbl[offset][idx];
- if (is_slave) {
- out_be32((void __iomem *)sdmact_port, JCHDCTxtbl[offset][idx]);
- out_be32((void __iomem *)scrcst_port, JCSTWTxtbl[offset][idx]);
- jcactsel = jcactsel << 2;
- out_be32((void __iomem *)tdvhsel_port, (in_be32((void __iomem *)tdvhsel_port) & ~TDVHSEL_SLAVE) | jcactsel);
- } else {
- out_be32((void __iomem *)mdmact_port, JCHDCTxtbl[offset][idx]);
- out_be32((void __iomem *)mcrcst_port, JCSTWTxtbl[offset][idx]);
- out_be32((void __iomem *)tdvhsel_port, (in_be32((void __iomem *)tdvhsel_port) & ~TDVHSEL_MASTER) | jcactsel);
- }
- reg = JCTSStbl[offset][idx] << 16 | JCENVTtbl[offset][idx];
- out_be32((void __iomem *)udenvt_port, reg);
-}
-
-static void scc_dma_host_set(ide_drive_t *drive, int on)
-{
- ide_hwif_t *hwif = drive->hwif;
- u8 unit = drive->dn & 1;
- u8 dma_stat = scc_dma_sff_read_status(hwif);
-
- if (on)
- dma_stat |= (1 << (5 + unit));
- else
- dma_stat &= ~(1 << (5 + unit));
-
- scc_ide_outb(dma_stat, hwif->dma_base + 4);
-}
-
-/**
- * scc_dma_setup - begin a DMA phase
- * @drive: target device
- * @cmd: command
- *
- * Build an IDE DMA PRD (IDE speak for scatter gather table)
- * and then set up the DMA transfer registers.
- *
- * Returns 0 on success. If a PIO fallback is required then 1
- * is returned.
- */
-
-static int scc_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
-{
- ide_hwif_t *hwif = drive->hwif;
- u32 rw = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 0 : ATA_DMA_WR;
- u8 dma_stat;
-
- /* fall back to pio! */
- if (ide_build_dmatable(drive, cmd) == 0)
- return 1;
-
- /* PRD table */
- out_be32((void __iomem *)(hwif->dma_base + 8), hwif->dmatable_dma);
-
- /* specify r/w */
- out_be32((void __iomem *)hwif->dma_base, rw);
-
- /* read DMA status for INTR & ERROR flags */
- dma_stat = scc_dma_sff_read_status(hwif);
-
- /* clear INTR & ERROR flags */
- out_be32((void __iomem *)(hwif->dma_base + 4), dma_stat | 6);
-
- return 0;
-}
-
-static void scc_dma_start(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- u8 dma_cmd = scc_ide_inb(hwif->dma_base);
-
- /* start DMA */
- scc_ide_outb(dma_cmd | 1, hwif->dma_base);
-}
-
-static int __scc_dma_end(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- u8 dma_stat, dma_cmd;
-
- /* get DMA command mode */
- dma_cmd = scc_ide_inb(hwif->dma_base);
- /* stop DMA */
- scc_ide_outb(dma_cmd & ~1, hwif->dma_base);
- /* get DMA status */
- dma_stat = scc_dma_sff_read_status(hwif);
- /* clear the INTR & ERROR bits */
- scc_ide_outb(dma_stat | 6, hwif->dma_base + 4);
- /* verify good DMA status */
- return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;
-}
-
-/**
- * scc_dma_end - Stop DMA
- * @drive: IDE drive
- *
- * Check and clear INT Status register.
- * Then call __scc_dma_end().
- */
-
-static int scc_dma_end(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- void __iomem *dma_base = (void __iomem *)hwif->dma_base;
- unsigned long intsts_port = hwif->dma_base + 0x014;
- u32 reg;
- int dma_stat, data_loss = 0;
- static int retry = 0;
-
- /* errata A308 workaround: Step5 (check data loss) */
- /* We don't check non ide_disk because it is limited to UDMA4 */
- if (!(in_be32((void __iomem *)hwif->io_ports.ctl_addr)
- & ATA_ERR) &&
- drive->media == ide_disk && drive->current_speed > XFER_UDMA_4) {
- reg = in_be32((void __iomem *)intsts_port);
- if (!(reg & INTSTS_ACTEINT)) {
- printk(KERN_WARNING "%s: operation failed (transfer data loss)\n",
- drive->name);
- data_loss = 1;
- if (retry++) {
- struct request *rq = hwif->rq;
- ide_drive_t *drive;
- int i;
-
- /* ERROR_RESET and drive->crc_count are needed
- * to reduce DMA transfer mode in retry process.
- */
- if (rq)
- rq->errors |= ERROR_RESET;
-
- ide_port_for_each_dev(i, drive, hwif)
- drive->crc_count++;
- }
- }
- }
-
- while (1) {
- reg = in_be32((void __iomem *)intsts_port);
-
- if (reg & INTSTS_SERROR) {
- printk(KERN_WARNING "%s: SERROR\n", SCC_PATA_NAME);
- out_be32((void __iomem *)intsts_port, INTSTS_SERROR|INTSTS_BMSINT);
-
- out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
- continue;
- }
-
- if (reg & INTSTS_PRERR) {
- u32 maea0, maec0;
- unsigned long ctl_base = hwif->config_data;
-
- maea0 = in_be32((void __iomem *)(ctl_base + 0xF50));
- maec0 = in_be32((void __iomem *)(ctl_base + 0xF54));
-
- printk(KERN_WARNING "%s: PRERR [addr:%x cmd:%x]\n", SCC_PATA_NAME, maea0, maec0);
-
- out_be32((void __iomem *)intsts_port, INTSTS_PRERR|INTSTS_BMSINT);
-
- out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
- continue;
- }
-
- if (reg & INTSTS_RERR) {
- printk(KERN_WARNING "%s: Response Error\n", SCC_PATA_NAME);
- out_be32((void __iomem *)intsts_port, INTSTS_RERR|INTSTS_BMSINT);
-
- out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
- continue;
- }
-
- if (reg & INTSTS_ICERR) {
- out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS);
-
- printk(KERN_WARNING "%s: Illegal Configuration\n", SCC_PATA_NAME);
- out_be32((void __iomem *)intsts_port, INTSTS_ICERR|INTSTS_BMSINT);
- continue;
- }
-
- if (reg & INTSTS_BMSINT) {
- printk(KERN_WARNING "%s: Internal Bus Error\n", SCC_PATA_NAME);
- out_be32((void __iomem *)intsts_port, INTSTS_BMSINT);
-
- ide_do_reset(drive);
- continue;
- }
-
- if (reg & INTSTS_BMHE) {
- out_be32((void __iomem *)intsts_port, INTSTS_BMHE);
- continue;
- }
-
- if (reg & INTSTS_ACTEINT) {
- out_be32((void __iomem *)intsts_port, INTSTS_ACTEINT);
- continue;
- }
-
- if (reg & INTSTS_IOIRQS) {
- out_be32((void __iomem *)intsts_port, INTSTS_IOIRQS);
- continue;
- }
- break;
- }
-
- dma_stat = __scc_dma_end(drive);
- if (data_loss)
- dma_stat |= 2; /* emulate DMA error (to retry command) */
- return dma_stat;
-}
-
-/* returns 1 if dma irq issued, 0 otherwise */
-static int scc_dma_test_irq(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- u32 int_stat = in_be32((void __iomem *)hwif->dma_base + 0x014);
-
- /* SCC errata A252,A308 workaround: Step4 */
- if ((in_be32((void __iomem *)hwif->io_ports.ctl_addr)
- & ATA_ERR) &&
- (int_stat & INTSTS_INTRQ))
- return 1;
-
- /* SCC errata A308 workaround: Step5 (polling IOIRQS) */
- if (int_stat & INTSTS_IOIRQS)
- return 1;
-
- return 0;
-}
-
-static u8 scc_udma_filter(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- u8 mask = hwif->ultra_mask;
-
- /* errata A308 workaround: limit non ide_disk drive to UDMA4 */
- if ((drive->media != ide_disk) && (mask & 0xE0)) {
- printk(KERN_INFO "%s: limit %s to UDMA4\n",
- SCC_PATA_NAME, drive->name);
- mask = ATA_UDMA4;
- }
-
- return mask;
-}
-
-/**
- * setup_mmio_scc - map CTRL/BMID region
- * @dev: PCI device we are configuring
- * @name: device name
- *
- */
-
-static int setup_mmio_scc (struct pci_dev *dev, const char *name)
-{
- void __iomem *ctl_addr;
- void __iomem *dma_addr;
- int i, ret;
-
- for (i = 0; i < MAX_HWIFS; i++) {
- if (scc_ports[i].ctl == 0)
- break;
- }
- if (i >= MAX_HWIFS)
- return -ENOMEM;
-
- ret = pci_request_selected_regions(dev, (1 << 2) - 1, name);
- if (ret < 0) {
- printk(KERN_ERR "%s: can't reserve resources\n", name);
- return ret;
- }
-
- ctl_addr = pci_ioremap_bar(dev, 0);
- if (!ctl_addr)
- goto fail_0;
-
- dma_addr = pci_ioremap_bar(dev, 1);
- if (!dma_addr)
- goto fail_1;
-
- pci_set_master(dev);
- scc_ports[i].ctl = (unsigned long)ctl_addr;
- scc_ports[i].dma = (unsigned long)dma_addr;
- pci_set_drvdata(dev, (void *) &scc_ports[i]);
-
- return 1;
-
- fail_1:
- iounmap(ctl_addr);
- fail_0:
- return -ENOMEM;
-}
-
-static int scc_ide_setup_pci_device(struct pci_dev *dev,
- const struct ide_port_info *d)
-{
- struct scc_ports *ports = pci_get_drvdata(dev);
- struct ide_host *host;
- struct ide_hw hw, *hws[] = { &hw };
- int i, rc;
-
- memset(&hw, 0, sizeof(hw));
- for (i = 0; i <= 8; i++)
- hw.io_ports_array[i] = ports->dma + 0x20 + i * 4;
- hw.irq = dev->irq;
- hw.dev = &dev->dev;
-
- rc = ide_host_add(d, hws, 1, &host);
- if (rc)
- return rc;
-
- ports->host = host;
-
- return 0;
-}
-
-/**
- * init_setup_scc - set up an SCC PATA Controller
- * @dev: PCI device
- * @d: IDE port info
- *
- * Perform the initial set up for this device.
- */
-
-static int init_setup_scc(struct pci_dev *dev, const struct ide_port_info *d)
-{
- unsigned long ctl_base;
- unsigned long dma_base;
- unsigned long cckctrl_port;
- unsigned long intmask_port;
- unsigned long mode_port;
- unsigned long ecmode_port;
- u32 reg = 0;
- struct scc_ports *ports;
- int rc;
-
- rc = pci_enable_device(dev);
- if (rc)
- goto end;
-
- rc = setup_mmio_scc(dev, d->name);
- if (rc < 0)
- goto end;
-
- ports = pci_get_drvdata(dev);
- ctl_base = ports->ctl;
- dma_base = ports->dma;
- cckctrl_port = ctl_base + 0xff0;
- intmask_port = dma_base + 0x010;
- mode_port = ctl_base + 0x024;
- ecmode_port = ctl_base + 0xf00;
-
- /* controller initialization */
- reg = 0;
- out_be32((void*)cckctrl_port, reg);
- reg |= CCKCTRL_ATACLKOEN;
- out_be32((void*)cckctrl_port, reg);
- reg |= CCKCTRL_LCLKEN | CCKCTRL_OCLKEN;
- out_be32((void*)cckctrl_port, reg);
- reg |= CCKCTRL_CRST;
- out_be32((void*)cckctrl_port, reg);
-
- for (;;) {
- reg = in_be32((void*)cckctrl_port);
- if (reg & CCKCTRL_CRST)
- break;
- udelay(5000);
- }
-
- reg |= CCKCTRL_ATARESET;
- out_be32((void*)cckctrl_port, reg);
-
- out_be32((void*)ecmode_port, ECMODE_VALUE);
- out_be32((void*)mode_port, MODE_JCUSFEN);
- out_be32((void*)intmask_port, INTMASK_MSK);
-
- rc = scc_ide_setup_pci_device(dev, d);
-
- end:
- return rc;
-}
-
-static void scc_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
-{
- struct ide_io_ports *io_ports = &drive->hwif->io_ports;
-
- if (valid & IDE_VALID_FEATURE)
- scc_ide_outb(tf->feature, io_ports->feature_addr);
- if (valid & IDE_VALID_NSECT)
- scc_ide_outb(tf->nsect, io_ports->nsect_addr);
- if (valid & IDE_VALID_LBAL)
- scc_ide_outb(tf->lbal, io_ports->lbal_addr);
- if (valid & IDE_VALID_LBAM)
- scc_ide_outb(tf->lbam, io_ports->lbam_addr);
- if (valid & IDE_VALID_LBAH)
- scc_ide_outb(tf->lbah, io_ports->lbah_addr);
- if (valid & IDE_VALID_DEVICE)
- scc_ide_outb(tf->device, io_ports->device_addr);
-}
-
-static void scc_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
-{
- struct ide_io_ports *io_ports = &drive->hwif->io_ports;
-
- if (valid & IDE_VALID_ERROR)
- tf->error = scc_ide_inb(io_ports->feature_addr);
- if (valid & IDE_VALID_NSECT)
- tf->nsect = scc_ide_inb(io_ports->nsect_addr);
- if (valid & IDE_VALID_LBAL)
- tf->lbal = scc_ide_inb(io_ports->lbal_addr);
- if (valid & IDE_VALID_LBAM)
- tf->lbam = scc_ide_inb(io_ports->lbam_addr);
- if (valid & IDE_VALID_LBAH)
- tf->lbah = scc_ide_inb(io_ports->lbah_addr);
- if (valid & IDE_VALID_DEVICE)
- tf->device = scc_ide_inb(io_ports->device_addr);
-}
-
-static void scc_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
- void *buf, unsigned int len)
-{
- unsigned long data_addr = drive->hwif->io_ports.data_addr;
-
- len++;
-
- if (drive->io_32bit) {
- scc_ide_insl(data_addr, buf, len / 4);
-
- if ((len & 3) >= 2)
- scc_ide_insw(data_addr, (u8 *)buf + (len & ~3), 1);
- } else
- scc_ide_insw(data_addr, buf, len / 2);
-}
-
-static void scc_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
- void *buf, unsigned int len)
-{
- unsigned long data_addr = drive->hwif->io_ports.data_addr;
-
- len++;
-
- if (drive->io_32bit) {
- scc_ide_outsl(data_addr, buf, len / 4);
-
- if ((len & 3) >= 2)
- scc_ide_outsw(data_addr, (u8 *)buf + (len & ~3), 1);
- } else
- scc_ide_outsw(data_addr, buf, len / 2);
-}
-
-/**
- * init_mmio_iops_scc - set up the iops for MMIO
- * @hwif: interface to set up
- *
- */
-
-static void init_mmio_iops_scc(ide_hwif_t *hwif)
-{
- struct pci_dev *dev = to_pci_dev(hwif->dev);
- struct scc_ports *ports = pci_get_drvdata(dev);
- unsigned long dma_base = ports->dma;
-
- ide_set_hwifdata(hwif, ports);
-
- hwif->dma_base = dma_base;
- hwif->config_data = ports->ctl;
-}
-
-/**
- * init_iops_scc - set up iops
- * @hwif: interface to set up
- *
- * Do the basic setup for the SCC hardware interface
- * and then do the MMIO setup.
- */
-
-static void init_iops_scc(ide_hwif_t *hwif)
-{
- struct pci_dev *dev = to_pci_dev(hwif->dev);
-
- hwif->hwif_data = NULL;
- if (pci_get_drvdata(dev) == NULL)
- return;
- init_mmio_iops_scc(hwif);
-}
-
-static int scc_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
-{
- return ide_allocate_dma_engine(hwif);
-}
-
-static u8 scc_cable_detect(ide_hwif_t *hwif)
-{
- return ATA_CBL_PATA80;
-}
-
-/**
- * init_hwif_scc - set up hwif
- * @hwif: interface to set up
- *
- * We do the basic set up of the interface structure. The SCC
- * requires several custom handlers so we override the default
- * ide DMA handlers appropriately.
- */
-
-static void init_hwif_scc(ide_hwif_t *hwif)
-{
- /* PTERADD */
- out_be32((void __iomem *)(hwif->dma_base + 0x018), hwif->dmatable_dma);
-
- if (in_be32((void __iomem *)(hwif->config_data + 0xff0)) & CCKCTRL_ATACLKOEN)
- hwif->ultra_mask = ATA_UDMA6; /* 133MHz */
- else
- hwif->ultra_mask = ATA_UDMA5; /* 100MHz */
-}
-
-static const struct ide_tp_ops scc_tp_ops = {
- .exec_command = scc_exec_command,
- .read_status = scc_read_status,
- .read_altstatus = scc_read_altstatus,
- .write_devctl = scc_write_devctl,
-
- .dev_select = ide_dev_select,
- .tf_load = scc_tf_load,
- .tf_read = scc_tf_read,
-
- .input_data = scc_input_data,
- .output_data = scc_output_data,
-};
-
-static const struct ide_port_ops scc_port_ops = {
- .set_pio_mode = scc_set_pio_mode,
- .set_dma_mode = scc_set_dma_mode,
- .udma_filter = scc_udma_filter,
- .cable_detect = scc_cable_detect,
-};
-
-static const struct ide_dma_ops scc_dma_ops = {
- .dma_host_set = scc_dma_host_set,
- .dma_setup = scc_dma_setup,
- .dma_start = scc_dma_start,
- .dma_end = scc_dma_end,
- .dma_test_irq = scc_dma_test_irq,
- .dma_lost_irq = ide_dma_lost_irq,
- .dma_timer_expiry = ide_dma_sff_timer_expiry,
- .dma_sff_read_status = scc_dma_sff_read_status,
-};
-
-static const struct ide_port_info scc_chipset = {
- .name = "sccIDE",
- .init_iops = init_iops_scc,
- .init_dma = scc_init_dma,
- .init_hwif = init_hwif_scc,
- .tp_ops = &scc_tp_ops,
- .port_ops = &scc_port_ops,
- .dma_ops = &scc_dma_ops,
- .host_flags = IDE_HFLAG_SINGLE,
- .irq_flags = IRQF_SHARED,
- .pio_mask = ATA_PIO4,
- .chipset = ide_pci,
-};
-
-/**
- * scc_init_one - pci layer discovery entry
- * @dev: PCI device
- * @id: ident table entry
- *
- * Called by the PCI code when it finds an SCC PATA controller.
- * We then use the IDE PCI generic helper to do most of the work.
- */
-
-static int scc_init_one(struct pci_dev *dev, const struct pci_device_id *id)
-{
- return init_setup_scc(dev, &scc_chipset);
-}
-
-/**
- * scc_remove - pci layer remove entry
- * @dev: PCI device
- *
- * Called by the PCI code when it removes an SCC PATA controller.
- */
-
-static void scc_remove(struct pci_dev *dev)
-{
- struct scc_ports *ports = pci_get_drvdata(dev);
- struct ide_host *host = ports->host;
-
- ide_host_remove(host);
-
- iounmap((void*)ports->dma);
- iounmap((void*)ports->ctl);
- pci_release_selected_regions(dev, (1 << 2) - 1);
- memset(ports, 0, sizeof(*ports));
-}
-
-static const struct pci_device_id scc_pci_tbl[] = {
- { PCI_VDEVICE(TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SCC_ATA), 0 },
- { 0, },
-};
-MODULE_DEVICE_TABLE(pci, scc_pci_tbl);
-
-static struct pci_driver scc_pci_driver = {
- .name = "SCC IDE",
- .id_table = scc_pci_tbl,
- .probe = scc_init_one,
- .remove = scc_remove,
-};
-
-static int __init scc_ide_init(void)
-{
- return ide_pci_register_driver(&scc_pci_driver);
-}
-
-static void __exit scc_ide_exit(void)
-{
- pci_unregister_driver(&scc_pci_driver);
-}
-
-module_init(scc_ide_init);
-module_exit(scc_ide_exit);
-
-MODULE_DESCRIPTION("PCI driver module for Toshiba SCC IDE");
-MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
index 7f55a6d7cd03..c6d5a3a40b60 100644
--- a/drivers/iio/accel/mma9551_core.c
+++ b/drivers/iio/accel/mma9551_core.c
@@ -389,7 +389,12 @@ int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
{
int ret, i;
int len_words = len / sizeof(u16);
- __be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+ __be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS / 2];
+
+ if (len_words > ARRAY_SIZE(be_buf)) {
+ dev_err(&client->dev, "Invalid buffer size %d\n", len);
+ return -EINVAL;
+ }
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
reg, NULL, 0, (u8 *) be_buf, len);
@@ -424,7 +429,12 @@ int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
{
int ret, i;
int len_words = len / sizeof(u16);
- __be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+ __be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS / 2];
+
+ if (len_words > ARRAY_SIZE(be_buf)) {
+ dev_err(&client->dev, "Invalid buffer size %d\n", len);
+ return -EINVAL;
+ }
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
reg, NULL, 0, (u8 *) be_buf, len);
@@ -459,7 +469,12 @@ int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
{
int i;
int len_words = len / sizeof(u16);
- __be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+ __be16 be_buf[(MMA9551_MAX_MAILBOX_DATA_REGS - 1) / 2];
+
+ if (len_words > ARRAY_SIZE(be_buf)) {
+ dev_err(&client->dev, "Invalid buffer size %d\n", len);
+ return -EINVAL;
+ }
for (i = 0; i < len_words; i++)
be_buf[i] = cpu_to_be16(buf[i]);
diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
index 2df1af7d43fc..365a109aaaef 100644
--- a/drivers/iio/accel/mma9553.c
+++ b/drivers/iio/accel/mma9553.c
@@ -54,6 +54,7 @@
#define MMA9553_MASK_CONF_STEPCOALESCE GENMASK(7, 0)
#define MMA9553_REG_CONF_ACTTHD 0x0E
+#define MMA9553_MAX_ACTTHD GENMASK(15, 0)
/* Pedometer status registers (R-only) */
#define MMA9553_REG_STATUS 0x00
@@ -316,22 +317,19 @@ static int mma9553_set_config(struct mma9553_data *data, u16 reg,
static int mma9553_read_activity_stepcnt(struct mma9553_data *data,
u8 *activity, u16 *stepcnt)
{
- u32 status_stepcnt;
- u16 status;
+ u16 buf[2];
int ret;
ret = mma9551_read_status_words(data->client, MMA9551_APPID_PEDOMETER,
- MMA9553_REG_STATUS, sizeof(u32),
- (u16 *) &status_stepcnt);
+ MMA9553_REG_STATUS, sizeof(u32), buf);
if (ret < 0) {
dev_err(&data->client->dev,
"error reading status and stepcnt\n");
return ret;
}
- status = status_stepcnt & MMA9553_MASK_CONF_WORD;
- *activity = mma9553_get_bits(status, MMA9553_MASK_STATUS_ACTIVITY);
- *stepcnt = status_stepcnt >> 16;
+ *activity = mma9553_get_bits(buf[0], MMA9553_MASK_STATUS_ACTIVITY);
+ *stepcnt = buf[1];
return 0;
}
@@ -872,6 +870,9 @@ static int mma9553_write_event_value(struct iio_dev *indio_dev,
case IIO_EV_INFO_PERIOD:
switch (chan->type) {
case IIO_ACTIVITY:
+ if (val < 0 || val > MMA9553_ACTIVITY_THD_TO_SEC(
+ MMA9553_MAX_ACTTHD))
+ return -EINVAL;
mutex_lock(&data->mutex);
ret = mma9553_set_config(data, MMA9553_REG_CONF_ACTTHD,
&data->conf.actthd,
@@ -971,7 +972,8 @@ static const struct iio_chan_spec_ext_info mma9553_ext_info[] = {
.modified = 1, \
.channel2 = _chan2, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT) | \
+ BIT(IIO_CHAN_INFO_ENABLE), \
.event_spec = mma9553_activity_events, \
.num_event_specs = ARRAY_SIZE(mma9553_activity_events), \
.ext_info = mma9553_ext_info, \
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index 58d1d13d552a..211b13271c61 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -546,6 +546,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &accel_info;
+ mutex_init(&adata->tb.buf_lock);
st_sensors_power_enable(indio_dev);
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
index 08bcfb061ca5..56008a86b78f 100644
--- a/drivers/iio/adc/axp288_adc.c
+++ b/drivers/iio/adc/axp288_adc.c
@@ -53,39 +53,42 @@ static const struct iio_chan_spec const axp288_adc_channels[] = {
.channel = 0,
.address = AXP288_TS_ADC_H,
.datasheet_name = "TS_PIN",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_TEMP,
.channel = 1,
.address = AXP288_PMIC_ADC_H,
.datasheet_name = "PMIC_TEMP",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_TEMP,
.channel = 2,
.address = AXP288_GP_ADC_H,
.datasheet_name = "GPADC",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_CURRENT,
.channel = 3,
.address = AXP20X_BATT_CHRG_I_H,
.datasheet_name = "BATT_CHG_I",
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_CURRENT,
.channel = 4,
.address = AXP20X_BATT_DISCHRG_I_H,
.datasheet_name = "BATT_DISCHRG_I",
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_VOLTAGE,
.channel = 5,
.address = AXP20X_BATT_V_H,
.datasheet_name = "BATT_V",
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
};
@@ -151,9 +154,6 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev,
chan->address))
dev_err(&indio_dev->dev, "TS pin restore\n");
break;
- case IIO_CHAN_INFO_PROCESSED:
- ret = axp288_adc_read_channel(val, chan->address, info->regmap);
- break;
default:
ret = -EINVAL;
}
diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c
index 51e2a83c9404..115f6e99a7fa 100644
--- a/drivers/iio/adc/cc10001_adc.c
+++ b/drivers/iio/adc/cc10001_adc.c
@@ -35,8 +35,9 @@
#define CC10001_ADC_EOC_SET BIT(0)
#define CC10001_ADC_CHSEL_SAMPLED 0x0c
-#define CC10001_ADC_POWER_UP 0x10
-#define CC10001_ADC_POWER_UP_SET BIT(0)
+#define CC10001_ADC_POWER_DOWN 0x10
+#define CC10001_ADC_POWER_DOWN_SET BIT(0)
+
#define CC10001_ADC_DEBUG 0x14
#define CC10001_ADC_DATA_COUNT 0x20
@@ -62,7 +63,6 @@ struct cc10001_adc_device {
u16 *buf;
struct mutex lock;
- unsigned long channel_map;
unsigned int start_delay_ns;
unsigned int eoc_delay_ns;
};
@@ -79,6 +79,18 @@ static inline u32 cc10001_adc_read_reg(struct cc10001_adc_device *adc_dev,
return readl(adc_dev->reg_base + reg);
}
+static void cc10001_adc_power_up(struct cc10001_adc_device *adc_dev)
+{
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_DOWN, 0);
+ ndelay(adc_dev->start_delay_ns);
+}
+
+static void cc10001_adc_power_down(struct cc10001_adc_device *adc_dev)
+{
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_DOWN,
+ CC10001_ADC_POWER_DOWN_SET);
+}
+
static void cc10001_adc_start(struct cc10001_adc_device *adc_dev,
unsigned int channel)
{
@@ -88,6 +100,7 @@ static void cc10001_adc_start(struct cc10001_adc_device *adc_dev,
val = (channel & CC10001_ADC_CH_MASK) | CC10001_ADC_MODE_SINGLE_CONV;
cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
+ udelay(1);
val = cc10001_adc_read_reg(adc_dev, CC10001_ADC_CONFIG);
val = val | CC10001_ADC_START_CONV;
cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
@@ -129,6 +142,7 @@ static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
struct iio_dev *indio_dev;
unsigned int delay_ns;
unsigned int channel;
+ unsigned int scan_idx;
bool sample_invalid;
u16 *data;
int i;
@@ -139,20 +153,17 @@ static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
mutex_lock(&adc_dev->lock);
- cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
- CC10001_ADC_POWER_UP_SET);
-
- /* Wait for 8 (6+2) clock cycles before activating START */
- ndelay(adc_dev->start_delay_ns);
+ cc10001_adc_power_up(adc_dev);
/* Calculate delay step for eoc and sampled data */
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
i = 0;
sample_invalid = false;
- for_each_set_bit(channel, indio_dev->active_scan_mask,
+ for_each_set_bit(scan_idx, indio_dev->active_scan_mask,
indio_dev->masklength) {
+ channel = indio_dev->channels[scan_idx].channel;
cc10001_adc_start(adc_dev, channel);
data[i] = cc10001_adc_poll_done(indio_dev, channel, delay_ns);
@@ -166,7 +177,7 @@ static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
}
done:
- cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
+ cc10001_adc_power_down(adc_dev);
mutex_unlock(&adc_dev->lock);
@@ -185,11 +196,7 @@ static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev,
unsigned int delay_ns;
u16 val;
- cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
- CC10001_ADC_POWER_UP_SET);
-
- /* Wait for 8 (6+2) clock cycles before activating START */
- ndelay(adc_dev->start_delay_ns);
+ cc10001_adc_power_up(adc_dev);
/* Calculate delay step for eoc and sampled data */
delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
@@ -198,7 +205,7 @@ static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev,
val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns);
- cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
+ cc10001_adc_power_down(adc_dev);
return val;
}
@@ -224,7 +231,7 @@ static int cc10001_adc_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
ret = regulator_get_voltage(adc_dev->reg);
- if (ret)
+ if (ret < 0)
return ret;
*val = ret / 1000;
@@ -255,22 +262,22 @@ static const struct iio_info cc10001_adc_info = {
.update_scan_mode = &cc10001_update_scan_mode,
};
-static int cc10001_adc_channel_init(struct iio_dev *indio_dev)
+static int cc10001_adc_channel_init(struct iio_dev *indio_dev,
+ unsigned long channel_map)
{
- struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
struct iio_chan_spec *chan_array, *timestamp;
unsigned int bit, idx = 0;
- indio_dev->num_channels = bitmap_weight(&adc_dev->channel_map,
- CC10001_ADC_NUM_CHANNELS);
+ indio_dev->num_channels = bitmap_weight(&channel_map,
+ CC10001_ADC_NUM_CHANNELS) + 1;
- chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels + 1,
+ chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels,
sizeof(struct iio_chan_spec),
GFP_KERNEL);
if (!chan_array)
return -ENOMEM;
- for_each_set_bit(bit, &adc_dev->channel_map, CC10001_ADC_NUM_CHANNELS) {
+ for_each_set_bit(bit, &channel_map, CC10001_ADC_NUM_CHANNELS) {
struct iio_chan_spec *chan = &chan_array[idx];
chan->type = IIO_VOLTAGE;
@@ -305,6 +312,7 @@ static int cc10001_adc_probe(struct platform_device *pdev)
unsigned long adc_clk_rate;
struct resource *res;
struct iio_dev *indio_dev;
+ unsigned long channel_map;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
@@ -313,9 +321,9 @@ static int cc10001_adc_probe(struct platform_device *pdev)
adc_dev = iio_priv(indio_dev);
- adc_dev->channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0);
+ channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0);
if (!of_property_read_u32(node, "adc-reserved-channels", &ret))
- adc_dev->channel_map &= ~ret;
+ channel_map &= ~ret;
adc_dev->reg = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(adc_dev->reg))
@@ -361,7 +369,7 @@ static int cc10001_adc_probe(struct platform_device *pdev)
adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES;
/* Setup the ADC channels available on the device */
- ret = cc10001_adc_channel_init(indio_dev);
+ ret = cc10001_adc_channel_init(indio_dev, channel_map);
if (ret < 0)
goto err_disable_clk;
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index efbfd12a4bfd..8d9c9b9215dd 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -60,12 +60,12 @@ struct mcp320x {
struct spi_message msg;
struct spi_transfer transfer[2];
- u8 tx_buf;
- u8 rx_buf[2];
-
struct regulator *reg;
struct mutex lock;
const struct mcp320x_chip_info *chip_info;
+
+ u8 tx_buf ____cacheline_aligned;
+ u8 rx_buf[2];
};
static int mcp320x_channel_to_tx_data(int device_index,
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
index 3211729bcb0b..0c4618b4d515 100644
--- a/drivers/iio/adc/qcom-spmi-vadc.c
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -18,6 +18,7 @@
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -471,11 +472,11 @@ static s32 vadc_calibrate(struct vadc_priv *vadc,
const struct vadc_channel_prop *prop, u16 adc_code)
{
const struct vadc_prescale_ratio *prescale;
- s32 voltage;
+ s64 voltage;
voltage = adc_code - vadc->graph[prop->calibration].gnd;
voltage *= vadc->graph[prop->calibration].dx;
- voltage = voltage / vadc->graph[prop->calibration].dy;
+ voltage = div64_s64(voltage, vadc->graph[prop->calibration].dy);
if (prop->calibration == VADC_CALIB_ABSOLUTE)
voltage += vadc->graph[prop->calibration].dx;
@@ -487,7 +488,7 @@ static s32 vadc_calibrate(struct vadc_priv *vadc,
voltage = voltage * prescale->den;
- return voltage / prescale->num;
+ return div64_s64(voltage, prescale->num);
}
static int vadc_decimation_from_dt(u32 value)
diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c
index 89d8aa1d2818..df12c57e6ce0 100644
--- a/drivers/iio/adc/twl6030-gpadc.c
+++ b/drivers/iio/adc/twl6030-gpadc.c
@@ -1001,7 +1001,7 @@ static struct platform_driver twl6030_gpadc_driver = {
module_platform_driver(twl6030_gpadc_driver);
-MODULE_ALIAS("platform: " DRIVER_NAME);
+MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_AUTHOR("Balaji T K <balajitk@ti.com>");
MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
MODULE_AUTHOR("Oleksandr Kozaruk <oleksandr.kozaruk@ti.com");
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index a221f7329b79..ce93bd8e3f68 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -856,6 +856,7 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
switch (chan->address) {
case XADC_REG_VCCINT:
case XADC_REG_VCCAUX:
+ case XADC_REG_VREFP:
case XADC_REG_VCCBRAM:
case XADC_REG_VCCPINT:
case XADC_REG_VCCPAUX:
@@ -996,7 +997,7 @@ static const struct iio_event_spec xadc_voltage_events[] = {
.num_event_specs = (_alarm) ? ARRAY_SIZE(xadc_voltage_events) : 0, \
.scan_index = (_scan_index), \
.scan_type = { \
- .sign = 'u', \
+ .sign = ((_addr) == XADC_REG_VREFN) ? 's' : 'u', \
.realbits = 12, \
.storagebits = 16, \
.shift = 4, \
@@ -1008,7 +1009,7 @@ static const struct iio_event_spec xadc_voltage_events[] = {
static const struct iio_chan_spec xadc_channels[] = {
XADC_CHAN_TEMP(0, 8, XADC_REG_TEMP),
XADC_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
- XADC_CHAN_VOLTAGE(1, 10, XADC_REG_VCCINT, "vccaux", true),
+ XADC_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
XADC_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
XADC_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true),
XADC_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true),
diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h
index c7487e8d7f80..54adc5087210 100644
--- a/drivers/iio/adc/xilinx-xadc.h
+++ b/drivers/iio/adc/xilinx-xadc.h
@@ -145,9 +145,9 @@ static inline int xadc_write_adc_reg(struct xadc *xadc, unsigned int reg,
#define XADC_REG_MAX_VCCPINT 0x28
#define XADC_REG_MAX_VCCPAUX 0x29
#define XADC_REG_MAX_VCCO_DDR 0x2a
-#define XADC_REG_MIN_VCCPINT 0x2b
-#define XADC_REG_MIN_VCCPAUX 0x2c
-#define XADC_REG_MIN_VCCO_DDR 0x2d
+#define XADC_REG_MIN_VCCPINT 0x2c
+#define XADC_REG_MIN_VCCPAUX 0x2d
+#define XADC_REG_MIN_VCCO_DDR 0x2e
#define XADC_REG_CONF0 0x40
#define XADC_REG_CONF1 0x41
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index edd13d2b4121..8dd0477e201c 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -304,8 +304,6 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
struct st_sensors_platform_data *of_pdata;
int err = 0;
- mutex_init(&sdata->tb.buf_lock);
-
/* If OF/DT pdata exists, it will take precedence of anything else */
of_pdata = st_sensors_of_probe(indio_dev->dev.parent, pdata);
if (of_pdata)
diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c
index 21395f26d227..ffe96642b6d0 100644
--- a/drivers/iio/gyro/st_gyro_core.c
+++ b/drivers/iio/gyro/st_gyro_core.c
@@ -400,6 +400,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &gyro_info;
+ mutex_init(&gdata->tb.buf_lock);
st_sensors_power_enable(indio_dev);
diff --git a/drivers/iio/imu/adis16400.h b/drivers/iio/imu/adis16400.h
index 0916bf6b6c31..73b189c1c0fb 100644
--- a/drivers/iio/imu/adis16400.h
+++ b/drivers/iio/imu/adis16400.h
@@ -139,6 +139,7 @@
#define ADIS16400_NO_BURST BIT(1)
#define ADIS16400_HAS_SLOW_MODE BIT(2)
#define ADIS16400_HAS_SERIAL_NUMBER BIT(3)
+#define ADIS16400_BURST_DIAG_STAT BIT(4)
struct adis16400_state;
@@ -165,6 +166,7 @@ struct adis16400_state {
int filt_int;
struct adis adis;
+ unsigned long avail_scan_mask[2];
};
/* At the moment triggers are only used for ring buffer
diff --git a/drivers/iio/imu/adis16400_buffer.c b/drivers/iio/imu/adis16400_buffer.c
index 6e727ffe5262..90c24a23c679 100644
--- a/drivers/iio/imu/adis16400_buffer.c
+++ b/drivers/iio/imu/adis16400_buffer.c
@@ -18,7 +18,8 @@ int adis16400_update_scan_mode(struct iio_dev *indio_dev,
{
struct adis16400_state *st = iio_priv(indio_dev);
struct adis *adis = &st->adis;
- uint16_t *tx;
+ unsigned int burst_length;
+ u8 *tx;
if (st->variant->flags & ADIS16400_NO_BURST)
return adis_update_scan_mode(indio_dev, scan_mask);
@@ -26,26 +27,29 @@ int adis16400_update_scan_mode(struct iio_dev *indio_dev,
kfree(adis->xfer);
kfree(adis->buffer);
+ /* All but the timestamp channel */
+ burst_length = (indio_dev->num_channels - 1) * sizeof(u16);
+ if (st->variant->flags & ADIS16400_BURST_DIAG_STAT)
+ burst_length += sizeof(u16);
+
adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL);
if (!adis->xfer)
return -ENOMEM;
- adis->buffer = kzalloc(indio_dev->scan_bytes + sizeof(u16),
- GFP_KERNEL);
+ adis->buffer = kzalloc(burst_length + sizeof(u16), GFP_KERNEL);
if (!adis->buffer)
return -ENOMEM;
- tx = adis->buffer + indio_dev->scan_bytes;
-
+ tx = adis->buffer + burst_length;
tx[0] = ADIS_READ_REG(ADIS16400_GLOB_CMD);
tx[1] = 0;
adis->xfer[0].tx_buf = tx;
adis->xfer[0].bits_per_word = 8;
adis->xfer[0].len = 2;
- adis->xfer[1].tx_buf = tx;
+ adis->xfer[1].rx_buf = adis->buffer;
adis->xfer[1].bits_per_word = 8;
- adis->xfer[1].len = indio_dev->scan_bytes;
+ adis->xfer[1].len = burst_length;
spi_message_init(&adis->msg);
spi_message_add_tail(&adis->xfer[0], &adis->msg);
@@ -61,6 +65,7 @@ irqreturn_t adis16400_trigger_handler(int irq, void *p)
struct adis16400_state *st = iio_priv(indio_dev);
struct adis *adis = &st->adis;
u32 old_speed_hz = st->adis.spi->max_speed_hz;
+ void *buffer;
int ret;
if (!adis->buffer)
@@ -81,7 +86,12 @@ irqreturn_t adis16400_trigger_handler(int irq, void *p)
spi_setup(st->adis.spi);
}
- iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
+ if (st->variant->flags & ADIS16400_BURST_DIAG_STAT)
+ buffer = adis->buffer + sizeof(u16);
+ else
+ buffer = adis->buffer;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
pf->timestamp);
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c
index fa795dcd5f75..2fd68f2219a7 100644
--- a/drivers/iio/imu/adis16400_core.c
+++ b/drivers/iio/imu/adis16400_core.c
@@ -405,6 +405,11 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
*val = st->variant->temp_scale_nano / 1000000;
*val2 = (st->variant->temp_scale_nano % 1000000);
return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_PRESSURE:
+ /* 20 uBar = 0.002kPascal */
+ *val = 0;
+ *val2 = 2000;
+ return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
@@ -454,10 +459,10 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
}
}
-#define ADIS16400_VOLTAGE_CHAN(addr, bits, name, si) { \
+#define ADIS16400_VOLTAGE_CHAN(addr, bits, name, si, chn) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
- .channel = 0, \
+ .channel = chn, \
.extend_name = name, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
@@ -474,10 +479,10 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
}
#define ADIS16400_SUPPLY_CHAN(addr, bits) \
- ADIS16400_VOLTAGE_CHAN(addr, bits, "supply", ADIS16400_SCAN_SUPPLY)
+ ADIS16400_VOLTAGE_CHAN(addr, bits, "supply", ADIS16400_SCAN_SUPPLY, 0)
#define ADIS16400_AUX_ADC_CHAN(addr, bits) \
- ADIS16400_VOLTAGE_CHAN(addr, bits, NULL, ADIS16400_SCAN_ADC)
+ ADIS16400_VOLTAGE_CHAN(addr, bits, NULL, ADIS16400_SCAN_ADC, 1)
#define ADIS16400_GYRO_CHAN(mod, addr, bits) { \
.type = IIO_ANGL_VEL, \
@@ -773,7 +778,8 @@ static struct adis16400_chip_info adis16400_chips[] = {
.channels = adis16448_channels,
.num_channels = ARRAY_SIZE(adis16448_channels),
.flags = ADIS16400_HAS_PROD_ID |
- ADIS16400_HAS_SERIAL_NUMBER,
+ ADIS16400_HAS_SERIAL_NUMBER |
+ ADIS16400_BURST_DIAG_STAT,
.gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */
.accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 g */
.temp_scale_nano = 73860000, /* 0.07386 C */
@@ -791,11 +797,6 @@ static const struct iio_info adis16400_info = {
.debugfs_reg_access = adis_debugfs_reg_access,
};
-static const unsigned long adis16400_burst_scan_mask[] = {
- ~0UL,
- 0,
-};
-
static const char * const adis16400_status_error_msgs[] = {
[ADIS16400_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
[ADIS16400_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
@@ -843,6 +844,20 @@ static const struct adis_data adis16400_data = {
BIT(ADIS16400_DIAG_STAT_POWER_LOW),
};
+static void adis16400_setup_chan_mask(struct adis16400_state *st)
+{
+ const struct adis16400_chip_info *chip_info = st->variant;
+ unsigned i;
+
+ for (i = 0; i < chip_info->num_channels; i++) {
+ const struct iio_chan_spec *ch = &chip_info->channels[i];
+
+ if (ch->scan_index >= 0 &&
+ ch->scan_index != ADIS16400_SCAN_TIMESTAMP)
+ st->avail_scan_mask[0] |= BIT(ch->scan_index);
+ }
+}
+
static int adis16400_probe(struct spi_device *spi)
{
struct adis16400_state *st;
@@ -866,8 +881,10 @@ static int adis16400_probe(struct spi_device *spi)
indio_dev->info = &adis16400_info;
indio_dev->modes = INDIO_DIRECT_MODE;
- if (!(st->variant->flags & ADIS16400_NO_BURST))
- indio_dev->available_scan_masks = adis16400_burst_scan_mask;
+ if (!(st->variant->flags & ADIS16400_NO_BURST)) {
+ adis16400_setup_chan_mask(st);
+ indio_dev->available_scan_masks = st->avail_scan_mask;
+ }
ret = adis_init(&st->adis, indio_dev, spi, &adis16400_data);
if (ret)
diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c
index 847ca561afe0..55c267bbfd2f 100644
--- a/drivers/iio/kfifo_buf.c
+++ b/drivers/iio/kfifo_buf.c
@@ -38,7 +38,8 @@ static int iio_request_update_kfifo(struct iio_buffer *r)
kfifo_free(&buf->kf);
ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,
buf->buffer.length);
- buf->update_needed = false;
+ if (ret >= 0)
+ buf->update_needed = false;
} else {
kfifo_reset_out(&buf->kf);
}
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index 91ecc46ffeaa..ef60bae738e3 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -43,8 +43,6 @@ struct prox_state {
static const struct iio_chan_spec prox_channels[] = {
{
.type = IIO_PROXIMITY,
- .modified = 1,
- .channel2 = IIO_NO_MOD,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
@@ -253,7 +251,6 @@ static int hid_prox_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct prox_state *prox_state;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
- struct iio_chan_spec *channels;
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct prox_state));
@@ -272,20 +269,21 @@ static int hid_prox_probe(struct platform_device *pdev)
return ret;
}
- channels = kmemdup(prox_channels, sizeof(prox_channels), GFP_KERNEL);
- if (!channels) {
+ indio_dev->channels = kmemdup(prox_channels, sizeof(prox_channels),
+ GFP_KERNEL);
+ if (!indio_dev->channels) {
dev_err(&pdev->dev, "failed to duplicate channels\n");
return -ENOMEM;
}
- ret = prox_parse_report(pdev, hsdev, channels,
+ ret = prox_parse_report(pdev, hsdev,
+ (struct iio_chan_spec *)indio_dev->channels,
HID_USAGE_SENSOR_PROX, prox_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
goto error_free_dev_mem;
}
- indio_dev->channels = channels;
indio_dev->num_channels =
ARRAY_SIZE(prox_channels);
indio_dev->dev.parent = &pdev->dev;
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index 8ade473f99fe..2e56f812a644 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -369,6 +369,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &magn_info;
+ mutex_init(&mdata->tb.buf_lock);
st_sensors_power_enable(indio_dev);
diff --git a/drivers/iio/pressure/bmp280.c b/drivers/iio/pressure/bmp280.c
index 7c623e2bd633..a2602d8dd6d5 100644
--- a/drivers/iio/pressure/bmp280.c
+++ b/drivers/iio/pressure/bmp280.c
@@ -172,6 +172,7 @@ static s32 bmp280_compensate_temp(struct bmp280_data *data,
var2 = (((((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1]))) *
((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1])))) >> 12) *
((s32)(s16)le16_to_cpu(buf[T3]))) >> 14;
+ data->t_fine = var1 + var2;
return (data->t_fine * 5 + 128) >> 8;
}
diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c
index 7bb8d4c1f7df..3cf0bd67d24c 100644
--- a/drivers/iio/pressure/hid-sensor-press.c
+++ b/drivers/iio/pressure/hid-sensor-press.c
@@ -47,8 +47,6 @@ struct press_state {
static const struct iio_chan_spec press_channels[] = {
{
.type = IIO_PRESSURE,
- .modified = 1,
- .channel2 = IIO_NO_MOD,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
index 97baf40d424b..e881fa6291e9 100644
--- a/drivers/iio/pressure/st_pressure_core.c
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -417,6 +417,7 @@ int st_press_common_probe(struct iio_dev *indio_dev)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &press_info;
+ mutex_init(&press_data->tb.buf_lock);
st_sensors_power_enable(indio_dev);
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index f80da50d84a5..746cdf56bc76 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -457,8 +457,8 @@ static void resolve_cb(int status, struct sockaddr *src_addr,
complete(&((struct resolve_cb_context *)context)->comp);
}
-int rdma_addr_find_dmac_by_grh(union ib_gid *sgid, union ib_gid *dgid, u8 *dmac,
- u16 *vlan_id)
+int rdma_addr_find_dmac_by_grh(const union ib_gid *sgid, const union ib_gid *dgid,
+ u8 *dmac, u16 *vlan_id)
{
int ret = 0;
struct rdma_dev_addr dev_addr;
@@ -472,13 +472,8 @@ int rdma_addr_find_dmac_by_grh(union ib_gid *sgid, union ib_gid *dgid, u8 *dmac,
} sgid_addr, dgid_addr;
- ret = rdma_gid2ip(&sgid_addr._sockaddr, sgid);
- if (ret)
- return ret;
-
- ret = rdma_gid2ip(&dgid_addr._sockaddr, dgid);
- if (ret)
- return ret;
+ rdma_gid2ip(&sgid_addr._sockaddr, sgid);
+ rdma_gid2ip(&dgid_addr._sockaddr, dgid);
memset(&dev_addr, 0, sizeof(dev_addr));
@@ -512,10 +507,8 @@ int rdma_addr_find_smac_by_sgid(union ib_gid *sgid, u8 *smac, u16 *vlan_id)
struct sockaddr_in6 _sockaddr_in6;
} gid_addr;
- ret = rdma_gid2ip(&gid_addr._sockaddr, sgid);
+ rdma_gid2ip(&gid_addr._sockaddr, sgid);
- if (ret)
- return ret;
memset(&dev_addr, 0, sizeof(dev_addr));
ret = rdma_translate_ip(&gid_addr._sockaddr, &dev_addr, vlan_id);
if (ret)
diff --git a/drivers/infiniband/core/agent.c b/drivers/infiniband/core/agent.c
index f6d29614cb01..c7dcfe4ca5f1 100644
--- a/drivers/infiniband/core/agent.c
+++ b/drivers/infiniband/core/agent.c
@@ -54,7 +54,7 @@ static DEFINE_SPINLOCK(ib_agent_port_list_lock);
static LIST_HEAD(ib_agent_port_list);
static struct ib_agent_port_private *
-__ib_get_agent_port(struct ib_device *device, int port_num)
+__ib_get_agent_port(const struct ib_device *device, int port_num)
{
struct ib_agent_port_private *entry;
@@ -67,7 +67,7 @@ __ib_get_agent_port(struct ib_device *device, int port_num)
}
static struct ib_agent_port_private *
-ib_get_agent_port(struct ib_device *device, int port_num)
+ib_get_agent_port(const struct ib_device *device, int port_num)
{
struct ib_agent_port_private *entry;
unsigned long flags;
@@ -78,9 +78,9 @@ ib_get_agent_port(struct ib_device *device, int port_num)
return entry;
}
-void agent_send_response(struct ib_mad *mad, struct ib_grh *grh,
- struct ib_wc *wc, struct ib_device *device,
- int port_num, int qpn)
+void agent_send_response(const struct ib_mad_hdr *mad_hdr, const struct ib_grh *grh,
+ const struct ib_wc *wc, const struct ib_device *device,
+ int port_num, int qpn, size_t resp_mad_len, bool opa)
{
struct ib_agent_port_private *port_priv;
struct ib_mad_agent *agent;
@@ -106,15 +106,20 @@ void agent_send_response(struct ib_mad *mad, struct ib_grh *grh,
return;
}
+ if (opa && mad_hdr->base_version != OPA_MGMT_BASE_VERSION)
+ resp_mad_len = IB_MGMT_MAD_SIZE;
+
send_buf = ib_create_send_mad(agent, wc->src_qp, wc->pkey_index, 0,
- IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
- GFP_KERNEL);
+ IB_MGMT_MAD_HDR,
+ resp_mad_len - IB_MGMT_MAD_HDR,
+ GFP_KERNEL,
+ mad_hdr->base_version);
if (IS_ERR(send_buf)) {
dev_err(&device->dev, "ib_create_send_mad error\n");
goto err1;
}
- memcpy(send_buf->mad, mad, sizeof *mad);
+ memcpy(send_buf->mad, mad_hdr, resp_mad_len);
send_buf->ah = ah;
if (device->node_type == RDMA_NODE_IB_SWITCH) {
@@ -156,7 +161,7 @@ int ib_agent_port_open(struct ib_device *device, int port_num)
goto error1;
}
- if (rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_INFINIBAND) {
+ if (rdma_cap_ib_smi(device, port_num)) {
/* Obtain send only MAD agent for SMI QP */
port_priv->agent[0] = ib_register_mad_agent(device, port_num,
IB_QPT_SMI, NULL, 0,
diff --git a/drivers/infiniband/core/agent.h b/drivers/infiniband/core/agent.h
index 6669287009c2..65f92bedae44 100644
--- a/drivers/infiniband/core/agent.h
+++ b/drivers/infiniband/core/agent.h
@@ -44,8 +44,8 @@ extern int ib_agent_port_open(struct ib_device *device, int port_num);
extern int ib_agent_port_close(struct ib_device *device, int port_num);
-extern void agent_send_response(struct ib_mad *mad, struct ib_grh *grh,
- struct ib_wc *wc, struct ib_device *device,
- int port_num, int qpn);
+extern void agent_send_response(const struct ib_mad_hdr *mad_hdr, const struct ib_grh *grh,
+ const struct ib_wc *wc, const struct ib_device *device,
+ int port_num, int qpn, size_t resp_mad_len, bool opa);
#endif /* __AGENT_H_ */
diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c
index 80f6cf2449fb..871da832d016 100644
--- a/drivers/infiniband/core/cache.c
+++ b/drivers/infiniband/core/cache.c
@@ -58,17 +58,6 @@ struct ib_update_work {
u8 port_num;
};
-static inline int start_port(struct ib_device *device)
-{
- return (device->node_type == RDMA_NODE_IB_SWITCH) ? 0 : 1;
-}
-
-static inline int end_port(struct ib_device *device)
-{
- return (device->node_type == RDMA_NODE_IB_SWITCH) ?
- 0 : device->phys_port_cnt;
-}
-
int ib_get_cached_gid(struct ib_device *device,
u8 port_num,
int index,
@@ -78,12 +67,12 @@ int ib_get_cached_gid(struct ib_device *device,
unsigned long flags;
int ret = 0;
- if (port_num < start_port(device) || port_num > end_port(device))
+ if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
return -EINVAL;
read_lock_irqsave(&device->cache.lock, flags);
- cache = device->cache.gid_cache[port_num - start_port(device)];
+ cache = device->cache.gid_cache[port_num - rdma_start_port(device)];
if (index < 0 || index >= cache->table_len)
ret = -EINVAL;
@@ -96,10 +85,10 @@ int ib_get_cached_gid(struct ib_device *device,
}
EXPORT_SYMBOL(ib_get_cached_gid);
-int ib_find_cached_gid(struct ib_device *device,
- union ib_gid *gid,
- u8 *port_num,
- u16 *index)
+int ib_find_cached_gid(struct ib_device *device,
+ const union ib_gid *gid,
+ u8 *port_num,
+ u16 *index)
{
struct ib_gid_cache *cache;
unsigned long flags;
@@ -112,11 +101,11 @@ int ib_find_cached_gid(struct ib_device *device,
read_lock_irqsave(&device->cache.lock, flags);
- for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+ for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) {
cache = device->cache.gid_cache[p];
for (i = 0; i < cache->table_len; ++i) {
if (!memcmp(gid, &cache->table[i], sizeof *gid)) {
- *port_num = p + start_port(device);
+ *port_num = p + rdma_start_port(device);
if (index)
*index = i;
ret = 0;
@@ -140,12 +129,12 @@ int ib_get_cached_pkey(struct ib_device *device,
unsigned long flags;
int ret = 0;
- if (port_num < start_port(device) || port_num > end_port(device))
+ if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
return -EINVAL;
read_lock_irqsave(&device->cache.lock, flags);
- cache = device->cache.pkey_cache[port_num - start_port(device)];
+ cache = device->cache.pkey_cache[port_num - rdma_start_port(device)];
if (index < 0 || index >= cache->table_len)
ret = -EINVAL;
@@ -169,12 +158,12 @@ int ib_find_cached_pkey(struct ib_device *device,
int ret = -ENOENT;
int partial_ix = -1;
- if (port_num < start_port(device) || port_num > end_port(device))
+ if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
return -EINVAL;
read_lock_irqsave(&device->cache.lock, flags);
- cache = device->cache.pkey_cache[port_num - start_port(device)];
+ cache = device->cache.pkey_cache[port_num - rdma_start_port(device)];
*index = -1;
@@ -209,12 +198,12 @@ int ib_find_exact_cached_pkey(struct ib_device *device,
int i;
int ret = -ENOENT;
- if (port_num < start_port(device) || port_num > end_port(device))
+ if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
return -EINVAL;
read_lock_irqsave(&device->cache.lock, flags);
- cache = device->cache.pkey_cache[port_num - start_port(device)];
+ cache = device->cache.pkey_cache[port_num - rdma_start_port(device)];
*index = -1;
@@ -238,11 +227,11 @@ int ib_get_cached_lmc(struct ib_device *device,
unsigned long flags;
int ret = 0;
- if (port_num < start_port(device) || port_num > end_port(device))
+ if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
return -EINVAL;
read_lock_irqsave(&device->cache.lock, flags);
- *lmc = device->cache.lmc_cache[port_num - start_port(device)];
+ *lmc = device->cache.lmc_cache[port_num - rdma_start_port(device)];
read_unlock_irqrestore(&device->cache.lock, flags);
return ret;
@@ -303,13 +292,13 @@ static void ib_cache_update(struct ib_device *device,
write_lock_irq(&device->cache.lock);
- old_pkey_cache = device->cache.pkey_cache[port - start_port(device)];
- old_gid_cache = device->cache.gid_cache [port - start_port(device)];
+ old_pkey_cache = device->cache.pkey_cache[port - rdma_start_port(device)];
+ old_gid_cache = device->cache.gid_cache [port - rdma_start_port(device)];
- device->cache.pkey_cache[port - start_port(device)] = pkey_cache;
- device->cache.gid_cache [port - start_port(device)] = gid_cache;
+ device->cache.pkey_cache[port - rdma_start_port(device)] = pkey_cache;
+ device->cache.gid_cache [port - rdma_start_port(device)] = gid_cache;
- device->cache.lmc_cache[port - start_port(device)] = tprops->lmc;
+ device->cache.lmc_cache[port - rdma_start_port(device)] = tprops->lmc;
write_unlock_irq(&device->cache.lock);
@@ -363,14 +352,14 @@ static void ib_cache_setup_one(struct ib_device *device)
device->cache.pkey_cache =
kmalloc(sizeof *device->cache.pkey_cache *
- (end_port(device) - start_port(device) + 1), GFP_KERNEL);
+ (rdma_end_port(device) - rdma_start_port(device) + 1), GFP_KERNEL);
device->cache.gid_cache =
kmalloc(sizeof *device->cache.gid_cache *
- (end_port(device) - start_port(device) + 1), GFP_KERNEL);
+ (rdma_end_port(device) - rdma_start_port(device) + 1), GFP_KERNEL);
device->cache.lmc_cache = kmalloc(sizeof *device->cache.lmc_cache *
- (end_port(device) -
- start_port(device) + 1),
+ (rdma_end_port(device) -
+ rdma_start_port(device) + 1),
GFP_KERNEL);
if (!device->cache.pkey_cache || !device->cache.gid_cache ||
@@ -380,10 +369,10 @@ static void ib_cache_setup_one(struct ib_device *device)
goto err;
}
- for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+ for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) {
device->cache.pkey_cache[p] = NULL;
device->cache.gid_cache [p] = NULL;
- ib_cache_update(device, p + start_port(device));
+ ib_cache_update(device, p + rdma_start_port(device));
}
INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
@@ -394,7 +383,7 @@ static void ib_cache_setup_one(struct ib_device *device)
return;
err_cache:
- for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+ for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) {
kfree(device->cache.pkey_cache[p]);
kfree(device->cache.gid_cache[p]);
}
@@ -412,7 +401,7 @@ static void ib_cache_cleanup_one(struct ib_device *device)
ib_unregister_event_handler(&device->cache.event_handler);
flush_workqueue(ib_wq);
- for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+ for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) {
kfree(device->cache.pkey_cache[p]);
kfree(device->cache.gid_cache[p]);
}
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index e28a494e2a3a..dbddddd6fb5d 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -267,7 +267,8 @@ static int cm_alloc_msg(struct cm_id_private *cm_id_priv,
m = ib_create_send_mad(mad_agent, cm_id_priv->id.remote_cm_qpn,
cm_id_priv->av.pkey_index,
0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
- GFP_ATOMIC);
+ GFP_ATOMIC,
+ IB_MGMT_BASE_VERSION);
if (IS_ERR(m)) {
ib_destroy_ah(ah);
return PTR_ERR(m);
@@ -297,7 +298,8 @@ static int cm_alloc_response_msg(struct cm_port *port,
m = ib_create_send_mad(port->mad_agent, 1, mad_recv_wc->wc->pkey_index,
0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
- GFP_ATOMIC);
+ GFP_ATOMIC,
+ IB_MGMT_BASE_VERSION);
if (IS_ERR(m)) {
ib_destroy_ah(ah);
return PTR_ERR(m);
@@ -437,39 +439,38 @@ static struct cm_id_private * cm_acquire_id(__be32 local_id, __be32 remote_id)
return cm_id_priv;
}
-static void cm_mask_copy(u8 *dst, u8 *src, u8 *mask)
+static void cm_mask_copy(u32 *dst, const u32 *src, const u32 *mask)
{
int i;
- for (i = 0; i < IB_CM_COMPARE_SIZE / sizeof(unsigned long); i++)
- ((unsigned long *) dst)[i] = ((unsigned long *) src)[i] &
- ((unsigned long *) mask)[i];
+ for (i = 0; i < IB_CM_COMPARE_SIZE; i++)
+ dst[i] = src[i] & mask[i];
}
static int cm_compare_data(struct ib_cm_compare_data *src_data,
struct ib_cm_compare_data *dst_data)
{
- u8 src[IB_CM_COMPARE_SIZE];
- u8 dst[IB_CM_COMPARE_SIZE];
+ u32 src[IB_CM_COMPARE_SIZE];
+ u32 dst[IB_CM_COMPARE_SIZE];
if (!src_data || !dst_data)
return 0;
cm_mask_copy(src, src_data->data, dst_data->mask);
cm_mask_copy(dst, dst_data->data, src_data->mask);
- return memcmp(src, dst, IB_CM_COMPARE_SIZE);
+ return memcmp(src, dst, sizeof(src));
}
-static int cm_compare_private_data(u8 *private_data,
+static int cm_compare_private_data(u32 *private_data,
struct ib_cm_compare_data *dst_data)
{
- u8 src[IB_CM_COMPARE_SIZE];
+ u32 src[IB_CM_COMPARE_SIZE];
if (!dst_data)
return 0;
cm_mask_copy(src, private_data, dst_data->mask);
- return memcmp(src, dst_data->data, IB_CM_COMPARE_SIZE);
+ return memcmp(src, dst_data->data, sizeof(src));
}
/*
@@ -538,7 +539,7 @@ static struct cm_id_private * cm_insert_listen(struct cm_id_private *cm_id_priv)
static struct cm_id_private * cm_find_listen(struct ib_device *device,
__be64 service_id,
- u8 *private_data)
+ u32 *private_data)
{
struct rb_node *node = cm.listen_service_table.rb_node;
struct cm_id_private *cm_id_priv;
@@ -862,6 +863,7 @@ retest:
cm_reject_sidr_req(cm_id_priv, IB_SIDR_REJECT);
break;
case IB_CM_REQ_SENT:
+ case IB_CM_MRA_REQ_RCVD:
ib_cancel_mad(cm_id_priv->av.port->mad_agent, cm_id_priv->msg);
spin_unlock_irq(&cm_id_priv->lock);
ib_send_cm_rej(cm_id, IB_CM_REJ_TIMEOUT,
@@ -880,7 +882,6 @@ retest:
NULL, 0, NULL, 0);
}
break;
- case IB_CM_MRA_REQ_RCVD:
case IB_CM_REP_SENT:
case IB_CM_MRA_REP_RCVD:
ib_cancel_mad(cm_id_priv->av.port->mad_agent, cm_id_priv->msg);
@@ -953,7 +954,7 @@ int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask,
cm_mask_copy(cm_id_priv->compare_data->data,
compare_data->data, compare_data->mask);
memcpy(cm_id_priv->compare_data->mask, compare_data->mask,
- IB_CM_COMPARE_SIZE);
+ sizeof(compare_data->mask));
}
cm_id->state = IB_CM_LISTEN;
@@ -3760,11 +3761,9 @@ static void cm_add_one(struct ib_device *ib_device)
};
unsigned long flags;
int ret;
+ int count = 0;
u8 i;
- if (rdma_node_get_transport(ib_device->node_type) != RDMA_TRANSPORT_IB)
- return;
-
cm_dev = kzalloc(sizeof(*cm_dev) + sizeof(*port) *
ib_device->phys_port_cnt, GFP_KERNEL);
if (!cm_dev)
@@ -3783,6 +3782,9 @@ static void cm_add_one(struct ib_device *ib_device)
set_bit(IB_MGMT_METHOD_SEND, reg_req.method_mask);
for (i = 1; i <= ib_device->phys_port_cnt; i++) {
+ if (!rdma_cap_ib_cm(ib_device, i))
+ continue;
+
port = kzalloc(sizeof *port, GFP_KERNEL);
if (!port)
goto error1;
@@ -3809,7 +3811,13 @@ static void cm_add_one(struct ib_device *ib_device)
ret = ib_modify_port(ib_device, i, 0, &port_modify);
if (ret)
goto error3;
+
+ count++;
}
+
+ if (!count)
+ goto free;
+
ib_set_client_data(ib_device, &cm_client, cm_dev);
write_lock_irqsave(&cm.device_lock, flags);
@@ -3825,11 +3833,15 @@ error1:
port_modify.set_port_cap_mask = 0;
port_modify.clr_port_cap_mask = IB_PORT_CM_SUP;
while (--i) {
+ if (!rdma_cap_ib_cm(ib_device, i))
+ continue;
+
port = cm_dev->port[i-1];
ib_modify_port(ib_device, port->port_num, 0, &port_modify);
ib_unregister_mad_agent(port->mad_agent);
cm_remove_port_fs(port);
}
+free:
device_unregister(cm_dev->device);
kfree(cm_dev);
}
@@ -3853,6 +3865,9 @@ static void cm_remove_one(struct ib_device *ib_device)
write_unlock_irqrestore(&cm.device_lock, flags);
for (i = 1; i <= ib_device->phys_port_cnt; i++) {
+ if (!rdma_cap_ib_cm(ib_device, i))
+ continue;
+
port = cm_dev->port[i-1];
ib_modify_port(ib_device, port->port_num, 0, &port_modify);
ib_unregister_mad_agent(port->mad_agent);
diff --git a/drivers/infiniband/core/cm_msgs.h b/drivers/infiniband/core/cm_msgs.h
index be068f47e47e..8b76f0ef965e 100644
--- a/drivers/infiniband/core/cm_msgs.h
+++ b/drivers/infiniband/core/cm_msgs.h
@@ -103,7 +103,7 @@ struct cm_req_msg {
/* local ACK timeout:5, rsvd:3 */
u8 alt_offset139;
- u8 private_data[IB_CM_REQ_PRIVATE_DATA_SIZE];
+ u32 private_data[IB_CM_REQ_PRIVATE_DATA_SIZE / sizeof(u32)];
} __attribute__ ((packed));
@@ -801,7 +801,7 @@ struct cm_sidr_req_msg {
__be16 rsvd;
__be64 service_id;
- u8 private_data[IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE];
+ u32 private_data[IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE / sizeof(u32)];
} __attribute__ ((packed));
struct cm_sidr_rep_msg {
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index d570030d899c..143ded2bbe7c 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -65,6 +65,34 @@ MODULE_LICENSE("Dual BSD/GPL");
#define CMA_CM_MRA_SETTING (IB_CM_MRA_FLAG_DELAY | 24)
#define CMA_IBOE_PACKET_LIFETIME 18
+static const char * const cma_events[] = {
+ [RDMA_CM_EVENT_ADDR_RESOLVED] = "address resolved",
+ [RDMA_CM_EVENT_ADDR_ERROR] = "address error",
+ [RDMA_CM_EVENT_ROUTE_RESOLVED] = "route resolved ",
+ [RDMA_CM_EVENT_ROUTE_ERROR] = "route error",
+ [RDMA_CM_EVENT_CONNECT_REQUEST] = "connect request",
+ [RDMA_CM_EVENT_CONNECT_RESPONSE] = "connect response",
+ [RDMA_CM_EVENT_CONNECT_ERROR] = "connect error",
+ [RDMA_CM_EVENT_UNREACHABLE] = "unreachable",
+ [RDMA_CM_EVENT_REJECTED] = "rejected",
+ [RDMA_CM_EVENT_ESTABLISHED] = "established",
+ [RDMA_CM_EVENT_DISCONNECTED] = "disconnected",
+ [RDMA_CM_EVENT_DEVICE_REMOVAL] = "device removal",
+ [RDMA_CM_EVENT_MULTICAST_JOIN] = "multicast join",
+ [RDMA_CM_EVENT_MULTICAST_ERROR] = "multicast error",
+ [RDMA_CM_EVENT_ADDR_CHANGE] = "address change",
+ [RDMA_CM_EVENT_TIMEWAIT_EXIT] = "timewait exit",
+};
+
+const char *rdma_event_msg(enum rdma_cm_event_type event)
+{
+ size_t index = event;
+
+ return (index < ARRAY_SIZE(cma_events) && cma_events[index]) ?
+ cma_events[index] : "unrecognized event";
+}
+EXPORT_SYMBOL(rdma_event_msg);
+
static void cma_add_one(struct ib_device *device);
static void cma_remove_one(struct ib_device *device);
@@ -349,18 +377,35 @@ static int cma_translate_addr(struct sockaddr *addr, struct rdma_dev_addr *dev_a
return ret;
}
+static inline int cma_validate_port(struct ib_device *device, u8 port,
+ union ib_gid *gid, int dev_type)
+{
+ u8 found_port;
+ int ret = -ENODEV;
+
+ if ((dev_type == ARPHRD_INFINIBAND) && !rdma_protocol_ib(device, port))
+ return ret;
+
+ if ((dev_type != ARPHRD_INFINIBAND) && rdma_protocol_ib(device, port))
+ return ret;
+
+ ret = ib_find_cached_gid(device, gid, &found_port, NULL);
+ if (port != found_port)
+ return -ENODEV;
+
+ return ret;
+}
+
static int cma_acquire_dev(struct rdma_id_private *id_priv,
struct rdma_id_private *listen_id_priv)
{
struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
struct cma_device *cma_dev;
- union ib_gid gid, iboe_gid;
+ union ib_gid gid, iboe_gid, *gidp;
int ret = -ENODEV;
- u8 port, found_port;
- enum rdma_link_layer dev_ll = dev_addr->dev_type == ARPHRD_INFINIBAND ?
- IB_LINK_LAYER_INFINIBAND : IB_LINK_LAYER_ETHERNET;
+ u8 port;
- if (dev_ll != IB_LINK_LAYER_INFINIBAND &&
+ if (dev_addr->dev_type != ARPHRD_INFINIBAND &&
id_priv->id.ps == RDMA_PS_IPOIB)
return -EINVAL;
@@ -370,41 +415,36 @@ static int cma_acquire_dev(struct rdma_id_private *id_priv,
memcpy(&gid, dev_addr->src_dev_addr +
rdma_addr_gid_offset(dev_addr), sizeof gid);
- if (listen_id_priv &&
- rdma_port_get_link_layer(listen_id_priv->id.device,
- listen_id_priv->id.port_num) == dev_ll) {
+
+ if (listen_id_priv) {
cma_dev = listen_id_priv->cma_dev;
port = listen_id_priv->id.port_num;
- if (rdma_node_get_transport(cma_dev->device->node_type) == RDMA_TRANSPORT_IB &&
- rdma_port_get_link_layer(cma_dev->device, port) == IB_LINK_LAYER_ETHERNET)
- ret = ib_find_cached_gid(cma_dev->device, &iboe_gid,
- &found_port, NULL);
- else
- ret = ib_find_cached_gid(cma_dev->device, &gid,
- &found_port, NULL);
+ gidp = rdma_protocol_roce(cma_dev->device, port) ?
+ &iboe_gid : &gid;
- if (!ret && (port == found_port)) {
- id_priv->id.port_num = found_port;
+ ret = cma_validate_port(cma_dev->device, port, gidp,
+ dev_addr->dev_type);
+ if (!ret) {
+ id_priv->id.port_num = port;
goto out;
}
}
+
list_for_each_entry(cma_dev, &dev_list, list) {
for (port = 1; port <= cma_dev->device->phys_port_cnt; ++port) {
if (listen_id_priv &&
listen_id_priv->cma_dev == cma_dev &&
listen_id_priv->id.port_num == port)
continue;
- if (rdma_port_get_link_layer(cma_dev->device, port) == dev_ll) {
- if (rdma_node_get_transport(cma_dev->device->node_type) == RDMA_TRANSPORT_IB &&
- rdma_port_get_link_layer(cma_dev->device, port) == IB_LINK_LAYER_ETHERNET)
- ret = ib_find_cached_gid(cma_dev->device, &iboe_gid, &found_port, NULL);
- else
- ret = ib_find_cached_gid(cma_dev->device, &gid, &found_port, NULL);
-
- if (!ret && (port == found_port)) {
- id_priv->id.port_num = found_port;
- goto out;
- }
+
+ gidp = rdma_protocol_roce(cma_dev->device, port) ?
+ &iboe_gid : &gid;
+
+ ret = cma_validate_port(cma_dev->device, port, gidp,
+ dev_addr->dev_type);
+ if (!ret) {
+ id_priv->id.port_num = port;
+ goto out;
}
}
}
@@ -435,10 +475,10 @@ static int cma_resolve_ib_dev(struct rdma_id_private *id_priv)
pkey = ntohs(addr->sib_pkey);
list_for_each_entry(cur_dev, &dev_list, list) {
- if (rdma_node_get_transport(cur_dev->device->node_type) != RDMA_TRANSPORT_IB)
- continue;
-
for (p = 1; p <= cur_dev->device->phys_port_cnt; ++p) {
+ if (!rdma_cap_af_ib(cur_dev->device, p))
+ continue;
+
if (ib_find_cached_pkey(cur_dev->device, p, pkey, &index))
continue;
@@ -633,10 +673,9 @@ static int cma_modify_qp_rtr(struct rdma_id_private *id_priv,
if (ret)
goto out;
- if (rdma_node_get_transport(id_priv->cma_dev->device->node_type)
- == RDMA_TRANSPORT_IB &&
- rdma_port_get_link_layer(id_priv->id.device, id_priv->id.port_num)
- == IB_LINK_LAYER_ETHERNET) {
+ BUG_ON(id_priv->cma_dev->device != id_priv->id.device);
+
+ if (rdma_protocol_roce(id_priv->id.device, id_priv->id.port_num)) {
ret = rdma_addr_find_smac_by_sgid(&sgid, qp_attr.smac, NULL);
if (ret)
@@ -700,11 +739,10 @@ static int cma_ib_init_qp_attr(struct rdma_id_private *id_priv,
int ret;
u16 pkey;
- if (rdma_port_get_link_layer(id_priv->id.device, id_priv->id.port_num) ==
- IB_LINK_LAYER_INFINIBAND)
- pkey = ib_addr_get_pkey(dev_addr);
- else
+ if (rdma_cap_eth_ah(id_priv->id.device, id_priv->id.port_num))
pkey = 0xffff;
+ else
+ pkey = ib_addr_get_pkey(dev_addr);
ret = ib_find_cached_pkey(id_priv->id.device, id_priv->id.port_num,
pkey, &qp_attr->pkey_index);
@@ -735,8 +773,7 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr,
int ret = 0;
id_priv = container_of(id, struct rdma_id_private, id);
- switch (rdma_node_get_transport(id_priv->id.device->node_type)) {
- case RDMA_TRANSPORT_IB:
+ if (rdma_cap_ib_cm(id->device, id->port_num)) {
if (!id_priv->cm_id.ib || (id_priv->id.qp_type == IB_QPT_UD))
ret = cma_ib_init_qp_attr(id_priv, qp_attr, qp_attr_mask);
else
@@ -745,19 +782,15 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr,
if (qp_attr->qp_state == IB_QPS_RTR)
qp_attr->rq_psn = id_priv->seq_num;
- break;
- case RDMA_TRANSPORT_IWARP:
+ } else if (rdma_cap_iw_cm(id->device, id->port_num)) {
if (!id_priv->cm_id.iw) {
qp_attr->qp_access_flags = 0;
*qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS;
} else
ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr,
qp_attr_mask);
- break;
- default:
+ } else
ret = -ENOSYS;
- break;
- }
return ret;
}
@@ -845,33 +878,49 @@ static void cma_save_ib_info(struct rdma_cm_id *id, struct rdma_cm_id *listen_id
listen_ib = (struct sockaddr_ib *) &listen_id->route.addr.src_addr;
ib = (struct sockaddr_ib *) &id->route.addr.src_addr;
ib->sib_family = listen_ib->sib_family;
- ib->sib_pkey = path->pkey;
- ib->sib_flowinfo = path->flow_label;
- memcpy(&ib->sib_addr, &path->sgid, 16);
+ if (path) {
+ ib->sib_pkey = path->pkey;
+ ib->sib_flowinfo = path->flow_label;
+ memcpy(&ib->sib_addr, &path->sgid, 16);
+ } else {
+ ib->sib_pkey = listen_ib->sib_pkey;
+ ib->sib_flowinfo = listen_ib->sib_flowinfo;
+ ib->sib_addr = listen_ib->sib_addr;
+ }
ib->sib_sid = listen_ib->sib_sid;
ib->sib_sid_mask = cpu_to_be64(0xffffffffffffffffULL);
ib->sib_scope_id = listen_ib->sib_scope_id;
- ib = (struct sockaddr_ib *) &id->route.addr.dst_addr;
- ib->sib_family = listen_ib->sib_family;
- ib->sib_pkey = path->pkey;
- ib->sib_flowinfo = path->flow_label;
- memcpy(&ib->sib_addr, &path->dgid, 16);
+ if (path) {
+ ib = (struct sockaddr_ib *) &id->route.addr.dst_addr;
+ ib->sib_family = listen_ib->sib_family;
+ ib->sib_pkey = path->pkey;
+ ib->sib_flowinfo = path->flow_label;
+ memcpy(&ib->sib_addr, &path->dgid, 16);
+ }
+}
+
+static __be16 ss_get_port(const struct sockaddr_storage *ss)
+{
+ if (ss->ss_family == AF_INET)
+ return ((struct sockaddr_in *)ss)->sin_port;
+ else if (ss->ss_family == AF_INET6)
+ return ((struct sockaddr_in6 *)ss)->sin6_port;
+ BUG();
}
static void cma_save_ip4_info(struct rdma_cm_id *id, struct rdma_cm_id *listen_id,
struct cma_hdr *hdr)
{
- struct sockaddr_in *listen4, *ip4;
+ struct sockaddr_in *ip4;
- listen4 = (struct sockaddr_in *) &listen_id->route.addr.src_addr;
ip4 = (struct sockaddr_in *) &id->route.addr.src_addr;
- ip4->sin_family = listen4->sin_family;
+ ip4->sin_family = AF_INET;
ip4->sin_addr.s_addr = hdr->dst_addr.ip4.addr;
- ip4->sin_port = listen4->sin_port;
+ ip4->sin_port = ss_get_port(&listen_id->route.addr.src_addr);
ip4 = (struct sockaddr_in *) &id->route.addr.dst_addr;
- ip4->sin_family = listen4->sin_family;
+ ip4->sin_family = AF_INET;
ip4->sin_addr.s_addr = hdr->src_addr.ip4.addr;
ip4->sin_port = hdr->port;
}
@@ -879,16 +928,15 @@ static void cma_save_ip4_info(struct rdma_cm_id *id, struct rdma_cm_id *listen_i
static void cma_save_ip6_info(struct rdma_cm_id *id, struct rdma_cm_id *listen_id,
struct cma_hdr *hdr)
{
- struct sockaddr_in6 *listen6, *ip6;
+ struct sockaddr_in6 *ip6;
- listen6 = (struct sockaddr_in6 *) &listen_id->route.addr.src_addr;
ip6 = (struct sockaddr_in6 *) &id->route.addr.src_addr;
- ip6->sin6_family = listen6->sin6_family;
+ ip6->sin6_family = AF_INET6;
ip6->sin6_addr = hdr->dst_addr.ip6;
- ip6->sin6_port = listen6->sin6_port;
+ ip6->sin6_port = ss_get_port(&listen_id->route.addr.src_addr);
ip6 = (struct sockaddr_in6 *) &id->route.addr.dst_addr;
- ip6->sin6_family = listen6->sin6_family;
+ ip6->sin6_family = AF_INET6;
ip6->sin6_addr = hdr->src_addr.ip6;
ip6->sin6_port = hdr->port;
}
@@ -898,9 +946,11 @@ static int cma_save_net_info(struct rdma_cm_id *id, struct rdma_cm_id *listen_id
{
struct cma_hdr *hdr;
- if ((listen_id->route.addr.src_addr.ss_family == AF_IB) &&
- (ib_event->event == IB_CM_REQ_RECEIVED)) {
- cma_save_ib_info(id, listen_id, ib_event->param.req_rcvd.primary_path);
+ if (listen_id->route.addr.src_addr.ss_family == AF_IB) {
+ if (ib_event->event == IB_CM_REQ_RECEIVED)
+ cma_save_ib_info(id, listen_id, ib_event->param.req_rcvd.primary_path);
+ else if (ib_event->event == IB_CM_SIDR_REQ_RECEIVED)
+ cma_save_ib_info(id, listen_id, NULL);
return 0;
}
@@ -928,13 +978,9 @@ static inline int cma_user_data_offset(struct rdma_id_private *id_priv)
static void cma_cancel_route(struct rdma_id_private *id_priv)
{
- switch (rdma_port_get_link_layer(id_priv->id.device, id_priv->id.port_num)) {
- case IB_LINK_LAYER_INFINIBAND:
+ if (rdma_cap_ib_sa(id_priv->id.device, id_priv->id.port_num)) {
if (id_priv->query)
ib_sa_cancel_query(id_priv->query_id, id_priv->query);
- break;
- default:
- break;
}
}
@@ -1006,17 +1052,12 @@ static void cma_leave_mc_groups(struct rdma_id_private *id_priv)
mc = container_of(id_priv->mc_list.next,
struct cma_multicast, list);
list_del(&mc->list);
- switch (rdma_port_get_link_layer(id_priv->cma_dev->device, id_priv->id.port_num)) {
- case IB_LINK_LAYER_INFINIBAND:
+ if (rdma_cap_ib_mcast(id_priv->cma_dev->device,
+ id_priv->id.port_num)) {
ib_sa_free_multicast(mc->multicast.ib);
kfree(mc);
- break;
- case IB_LINK_LAYER_ETHERNET:
+ } else
kref_put(&mc->mcref, release_mc);
- break;
- default:
- break;
- }
}
}
@@ -1037,17 +1078,12 @@ void rdma_destroy_id(struct rdma_cm_id *id)
mutex_unlock(&id_priv->handler_mutex);
if (id_priv->cma_dev) {
- switch (rdma_node_get_transport(id_priv->id.device->node_type)) {
- case RDMA_TRANSPORT_IB:
+ if (rdma_cap_ib_cm(id_priv->id.device, 1)) {
if (id_priv->cm_id.ib)
ib_destroy_cm_id(id_priv->cm_id.ib);
- break;
- case RDMA_TRANSPORT_IWARP:
+ } else if (rdma_cap_iw_cm(id_priv->id.device, 1)) {
if (id_priv->cm_id.iw)
iw_destroy_cm_id(id_priv->cm_id.iw);
- break;
- default:
- break;
}
cma_leave_mc_groups(id_priv);
cma_release_dev(id_priv);
@@ -1593,6 +1629,7 @@ static int cma_iw_listen(struct rdma_id_private *id_priv, int backlog)
if (IS_ERR(id))
return PTR_ERR(id);
+ id->tos = id_priv->tos;
id_priv->cm_id.iw = id;
memcpy(&id_priv->cm_id.iw->local_addr, cma_src_addr(id_priv),
@@ -1625,8 +1662,7 @@ static void cma_listen_on_dev(struct rdma_id_private *id_priv,
struct rdma_cm_id *id;
int ret;
- if (cma_family(id_priv) == AF_IB &&
- rdma_node_get_transport(cma_dev->device->node_type) != RDMA_TRANSPORT_IB)
+ if (cma_family(id_priv) == AF_IB && !rdma_cap_ib_cm(cma_dev->device, 1))
return;
id = rdma_create_id(cma_listen_handler, id_priv, id_priv->id.ps,
@@ -1967,26 +2003,15 @@ int rdma_resolve_route(struct rdma_cm_id *id, int timeout_ms)
return -EINVAL;
atomic_inc(&id_priv->refcount);
- switch (rdma_node_get_transport(id->device->node_type)) {
- case RDMA_TRANSPORT_IB:
- switch (rdma_port_get_link_layer(id->device, id->port_num)) {
- case IB_LINK_LAYER_INFINIBAND:
- ret = cma_resolve_ib_route(id_priv, timeout_ms);
- break;
- case IB_LINK_LAYER_ETHERNET:
- ret = cma_resolve_iboe_route(id_priv);
- break;
- default:
- ret = -ENOSYS;
- }
- break;
- case RDMA_TRANSPORT_IWARP:
+ if (rdma_cap_ib_sa(id->device, id->port_num))
+ ret = cma_resolve_ib_route(id_priv, timeout_ms);
+ else if (rdma_protocol_roce(id->device, id->port_num))
+ ret = cma_resolve_iboe_route(id_priv);
+ else if (rdma_protocol_iwarp(id->device, id->port_num))
ret = cma_resolve_iw_route(id_priv, timeout_ms);
- break;
- default:
+ else
ret = -ENOSYS;
- break;
- }
+
if (ret)
goto err;
@@ -2028,7 +2053,7 @@ static int cma_bind_loopback(struct rdma_id_private *id_priv)
mutex_lock(&lock);
list_for_each_entry(cur_dev, &dev_list, list) {
if (cma_family(id_priv) == AF_IB &&
- rdma_node_get_transport(cur_dev->device->node_type) != RDMA_TRANSPORT_IB)
+ !rdma_cap_ib_cm(cur_dev->device, 1))
continue;
if (!cma_dev)
@@ -2060,7 +2085,7 @@ port_found:
goto out;
id_priv->id.route.addr.dev_addr.dev_type =
- (rdma_port_get_link_layer(cma_dev->device, p) == IB_LINK_LAYER_INFINIBAND) ?
+ (rdma_protocol_ib(cma_dev->device, p)) ?
ARPHRD_INFINIBAND : ARPHRD_ETHER;
rdma_addr_set_sgid(&id_priv->id.route.addr.dev_addr, &gid);
@@ -2537,18 +2562,15 @@ int rdma_listen(struct rdma_cm_id *id, int backlog)
id_priv->backlog = backlog;
if (id->device) {
- switch (rdma_node_get_transport(id->device->node_type)) {
- case RDMA_TRANSPORT_IB:
+ if (rdma_cap_ib_cm(id->device, 1)) {
ret = cma_ib_listen(id_priv);
if (ret)
goto err;
- break;
- case RDMA_TRANSPORT_IWARP:
+ } else if (rdma_cap_iw_cm(id->device, 1)) {
ret = cma_iw_listen(id_priv, backlog);
if (ret)
goto err;
- break;
- default:
+ } else {
ret = -ENOSYS;
goto err;
}
@@ -2840,6 +2862,7 @@ static int cma_connect_iw(struct rdma_id_private *id_priv,
if (IS_ERR(cm_id))
return PTR_ERR(cm_id);
+ cm_id->tos = id_priv->tos;
id_priv->cm_id.iw = cm_id;
memcpy(&cm_id->local_addr, cma_src_addr(id_priv),
@@ -2884,20 +2907,15 @@ int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
id_priv->srq = conn_param->srq;
}
- switch (rdma_node_get_transport(id->device->node_type)) {
- case RDMA_TRANSPORT_IB:
+ if (rdma_cap_ib_cm(id->device, id->port_num)) {
if (id->qp_type == IB_QPT_UD)
ret = cma_resolve_ib_udp(id_priv, conn_param);
else
ret = cma_connect_ib(id_priv, conn_param);
- break;
- case RDMA_TRANSPORT_IWARP:
+ } else if (rdma_cap_iw_cm(id->device, id->port_num))
ret = cma_connect_iw(id_priv, conn_param);
- break;
- default:
+ else
ret = -ENOSYS;
- break;
- }
if (ret)
goto err;
@@ -3000,8 +3018,7 @@ int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
id_priv->srq = conn_param->srq;
}
- switch (rdma_node_get_transport(id->device->node_type)) {
- case RDMA_TRANSPORT_IB:
+ if (rdma_cap_ib_cm(id->device, id->port_num)) {
if (id->qp_type == IB_QPT_UD) {
if (conn_param)
ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
@@ -3017,14 +3034,10 @@ int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
else
ret = cma_rep_recv(id_priv);
}
- break;
- case RDMA_TRANSPORT_IWARP:
+ } else if (rdma_cap_iw_cm(id->device, id->port_num))
ret = cma_accept_iw(id_priv, conn_param);
- break;
- default:
+ else
ret = -ENOSYS;
- break;
- }
if (ret)
goto reject;
@@ -3068,8 +3081,7 @@ int rdma_reject(struct rdma_cm_id *id, const void *private_data,
if (!id_priv->cm_id.ib)
return -EINVAL;
- switch (rdma_node_get_transport(id->device->node_type)) {
- case RDMA_TRANSPORT_IB:
+ if (rdma_cap_ib_cm(id->device, id->port_num)) {
if (id->qp_type == IB_QPT_UD)
ret = cma_send_sidr_rep(id_priv, IB_SIDR_REJECT, 0,
private_data, private_data_len);
@@ -3077,15 +3089,12 @@ int rdma_reject(struct rdma_cm_id *id, const void *private_data,
ret = ib_send_cm_rej(id_priv->cm_id.ib,
IB_CM_REJ_CONSUMER_DEFINED, NULL,
0, private_data, private_data_len);
- break;
- case RDMA_TRANSPORT_IWARP:
+ } else if (rdma_cap_iw_cm(id->device, id->port_num)) {
ret = iw_cm_reject(id_priv->cm_id.iw,
private_data, private_data_len);
- break;
- default:
+ } else
ret = -ENOSYS;
- break;
- }
+
return ret;
}
EXPORT_SYMBOL(rdma_reject);
@@ -3099,22 +3108,18 @@ int rdma_disconnect(struct rdma_cm_id *id)
if (!id_priv->cm_id.ib)
return -EINVAL;
- switch (rdma_node_get_transport(id->device->node_type)) {
- case RDMA_TRANSPORT_IB:
+ if (rdma_cap_ib_cm(id->device, id->port_num)) {
ret = cma_modify_qp_err(id_priv);
if (ret)
goto out;
/* Initiate or respond to a disconnect. */
if (ib_send_cm_dreq(id_priv->cm_id.ib, NULL, 0))
ib_send_cm_drep(id_priv->cm_id.ib, NULL, 0);
- break;
- case RDMA_TRANSPORT_IWARP:
+ } else if (rdma_cap_iw_cm(id->device, id->port_num)) {
ret = iw_cm_disconnect(id_priv->cm_id.iw, 0);
- break;
- default:
+ } else
ret = -EINVAL;
- break;
- }
+
out:
return ret;
}
@@ -3360,24 +3365,13 @@ int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr,
list_add(&mc->list, &id_priv->mc_list);
spin_unlock(&id_priv->lock);
- switch (rdma_node_get_transport(id->device->node_type)) {
- case RDMA_TRANSPORT_IB:
- switch (rdma_port_get_link_layer(id->device, id->port_num)) {
- case IB_LINK_LAYER_INFINIBAND:
- ret = cma_join_ib_multicast(id_priv, mc);
- break;
- case IB_LINK_LAYER_ETHERNET:
- kref_init(&mc->mcref);
- ret = cma_iboe_join_multicast(id_priv, mc);
- break;
- default:
- ret = -EINVAL;
- }
- break;
- default:
+ if (rdma_protocol_roce(id->device, id->port_num)) {
+ kref_init(&mc->mcref);
+ ret = cma_iboe_join_multicast(id_priv, mc);
+ } else if (rdma_cap_ib_mcast(id->device, id->port_num))
+ ret = cma_join_ib_multicast(id_priv, mc);
+ else
ret = -ENOSYS;
- break;
- }
if (ret) {
spin_lock_irq(&id_priv->lock);
@@ -3405,19 +3399,15 @@ void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr)
ib_detach_mcast(id->qp,
&mc->multicast.ib->rec.mgid,
be16_to_cpu(mc->multicast.ib->rec.mlid));
- if (rdma_node_get_transport(id_priv->cma_dev->device->node_type) == RDMA_TRANSPORT_IB) {
- switch (rdma_port_get_link_layer(id->device, id->port_num)) {
- case IB_LINK_LAYER_INFINIBAND:
- ib_sa_free_multicast(mc->multicast.ib);
- kfree(mc);
- break;
- case IB_LINK_LAYER_ETHERNET:
- kref_put(&mc->mcref, release_mc);
- break;
- default:
- break;
- }
- }
+
+ BUG_ON(id_priv->cma_dev->device != id->device);
+
+ if (rdma_cap_ib_mcast(id->device, id->port_num)) {
+ ib_sa_free_multicast(mc->multicast.ib);
+ kfree(mc);
+ } else if (rdma_protocol_roce(id->device, id->port_num))
+ kref_put(&mc->mcref, release_mc);
+
return;
}
}
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 18c1ece765f2..9567756ca4f9 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -92,7 +92,8 @@ static int ib_device_check_mandatory(struct ib_device *device)
IB_MANDATORY_FUNC(poll_cq),
IB_MANDATORY_FUNC(req_notify_cq),
IB_MANDATORY_FUNC(get_dma_mr),
- IB_MANDATORY_FUNC(dereg_mr)
+ IB_MANDATORY_FUNC(dereg_mr),
+ IB_MANDATORY_FUNC(get_port_immutable)
};
int i;
@@ -151,18 +152,6 @@ static int alloc_name(char *name)
return 0;
}
-static int start_port(struct ib_device *device)
-{
- return (device->node_type == RDMA_NODE_IB_SWITCH) ? 0 : 1;
-}
-
-
-static int end_port(struct ib_device *device)
-{
- return (device->node_type == RDMA_NODE_IB_SWITCH) ?
- 0 : device->phys_port_cnt;
-}
-
/**
* ib_alloc_device - allocate an IB device struct
* @size:size of structure to allocate
@@ -222,42 +211,49 @@ static int add_client_context(struct ib_device *device, struct ib_client *client
return 0;
}
-static int read_port_table_lengths(struct ib_device *device)
+static int verify_immutable(const struct ib_device *dev, u8 port)
{
- struct ib_port_attr *tprops = NULL;
- int num_ports, ret = -ENOMEM;
- u8 port_index;
-
- tprops = kmalloc(sizeof *tprops, GFP_KERNEL);
- if (!tprops)
- goto out;
-
- num_ports = end_port(device) - start_port(device) + 1;
+ return WARN_ON(!rdma_cap_ib_mad(dev, port) &&
+ rdma_max_mad_size(dev, port) != 0);
+}
- device->pkey_tbl_len = kmalloc(sizeof *device->pkey_tbl_len * num_ports,
- GFP_KERNEL);
- device->gid_tbl_len = kmalloc(sizeof *device->gid_tbl_len * num_ports,
- GFP_KERNEL);
- if (!device->pkey_tbl_len || !device->gid_tbl_len)
+static int read_port_immutable(struct ib_device *device)
+{
+ int ret = -ENOMEM;
+ u8 start_port = rdma_start_port(device);
+ u8 end_port = rdma_end_port(device);
+ u8 port;
+
+ /**
+ * device->port_immutable is indexed directly by the port number to make
+ * access to this data as efficient as possible.
+ *
+ * Therefore port_immutable is declared as a 1 based array with
+ * potential empty slots at the beginning.
+ */
+ device->port_immutable = kzalloc(sizeof(*device->port_immutable)
+ * (end_port + 1),
+ GFP_KERNEL);
+ if (!device->port_immutable)
goto err;
- for (port_index = 0; port_index < num_ports; ++port_index) {
- ret = ib_query_port(device, port_index + start_port(device),
- tprops);
+ for (port = start_port; port <= end_port; ++port) {
+ ret = device->get_port_immutable(device, port,
+ &device->port_immutable[port]);
if (ret)
goto err;
- device->pkey_tbl_len[port_index] = tprops->pkey_tbl_len;
- device->gid_tbl_len[port_index] = tprops->gid_tbl_len;
+
+ if (verify_immutable(device, port)) {
+ ret = -EINVAL;
+ goto err;
+ }
}
ret = 0;
goto out;
-
err:
- kfree(device->gid_tbl_len);
- kfree(device->pkey_tbl_len);
+ kfree(device->port_immutable);
out:
- kfree(tprops);
return ret;
}
@@ -294,9 +290,9 @@ int ib_register_device(struct ib_device *device,
spin_lock_init(&device->event_handler_lock);
spin_lock_init(&device->client_data_lock);
- ret = read_port_table_lengths(device);
+ ret = read_port_immutable(device);
if (ret) {
- printk(KERN_WARNING "Couldn't create table lengths cache for device %s\n",
+ printk(KERN_WARNING "Couldn't create per port immutable data %s\n",
device->name);
goto out;
}
@@ -305,8 +301,7 @@ int ib_register_device(struct ib_device *device,
if (ret) {
printk(KERN_WARNING "Couldn't register device %s with driver model\n",
device->name);
- kfree(device->gid_tbl_len);
- kfree(device->pkey_tbl_len);
+ kfree(device->port_immutable);
goto out;
}
@@ -348,9 +343,6 @@ void ib_unregister_device(struct ib_device *device)
list_del(&device->core_list);
- kfree(device->gid_tbl_len);
- kfree(device->pkey_tbl_len);
-
mutex_unlock(&device_mutex);
ib_device_unregister_sysfs(device);
@@ -558,7 +550,11 @@ EXPORT_SYMBOL(ib_dispatch_event);
int ib_query_device(struct ib_device *device,
struct ib_device_attr *device_attr)
{
- return device->query_device(device, device_attr);
+ struct ib_udata uhw = {.outlen = 0, .inlen = 0};
+
+ memset(device_attr, 0, sizeof(*device_attr));
+
+ return device->query_device(device, device_attr, &uhw);
}
EXPORT_SYMBOL(ib_query_device);
@@ -575,7 +571,7 @@ int ib_query_port(struct ib_device *device,
u8 port_num,
struct ib_port_attr *port_attr)
{
- if (port_num < start_port(device) || port_num > end_port(device))
+ if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
return -EINVAL;
return device->query_port(device, port_num, port_attr);
@@ -653,7 +649,7 @@ int ib_modify_port(struct ib_device *device,
if (!device->modify_port)
return -ENOSYS;
- if (port_num < start_port(device) || port_num > end_port(device))
+ if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
return -EINVAL;
return device->modify_port(device, port_num, port_modify_mask,
@@ -676,8 +672,8 @@ int ib_find_gid(struct ib_device *device, union ib_gid *gid,
union ib_gid tmp_gid;
int ret, port, i;
- for (port = start_port(device); port <= end_port(device); ++port) {
- for (i = 0; i < device->gid_tbl_len[port - start_port(device)]; ++i) {
+ for (port = rdma_start_port(device); port <= rdma_end_port(device); ++port) {
+ for (i = 0; i < device->port_immutable[port].gid_tbl_len; ++i) {
ret = ib_query_gid(device, port, i, &tmp_gid);
if (ret)
return ret;
@@ -709,7 +705,7 @@ int ib_find_pkey(struct ib_device *device,
u16 tmp_pkey;
int partial_ix = -1;
- for (i = 0; i < device->pkey_tbl_len[port_num - start_port(device)]; ++i) {
+ for (i = 0; i < device->port_immutable[port_num].pkey_tbl_len; ++i) {
ret = ib_query_pkey(device, port_num, i, &tmp_pkey);
if (ret)
return ret;
diff --git a/drivers/infiniband/core/iwpm_msg.c b/drivers/infiniband/core/iwpm_msg.c
index b85ddbc979e0..e6ffa2e66c1a 100644
--- a/drivers/infiniband/core/iwpm_msg.c
+++ b/drivers/infiniband/core/iwpm_msg.c
@@ -33,7 +33,7 @@
#include "iwpm_util.h"
-static const char iwpm_ulib_name[] = "iWarpPortMapperUser";
+static const char iwpm_ulib_name[IWPM_ULIBNAME_SIZE] = "iWarpPortMapperUser";
static int iwpm_ulib_version = 3;
static int iwpm_user_pid = IWPM_PID_UNDEFINED;
static atomic_t echo_nlmsg_seq;
@@ -468,7 +468,8 @@ add_mapping_response_exit:
}
EXPORT_SYMBOL(iwpm_add_mapping_cb);
-/* netlink attribute policy for the response to add and query mapping request */
+/* netlink attribute policy for the response to add and query mapping request
+ * and response with remote address info */
static const struct nla_policy resp_query_policy[IWPM_NLA_RQUERY_MAPPING_MAX] = {
[IWPM_NLA_QUERY_MAPPING_SEQ] = { .type = NLA_U32 },
[IWPM_NLA_QUERY_LOCAL_ADDR] = { .len = sizeof(struct sockaddr_storage) },
@@ -559,6 +560,76 @@ query_mapping_response_exit:
}
EXPORT_SYMBOL(iwpm_add_and_query_mapping_cb);
+/*
+ * iwpm_remote_info_cb - Process a port mapper message, containing
+ * the remote connecting peer address info
+ */
+int iwpm_remote_info_cb(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct nlattr *nltb[IWPM_NLA_RQUERY_MAPPING_MAX];
+ struct sockaddr_storage *local_sockaddr, *remote_sockaddr;
+ struct sockaddr_storage *mapped_loc_sockaddr, *mapped_rem_sockaddr;
+ struct iwpm_remote_info *rem_info;
+ const char *msg_type;
+ u8 nl_client;
+ int ret = -EINVAL;
+
+ msg_type = "Remote Mapping info";
+ if (iwpm_parse_nlmsg(cb, IWPM_NLA_RQUERY_MAPPING_MAX,
+ resp_query_policy, nltb, msg_type))
+ return ret;
+
+ nl_client = RDMA_NL_GET_CLIENT(cb->nlh->nlmsg_type);
+ if (!iwpm_valid_client(nl_client)) {
+ pr_info("%s: Invalid port mapper client = %d\n",
+ __func__, nl_client);
+ return ret;
+ }
+ atomic_set(&echo_nlmsg_seq, cb->nlh->nlmsg_seq);
+
+ local_sockaddr = (struct sockaddr_storage *)
+ nla_data(nltb[IWPM_NLA_QUERY_LOCAL_ADDR]);
+ remote_sockaddr = (struct sockaddr_storage *)
+ nla_data(nltb[IWPM_NLA_QUERY_REMOTE_ADDR]);
+ mapped_loc_sockaddr = (struct sockaddr_storage *)
+ nla_data(nltb[IWPM_NLA_RQUERY_MAPPED_LOC_ADDR]);
+ mapped_rem_sockaddr = (struct sockaddr_storage *)
+ nla_data(nltb[IWPM_NLA_RQUERY_MAPPED_REM_ADDR]);
+
+ if (mapped_loc_sockaddr->ss_family != local_sockaddr->ss_family ||
+ mapped_rem_sockaddr->ss_family != remote_sockaddr->ss_family) {
+ pr_info("%s: Sockaddr family doesn't match the requested one\n",
+ __func__);
+ return ret;
+ }
+ rem_info = kzalloc(sizeof(struct iwpm_remote_info), GFP_ATOMIC);
+ if (!rem_info) {
+ pr_err("%s: Unable to allocate a remote info\n", __func__);
+ ret = -ENOMEM;
+ return ret;
+ }
+ memcpy(&rem_info->mapped_loc_sockaddr, mapped_loc_sockaddr,
+ sizeof(struct sockaddr_storage));
+ memcpy(&rem_info->remote_sockaddr, remote_sockaddr,
+ sizeof(struct sockaddr_storage));
+ memcpy(&rem_info->mapped_rem_sockaddr, mapped_rem_sockaddr,
+ sizeof(struct sockaddr_storage));
+ rem_info->nl_client = nl_client;
+
+ iwpm_add_remote_info(rem_info);
+
+ iwpm_print_sockaddr(local_sockaddr,
+ "remote_info: Local sockaddr:");
+ iwpm_print_sockaddr(mapped_loc_sockaddr,
+ "remote_info: Mapped local sockaddr:");
+ iwpm_print_sockaddr(remote_sockaddr,
+ "remote_info: Remote sockaddr:");
+ iwpm_print_sockaddr(mapped_rem_sockaddr,
+ "remote_info: Mapped remote sockaddr:");
+ return ret;
+}
+EXPORT_SYMBOL(iwpm_remote_info_cb);
+
/* netlink attribute policy for the received request for mapping info */
static const struct nla_policy resp_mapinfo_policy[IWPM_NLA_MAPINFO_REQ_MAX] = {
[IWPM_NLA_MAPINFO_ULIB_NAME] = { .type = NLA_STRING,
diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c
index 69e9f84c1605..a626795bf9c7 100644
--- a/drivers/infiniband/core/iwpm_util.c
+++ b/drivers/infiniband/core/iwpm_util.c
@@ -33,8 +33,10 @@
#include "iwpm_util.h"
-#define IWPM_HASH_BUCKET_SIZE 512
-#define IWPM_HASH_BUCKET_MASK (IWPM_HASH_BUCKET_SIZE - 1)
+#define IWPM_MAPINFO_HASH_SIZE 512
+#define IWPM_MAPINFO_HASH_MASK (IWPM_MAPINFO_HASH_SIZE - 1)
+#define IWPM_REMINFO_HASH_SIZE 64
+#define IWPM_REMINFO_HASH_MASK (IWPM_REMINFO_HASH_SIZE - 1)
static LIST_HEAD(iwpm_nlmsg_req_list);
static DEFINE_SPINLOCK(iwpm_nlmsg_req_lock);
@@ -42,31 +44,49 @@ static DEFINE_SPINLOCK(iwpm_nlmsg_req_lock);
static struct hlist_head *iwpm_hash_bucket;
static DEFINE_SPINLOCK(iwpm_mapinfo_lock);
+static struct hlist_head *iwpm_reminfo_bucket;
+static DEFINE_SPINLOCK(iwpm_reminfo_lock);
+
static DEFINE_MUTEX(iwpm_admin_lock);
static struct iwpm_admin_data iwpm_admin;
int iwpm_init(u8 nl_client)
{
+ int ret = 0;
if (iwpm_valid_client(nl_client))
return -EINVAL;
mutex_lock(&iwpm_admin_lock);
if (atomic_read(&iwpm_admin.refcount) == 0) {
- iwpm_hash_bucket = kzalloc(IWPM_HASH_BUCKET_SIZE *
+ iwpm_hash_bucket = kzalloc(IWPM_MAPINFO_HASH_SIZE *
sizeof(struct hlist_head), GFP_KERNEL);
if (!iwpm_hash_bucket) {
- mutex_unlock(&iwpm_admin_lock);
+ ret = -ENOMEM;
pr_err("%s Unable to create mapinfo hash table\n", __func__);
- return -ENOMEM;
+ goto init_exit;
+ }
+ iwpm_reminfo_bucket = kzalloc(IWPM_REMINFO_HASH_SIZE *
+ sizeof(struct hlist_head), GFP_KERNEL);
+ if (!iwpm_reminfo_bucket) {
+ kfree(iwpm_hash_bucket);
+ ret = -ENOMEM;
+ pr_err("%s Unable to create reminfo hash table\n", __func__);
+ goto init_exit;
}
}
atomic_inc(&iwpm_admin.refcount);
+init_exit:
mutex_unlock(&iwpm_admin_lock);
- iwpm_set_valid(nl_client, 1);
- return 0;
+ if (!ret) {
+ iwpm_set_valid(nl_client, 1);
+ pr_debug("%s: Mapinfo and reminfo tables are created\n",
+ __func__);
+ }
+ return ret;
}
EXPORT_SYMBOL(iwpm_init);
static void free_hash_bucket(void);
+static void free_reminfo_bucket(void);
int iwpm_exit(u8 nl_client)
{
@@ -81,7 +101,8 @@ int iwpm_exit(u8 nl_client)
}
if (atomic_dec_and_test(&iwpm_admin.refcount)) {
free_hash_bucket();
- pr_debug("%s: Mapinfo hash table is destroyed\n", __func__);
+ free_reminfo_bucket();
+ pr_debug("%s: Resources are destroyed\n", __func__);
}
mutex_unlock(&iwpm_admin_lock);
iwpm_set_valid(nl_client, 0);
@@ -89,7 +110,7 @@ int iwpm_exit(u8 nl_client)
}
EXPORT_SYMBOL(iwpm_exit);
-static struct hlist_head *get_hash_bucket_head(struct sockaddr_storage *,
+static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage *,
struct sockaddr_storage *);
int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr,
@@ -99,9 +120,10 @@ int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr,
struct hlist_head *hash_bucket_head;
struct iwpm_mapping_info *map_info;
unsigned long flags;
+ int ret = -EINVAL;
if (!iwpm_valid_client(nl_client))
- return -EINVAL;
+ return ret;
map_info = kzalloc(sizeof(struct iwpm_mapping_info), GFP_KERNEL);
if (!map_info) {
pr_err("%s: Unable to allocate a mapping info\n", __func__);
@@ -115,13 +137,16 @@ int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr,
spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
if (iwpm_hash_bucket) {
- hash_bucket_head = get_hash_bucket_head(
+ hash_bucket_head = get_mapinfo_hash_bucket(
&map_info->local_sockaddr,
&map_info->mapped_sockaddr);
- hlist_add_head(&map_info->hlist_node, hash_bucket_head);
+ if (hash_bucket_head) {
+ hlist_add_head(&map_info->hlist_node, hash_bucket_head);
+ ret = 0;
+ }
}
spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(iwpm_create_mapinfo);
@@ -136,9 +161,12 @@ int iwpm_remove_mapinfo(struct sockaddr_storage *local_sockaddr,
spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
if (iwpm_hash_bucket) {
- hash_bucket_head = get_hash_bucket_head(
+ hash_bucket_head = get_mapinfo_hash_bucket(
local_sockaddr,
mapped_local_addr);
+ if (!hash_bucket_head)
+ goto remove_mapinfo_exit;
+
hlist_for_each_entry_safe(map_info, tmp_hlist_node,
hash_bucket_head, hlist_node) {
@@ -152,6 +180,7 @@ int iwpm_remove_mapinfo(struct sockaddr_storage *local_sockaddr,
}
}
}
+remove_mapinfo_exit:
spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
return ret;
}
@@ -166,7 +195,7 @@ static void free_hash_bucket(void)
/* remove all the mapinfo data from the list */
spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
- for (i = 0; i < IWPM_HASH_BUCKET_SIZE; i++) {
+ for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) {
hlist_for_each_entry_safe(map_info, tmp_hlist_node,
&iwpm_hash_bucket[i], hlist_node) {
@@ -180,6 +209,96 @@ static void free_hash_bucket(void)
spin_unlock_irqrestore(&iwpm_mapinfo_lock, flags);
}
+static void free_reminfo_bucket(void)
+{
+ struct hlist_node *tmp_hlist_node;
+ struct iwpm_remote_info *rem_info;
+ unsigned long flags;
+ int i;
+
+ /* remove all the remote info from the list */
+ spin_lock_irqsave(&iwpm_reminfo_lock, flags);
+ for (i = 0; i < IWPM_REMINFO_HASH_SIZE; i++) {
+ hlist_for_each_entry_safe(rem_info, tmp_hlist_node,
+ &iwpm_reminfo_bucket[i], hlist_node) {
+
+ hlist_del_init(&rem_info->hlist_node);
+ kfree(rem_info);
+ }
+ }
+ /* free the hash list */
+ kfree(iwpm_reminfo_bucket);
+ iwpm_reminfo_bucket = NULL;
+ spin_unlock_irqrestore(&iwpm_reminfo_lock, flags);
+}
+
+static struct hlist_head *get_reminfo_hash_bucket(struct sockaddr_storage *,
+ struct sockaddr_storage *);
+
+void iwpm_add_remote_info(struct iwpm_remote_info *rem_info)
+{
+ struct hlist_head *hash_bucket_head;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iwpm_reminfo_lock, flags);
+ if (iwpm_reminfo_bucket) {
+ hash_bucket_head = get_reminfo_hash_bucket(
+ &rem_info->mapped_loc_sockaddr,
+ &rem_info->mapped_rem_sockaddr);
+ if (hash_bucket_head)
+ hlist_add_head(&rem_info->hlist_node, hash_bucket_head);
+ }
+ spin_unlock_irqrestore(&iwpm_reminfo_lock, flags);
+}
+
+int iwpm_get_remote_info(struct sockaddr_storage *mapped_loc_addr,
+ struct sockaddr_storage *mapped_rem_addr,
+ struct sockaddr_storage *remote_addr,
+ u8 nl_client)
+{
+ struct hlist_node *tmp_hlist_node;
+ struct hlist_head *hash_bucket_head;
+ struct iwpm_remote_info *rem_info = NULL;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ if (!iwpm_valid_client(nl_client)) {
+ pr_info("%s: Invalid client = %d\n", __func__, nl_client);
+ return ret;
+ }
+ spin_lock_irqsave(&iwpm_reminfo_lock, flags);
+ if (iwpm_reminfo_bucket) {
+ hash_bucket_head = get_reminfo_hash_bucket(
+ mapped_loc_addr,
+ mapped_rem_addr);
+ if (!hash_bucket_head)
+ goto get_remote_info_exit;
+ hlist_for_each_entry_safe(rem_info, tmp_hlist_node,
+ hash_bucket_head, hlist_node) {
+
+ if (!iwpm_compare_sockaddr(&rem_info->mapped_loc_sockaddr,
+ mapped_loc_addr) &&
+ !iwpm_compare_sockaddr(&rem_info->mapped_rem_sockaddr,
+ mapped_rem_addr)) {
+
+ memcpy(remote_addr, &rem_info->remote_sockaddr,
+ sizeof(struct sockaddr_storage));
+ iwpm_print_sockaddr(remote_addr,
+ "get_remote_info: Remote sockaddr:");
+
+ hlist_del_init(&rem_info->hlist_node);
+ kfree(rem_info);
+ ret = 0;
+ break;
+ }
+ }
+ }
+get_remote_info_exit:
+ spin_unlock_irqrestore(&iwpm_reminfo_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(iwpm_get_remote_info);
+
struct iwpm_nlmsg_request *iwpm_get_nlmsg_request(__u32 nlmsg_seq,
u8 nl_client, gfp_t gfp)
{
@@ -409,31 +528,54 @@ static u32 iwpm_ipv4_jhash(struct sockaddr_in *ipv4_sockaddr)
return hash;
}
-static struct hlist_head *get_hash_bucket_head(struct sockaddr_storage
- *local_sockaddr,
- struct sockaddr_storage
- *mapped_sockaddr)
+static int get_hash_bucket(struct sockaddr_storage *a_sockaddr,
+ struct sockaddr_storage *b_sockaddr, u32 *hash)
{
- u32 local_hash, mapped_hash, hash;
+ u32 a_hash, b_hash;
- if (local_sockaddr->ss_family == AF_INET) {
- local_hash = iwpm_ipv4_jhash((struct sockaddr_in *) local_sockaddr);
- mapped_hash = iwpm_ipv4_jhash((struct sockaddr_in *) mapped_sockaddr);
+ if (a_sockaddr->ss_family == AF_INET) {
+ a_hash = iwpm_ipv4_jhash((struct sockaddr_in *) a_sockaddr);
+ b_hash = iwpm_ipv4_jhash((struct sockaddr_in *) b_sockaddr);
- } else if (local_sockaddr->ss_family == AF_INET6) {
- local_hash = iwpm_ipv6_jhash((struct sockaddr_in6 *) local_sockaddr);
- mapped_hash = iwpm_ipv6_jhash((struct sockaddr_in6 *) mapped_sockaddr);
+ } else if (a_sockaddr->ss_family == AF_INET6) {
+ a_hash = iwpm_ipv6_jhash((struct sockaddr_in6 *) a_sockaddr);
+ b_hash = iwpm_ipv6_jhash((struct sockaddr_in6 *) b_sockaddr);
} else {
pr_err("%s: Invalid sockaddr family\n", __func__);
- return NULL;
+ return -EINVAL;
}
- if (local_hash == mapped_hash) /* if port mapper isn't available */
- hash = local_hash;
+ if (a_hash == b_hash) /* if port mapper isn't available */
+ *hash = a_hash;
else
- hash = jhash_2words(local_hash, mapped_hash, 0);
+ *hash = jhash_2words(a_hash, b_hash, 0);
+ return 0;
+}
+
+static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage
+ *local_sockaddr, struct sockaddr_storage
+ *mapped_sockaddr)
+{
+ u32 hash;
+ int ret;
- return &iwpm_hash_bucket[hash & IWPM_HASH_BUCKET_MASK];
+ ret = get_hash_bucket(local_sockaddr, mapped_sockaddr, &hash);
+ if (ret)
+ return NULL;
+ return &iwpm_hash_bucket[hash & IWPM_MAPINFO_HASH_MASK];
+}
+
+static struct hlist_head *get_reminfo_hash_bucket(struct sockaddr_storage
+ *mapped_loc_sockaddr, struct sockaddr_storage
+ *mapped_rem_sockaddr)
+{
+ u32 hash;
+ int ret;
+
+ ret = get_hash_bucket(mapped_loc_sockaddr, mapped_rem_sockaddr, &hash);
+ if (ret)
+ return NULL;
+ return &iwpm_reminfo_bucket[hash & IWPM_REMINFO_HASH_MASK];
}
static int send_mapinfo_num(u32 mapping_num, u8 nl_client, int iwpm_pid)
@@ -512,7 +654,7 @@ int iwpm_send_mapinfo(u8 nl_client, int iwpm_pid)
}
skb_num++;
spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
- for (i = 0; i < IWPM_HASH_BUCKET_SIZE; i++) {
+ for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) {
hlist_for_each_entry(map_info, &iwpm_hash_bucket[i],
hlist_node) {
if (map_info->nl_client != nl_client)
@@ -595,7 +737,7 @@ int iwpm_mapinfo_available(void)
spin_lock_irqsave(&iwpm_mapinfo_lock, flags);
if (iwpm_hash_bucket) {
- for (i = 0; i < IWPM_HASH_BUCKET_SIZE; i++) {
+ for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) {
if (!hlist_empty(&iwpm_hash_bucket[i])) {
full_bucket = 1;
break;
diff --git a/drivers/infiniband/core/iwpm_util.h b/drivers/infiniband/core/iwpm_util.h
index 9777c869a140..ee2d9ff095be 100644
--- a/drivers/infiniband/core/iwpm_util.h
+++ b/drivers/infiniband/core/iwpm_util.h
@@ -76,6 +76,14 @@ struct iwpm_mapping_info {
u8 nl_client;
};
+struct iwpm_remote_info {
+ struct hlist_node hlist_node;
+ struct sockaddr_storage remote_sockaddr;
+ struct sockaddr_storage mapped_loc_sockaddr;
+ struct sockaddr_storage mapped_rem_sockaddr;
+ u8 nl_client;
+};
+
struct iwpm_admin_data {
atomic_t refcount;
atomic_t nlmsg_seq;
@@ -128,6 +136,13 @@ int iwpm_wait_complete_req(struct iwpm_nlmsg_request *nlmsg_request);
int iwpm_get_nlmsg_seq(void);
/**
+ * iwpm_add_reminfo - Add remote address info of the connecting peer
+ * to the remote info hash table
+ * @reminfo: The remote info to be added
+ */
+void iwpm_add_remote_info(struct iwpm_remote_info *reminfo);
+
+/**
* iwpm_valid_client - Check if the port mapper client is valid
* @nl_client: The index of the netlink client
*
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 74c30f4c557e..a4b1466c1bf6 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -3,6 +3,7 @@
* Copyright (c) 2005 Intel Corporation. All rights reserved.
* Copyright (c) 2005 Mellanox Technologies Ltd. All rights reserved.
* Copyright (c) 2009 HNR Consulting. All rights reserved.
+ * Copyright (c) 2014 Intel Corporation. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -44,6 +45,7 @@
#include "mad_priv.h"
#include "mad_rmpp.h"
#include "smi.h"
+#include "opa_smi.h"
#include "agent.h"
MODULE_LICENSE("Dual BSD/GPL");
@@ -59,8 +61,6 @@ MODULE_PARM_DESC(send_queue_size, "Size of send queue in number of work requests
module_param_named(recv_queue_size, mad_recvq_size, int, 0444);
MODULE_PARM_DESC(recv_queue_size, "Size of receive queue in number of work requests");
-static struct kmem_cache *ib_mad_cache;
-
static struct list_head ib_mad_port_list;
static u32 ib_mad_client_id = 0;
@@ -73,7 +73,7 @@ static int method_in_use(struct ib_mad_mgmt_method_table **method,
static void remove_mad_reg_req(struct ib_mad_agent_private *priv);
static struct ib_mad_agent_private *find_mad_agent(
struct ib_mad_port_private *port_priv,
- struct ib_mad *mad);
+ const struct ib_mad_hdr *mad);
static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
struct ib_mad_private *mad);
static void cancel_mads(struct ib_mad_agent_private *mad_agent_priv);
@@ -179,12 +179,12 @@ static int is_vendor_method_in_use(
return 0;
}
-int ib_response_mad(struct ib_mad *mad)
+int ib_response_mad(const struct ib_mad_hdr *hdr)
{
- return ((mad->mad_hdr.method & IB_MGMT_METHOD_RESP) ||
- (mad->mad_hdr.method == IB_MGMT_METHOD_TRAP_REPRESS) ||
- ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_BM) &&
- (mad->mad_hdr.attr_mod & IB_BM_ATTR_MOD_RESP)));
+ return ((hdr->method & IB_MGMT_METHOD_RESP) ||
+ (hdr->method == IB_MGMT_METHOD_TRAP_REPRESS) ||
+ ((hdr->mgmt_class == IB_MGMT_CLASS_BM) &&
+ (hdr->attr_mod & IB_BM_ATTR_MOD_RESP)));
}
EXPORT_SYMBOL(ib_response_mad);
@@ -717,6 +717,32 @@ static void build_smp_wc(struct ib_qp *qp,
wc->port_num = port_num;
}
+static size_t mad_priv_size(const struct ib_mad_private *mp)
+{
+ return sizeof(struct ib_mad_private) + mp->mad_size;
+}
+
+static struct ib_mad_private *alloc_mad_private(size_t mad_size, gfp_t flags)
+{
+ size_t size = sizeof(struct ib_mad_private) + mad_size;
+ struct ib_mad_private *ret = kzalloc(size, flags);
+
+ if (ret)
+ ret->mad_size = mad_size;
+
+ return ret;
+}
+
+static size_t port_mad_size(const struct ib_mad_port_private *port_priv)
+{
+ return rdma_max_mad_size(port_priv->device, port_priv->port_num);
+}
+
+static size_t mad_priv_dma_size(const struct ib_mad_private *mp)
+{
+ return sizeof(struct ib_grh) + mp->mad_size;
+}
+
/*
* Return 0 if SMP is to be sent
* Return 1 if SMP was consumed locally (whether or not solicited)
@@ -727,6 +753,7 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
{
int ret = 0;
struct ib_smp *smp = mad_send_wr->send_buf.mad;
+ struct opa_smp *opa_smp = (struct opa_smp *)smp;
unsigned long flags;
struct ib_mad_local_private *local;
struct ib_mad_private *mad_priv;
@@ -736,6 +763,11 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
u8 port_num;
struct ib_wc mad_wc;
struct ib_send_wr *send_wr = &mad_send_wr->send_wr;
+ size_t mad_size = port_mad_size(mad_agent_priv->qp_info->port_priv);
+ u16 out_mad_pkey_index = 0;
+ u16 drslid;
+ bool opa = rdma_cap_opa_mad(mad_agent_priv->qp_info->port_priv->device,
+ mad_agent_priv->qp_info->port_priv->port_num);
if (device->node_type == RDMA_NODE_IB_SWITCH &&
smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
@@ -749,19 +781,48 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
* If we are at the start of the LID routed part, don't update the
* hop_ptr or hop_cnt. See section 14.2.2, Vol 1 IB spec.
*/
- if ((ib_get_smp_direction(smp) ? smp->dr_dlid : smp->dr_slid) ==
- IB_LID_PERMISSIVE &&
- smi_handle_dr_smp_send(smp, device->node_type, port_num) ==
- IB_SMI_DISCARD) {
- ret = -EINVAL;
- dev_err(&device->dev, "Invalid directed route\n");
- goto out;
- }
+ if (opa && smp->class_version == OPA_SMP_CLASS_VERSION) {
+ u32 opa_drslid;
+
+ if ((opa_get_smp_direction(opa_smp)
+ ? opa_smp->route.dr.dr_dlid : opa_smp->route.dr.dr_slid) ==
+ OPA_LID_PERMISSIVE &&
+ opa_smi_handle_dr_smp_send(opa_smp, device->node_type,
+ port_num) == IB_SMI_DISCARD) {
+ ret = -EINVAL;
+ dev_err(&device->dev, "OPA Invalid directed route\n");
+ goto out;
+ }
+ opa_drslid = be32_to_cpu(opa_smp->route.dr.dr_slid);
+ if (opa_drslid != OPA_LID_PERMISSIVE &&
+ opa_drslid & 0xffff0000) {
+ ret = -EINVAL;
+ dev_err(&device->dev, "OPA Invalid dr_slid 0x%x\n",
+ opa_drslid);
+ goto out;
+ }
+ drslid = (u16)(opa_drslid & 0x0000ffff);
- /* Check to post send on QP or process locally */
- if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD &&
- smi_check_local_returning_smp(smp, device) == IB_SMI_DISCARD)
- goto out;
+ /* Check to post send on QP or process locally */
+ if (opa_smi_check_local_smp(opa_smp, device) == IB_SMI_DISCARD &&
+ opa_smi_check_local_returning_smp(opa_smp, device) == IB_SMI_DISCARD)
+ goto out;
+ } else {
+ if ((ib_get_smp_direction(smp) ? smp->dr_dlid : smp->dr_slid) ==
+ IB_LID_PERMISSIVE &&
+ smi_handle_dr_smp_send(smp, device->node_type, port_num) ==
+ IB_SMI_DISCARD) {
+ ret = -EINVAL;
+ dev_err(&device->dev, "Invalid directed route\n");
+ goto out;
+ }
+ drslid = be16_to_cpu(smp->dr_slid);
+
+ /* Check to post send on QP or process locally */
+ if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD &&
+ smi_check_local_returning_smp(smp, device) == IB_SMI_DISCARD)
+ goto out;
+ }
local = kmalloc(sizeof *local, GFP_ATOMIC);
if (!local) {
@@ -771,7 +832,7 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
}
local->mad_priv = NULL;
local->recv_mad_agent = NULL;
- mad_priv = kmem_cache_alloc(ib_mad_cache, GFP_ATOMIC);
+ mad_priv = alloc_mad_private(mad_size, GFP_ATOMIC);
if (!mad_priv) {
ret = -ENOMEM;
dev_err(&device->dev, "No memory for local response MAD\n");
@@ -780,18 +841,25 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
}
build_smp_wc(mad_agent_priv->agent.qp,
- send_wr->wr_id, be16_to_cpu(smp->dr_slid),
+ send_wr->wr_id, drslid,
send_wr->wr.ud.pkey_index,
send_wr->wr.ud.port_num, &mad_wc);
+ if (opa && smp->base_version == OPA_MGMT_BASE_VERSION) {
+ mad_wc.byte_len = mad_send_wr->send_buf.hdr_len
+ + mad_send_wr->send_buf.data_len
+ + sizeof(struct ib_grh);
+ }
+
/* No GRH for DR SMP */
ret = device->process_mad(device, 0, port_num, &mad_wc, NULL,
- (struct ib_mad *)smp,
- (struct ib_mad *)&mad_priv->mad);
+ (const struct ib_mad_hdr *)smp, mad_size,
+ (struct ib_mad_hdr *)mad_priv->mad,
+ &mad_size, &out_mad_pkey_index);
switch (ret)
{
case IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY:
- if (ib_response_mad(&mad_priv->mad.mad) &&
+ if (ib_response_mad((const struct ib_mad_hdr *)mad_priv->mad) &&
mad_agent_priv->agent.recv_handler) {
local->mad_priv = mad_priv;
local->recv_mad_agent = mad_agent_priv;
@@ -801,39 +869,43 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
*/
atomic_inc(&mad_agent_priv->refcount);
} else
- kmem_cache_free(ib_mad_cache, mad_priv);
+ kfree(mad_priv);
break;
case IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED:
- kmem_cache_free(ib_mad_cache, mad_priv);
+ kfree(mad_priv);
break;
case IB_MAD_RESULT_SUCCESS:
/* Treat like an incoming receive MAD */
port_priv = ib_get_mad_port(mad_agent_priv->agent.device,
mad_agent_priv->agent.port_num);
if (port_priv) {
- memcpy(&mad_priv->mad.mad, smp, sizeof(struct ib_mad));
+ memcpy(mad_priv->mad, smp, mad_priv->mad_size);
recv_mad_agent = find_mad_agent(port_priv,
- &mad_priv->mad.mad);
+ (const struct ib_mad_hdr *)mad_priv->mad);
}
if (!port_priv || !recv_mad_agent) {
/*
* No receiving agent so drop packet and
* generate send completion.
*/
- kmem_cache_free(ib_mad_cache, mad_priv);
+ kfree(mad_priv);
break;
}
local->mad_priv = mad_priv;
local->recv_mad_agent = recv_mad_agent;
break;
default:
- kmem_cache_free(ib_mad_cache, mad_priv);
+ kfree(mad_priv);
kfree(local);
ret = -EINVAL;
goto out;
}
local->mad_send_wr = mad_send_wr;
+ if (opa) {
+ local->mad_send_wr->send_wr.wr.ud.pkey_index = out_mad_pkey_index;
+ local->return_wc_byte_len = mad_size;
+ }
/* Reference MAD agent until send side of local completion handled */
atomic_inc(&mad_agent_priv->refcount);
/* Queue local completion to local list */
@@ -847,11 +919,11 @@ out:
return ret;
}
-static int get_pad_size(int hdr_len, int data_len)
+static int get_pad_size(int hdr_len, int data_len, size_t mad_size)
{
int seg_size, pad;
- seg_size = sizeof(struct ib_mad) - hdr_len;
+ seg_size = mad_size - hdr_len;
if (data_len && seg_size) {
pad = seg_size - data_len % seg_size;
return pad == seg_size ? 0 : pad;
@@ -870,14 +942,15 @@ static void free_send_rmpp_list(struct ib_mad_send_wr_private *mad_send_wr)
}
static int alloc_send_rmpp_list(struct ib_mad_send_wr_private *send_wr,
- gfp_t gfp_mask)
+ size_t mad_size, gfp_t gfp_mask)
{
struct ib_mad_send_buf *send_buf = &send_wr->send_buf;
struct ib_rmpp_mad *rmpp_mad = send_buf->mad;
struct ib_rmpp_segment *seg = NULL;
int left, seg_size, pad;
- send_buf->seg_size = sizeof (struct ib_mad) - send_buf->hdr_len;
+ send_buf->seg_size = mad_size - send_buf->hdr_len;
+ send_buf->seg_rmpp_size = mad_size - IB_MGMT_RMPP_HDR;
seg_size = send_buf->seg_size;
pad = send_wr->pad;
@@ -910,7 +983,7 @@ static int alloc_send_rmpp_list(struct ib_mad_send_wr_private *send_wr,
return 0;
}
-int ib_mad_kernel_rmpp_agent(struct ib_mad_agent *agent)
+int ib_mad_kernel_rmpp_agent(const struct ib_mad_agent *agent)
{
return agent->rmpp_version && !(agent->flags & IB_MAD_USER_RMPP);
}
@@ -920,26 +993,37 @@ struct ib_mad_send_buf * ib_create_send_mad(struct ib_mad_agent *mad_agent,
u32 remote_qpn, u16 pkey_index,
int rmpp_active,
int hdr_len, int data_len,
- gfp_t gfp_mask)
+ gfp_t gfp_mask,
+ u8 base_version)
{
struct ib_mad_agent_private *mad_agent_priv;
struct ib_mad_send_wr_private *mad_send_wr;
int pad, message_size, ret, size;
void *buf;
+ size_t mad_size;
+ bool opa;
mad_agent_priv = container_of(mad_agent, struct ib_mad_agent_private,
agent);
- pad = get_pad_size(hdr_len, data_len);
+
+ opa = rdma_cap_opa_mad(mad_agent->device, mad_agent->port_num);
+
+ if (opa && base_version == OPA_MGMT_BASE_VERSION)
+ mad_size = sizeof(struct opa_mad);
+ else
+ mad_size = sizeof(struct ib_mad);
+
+ pad = get_pad_size(hdr_len, data_len, mad_size);
message_size = hdr_len + data_len + pad;
if (ib_mad_kernel_rmpp_agent(mad_agent)) {
- if (!rmpp_active && message_size > sizeof(struct ib_mad))
+ if (!rmpp_active && message_size > mad_size)
return ERR_PTR(-EINVAL);
} else
- if (rmpp_active || message_size > sizeof(struct ib_mad))
+ if (rmpp_active || message_size > mad_size)
return ERR_PTR(-EINVAL);
- size = rmpp_active ? hdr_len : sizeof(struct ib_mad);
+ size = rmpp_active ? hdr_len : mad_size;
buf = kzalloc(sizeof *mad_send_wr + size, gfp_mask);
if (!buf)
return ERR_PTR(-ENOMEM);
@@ -954,7 +1038,14 @@ struct ib_mad_send_buf * ib_create_send_mad(struct ib_mad_agent *mad_agent,
mad_send_wr->mad_agent_priv = mad_agent_priv;
mad_send_wr->sg_list[0].length = hdr_len;
mad_send_wr->sg_list[0].lkey = mad_agent->mr->lkey;
- mad_send_wr->sg_list[1].length = sizeof(struct ib_mad) - hdr_len;
+
+ /* OPA MADs don't have to be the full 2048 bytes */
+ if (opa && base_version == OPA_MGMT_BASE_VERSION &&
+ data_len < mad_size - hdr_len)
+ mad_send_wr->sg_list[1].length = data_len;
+ else
+ mad_send_wr->sg_list[1].length = mad_size - hdr_len;
+
mad_send_wr->sg_list[1].lkey = mad_agent->mr->lkey;
mad_send_wr->send_wr.wr_id = (unsigned long) mad_send_wr;
@@ -967,7 +1058,7 @@ struct ib_mad_send_buf * ib_create_send_mad(struct ib_mad_agent *mad_agent,
mad_send_wr->send_wr.wr.ud.pkey_index = pkey_index;
if (rmpp_active) {
- ret = alloc_send_rmpp_list(mad_send_wr, gfp_mask);
+ ret = alloc_send_rmpp_list(mad_send_wr, mad_size, gfp_mask);
if (ret) {
kfree(buf);
return ERR_PTR(ret);
@@ -1237,7 +1328,7 @@ void ib_free_recv_mad(struct ib_mad_recv_wc *mad_recv_wc)
recv_wc);
priv = container_of(mad_priv_hdr, struct ib_mad_private,
header);
- kmem_cache_free(ib_mad_cache, priv);
+ kfree(priv);
}
}
EXPORT_SYMBOL(ib_free_recv_mad);
@@ -1324,7 +1415,7 @@ static int check_vendor_class(struct ib_mad_mgmt_vendor_class *vendor_class)
}
static int find_vendor_oui(struct ib_mad_mgmt_vendor_class *vendor_class,
- char *oui)
+ const char *oui)
{
int i;
@@ -1622,13 +1713,13 @@ out:
static struct ib_mad_agent_private *
find_mad_agent(struct ib_mad_port_private *port_priv,
- struct ib_mad *mad)
+ const struct ib_mad_hdr *mad_hdr)
{
struct ib_mad_agent_private *mad_agent = NULL;
unsigned long flags;
spin_lock_irqsave(&port_priv->reg_lock, flags);
- if (ib_response_mad(mad)) {
+ if (ib_response_mad(mad_hdr)) {
u32 hi_tid;
struct ib_mad_agent_private *entry;
@@ -1636,7 +1727,7 @@ find_mad_agent(struct ib_mad_port_private *port_priv,
* Routing is based on high 32 bits of transaction ID
* of MAD.
*/
- hi_tid = be64_to_cpu(mad->mad_hdr.tid) >> 32;
+ hi_tid = be64_to_cpu(mad_hdr->tid) >> 32;
list_for_each_entry(entry, &port_priv->agent_list, agent_list) {
if (entry->agent.hi_tid == hi_tid) {
mad_agent = entry;
@@ -1648,45 +1739,45 @@ find_mad_agent(struct ib_mad_port_private *port_priv,
struct ib_mad_mgmt_method_table *method;
struct ib_mad_mgmt_vendor_class_table *vendor;
struct ib_mad_mgmt_vendor_class *vendor_class;
- struct ib_vendor_mad *vendor_mad;
+ const struct ib_vendor_mad *vendor_mad;
int index;
/*
* Routing is based on version, class, and method
* For "newer" vendor MADs, also based on OUI
*/
- if (mad->mad_hdr.class_version >= MAX_MGMT_VERSION)
+ if (mad_hdr->class_version >= MAX_MGMT_VERSION)
goto out;
- if (!is_vendor_class(mad->mad_hdr.mgmt_class)) {
+ if (!is_vendor_class(mad_hdr->mgmt_class)) {
class = port_priv->version[
- mad->mad_hdr.class_version].class;
+ mad_hdr->class_version].class;
if (!class)
goto out;
- if (convert_mgmt_class(mad->mad_hdr.mgmt_class) >=
+ if (convert_mgmt_class(mad_hdr->mgmt_class) >=
IB_MGMT_MAX_METHODS)
goto out;
method = class->method_table[convert_mgmt_class(
- mad->mad_hdr.mgmt_class)];
+ mad_hdr->mgmt_class)];
if (method)
- mad_agent = method->agent[mad->mad_hdr.method &
+ mad_agent = method->agent[mad_hdr->method &
~IB_MGMT_METHOD_RESP];
} else {
vendor = port_priv->version[
- mad->mad_hdr.class_version].vendor;
+ mad_hdr->class_version].vendor;
if (!vendor)
goto out;
vendor_class = vendor->vendor_class[vendor_class_index(
- mad->mad_hdr.mgmt_class)];
+ mad_hdr->mgmt_class)];
if (!vendor_class)
goto out;
/* Find matching OUI */
- vendor_mad = (struct ib_vendor_mad *)mad;
+ vendor_mad = (const struct ib_vendor_mad *)mad_hdr;
index = find_vendor_oui(vendor_class, vendor_mad->oui);
if (index == -1)
goto out;
method = vendor_class->method_table[index];
if (method) {
- mad_agent = method->agent[mad->mad_hdr.method &
+ mad_agent = method->agent[mad_hdr->method &
~IB_MGMT_METHOD_RESP];
}
}
@@ -1708,20 +1799,24 @@ out:
return mad_agent;
}
-static int validate_mad(struct ib_mad *mad, u32 qp_num)
+static int validate_mad(const struct ib_mad_hdr *mad_hdr,
+ const struct ib_mad_qp_info *qp_info,
+ bool opa)
{
int valid = 0;
+ u32 qp_num = qp_info->qp->qp_num;
/* Make sure MAD base version is understood */
- if (mad->mad_hdr.base_version != IB_MGMT_BASE_VERSION) {
- pr_err("MAD received with unsupported base version %d\n",
- mad->mad_hdr.base_version);
+ if (mad_hdr->base_version != IB_MGMT_BASE_VERSION &&
+ (!opa || mad_hdr->base_version != OPA_MGMT_BASE_VERSION)) {
+ pr_err("MAD received with unsupported base version %d %s\n",
+ mad_hdr->base_version, opa ? "(opa)" : "");
goto out;
}
/* Filter SMI packets sent to other than QP0 */
- if ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED) ||
- (mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)) {
+ if ((mad_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED) ||
+ (mad_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)) {
if (qp_num == 0)
valid = 1;
} else {
@@ -1734,8 +1829,8 @@ out:
return valid;
}
-static int is_data_mad(struct ib_mad_agent_private *mad_agent_priv,
- struct ib_mad_hdr *mad_hdr)
+static int is_rmpp_data_mad(const struct ib_mad_agent_private *mad_agent_priv,
+ const struct ib_mad_hdr *mad_hdr)
{
struct ib_rmpp_mad *rmpp_mad;
@@ -1747,16 +1842,16 @@ static int is_data_mad(struct ib_mad_agent_private *mad_agent_priv,
(rmpp_mad->rmpp_hdr.rmpp_type == IB_MGMT_RMPP_TYPE_DATA);
}
-static inline int rcv_has_same_class(struct ib_mad_send_wr_private *wr,
- struct ib_mad_recv_wc *rwc)
+static inline int rcv_has_same_class(const struct ib_mad_send_wr_private *wr,
+ const struct ib_mad_recv_wc *rwc)
{
- return ((struct ib_mad *)(wr->send_buf.mad))->mad_hdr.mgmt_class ==
+ return ((struct ib_mad_hdr *)(wr->send_buf.mad))->mgmt_class ==
rwc->recv_buf.mad->mad_hdr.mgmt_class;
}
-static inline int rcv_has_same_gid(struct ib_mad_agent_private *mad_agent_priv,
- struct ib_mad_send_wr_private *wr,
- struct ib_mad_recv_wc *rwc )
+static inline int rcv_has_same_gid(const struct ib_mad_agent_private *mad_agent_priv,
+ const struct ib_mad_send_wr_private *wr,
+ const struct ib_mad_recv_wc *rwc )
{
struct ib_ah_attr attr;
u8 send_resp, rcv_resp;
@@ -1765,8 +1860,8 @@ static inline int rcv_has_same_gid(struct ib_mad_agent_private *mad_agent_priv,
u8 port_num = mad_agent_priv->agent.port_num;
u8 lmc;
- send_resp = ib_response_mad((struct ib_mad *)wr->send_buf.mad);
- rcv_resp = ib_response_mad(rwc->recv_buf.mad);
+ send_resp = ib_response_mad((struct ib_mad_hdr *)wr->send_buf.mad);
+ rcv_resp = ib_response_mad(&rwc->recv_buf.mad->mad_hdr);
if (send_resp == rcv_resp)
/* both requests, or both responses. GIDs different */
@@ -1811,22 +1906,22 @@ static inline int is_direct(u8 class)
}
struct ib_mad_send_wr_private*
-ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
- struct ib_mad_recv_wc *wc)
+ib_find_send_mad(const struct ib_mad_agent_private *mad_agent_priv,
+ const struct ib_mad_recv_wc *wc)
{
struct ib_mad_send_wr_private *wr;
- struct ib_mad *mad;
+ const struct ib_mad_hdr *mad_hdr;
- mad = (struct ib_mad *)wc->recv_buf.mad;
+ mad_hdr = &wc->recv_buf.mad->mad_hdr;
list_for_each_entry(wr, &mad_agent_priv->wait_list, agent_list) {
- if ((wr->tid == mad->mad_hdr.tid) &&
+ if ((wr->tid == mad_hdr->tid) &&
rcv_has_same_class(wr, wc) &&
/*
* Don't check GID for direct routed MADs.
* These might have permissive LIDs.
*/
- (is_direct(wc->recv_buf.mad->mad_hdr.mgmt_class) ||
+ (is_direct(mad_hdr->mgmt_class) ||
rcv_has_same_gid(mad_agent_priv, wr, wc)))
return (wr->status == IB_WC_SUCCESS) ? wr : NULL;
}
@@ -1836,15 +1931,15 @@ ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
* been notified that the send has completed
*/
list_for_each_entry(wr, &mad_agent_priv->send_list, agent_list) {
- if (is_data_mad(mad_agent_priv, wr->send_buf.mad) &&
- wr->tid == mad->mad_hdr.tid &&
+ if (is_rmpp_data_mad(mad_agent_priv, wr->send_buf.mad) &&
+ wr->tid == mad_hdr->tid &&
wr->timeout &&
rcv_has_same_class(wr, wc) &&
/*
* Don't check GID for direct routed MADs.
* These might have permissive LIDs.
*/
- (is_direct(wc->recv_buf.mad->mad_hdr.mgmt_class) ||
+ (is_direct(mad_hdr->mgmt_class) ||
rcv_has_same_gid(mad_agent_priv, wr, wc)))
/* Verify request has not been canceled */
return (wr->status == IB_WC_SUCCESS) ? wr : NULL;
@@ -1879,7 +1974,7 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
}
/* Complete corresponding request */
- if (ib_response_mad(mad_recv_wc->recv_buf.mad)) {
+ if (ib_response_mad(&mad_recv_wc->recv_buf.mad->mad_hdr)) {
spin_lock_irqsave(&mad_agent_priv->lock, flags);
mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc);
if (!mad_send_wr) {
@@ -1924,26 +2019,163 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
}
}
-static bool generate_unmatched_resp(struct ib_mad_private *recv,
- struct ib_mad_private *response)
+static enum smi_action handle_ib_smi(const struct ib_mad_port_private *port_priv,
+ const struct ib_mad_qp_info *qp_info,
+ const struct ib_wc *wc,
+ int port_num,
+ struct ib_mad_private *recv,
+ struct ib_mad_private *response)
+{
+ enum smi_forward_action retsmi;
+ struct ib_smp *smp = (struct ib_smp *)recv->mad;
+
+ if (smi_handle_dr_smp_recv(smp,
+ port_priv->device->node_type,
+ port_num,
+ port_priv->device->phys_port_cnt) ==
+ IB_SMI_DISCARD)
+ return IB_SMI_DISCARD;
+
+ retsmi = smi_check_forward_dr_smp(smp);
+ if (retsmi == IB_SMI_LOCAL)
+ return IB_SMI_HANDLE;
+
+ if (retsmi == IB_SMI_SEND) { /* don't forward */
+ if (smi_handle_dr_smp_send(smp,
+ port_priv->device->node_type,
+ port_num) == IB_SMI_DISCARD)
+ return IB_SMI_DISCARD;
+
+ if (smi_check_local_smp(smp, port_priv->device) == IB_SMI_DISCARD)
+ return IB_SMI_DISCARD;
+ } else if (port_priv->device->node_type == RDMA_NODE_IB_SWITCH) {
+ /* forward case for switches */
+ memcpy(response, recv, mad_priv_size(response));
+ response->header.recv_wc.wc = &response->header.wc;
+ response->header.recv_wc.recv_buf.mad = (struct ib_mad *)response->mad;
+ response->header.recv_wc.recv_buf.grh = &response->grh;
+
+ agent_send_response((const struct ib_mad_hdr *)response->mad,
+ &response->grh, wc,
+ port_priv->device,
+ smi_get_fwd_port(smp),
+ qp_info->qp->qp_num,
+ response->mad_size,
+ false);
+
+ return IB_SMI_DISCARD;
+ }
+ return IB_SMI_HANDLE;
+}
+
+static bool generate_unmatched_resp(const struct ib_mad_private *recv,
+ struct ib_mad_private *response,
+ size_t *resp_len, bool opa)
{
- if (recv->mad.mad.mad_hdr.method == IB_MGMT_METHOD_GET ||
- recv->mad.mad.mad_hdr.method == IB_MGMT_METHOD_SET) {
- memcpy(response, recv, sizeof *response);
+ const struct ib_mad_hdr *recv_hdr = (const struct ib_mad_hdr *)recv->mad;
+ struct ib_mad_hdr *resp_hdr = (struct ib_mad_hdr *)response->mad;
+
+ if (recv_hdr->method == IB_MGMT_METHOD_GET ||
+ recv_hdr->method == IB_MGMT_METHOD_SET) {
+ memcpy(response, recv, mad_priv_size(response));
response->header.recv_wc.wc = &response->header.wc;
- response->header.recv_wc.recv_buf.mad = &response->mad.mad;
+ response->header.recv_wc.recv_buf.mad = (struct ib_mad *)response->mad;
response->header.recv_wc.recv_buf.grh = &response->grh;
- response->mad.mad.mad_hdr.method = IB_MGMT_METHOD_GET_RESP;
- response->mad.mad.mad_hdr.status =
- cpu_to_be16(IB_MGMT_MAD_STATUS_UNSUPPORTED_METHOD_ATTRIB);
- if (recv->mad.mad.mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
- response->mad.mad.mad_hdr.status |= IB_SMP_DIRECTION;
+ resp_hdr->method = IB_MGMT_METHOD_GET_RESP;
+ resp_hdr->status = cpu_to_be16(IB_MGMT_MAD_STATUS_UNSUPPORTED_METHOD_ATTRIB);
+ if (recv_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+ resp_hdr->status |= IB_SMP_DIRECTION;
+
+ if (opa && recv_hdr->base_version == OPA_MGMT_BASE_VERSION) {
+ if (recv_hdr->mgmt_class ==
+ IB_MGMT_CLASS_SUBN_LID_ROUTED ||
+ recv_hdr->mgmt_class ==
+ IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+ *resp_len = opa_get_smp_header_size(
+ (struct opa_smp *)recv->mad);
+ else
+ *resp_len = sizeof(struct ib_mad_hdr);
+ }
return true;
} else {
return false;
}
}
+
+static enum smi_action
+handle_opa_smi(struct ib_mad_port_private *port_priv,
+ struct ib_mad_qp_info *qp_info,
+ struct ib_wc *wc,
+ int port_num,
+ struct ib_mad_private *recv,
+ struct ib_mad_private *response)
+{
+ enum smi_forward_action retsmi;
+ struct opa_smp *smp = (struct opa_smp *)recv->mad;
+
+ if (opa_smi_handle_dr_smp_recv(smp,
+ port_priv->device->node_type,
+ port_num,
+ port_priv->device->phys_port_cnt) ==
+ IB_SMI_DISCARD)
+ return IB_SMI_DISCARD;
+
+ retsmi = opa_smi_check_forward_dr_smp(smp);
+ if (retsmi == IB_SMI_LOCAL)
+ return IB_SMI_HANDLE;
+
+ if (retsmi == IB_SMI_SEND) { /* don't forward */
+ if (opa_smi_handle_dr_smp_send(smp,
+ port_priv->device->node_type,
+ port_num) == IB_SMI_DISCARD)
+ return IB_SMI_DISCARD;
+
+ if (opa_smi_check_local_smp(smp, port_priv->device) ==
+ IB_SMI_DISCARD)
+ return IB_SMI_DISCARD;
+
+ } else if (port_priv->device->node_type == RDMA_NODE_IB_SWITCH) {
+ /* forward case for switches */
+ memcpy(response, recv, mad_priv_size(response));
+ response->header.recv_wc.wc = &response->header.wc;
+ response->header.recv_wc.recv_buf.opa_mad =
+ (struct opa_mad *)response->mad;
+ response->header.recv_wc.recv_buf.grh = &response->grh;
+
+ agent_send_response((const struct ib_mad_hdr *)response->mad,
+ &response->grh, wc,
+ port_priv->device,
+ opa_smi_get_fwd_port(smp),
+ qp_info->qp->qp_num,
+ recv->header.wc.byte_len,
+ true);
+
+ return IB_SMI_DISCARD;
+ }
+
+ return IB_SMI_HANDLE;
+}
+
+static enum smi_action
+handle_smi(struct ib_mad_port_private *port_priv,
+ struct ib_mad_qp_info *qp_info,
+ struct ib_wc *wc,
+ int port_num,
+ struct ib_mad_private *recv,
+ struct ib_mad_private *response,
+ bool opa)
+{
+ struct ib_mad_hdr *mad_hdr = (struct ib_mad_hdr *)recv->mad;
+
+ if (opa && mad_hdr->base_version == OPA_MGMT_BASE_VERSION &&
+ mad_hdr->class_version == OPA_SMI_CLASS_VERSION)
+ return handle_opa_smi(port_priv, qp_info, wc, port_num, recv,
+ response);
+
+ return handle_ib_smi(port_priv, qp_info, wc, port_num, recv, response);
+}
+
static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
struct ib_wc *wc)
{
@@ -1954,35 +2186,49 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
struct ib_mad_agent_private *mad_agent;
int port_num;
int ret = IB_MAD_RESULT_SUCCESS;
+ size_t mad_size;
+ u16 resp_mad_pkey_index = 0;
+ bool opa;
mad_list = (struct ib_mad_list_head *)(unsigned long)wc->wr_id;
qp_info = mad_list->mad_queue->qp_info;
dequeue_mad(mad_list);
+ opa = rdma_cap_opa_mad(qp_info->port_priv->device,
+ qp_info->port_priv->port_num);
+
mad_priv_hdr = container_of(mad_list, struct ib_mad_private_header,
mad_list);
recv = container_of(mad_priv_hdr, struct ib_mad_private, header);
ib_dma_unmap_single(port_priv->device,
recv->header.mapping,
- sizeof(struct ib_mad_private) -
- sizeof(struct ib_mad_private_header),
+ mad_priv_dma_size(recv),
DMA_FROM_DEVICE);
/* Setup MAD receive work completion from "normal" work completion */
recv->header.wc = *wc;
recv->header.recv_wc.wc = &recv->header.wc;
- recv->header.recv_wc.mad_len = sizeof(struct ib_mad);
- recv->header.recv_wc.recv_buf.mad = &recv->mad.mad;
+
+ if (opa && ((struct ib_mad_hdr *)(recv->mad))->base_version == OPA_MGMT_BASE_VERSION) {
+ recv->header.recv_wc.mad_len = wc->byte_len - sizeof(struct ib_grh);
+ recv->header.recv_wc.mad_seg_size = sizeof(struct opa_mad);
+ } else {
+ recv->header.recv_wc.mad_len = sizeof(struct ib_mad);
+ recv->header.recv_wc.mad_seg_size = sizeof(struct ib_mad);
+ }
+
+ recv->header.recv_wc.recv_buf.mad = (struct ib_mad *)recv->mad;
recv->header.recv_wc.recv_buf.grh = &recv->grh;
if (atomic_read(&qp_info->snoop_count))
snoop_recv(qp_info, &recv->header.recv_wc, IB_MAD_SNOOP_RECVS);
/* Validate MAD */
- if (!validate_mad(&recv->mad.mad, qp_info->qp->qp_num))
+ if (!validate_mad((const struct ib_mad_hdr *)recv->mad, qp_info, opa))
goto out;
- response = kmem_cache_alloc(ib_mad_cache, GFP_KERNEL);
+ mad_size = recv->mad_size;
+ response = alloc_mad_private(mad_size, GFP_KERNEL);
if (!response) {
dev_err(&port_priv->device->dev,
"ib_mad_recv_done_handler no memory for response buffer\n");
@@ -1994,69 +2240,43 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
else
port_num = port_priv->port_num;
- if (recv->mad.mad.mad_hdr.mgmt_class ==
+ if (((struct ib_mad_hdr *)recv->mad)->mgmt_class ==
IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
- enum smi_forward_action retsmi;
-
- if (smi_handle_dr_smp_recv(&recv->mad.smp,
- port_priv->device->node_type,
- port_num,
- port_priv->device->phys_port_cnt) ==
- IB_SMI_DISCARD)
+ if (handle_smi(port_priv, qp_info, wc, port_num, recv,
+ response, opa)
+ == IB_SMI_DISCARD)
goto out;
-
- retsmi = smi_check_forward_dr_smp(&recv->mad.smp);
- if (retsmi == IB_SMI_LOCAL)
- goto local;
-
- if (retsmi == IB_SMI_SEND) { /* don't forward */
- if (smi_handle_dr_smp_send(&recv->mad.smp,
- port_priv->device->node_type,
- port_num) == IB_SMI_DISCARD)
- goto out;
-
- if (smi_check_local_smp(&recv->mad.smp, port_priv->device) == IB_SMI_DISCARD)
- goto out;
- } else if (port_priv->device->node_type == RDMA_NODE_IB_SWITCH) {
- /* forward case for switches */
- memcpy(response, recv, sizeof(*response));
- response->header.recv_wc.wc = &response->header.wc;
- response->header.recv_wc.recv_buf.mad = &response->mad.mad;
- response->header.recv_wc.recv_buf.grh = &response->grh;
-
- agent_send_response(&response->mad.mad,
- &response->grh, wc,
- port_priv->device,
- smi_get_fwd_port(&recv->mad.smp),
- qp_info->qp->qp_num);
-
- goto out;
- }
}
-local:
/* Give driver "right of first refusal" on incoming MAD */
if (port_priv->device->process_mad) {
ret = port_priv->device->process_mad(port_priv->device, 0,
port_priv->port_num,
wc, &recv->grh,
- &recv->mad.mad,
- &response->mad.mad);
+ (const struct ib_mad_hdr *)recv->mad,
+ recv->mad_size,
+ (struct ib_mad_hdr *)response->mad,
+ &mad_size, &resp_mad_pkey_index);
+
+ if (opa)
+ wc->pkey_index = resp_mad_pkey_index;
+
if (ret & IB_MAD_RESULT_SUCCESS) {
if (ret & IB_MAD_RESULT_CONSUMED)
goto out;
if (ret & IB_MAD_RESULT_REPLY) {
- agent_send_response(&response->mad.mad,
+ agent_send_response((const struct ib_mad_hdr *)response->mad,
&recv->grh, wc,
port_priv->device,
port_num,
- qp_info->qp->qp_num);
+ qp_info->qp->qp_num,
+ mad_size, opa);
goto out;
}
}
}
- mad_agent = find_mad_agent(port_priv, &recv->mad.mad);
+ mad_agent = find_mad_agent(port_priv, (const struct ib_mad_hdr *)recv->mad);
if (mad_agent) {
ib_mad_complete_recv(mad_agent, &recv->header.recv_wc);
/*
@@ -2065,17 +2285,17 @@ local:
*/
recv = NULL;
} else if ((ret & IB_MAD_RESULT_SUCCESS) &&
- generate_unmatched_resp(recv, response)) {
- agent_send_response(&response->mad.mad, &recv->grh, wc,
- port_priv->device, port_num, qp_info->qp->qp_num);
+ generate_unmatched_resp(recv, response, &mad_size, opa)) {
+ agent_send_response((const struct ib_mad_hdr *)response->mad, &recv->grh, wc,
+ port_priv->device, port_num,
+ qp_info->qp->qp_num, mad_size, opa);
}
out:
/* Post another receive request for this QP */
if (response) {
ib_mad_post_receive_mads(qp_info, response);
- if (recv)
- kmem_cache_free(ib_mad_cache, recv);
+ kfree(recv);
} else
ib_mad_post_receive_mads(qp_info, recv);
}
@@ -2411,7 +2631,8 @@ find_send_wr(struct ib_mad_agent_private *mad_agent_priv,
list_for_each_entry(mad_send_wr, &mad_agent_priv->send_list,
agent_list) {
- if (is_data_mad(mad_agent_priv, mad_send_wr->send_buf.mad) &&
+ if (is_rmpp_data_mad(mad_agent_priv,
+ mad_send_wr->send_buf.mad) &&
&mad_send_wr->send_buf == send_buf)
return mad_send_wr;
}
@@ -2468,10 +2689,14 @@ static void local_completions(struct work_struct *work)
int free_mad;
struct ib_wc wc;
struct ib_mad_send_wc mad_send_wc;
+ bool opa;
mad_agent_priv =
container_of(work, struct ib_mad_agent_private, local_work);
+ opa = rdma_cap_opa_mad(mad_agent_priv->qp_info->port_priv->device,
+ mad_agent_priv->qp_info->port_priv->port_num);
+
spin_lock_irqsave(&mad_agent_priv->lock, flags);
while (!list_empty(&mad_agent_priv->local_list)) {
local = list_entry(mad_agent_priv->local_list.next,
@@ -2481,6 +2706,7 @@ static void local_completions(struct work_struct *work)
spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
free_mad = 0;
if (local->mad_priv) {
+ u8 base_version;
recv_mad_agent = local->recv_mad_agent;
if (!recv_mad_agent) {
dev_err(&mad_agent_priv->agent.device->dev,
@@ -2496,17 +2722,26 @@ static void local_completions(struct work_struct *work)
build_smp_wc(recv_mad_agent->agent.qp,
(unsigned long) local->mad_send_wr,
be16_to_cpu(IB_LID_PERMISSIVE),
- 0, recv_mad_agent->agent.port_num, &wc);
+ local->mad_send_wr->send_wr.wr.ud.pkey_index,
+ recv_mad_agent->agent.port_num, &wc);
local->mad_priv->header.recv_wc.wc = &wc;
- local->mad_priv->header.recv_wc.mad_len =
- sizeof(struct ib_mad);
+
+ base_version = ((struct ib_mad_hdr *)(local->mad_priv->mad))->base_version;
+ if (opa && base_version == OPA_MGMT_BASE_VERSION) {
+ local->mad_priv->header.recv_wc.mad_len = local->return_wc_byte_len;
+ local->mad_priv->header.recv_wc.mad_seg_size = sizeof(struct opa_mad);
+ } else {
+ local->mad_priv->header.recv_wc.mad_len = sizeof(struct ib_mad);
+ local->mad_priv->header.recv_wc.mad_seg_size = sizeof(struct ib_mad);
+ }
+
INIT_LIST_HEAD(&local->mad_priv->header.recv_wc.rmpp_list);
list_add(&local->mad_priv->header.recv_wc.recv_buf.list,
&local->mad_priv->header.recv_wc.rmpp_list);
local->mad_priv->header.recv_wc.recv_buf.grh = NULL;
local->mad_priv->header.recv_wc.recv_buf.mad =
- &local->mad_priv->mad.mad;
+ (struct ib_mad *)local->mad_priv->mad;
if (atomic_read(&recv_mad_agent->qp_info->snoop_count))
snoop_recv(recv_mad_agent->qp_info,
&local->mad_priv->header.recv_wc,
@@ -2534,7 +2769,7 @@ local_send_completion:
spin_lock_irqsave(&mad_agent_priv->lock, flags);
atomic_dec(&mad_agent_priv->refcount);
if (free_mad)
- kmem_cache_free(ib_mad_cache, local->mad_priv);
+ kfree(local->mad_priv);
kfree(local);
}
spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
@@ -2649,7 +2884,6 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
struct ib_mad_queue *recv_queue = &qp_info->recv_queue;
/* Initialize common scatter list fields */
- sg_list.length = sizeof *mad_priv - sizeof mad_priv->header;
sg_list.lkey = (*qp_info->port_priv->mr).lkey;
/* Initialize common receive WR fields */
@@ -2663,7 +2897,8 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
mad_priv = mad;
mad = NULL;
} else {
- mad_priv = kmem_cache_alloc(ib_mad_cache, GFP_KERNEL);
+ mad_priv = alloc_mad_private(port_mad_size(qp_info->port_priv),
+ GFP_ATOMIC);
if (!mad_priv) {
dev_err(&qp_info->port_priv->device->dev,
"No memory for receive buffer\n");
@@ -2671,10 +2906,10 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
break;
}
}
+ sg_list.length = mad_priv_dma_size(mad_priv);
sg_list.addr = ib_dma_map_single(qp_info->port_priv->device,
&mad_priv->grh,
- sizeof *mad_priv -
- sizeof mad_priv->header,
+ mad_priv_dma_size(mad_priv),
DMA_FROM_DEVICE);
if (unlikely(ib_dma_mapping_error(qp_info->port_priv->device,
sg_list.addr))) {
@@ -2698,10 +2933,9 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
spin_unlock_irqrestore(&recv_queue->lock, flags);
ib_dma_unmap_single(qp_info->port_priv->device,
mad_priv->header.mapping,
- sizeof *mad_priv -
- sizeof mad_priv->header,
+ mad_priv_dma_size(mad_priv),
DMA_FROM_DEVICE);
- kmem_cache_free(ib_mad_cache, mad_priv);
+ kfree(mad_priv);
dev_err(&qp_info->port_priv->device->dev,
"ib_post_recv failed: %d\n", ret);
break;
@@ -2738,10 +2972,9 @@ static void cleanup_recv_queue(struct ib_mad_qp_info *qp_info)
ib_dma_unmap_single(qp_info->port_priv->device,
recv->header.mapping,
- sizeof(struct ib_mad_private) -
- sizeof(struct ib_mad_private_header),
+ mad_priv_dma_size(recv),
DMA_FROM_DEVICE);
- kmem_cache_free(ib_mad_cache, recv);
+ kfree(recv);
}
qp_info->recv_queue.count = 0;
@@ -2922,6 +3155,14 @@ static int ib_mad_port_open(struct ib_device *device,
unsigned long flags;
char name[sizeof "ib_mad123"];
int has_smi;
+ struct ib_cq_init_attr cq_attr = {};
+
+ if (WARN_ON(rdma_max_mad_size(device, port_num) < IB_MGMT_MAD_SIZE))
+ return -EFAULT;
+
+ if (WARN_ON(rdma_cap_opa_mad(device, port_num) &&
+ rdma_max_mad_size(device, port_num) < OPA_MGMT_MAD_SIZE))
+ return -EFAULT;
/* Create new device info */
port_priv = kzalloc(sizeof *port_priv, GFP_KERNEL);
@@ -2938,13 +3179,14 @@ static int ib_mad_port_open(struct ib_device *device,
init_mad_qp(port_priv, &port_priv->qp_info[1]);
cq_size = mad_sendq_size + mad_recvq_size;
- has_smi = rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_INFINIBAND;
+ has_smi = rdma_cap_ib_smi(device, port_num);
if (has_smi)
cq_size *= 2;
+ cq_attr.cqe = cq_size;
port_priv->cq = ib_create_cq(port_priv->device,
ib_mad_thread_completion_handler,
- NULL, port_priv, cq_size, 0);
+ NULL, port_priv, &cq_attr);
if (IS_ERR(port_priv->cq)) {
dev_err(&device->dev, "Couldn't create ib_mad CQ\n");
ret = PTR_ERR(port_priv->cq);
@@ -3057,9 +3299,6 @@ static void ib_mad_init_device(struct ib_device *device)
{
int start, end, i;
- if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
- return;
-
if (device->node_type == RDMA_NODE_IB_SWITCH) {
start = 0;
end = 0;
@@ -3069,6 +3308,9 @@ static void ib_mad_init_device(struct ib_device *device)
}
for (i = start; i <= end; i++) {
+ if (!rdma_cap_ib_mad(device, i))
+ continue;
+
if (ib_mad_port_open(device, i)) {
dev_err(&device->dev, "Couldn't open port %d\n", i);
goto error;
@@ -3086,40 +3328,39 @@ error_agent:
dev_err(&device->dev, "Couldn't close port %d\n", i);
error:
- i--;
+ while (--i >= start) {
+ if (!rdma_cap_ib_mad(device, i))
+ continue;
- while (i >= start) {
if (ib_agent_port_close(device, i))
dev_err(&device->dev,
"Couldn't close port %d for agents\n", i);
if (ib_mad_port_close(device, i))
dev_err(&device->dev, "Couldn't close port %d\n", i);
- i--;
}
}
static void ib_mad_remove_device(struct ib_device *device)
{
- int i, num_ports, cur_port;
-
- if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
- return;
+ int start, end, i;
if (device->node_type == RDMA_NODE_IB_SWITCH) {
- num_ports = 1;
- cur_port = 0;
+ start = 0;
+ end = 0;
} else {
- num_ports = device->phys_port_cnt;
- cur_port = 1;
+ start = 1;
+ end = device->phys_port_cnt;
}
- for (i = 0; i < num_ports; i++, cur_port++) {
- if (ib_agent_port_close(device, cur_port))
+
+ for (i = start; i <= end; i++) {
+ if (!rdma_cap_ib_mad(device, i))
+ continue;
+
+ if (ib_agent_port_close(device, i))
dev_err(&device->dev,
- "Couldn't close port %d for agents\n",
- cur_port);
- if (ib_mad_port_close(device, cur_port))
- dev_err(&device->dev, "Couldn't close port %d\n",
- cur_port);
+ "Couldn't close port %d for agents\n", i);
+ if (ib_mad_port_close(device, i))
+ dev_err(&device->dev, "Couldn't close port %d\n", i);
}
}
@@ -3131,45 +3372,25 @@ static struct ib_client mad_client = {
static int __init ib_mad_init_module(void)
{
- int ret;
-
mad_recvq_size = min(mad_recvq_size, IB_MAD_QP_MAX_SIZE);
mad_recvq_size = max(mad_recvq_size, IB_MAD_QP_MIN_SIZE);
mad_sendq_size = min(mad_sendq_size, IB_MAD_QP_MAX_SIZE);
mad_sendq_size = max(mad_sendq_size, IB_MAD_QP_MIN_SIZE);
- ib_mad_cache = kmem_cache_create("ib_mad",
- sizeof(struct ib_mad_private),
- 0,
- SLAB_HWCACHE_ALIGN,
- NULL);
- if (!ib_mad_cache) {
- pr_err("Couldn't create ib_mad cache\n");
- ret = -ENOMEM;
- goto error1;
- }
-
INIT_LIST_HEAD(&ib_mad_port_list);
if (ib_register_client(&mad_client)) {
pr_err("Couldn't register ib_mad client\n");
- ret = -EINVAL;
- goto error2;
+ return -EINVAL;
}
return 0;
-
-error2:
- kmem_cache_destroy(ib_mad_cache);
-error1:
- return ret;
}
static void __exit ib_mad_cleanup_module(void)
{
ib_unregister_client(&mad_client);
- kmem_cache_destroy(ib_mad_cache);
}
module_init(ib_mad_init_module);
diff --git a/drivers/infiniband/core/mad_priv.h b/drivers/infiniband/core/mad_priv.h
index d1a0b0ee9444..5be89f98928f 100644
--- a/drivers/infiniband/core/mad_priv.h
+++ b/drivers/infiniband/core/mad_priv.h
@@ -41,6 +41,7 @@
#include <linux/workqueue.h>
#include <rdma/ib_mad.h>
#include <rdma/ib_smi.h>
+#include <rdma/opa_smi.h>
#define IB_MAD_QPS_CORE 2 /* Always QP0 and QP1 as a minimum */
@@ -56,7 +57,7 @@
/* Registration table sizes */
#define MAX_MGMT_CLASS 80
-#define MAX_MGMT_VERSION 8
+#define MAX_MGMT_VERSION 0x83
#define MAX_MGMT_OUI 8
#define MAX_MGMT_VENDOR_RANGE2 (IB_MGMT_CLASS_VENDOR_RANGE2_END - \
IB_MGMT_CLASS_VENDOR_RANGE2_START + 1)
@@ -75,12 +76,9 @@ struct ib_mad_private_header {
struct ib_mad_private {
struct ib_mad_private_header header;
+ size_t mad_size;
struct ib_grh grh;
- union {
- struct ib_mad mad;
- struct ib_rmpp_mad rmpp_mad;
- struct ib_smp smp;
- } mad;
+ u8 mad[0];
} __attribute__ ((packed));
struct ib_rmpp_segment {
@@ -150,6 +148,7 @@ struct ib_mad_local_private {
struct ib_mad_private *mad_priv;
struct ib_mad_agent_private *recv_mad_agent;
struct ib_mad_send_wr_private *mad_send_wr;
+ size_t return_wc_byte_len;
};
struct ib_mad_mgmt_method_table {
@@ -213,8 +212,8 @@ struct ib_mad_port_private {
int ib_send_mad(struct ib_mad_send_wr_private *mad_send_wr);
struct ib_mad_send_wr_private *
-ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
- struct ib_mad_recv_wc *mad_recv_wc);
+ib_find_send_mad(const struct ib_mad_agent_private *mad_agent_priv,
+ const struct ib_mad_recv_wc *mad_recv_wc);
void ib_mad_complete_send_wr(struct ib_mad_send_wr_private *mad_send_wr,
struct ib_mad_send_wc *mad_send_wc);
diff --git a/drivers/infiniband/core/mad_rmpp.c b/drivers/infiniband/core/mad_rmpp.c
index f37878c9c06e..382941b46e43 100644
--- a/drivers/infiniband/core/mad_rmpp.c
+++ b/drivers/infiniband/core/mad_rmpp.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005 Intel Inc. All rights reserved.
* Copyright (c) 2005-2006 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2014 Intel Corporation. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -67,6 +68,7 @@ struct mad_rmpp_recv {
u8 mgmt_class;
u8 class_version;
u8 method;
+ u8 base_version;
};
static inline void deref_rmpp_recv(struct mad_rmpp_recv *rmpp_recv)
@@ -139,7 +141,8 @@ static void ack_recv(struct mad_rmpp_recv *rmpp_recv,
hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
msg = ib_create_send_mad(&rmpp_recv->agent->agent, recv_wc->wc->src_qp,
recv_wc->wc->pkey_index, 1, hdr_len,
- 0, GFP_KERNEL);
+ 0, GFP_KERNEL,
+ IB_MGMT_BASE_VERSION);
if (IS_ERR(msg))
return;
@@ -165,7 +168,8 @@ static struct ib_mad_send_buf *alloc_response_msg(struct ib_mad_agent *agent,
hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
msg = ib_create_send_mad(agent, recv_wc->wc->src_qp,
recv_wc->wc->pkey_index, 1,
- hdr_len, 0, GFP_KERNEL);
+ hdr_len, 0, GFP_KERNEL,
+ IB_MGMT_BASE_VERSION);
if (IS_ERR(msg))
ib_destroy_ah(ah);
else {
@@ -316,6 +320,7 @@ create_rmpp_recv(struct ib_mad_agent_private *agent,
rmpp_recv->mgmt_class = mad_hdr->mgmt_class;
rmpp_recv->class_version = mad_hdr->class_version;
rmpp_recv->method = mad_hdr->method;
+ rmpp_recv->base_version = mad_hdr->base_version;
return rmpp_recv;
error: kfree(rmpp_recv);
@@ -431,14 +436,23 @@ static inline int get_mad_len(struct mad_rmpp_recv *rmpp_recv)
{
struct ib_rmpp_mad *rmpp_mad;
int hdr_size, data_size, pad;
+ bool opa = rdma_cap_opa_mad(rmpp_recv->agent->qp_info->port_priv->device,
+ rmpp_recv->agent->qp_info->port_priv->port_num);
rmpp_mad = (struct ib_rmpp_mad *)rmpp_recv->cur_seg_buf->mad;
hdr_size = ib_get_mad_data_offset(rmpp_mad->mad_hdr.mgmt_class);
- data_size = sizeof(struct ib_rmpp_mad) - hdr_size;
- pad = IB_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
- if (pad > IB_MGMT_RMPP_DATA || pad < 0)
- pad = 0;
+ if (opa && rmpp_recv->base_version == OPA_MGMT_BASE_VERSION) {
+ data_size = sizeof(struct opa_rmpp_mad) - hdr_size;
+ pad = OPA_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
+ if (pad > OPA_MGMT_RMPP_DATA || pad < 0)
+ pad = 0;
+ } else {
+ data_size = sizeof(struct ib_rmpp_mad) - hdr_size;
+ pad = IB_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
+ if (pad > IB_MGMT_RMPP_DATA || pad < 0)
+ pad = 0;
+ }
return hdr_size + rmpp_recv->seg_num * data_size - pad;
}
@@ -570,13 +584,14 @@ static int send_next_seg(struct ib_mad_send_wr_private *mad_send_wr)
if (mad_send_wr->seg_num == 1) {
rmpp_mad->rmpp_hdr.rmpp_rtime_flags |= IB_MGMT_RMPP_FLAG_FIRST;
- paylen = mad_send_wr->send_buf.seg_count * IB_MGMT_RMPP_DATA -
- mad_send_wr->pad;
+ paylen = (mad_send_wr->send_buf.seg_count *
+ mad_send_wr->send_buf.seg_rmpp_size) -
+ mad_send_wr->pad;
}
if (mad_send_wr->seg_num == mad_send_wr->send_buf.seg_count) {
rmpp_mad->rmpp_hdr.rmpp_rtime_flags |= IB_MGMT_RMPP_FLAG_LAST;
- paylen = IB_MGMT_RMPP_DATA - mad_send_wr->pad;
+ paylen = mad_send_wr->send_buf.seg_rmpp_size - mad_send_wr->pad;
}
rmpp_mad->rmpp_hdr.paylen_newwin = cpu_to_be32(paylen);
diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c
index fa17b552ff78..1244f02a5c6d 100644
--- a/drivers/infiniband/core/multicast.c
+++ b/drivers/infiniband/core/multicast.c
@@ -780,8 +780,7 @@ static void mcast_event_handler(struct ib_event_handler *handler,
int index;
dev = container_of(handler, struct mcast_device, event_handler);
- if (rdma_port_get_link_layer(dev->device, event->element.port_num) !=
- IB_LINK_LAYER_INFINIBAND)
+ if (!rdma_cap_ib_mcast(dev->device, event->element.port_num))
return;
index = event->element.port_num - dev->start_port;
@@ -808,9 +807,6 @@ static void mcast_add_one(struct ib_device *device)
int i;
int count = 0;
- if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
- return;
-
dev = kmalloc(sizeof *dev + device->phys_port_cnt * sizeof *port,
GFP_KERNEL);
if (!dev)
@@ -824,8 +820,7 @@ static void mcast_add_one(struct ib_device *device)
}
for (i = 0; i <= dev->end_port - dev->start_port; i++) {
- if (rdma_port_get_link_layer(device, dev->start_port + i) !=
- IB_LINK_LAYER_INFINIBAND)
+ if (!rdma_cap_ib_mcast(device, dev->start_port + i))
continue;
port = &dev->port[i];
port->dev = dev;
@@ -863,8 +858,7 @@ static void mcast_remove_one(struct ib_device *device)
flush_workqueue(mcast_wq);
for (i = 0; i <= dev->end_port - dev->start_port; i++) {
- if (rdma_port_get_link_layer(device, dev->start_port + i) ==
- IB_LINK_LAYER_INFINIBAND) {
+ if (rdma_cap_ib_mcast(device, dev->start_port + i)) {
port = &dev->port[i];
deref_port(port);
wait_for_completion(&port->comp);
diff --git a/drivers/infiniband/core/opa_smi.h b/drivers/infiniband/core/opa_smi.h
new file mode 100644
index 000000000000..62d91bfa4cb7
--- /dev/null
+++ b/drivers/infiniband/core/opa_smi.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2014 Intel Corporation. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef __OPA_SMI_H_
+#define __OPA_SMI_H_
+
+#include <rdma/ib_smi.h>
+#include <rdma/opa_smi.h>
+
+#include "smi.h"
+
+enum smi_action opa_smi_handle_dr_smp_recv(struct opa_smp *smp, u8 node_type,
+ int port_num, int phys_port_cnt);
+int opa_smi_get_fwd_port(struct opa_smp *smp);
+extern enum smi_forward_action opa_smi_check_forward_dr_smp(struct opa_smp *smp);
+extern enum smi_action opa_smi_handle_dr_smp_send(struct opa_smp *smp,
+ u8 node_type, int port_num);
+
+/*
+ * Return IB_SMI_HANDLE if the SMP should be handled by the local SMA/SM
+ * via process_mad
+ */
+static inline enum smi_action opa_smi_check_local_smp(struct opa_smp *smp,
+ struct ib_device *device)
+{
+ /* C14-9:3 -- We're at the end of the DR segment of path */
+ /* C14-9:4 -- Hop Pointer = Hop Count + 1 -> give to SMA/SM */
+ return (device->process_mad &&
+ !opa_get_smp_direction(smp) &&
+ (smp->hop_ptr == smp->hop_cnt + 1)) ?
+ IB_SMI_HANDLE : IB_SMI_DISCARD;
+}
+
+/*
+ * Return IB_SMI_HANDLE if the SMP should be handled by the local SMA/SM
+ * via process_mad
+ */
+static inline enum smi_action opa_smi_check_local_returning_smp(struct opa_smp *smp,
+ struct ib_device *device)
+{
+ /* C14-13:3 -- We're at the end of the DR segment of path */
+ /* C14-13:4 -- Hop Pointer == 0 -> give to SM */
+ return (device->process_mad &&
+ opa_get_smp_direction(smp) &&
+ !smp->hop_ptr) ? IB_SMI_HANDLE : IB_SMI_DISCARD;
+}
+
+#endif /* __OPA_SMI_H_ */
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index c38f030f0dc9..0fae85062a65 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -450,7 +450,7 @@ static void ib_sa_event(struct ib_event_handler *handler, struct ib_event *event
struct ib_sa_port *port =
&sa_dev->port[event->element.port_num - sa_dev->start_port];
- if (rdma_port_get_link_layer(handler->device, port->port_num) != IB_LINK_LAYER_INFINIBAND)
+ if (!rdma_cap_ib_sa(handler->device, port->port_num))
return;
spin_lock_irqsave(&port->ah_lock, flags);
@@ -540,7 +540,7 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num,
ah_attr->port_num = port_num;
ah_attr->static_rate = rec->rate;
- force_grh = rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_ETHERNET;
+ force_grh = rdma_cap_eth_ah(device, port_num);
if (rec->hop_limit > 1 || force_grh) {
ah_attr->ah_flags = IB_AH_GRH;
@@ -583,7 +583,8 @@ static int alloc_mad(struct ib_sa_query *query, gfp_t gfp_mask)
query->mad_buf = ib_create_send_mad(query->port->agent, 1,
query->sm_ah->pkey_index,
0, IB_MGMT_SA_HDR, IB_MGMT_SA_DATA,
- gfp_mask);
+ gfp_mask,
+ IB_MGMT_BASE_VERSION);
if (IS_ERR(query->mad_buf)) {
kref_put(&query->sm_ah->ref, free_sm_ah);
return -ENOMEM;
@@ -1153,9 +1154,7 @@ static void ib_sa_add_one(struct ib_device *device)
{
struct ib_sa_device *sa_dev;
int s, e, i;
-
- if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
- return;
+ int count = 0;
if (device->node_type == RDMA_NODE_IB_SWITCH)
s = e = 0;
@@ -1175,7 +1174,7 @@ static void ib_sa_add_one(struct ib_device *device)
for (i = 0; i <= e - s; ++i) {
spin_lock_init(&sa_dev->port[i].ah_lock);
- if (rdma_port_get_link_layer(device, i + 1) != IB_LINK_LAYER_INFINIBAND)
+ if (!rdma_cap_ib_sa(device, i + 1))
continue;
sa_dev->port[i].sm_ah = NULL;
@@ -1189,8 +1188,13 @@ static void ib_sa_add_one(struct ib_device *device)
goto err;
INIT_WORK(&sa_dev->port[i].update_task, update_sm_ah);
+
+ count++;
}
+ if (!count)
+ goto free;
+
ib_set_client_data(device, &sa_client, sa_dev);
/*
@@ -1204,19 +1208,20 @@ static void ib_sa_add_one(struct ib_device *device)
if (ib_register_event_handler(&sa_dev->event_handler))
goto err;
- for (i = 0; i <= e - s; ++i)
- if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND)
+ for (i = 0; i <= e - s; ++i) {
+ if (rdma_cap_ib_sa(device, i + 1))
update_sm_ah(&sa_dev->port[i].update_task);
+ }
return;
err:
- while (--i >= 0)
- if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND)
+ while (--i >= 0) {
+ if (rdma_cap_ib_sa(device, i + 1))
ib_unregister_mad_agent(sa_dev->port[i].agent);
-
+ }
+free:
kfree(sa_dev);
-
return;
}
@@ -1233,7 +1238,7 @@ static void ib_sa_remove_one(struct ib_device *device)
flush_workqueue(ib_wq);
for (i = 0; i <= sa_dev->end_port - sa_dev->start_port; ++i) {
- if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND) {
+ if (rdma_cap_ib_sa(device, i + 1)) {
ib_unregister_mad_agent(sa_dev->port[i].agent);
if (sa_dev->port[i].sm_ah)
kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah);
diff --git a/drivers/infiniband/core/smi.c b/drivers/infiniband/core/smi.c
index 5855e4405d9b..368a561d1a5d 100644
--- a/drivers/infiniband/core/smi.c
+++ b/drivers/infiniband/core/smi.c
@@ -5,6 +5,7 @@
* Copyright (c) 2004, 2005 Topspin Corporation. All rights reserved.
* Copyright (c) 2004-2007 Voltaire Corporation. All rights reserved.
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright (c) 2014 Intel Corporation. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
@@ -38,85 +39,82 @@
#include <rdma/ib_smi.h>
#include "smi.h"
-
-/*
- * Fixup a directed route SMP for sending
- * Return 0 if the SMP should be discarded
- */
-enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
- u8 node_type, int port_num)
+#include "opa_smi.h"
+
+static enum smi_action __smi_handle_dr_smp_send(u8 node_type, int port_num,
+ u8 *hop_ptr, u8 hop_cnt,
+ const u8 *initial_path,
+ const u8 *return_path,
+ u8 direction,
+ bool dr_dlid_is_permissive,
+ bool dr_slid_is_permissive)
{
- u8 hop_ptr, hop_cnt;
-
- hop_ptr = smp->hop_ptr;
- hop_cnt = smp->hop_cnt;
-
/* See section 14.2.2.2, Vol 1 IB spec */
/* C14-6 -- valid hop_cnt values are from 0 to 63 */
if (hop_cnt >= IB_SMP_MAX_PATH_HOPS)
return IB_SMI_DISCARD;
- if (!ib_get_smp_direction(smp)) {
+ if (!direction) {
/* C14-9:1 */
- if (hop_cnt && hop_ptr == 0) {
- smp->hop_ptr++;
- return (smp->initial_path[smp->hop_ptr] ==
+ if (hop_cnt && *hop_ptr == 0) {
+ (*hop_ptr)++;
+ return (initial_path[*hop_ptr] ==
port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-9:2 */
- if (hop_ptr && hop_ptr < hop_cnt) {
+ if (*hop_ptr && *hop_ptr < hop_cnt) {
if (node_type != RDMA_NODE_IB_SWITCH)
return IB_SMI_DISCARD;
- /* smp->return_path set when received */
- smp->hop_ptr++;
- return (smp->initial_path[smp->hop_ptr] ==
+ /* return_path set when received */
+ (*hop_ptr)++;
+ return (initial_path[*hop_ptr] ==
port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-9:3 -- We're at the end of the DR segment of path */
- if (hop_ptr == hop_cnt) {
- /* smp->return_path set when received */
- smp->hop_ptr++;
+ if (*hop_ptr == hop_cnt) {
+ /* return_path set when received */
+ (*hop_ptr)++;
return (node_type == RDMA_NODE_IB_SWITCH ||
- smp->dr_dlid == IB_LID_PERMISSIVE ?
+ dr_dlid_is_permissive ?
IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
/* C14-9:5 -- Fail unreasonable hop pointer */
- return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
+ return (*hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
} else {
/* C14-13:1 */
- if (hop_cnt && hop_ptr == hop_cnt + 1) {
- smp->hop_ptr--;
- return (smp->return_path[smp->hop_ptr] ==
+ if (hop_cnt && *hop_ptr == hop_cnt + 1) {
+ (*hop_ptr)--;
+ return (return_path[*hop_ptr] ==
port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:2 */
- if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
+ if (2 <= *hop_ptr && *hop_ptr <= hop_cnt) {
if (node_type != RDMA_NODE_IB_SWITCH)
return IB_SMI_DISCARD;
- smp->hop_ptr--;
- return (smp->return_path[smp->hop_ptr] ==
+ (*hop_ptr)--;
+ return (return_path[*hop_ptr] ==
port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:3 -- at the end of the DR segment of path */
- if (hop_ptr == 1) {
- smp->hop_ptr--;
+ if (*hop_ptr == 1) {
+ (*hop_ptr)--;
/* C14-13:3 -- SMPs destined for SM shouldn't be here */
return (node_type == RDMA_NODE_IB_SWITCH ||
- smp->dr_slid == IB_LID_PERMISSIVE ?
+ dr_slid_is_permissive ?
IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:4 -- hop_ptr = 0 -> should have gone to SM */
- if (hop_ptr == 0)
+ if (*hop_ptr == 0)
return IB_SMI_HANDLE;
/* C14-13:5 -- Check for unreasonable hop pointer */
@@ -125,105 +123,164 @@ enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
}
/*
- * Adjust information for a received SMP
- * Return 0 if the SMP should be dropped
+ * Fixup a directed route SMP for sending
+ * Return IB_SMI_DISCARD if the SMP should be discarded
*/
-enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type,
- int port_num, int phys_port_cnt)
+enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
+ u8 node_type, int port_num)
{
- u8 hop_ptr, hop_cnt;
+ return __smi_handle_dr_smp_send(node_type, port_num,
+ &smp->hop_ptr, smp->hop_cnt,
+ smp->initial_path,
+ smp->return_path,
+ ib_get_smp_direction(smp),
+ smp->dr_dlid == IB_LID_PERMISSIVE,
+ smp->dr_slid == IB_LID_PERMISSIVE);
+}
- hop_ptr = smp->hop_ptr;
- hop_cnt = smp->hop_cnt;
+enum smi_action opa_smi_handle_dr_smp_send(struct opa_smp *smp,
+ u8 node_type, int port_num)
+{
+ return __smi_handle_dr_smp_send(node_type, port_num,
+ &smp->hop_ptr, smp->hop_cnt,
+ smp->route.dr.initial_path,
+ smp->route.dr.return_path,
+ opa_get_smp_direction(smp),
+ smp->route.dr.dr_dlid ==
+ OPA_LID_PERMISSIVE,
+ smp->route.dr.dr_slid ==
+ OPA_LID_PERMISSIVE);
+}
+static enum smi_action __smi_handle_dr_smp_recv(u8 node_type, int port_num,
+ int phys_port_cnt,
+ u8 *hop_ptr, u8 hop_cnt,
+ const u8 *initial_path,
+ u8 *return_path,
+ u8 direction,
+ bool dr_dlid_is_permissive,
+ bool dr_slid_is_permissive)
+{
/* See section 14.2.2.2, Vol 1 IB spec */
/* C14-6 -- valid hop_cnt values are from 0 to 63 */
if (hop_cnt >= IB_SMP_MAX_PATH_HOPS)
return IB_SMI_DISCARD;
- if (!ib_get_smp_direction(smp)) {
+ if (!direction) {
/* C14-9:1 -- sender should have incremented hop_ptr */
- if (hop_cnt && hop_ptr == 0)
+ if (hop_cnt && *hop_ptr == 0)
return IB_SMI_DISCARD;
/* C14-9:2 -- intermediate hop */
- if (hop_ptr && hop_ptr < hop_cnt) {
+ if (*hop_ptr && *hop_ptr < hop_cnt) {
if (node_type != RDMA_NODE_IB_SWITCH)
return IB_SMI_DISCARD;
- smp->return_path[hop_ptr] = port_num;
- /* smp->hop_ptr updated when sending */
- return (smp->initial_path[hop_ptr+1] <= phys_port_cnt ?
+ return_path[*hop_ptr] = port_num;
+ /* hop_ptr updated when sending */
+ return (initial_path[*hop_ptr+1] <= phys_port_cnt ?
IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-9:3 -- We're at the end of the DR segment of path */
- if (hop_ptr == hop_cnt) {
+ if (*hop_ptr == hop_cnt) {
if (hop_cnt)
- smp->return_path[hop_ptr] = port_num;
- /* smp->hop_ptr updated when sending */
+ return_path[*hop_ptr] = port_num;
+ /* hop_ptr updated when sending */
return (node_type == RDMA_NODE_IB_SWITCH ||
- smp->dr_dlid == IB_LID_PERMISSIVE ?
+ dr_dlid_is_permissive ?
IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
/* C14-9:5 -- fail unreasonable hop pointer */
- return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
+ return (*hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
} else {
/* C14-13:1 */
- if (hop_cnt && hop_ptr == hop_cnt + 1) {
- smp->hop_ptr--;
- return (smp->return_path[smp->hop_ptr] ==
+ if (hop_cnt && *hop_ptr == hop_cnt + 1) {
+ (*hop_ptr)--;
+ return (return_path[*hop_ptr] ==
port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:2 */
- if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
+ if (2 <= *hop_ptr && *hop_ptr <= hop_cnt) {
if (node_type != RDMA_NODE_IB_SWITCH)
return IB_SMI_DISCARD;
- /* smp->hop_ptr updated when sending */
- return (smp->return_path[hop_ptr-1] <= phys_port_cnt ?
+ /* hop_ptr updated when sending */
+ return (return_path[*hop_ptr-1] <= phys_port_cnt ?
IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:3 -- We're at the end of the DR segment of path */
- if (hop_ptr == 1) {
- if (smp->dr_slid == IB_LID_PERMISSIVE) {
+ if (*hop_ptr == 1) {
+ if (dr_slid_is_permissive) {
/* giving SMP to SM - update hop_ptr */
- smp->hop_ptr--;
+ (*hop_ptr)--;
return IB_SMI_HANDLE;
}
- /* smp->hop_ptr updated when sending */
+ /* hop_ptr updated when sending */
return (node_type == RDMA_NODE_IB_SWITCH ?
IB_SMI_HANDLE : IB_SMI_DISCARD);
}
/* C14-13:4 -- hop_ptr = 0 -> give to SM */
/* C14-13:5 -- Check for unreasonable hop pointer */
- return (hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
+ return (*hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
}
}
-enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
+/*
+ * Adjust information for a received SMP
+ * Return IB_SMI_DISCARD if the SMP should be dropped
+ */
+enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type,
+ int port_num, int phys_port_cnt)
{
- u8 hop_ptr, hop_cnt;
+ return __smi_handle_dr_smp_recv(node_type, port_num, phys_port_cnt,
+ &smp->hop_ptr, smp->hop_cnt,
+ smp->initial_path,
+ smp->return_path,
+ ib_get_smp_direction(smp),
+ smp->dr_dlid == IB_LID_PERMISSIVE,
+ smp->dr_slid == IB_LID_PERMISSIVE);
+}
- hop_ptr = smp->hop_ptr;
- hop_cnt = smp->hop_cnt;
+/*
+ * Adjust information for a received SMP
+ * Return IB_SMI_DISCARD if the SMP should be dropped
+ */
+enum smi_action opa_smi_handle_dr_smp_recv(struct opa_smp *smp, u8 node_type,
+ int port_num, int phys_port_cnt)
+{
+ return __smi_handle_dr_smp_recv(node_type, port_num, phys_port_cnt,
+ &smp->hop_ptr, smp->hop_cnt,
+ smp->route.dr.initial_path,
+ smp->route.dr.return_path,
+ opa_get_smp_direction(smp),
+ smp->route.dr.dr_dlid ==
+ OPA_LID_PERMISSIVE,
+ smp->route.dr.dr_slid ==
+ OPA_LID_PERMISSIVE);
+}
- if (!ib_get_smp_direction(smp)) {
+static enum smi_forward_action __smi_check_forward_dr_smp(u8 hop_ptr, u8 hop_cnt,
+ u8 direction,
+ bool dr_dlid_is_permissive,
+ bool dr_slid_is_permissive)
+{
+ if (!direction) {
/* C14-9:2 -- intermediate hop */
if (hop_ptr && hop_ptr < hop_cnt)
return IB_SMI_FORWARD;
/* C14-9:3 -- at the end of the DR segment of path */
if (hop_ptr == hop_cnt)
- return (smp->dr_dlid == IB_LID_PERMISSIVE ?
+ return (dr_dlid_is_permissive ?
IB_SMI_SEND : IB_SMI_LOCAL);
/* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
@@ -236,10 +293,29 @@ enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
/* C14-13:3 -- at the end of the DR segment of path */
if (hop_ptr == 1)
- return (smp->dr_slid != IB_LID_PERMISSIVE ?
+ return (!dr_slid_is_permissive ?
IB_SMI_SEND : IB_SMI_LOCAL);
}
return IB_SMI_LOCAL;
+
+}
+
+enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
+{
+ return __smi_check_forward_dr_smp(smp->hop_ptr, smp->hop_cnt,
+ ib_get_smp_direction(smp),
+ smp->dr_dlid == IB_LID_PERMISSIVE,
+ smp->dr_slid == IB_LID_PERMISSIVE);
+}
+
+enum smi_forward_action opa_smi_check_forward_dr_smp(struct opa_smp *smp)
+{
+ return __smi_check_forward_dr_smp(smp->hop_ptr, smp->hop_cnt,
+ opa_get_smp_direction(smp),
+ smp->route.dr.dr_dlid ==
+ OPA_LID_PERMISSIVE,
+ smp->route.dr.dr_slid ==
+ OPA_LID_PERMISSIVE);
}
/*
@@ -251,3 +327,13 @@ int smi_get_fwd_port(struct ib_smp *smp)
return (!ib_get_smp_direction(smp) ? smp->initial_path[smp->hop_ptr+1] :
smp->return_path[smp->hop_ptr-1]);
}
+
+/*
+ * Return the forwarding port number from initial_path for outgoing SMP and
+ * from return_path for returning SMP
+ */
+int opa_smi_get_fwd_port(struct opa_smp *smp)
+{
+ return !opa_get_smp_direction(smp) ? smp->route.dr.initial_path[smp->hop_ptr+1] :
+ smp->route.dr.return_path[smp->hop_ptr-1];
+}
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index cbd0383f622e..ed6b6c85c334 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -326,6 +326,8 @@ static ssize_t show_pma_counter(struct ib_port *p, struct port_attribute *attr,
int width = (tab_attr->index >> 16) & 0xff;
struct ib_mad *in_mad = NULL;
struct ib_mad *out_mad = NULL;
+ size_t mad_size = sizeof(*out_mad);
+ u16 out_mad_pkey_index = 0;
ssize_t ret;
if (!p->ibdev->process_mad)
@@ -347,7 +349,10 @@ static ssize_t show_pma_counter(struct ib_port *p, struct port_attribute *attr,
in_mad->data[41] = p->port_num; /* PortSelect field */
if ((p->ibdev->process_mad(p->ibdev, IB_MAD_IGNORE_MKEY,
- p->port_num, NULL, NULL, in_mad, out_mad) &
+ p->port_num, NULL, NULL,
+ (const struct ib_mad_hdr *)in_mad, mad_size,
+ (struct ib_mad_hdr *)out_mad, &mad_size,
+ &out_mad_pkey_index) &
(IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY)) !=
(IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY)) {
ret = -EINVAL;
@@ -456,6 +461,7 @@ static void ib_device_release(struct device *device)
{
struct ib_device *dev = container_of(device, struct ib_device, dev);
+ kfree(dev->port_immutable);
kfree(dev);
}
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index f2f63933e8a9..62c24b1452b8 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -1253,8 +1253,7 @@ static void ib_ucm_add_one(struct ib_device *device)
dev_t base;
struct ib_ucm_device *ucm_dev;
- if (!device->alloc_ucontext ||
- rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
+ if (!device->alloc_ucontext || !rdma_cap_ib_cm(device, 1))
return;
ucm_dev = kzalloc(sizeof *ucm_dev, GFP_KERNEL);
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 45d67e9228d7..ad45469f7582 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -722,26 +722,13 @@ static ssize_t ucma_query_route(struct ucma_file *file,
resp.node_guid = (__force __u64) ctx->cm_id->device->node_guid;
resp.port_num = ctx->cm_id->port_num;
- switch (rdma_node_get_transport(ctx->cm_id->device->node_type)) {
- case RDMA_TRANSPORT_IB:
- switch (rdma_port_get_link_layer(ctx->cm_id->device,
- ctx->cm_id->port_num)) {
- case IB_LINK_LAYER_INFINIBAND:
- ucma_copy_ib_route(&resp, &ctx->cm_id->route);
- break;
- case IB_LINK_LAYER_ETHERNET:
- ucma_copy_iboe_route(&resp, &ctx->cm_id->route);
- break;
- default:
- break;
- }
- break;
- case RDMA_TRANSPORT_IWARP:
+
+ if (rdma_cap_ib_sa(ctx->cm_id->device, ctx->cm_id->port_num))
+ ucma_copy_ib_route(&resp, &ctx->cm_id->route);
+ else if (rdma_protocol_roce(ctx->cm_id->device, ctx->cm_id->port_num))
+ ucma_copy_iboe_route(&resp, &ctx->cm_id->route);
+ else if (rdma_protocol_iwarp(ctx->cm_id->device, ctx->cm_id->port_num))
ucma_copy_iw_route(&resp, &ctx->cm_id->route);
- break;
- default:
- break;
- }
out:
if (copy_to_user((void __user *)(unsigned long)cmd.response,
diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c
index 8b8cc6fa0ab0..40becdb3196e 100644
--- a/drivers/infiniband/core/umem_odp.c
+++ b/drivers/infiniband/core/umem_odp.c
@@ -446,7 +446,6 @@ static int ib_umem_odp_map_dma_single_page(
int remove_existing_mapping = 0;
int ret = 0;
- mutex_lock(&umem->odp_data->umem_mutex);
/*
* Note: we avoid writing if seq is different from the initial seq, to
* handle case of a racing notifier. This check also allows us to bail
@@ -479,8 +478,6 @@ static int ib_umem_odp_map_dma_single_page(
}
out:
- mutex_unlock(&umem->odp_data->umem_mutex);
-
/* On Demand Paging - avoid pinning the page */
if (umem->context->invalidate_range || !stored_page)
put_page(page);
@@ -586,6 +583,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt,
bcnt -= min_t(size_t, npages << PAGE_SHIFT, bcnt);
user_virt += npages << PAGE_SHIFT;
+ mutex_lock(&umem->odp_data->umem_mutex);
for (j = 0; j < npages; ++j) {
ret = ib_umem_odp_map_dma_single_page(
umem, k, base_virt_addr, local_page_list[j],
@@ -594,6 +592,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 user_virt, u64 bcnt,
break;
k++;
}
+ mutex_unlock(&umem->odp_data->umem_mutex);
if (ret < 0) {
/* Release left over pages when handling errors. */
@@ -633,12 +632,11 @@ void ib_umem_odp_unmap_dma_pages(struct ib_umem *umem, u64 virt,
* faults from completion. We might be racing with other
* invalidations, so we must make sure we free each page only
* once. */
+ mutex_lock(&umem->odp_data->umem_mutex);
for (addr = virt; addr < bound; addr += (u64)umem->page_size) {
idx = (addr - ib_umem_start(umem)) / PAGE_SIZE;
- mutex_lock(&umem->odp_data->umem_mutex);
if (umem->odp_data->page_list[idx]) {
struct page *page = umem->odp_data->page_list[idx];
- struct page *head_page = compound_head(page);
dma_addr_t dma = umem->odp_data->dma_list[idx];
dma_addr_t dma_addr = dma & ODP_DMA_ADDR_MASK;
@@ -646,7 +644,8 @@ void ib_umem_odp_unmap_dma_pages(struct ib_umem *umem, u64 virt,
ib_dma_unmap_page(dev, dma_addr, PAGE_SIZE,
DMA_BIDIRECTIONAL);
- if (dma & ODP_WRITE_ALLOWED_BIT)
+ if (dma & ODP_WRITE_ALLOWED_BIT) {
+ struct page *head_page = compound_head(page);
/*
* set_page_dirty prefers being called with
* the page lock. However, MMU notifiers are
@@ -657,13 +656,14 @@ void ib_umem_odp_unmap_dma_pages(struct ib_umem *umem, u64 virt,
* be removed.
*/
set_page_dirty(head_page);
+ }
/* on demand pinning support */
if (!umem->context->invalidate_range)
put_page(page);
umem->odp_data->page_list[idx] = NULL;
umem->odp_data->dma_list[idx] = 0;
}
- mutex_unlock(&umem->odp_data->umem_mutex);
}
+ mutex_unlock(&umem->odp_data->umem_mutex);
}
EXPORT_SYMBOL(ib_umem_odp_unmap_dma_pages);
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index 928cdd20e2d1..35567fffaa4e 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -99,7 +99,6 @@ struct ib_umad_port {
};
struct ib_umad_device {
- int start_port, end_port;
struct kobject kobj;
struct ib_umad_port port[0];
};
@@ -263,20 +262,23 @@ static ssize_t copy_recv_mad(struct ib_umad_file *file, char __user *buf,
{
struct ib_mad_recv_buf *recv_buf;
int left, seg_payload, offset, max_seg_payload;
+ size_t seg_size;
- /* We need enough room to copy the first (or only) MAD segment. */
recv_buf = &packet->recv_wc->recv_buf;
- if ((packet->length <= sizeof (*recv_buf->mad) &&
+ seg_size = packet->recv_wc->mad_seg_size;
+
+ /* We need enough room to copy the first (or only) MAD segment. */
+ if ((packet->length <= seg_size &&
count < hdr_size(file) + packet->length) ||
- (packet->length > sizeof (*recv_buf->mad) &&
- count < hdr_size(file) + sizeof (*recv_buf->mad)))
+ (packet->length > seg_size &&
+ count < hdr_size(file) + seg_size))
return -EINVAL;
if (copy_to_user(buf, &packet->mad, hdr_size(file)))
return -EFAULT;
buf += hdr_size(file);
- seg_payload = min_t(int, packet->length, sizeof (*recv_buf->mad));
+ seg_payload = min_t(int, packet->length, seg_size);
if (copy_to_user(buf, recv_buf->mad, seg_payload))
return -EFAULT;
@@ -293,7 +295,7 @@ static ssize_t copy_recv_mad(struct ib_umad_file *file, char __user *buf,
return -ENOSPC;
}
offset = ib_get_mad_data_offset(recv_buf->mad->mad_hdr.mgmt_class);
- max_seg_payload = sizeof (struct ib_mad) - offset;
+ max_seg_payload = seg_size - offset;
for (left = packet->length - seg_payload, buf += seg_payload;
left; left -= seg_payload, buf += seg_payload) {
@@ -426,11 +428,11 @@ static int is_duplicate(struct ib_umad_file *file,
* the same TID, reject the second as a duplicate. This is more
* restrictive than required by the spec.
*/
- if (!ib_response_mad((struct ib_mad *) hdr)) {
- if (!ib_response_mad((struct ib_mad *) sent_hdr))
+ if (!ib_response_mad(hdr)) {
+ if (!ib_response_mad(sent_hdr))
return 1;
continue;
- } else if (!ib_response_mad((struct ib_mad *) sent_hdr))
+ } else if (!ib_response_mad(sent_hdr))
continue;
if (same_destination(&packet->mad.hdr, &sent_packet->mad.hdr))
@@ -451,6 +453,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
struct ib_rmpp_mad *rmpp_mad;
__be64 *tid;
int ret, data_len, hdr_len, copy_offset, rmpp_active;
+ u8 base_version;
if (count < hdr_size(file) + IB_MGMT_RMPP_HDR)
return -EINVAL;
@@ -517,11 +520,13 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
rmpp_active = 0;
}
+ base_version = ((struct ib_mad_hdr *)&packet->mad.data)->base_version;
data_len = count - hdr_size(file) - hdr_len;
packet->msg = ib_create_send_mad(agent,
be32_to_cpu(packet->mad.hdr.qpn),
packet->mad.hdr.pkey_index, rmpp_active,
- hdr_len, data_len, GFP_KERNEL);
+ hdr_len, data_len, GFP_KERNEL,
+ base_version);
if (IS_ERR(packet->msg)) {
ret = PTR_ERR(packet->msg);
goto err_ah;
@@ -1273,16 +1278,10 @@ static void ib_umad_add_one(struct ib_device *device)
{
struct ib_umad_device *umad_dev;
int s, e, i;
+ int count = 0;
- if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
- return;
-
- if (device->node_type == RDMA_NODE_IB_SWITCH)
- s = e = 0;
- else {
- s = 1;
- e = device->phys_port_cnt;
- }
+ s = rdma_start_port(device);
+ e = rdma_end_port(device);
umad_dev = kzalloc(sizeof *umad_dev +
(e - s + 1) * sizeof (struct ib_umad_port),
@@ -1292,25 +1291,34 @@ static void ib_umad_add_one(struct ib_device *device)
kobject_init(&umad_dev->kobj, &ib_umad_dev_ktype);
- umad_dev->start_port = s;
- umad_dev->end_port = e;
-
for (i = s; i <= e; ++i) {
+ if (!rdma_cap_ib_mad(device, i))
+ continue;
+
umad_dev->port[i - s].umad_dev = umad_dev;
if (ib_umad_init_port(device, i, umad_dev,
&umad_dev->port[i - s]))
goto err;
+
+ count++;
}
+ if (!count)
+ goto free;
+
ib_set_client_data(device, &umad_client, umad_dev);
return;
err:
- while (--i >= s)
- ib_umad_kill_port(&umad_dev->port[i - s]);
+ while (--i >= s) {
+ if (!rdma_cap_ib_mad(device, i))
+ continue;
+ ib_umad_kill_port(&umad_dev->port[i - s]);
+ }
+free:
kobject_put(&umad_dev->kobj);
}
@@ -1322,8 +1330,10 @@ static void ib_umad_remove_one(struct ib_device *device)
if (!umad_dev)
return;
- for (i = 0; i <= umad_dev->end_port - umad_dev->start_port; ++i)
- ib_umad_kill_port(&umad_dev->port[i]);
+ for (i = 0; i <= rdma_end_port(device) - rdma_start_port(device); ++i) {
+ if (rdma_cap_ib_mad(device, i + rdma_start_port(device)))
+ ib_umad_kill_port(&umad_dev->port[i]);
+ }
kobject_put(&umad_dev->kobj);
}
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index b716b0815644..ba365b6d1e8d 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -259,5 +259,6 @@ IB_UVERBS_DECLARE_CMD(close_xrcd);
IB_UVERBS_DECLARE_EX_CMD(create_flow);
IB_UVERBS_DECLARE_EX_CMD(destroy_flow);
IB_UVERBS_DECLARE_EX_CMD(query_device);
+IB_UVERBS_DECLARE_EX_CMD(create_cq);
#endif /* UVERBS_H */
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index a9f048990dfc..bbb02ffe87df 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -1330,40 +1330,37 @@ ssize_t ib_uverbs_create_comp_channel(struct ib_uverbs_file *file,
return in_len;
}
-ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
- const char __user *buf, int in_len,
- int out_len)
+static struct ib_ucq_object *create_cq(struct ib_uverbs_file *file,
+ struct ib_udata *ucore,
+ struct ib_udata *uhw,
+ struct ib_uverbs_ex_create_cq *cmd,
+ size_t cmd_sz,
+ int (*cb)(struct ib_uverbs_file *file,
+ struct ib_ucq_object *obj,
+ struct ib_uverbs_ex_create_cq_resp *resp,
+ struct ib_udata *udata,
+ void *context),
+ void *context)
{
- struct ib_uverbs_create_cq cmd;
- struct ib_uverbs_create_cq_resp resp;
- struct ib_udata udata;
struct ib_ucq_object *obj;
struct ib_uverbs_event_file *ev_file = NULL;
struct ib_cq *cq;
int ret;
+ struct ib_uverbs_ex_create_cq_resp resp;
+ struct ib_cq_init_attr attr = {};
- if (out_len < sizeof resp)
- return -ENOSPC;
-
- if (copy_from_user(&cmd, buf, sizeof cmd))
- return -EFAULT;
-
- INIT_UDATA(&udata, buf + sizeof cmd,
- (unsigned long) cmd.response + sizeof resp,
- in_len - sizeof cmd, out_len - sizeof resp);
-
- if (cmd.comp_vector >= file->device->num_comp_vectors)
- return -EINVAL;
+ if (cmd->comp_vector >= file->device->num_comp_vectors)
+ return ERR_PTR(-EINVAL);
obj = kmalloc(sizeof *obj, GFP_KERNEL);
if (!obj)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
- init_uobj(&obj->uobject, cmd.user_handle, file->ucontext, &cq_lock_class);
+ init_uobj(&obj->uobject, cmd->user_handle, file->ucontext, &cq_lock_class);
down_write(&obj->uobject.mutex);
- if (cmd.comp_channel >= 0) {
- ev_file = ib_uverbs_lookup_comp_file(cmd.comp_channel);
+ if (cmd->comp_channel >= 0) {
+ ev_file = ib_uverbs_lookup_comp_file(cmd->comp_channel);
if (!ev_file) {
ret = -EINVAL;
goto err;
@@ -1376,9 +1373,14 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
INIT_LIST_HEAD(&obj->comp_list);
INIT_LIST_HEAD(&obj->async_list);
- cq = file->device->ib_dev->create_cq(file->device->ib_dev, cmd.cqe,
- cmd.comp_vector,
- file->ucontext, &udata);
+ attr.cqe = cmd->cqe;
+ attr.comp_vector = cmd->comp_vector;
+
+ if (cmd_sz > offsetof(typeof(*cmd), flags) + sizeof(cmd->flags))
+ attr.flags = cmd->flags;
+
+ cq = file->device->ib_dev->create_cq(file->device->ib_dev, &attr,
+ file->ucontext, uhw);
if (IS_ERR(cq)) {
ret = PTR_ERR(cq);
goto err_file;
@@ -1397,14 +1399,15 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
goto err_free;
memset(&resp, 0, sizeof resp);
- resp.cq_handle = obj->uobject.id;
- resp.cqe = cq->cqe;
+ resp.base.cq_handle = obj->uobject.id;
+ resp.base.cqe = cq->cqe;
- if (copy_to_user((void __user *) (unsigned long) cmd.response,
- &resp, sizeof resp)) {
- ret = -EFAULT;
- goto err_copy;
- }
+ resp.response_length = offsetof(typeof(resp), response_length) +
+ sizeof(resp.response_length);
+
+ ret = cb(file, obj, &resp, ucore, context);
+ if (ret)
+ goto err_cb;
mutex_lock(&file->mutex);
list_add_tail(&obj->uobject.list, &file->ucontext->cq_list);
@@ -1414,9 +1417,9 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
up_write(&obj->uobject.mutex);
- return in_len;
+ return obj;
-err_copy:
+err_cb:
idr_remove_uobj(&ib_uverbs_cq_idr, &obj->uobject);
err_free:
@@ -1428,7 +1431,106 @@ err_file:
err:
put_uobj_write(&obj->uobject);
- return ret;
+
+ return ERR_PTR(ret);
+}
+
+static int ib_uverbs_create_cq_cb(struct ib_uverbs_file *file,
+ struct ib_ucq_object *obj,
+ struct ib_uverbs_ex_create_cq_resp *resp,
+ struct ib_udata *ucore, void *context)
+{
+ if (ib_copy_to_udata(ucore, &resp->base, sizeof(resp->base)))
+ return -EFAULT;
+
+ return 0;
+}
+
+ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
+ const char __user *buf, int in_len,
+ int out_len)
+{
+ struct ib_uverbs_create_cq cmd;
+ struct ib_uverbs_ex_create_cq cmd_ex;
+ struct ib_uverbs_create_cq_resp resp;
+ struct ib_udata ucore;
+ struct ib_udata uhw;
+ struct ib_ucq_object *obj;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ if (copy_from_user(&cmd, buf, sizeof(cmd)))
+ return -EFAULT;
+
+ INIT_UDATA(&ucore, buf, cmd.response, sizeof(cmd), sizeof(resp));
+
+ INIT_UDATA(&uhw, buf + sizeof(cmd),
+ (unsigned long)cmd.response + sizeof(resp),
+ in_len - sizeof(cmd), out_len - sizeof(resp));
+
+ memset(&cmd_ex, 0, sizeof(cmd_ex));
+ cmd_ex.user_handle = cmd.user_handle;
+ cmd_ex.cqe = cmd.cqe;
+ cmd_ex.comp_vector = cmd.comp_vector;
+ cmd_ex.comp_channel = cmd.comp_channel;
+
+ obj = create_cq(file, &ucore, &uhw, &cmd_ex,
+ offsetof(typeof(cmd_ex), comp_channel) +
+ sizeof(cmd.comp_channel), ib_uverbs_create_cq_cb,
+ NULL);
+
+ if (IS_ERR(obj))
+ return PTR_ERR(obj);
+
+ return in_len;
+}
+
+static int ib_uverbs_ex_create_cq_cb(struct ib_uverbs_file *file,
+ struct ib_ucq_object *obj,
+ struct ib_uverbs_ex_create_cq_resp *resp,
+ struct ib_udata *ucore, void *context)
+{
+ if (ib_copy_to_udata(ucore, resp, resp->response_length))
+ return -EFAULT;
+
+ return 0;
+}
+
+int ib_uverbs_ex_create_cq(struct ib_uverbs_file *file,
+ struct ib_udata *ucore,
+ struct ib_udata *uhw)
+{
+ struct ib_uverbs_ex_create_cq_resp resp;
+ struct ib_uverbs_ex_create_cq cmd;
+ struct ib_ucq_object *obj;
+ int err;
+
+ if (ucore->inlen < sizeof(cmd))
+ return -EINVAL;
+
+ err = ib_copy_from_udata(&cmd, ucore, sizeof(cmd));
+ if (err)
+ return err;
+
+ if (cmd.comp_mask)
+ return -EINVAL;
+
+ if (cmd.reserved)
+ return -EINVAL;
+
+ if (ucore->outlen < (offsetof(typeof(resp), response_length) +
+ sizeof(resp.response_length)))
+ return -ENOSPC;
+
+ obj = create_cq(file, ucore, uhw, &cmd,
+ min(ucore->inlen, sizeof(cmd)),
+ ib_uverbs_ex_create_cq_cb, NULL);
+
+ if (IS_ERR(obj))
+ return PTR_ERR(obj);
+
+ return 0;
}
ssize_t ib_uverbs_resize_cq(struct ib_uverbs_file *file,
@@ -3324,7 +3426,9 @@ int ib_uverbs_ex_query_device(struct ib_uverbs_file *file,
if (ucore->outlen < resp.response_length)
return -ENOSPC;
- err = device->query_device(device, &attr);
+ memset(&attr, 0, sizeof(attr));
+
+ err = device->query_device(device, &attr, uhw);
if (err)
return err;
@@ -3348,6 +3452,18 @@ int ib_uverbs_ex_query_device(struct ib_uverbs_file *file,
#endif
resp.response_length += sizeof(resp.odp_caps);
+ if (ucore->outlen < resp.response_length + sizeof(resp.timestamp_mask))
+ goto end;
+
+ resp.timestamp_mask = attr.timestamp_mask;
+ resp.response_length += sizeof(resp.timestamp_mask);
+
+ if (ucore->outlen < resp.response_length + sizeof(resp.hca_core_clock))
+ goto end;
+
+ resp.hca_core_clock = attr.hca_core_clock;
+ resp.response_length += sizeof(resp.hca_core_clock);
+
end:
err = ib_copy_to_udata(ucore, &resp, resp.response_length);
if (err)
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 88cce9bb72fe..f6eef2da7097 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -124,6 +124,7 @@ static int (*uverbs_ex_cmd_table[])(struct ib_uverbs_file *file,
[IB_USER_VERBS_EX_CMD_CREATE_FLOW] = ib_uverbs_ex_create_flow,
[IB_USER_VERBS_EX_CMD_DESTROY_FLOW] = ib_uverbs_ex_destroy_flow,
[IB_USER_VERBS_EX_CMD_QUERY_DEVICE] = ib_uverbs_ex_query_device,
+ [IB_USER_VERBS_EX_CMD_CREATE_CQ] = ib_uverbs_ex_create_cq,
};
static void ib_uverbs_add_one(struct ib_device *device);
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index f93eb8da7b5a..bac3fb406a74 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -48,6 +48,71 @@
#include "core_priv.h"
+static const char * const ib_events[] = {
+ [IB_EVENT_CQ_ERR] = "CQ error",
+ [IB_EVENT_QP_FATAL] = "QP fatal error",
+ [IB_EVENT_QP_REQ_ERR] = "QP request error",
+ [IB_EVENT_QP_ACCESS_ERR] = "QP access error",
+ [IB_EVENT_COMM_EST] = "communication established",
+ [IB_EVENT_SQ_DRAINED] = "send queue drained",
+ [IB_EVENT_PATH_MIG] = "path migration successful",
+ [IB_EVENT_PATH_MIG_ERR] = "path migration error",
+ [IB_EVENT_DEVICE_FATAL] = "device fatal error",
+ [IB_EVENT_PORT_ACTIVE] = "port active",
+ [IB_EVENT_PORT_ERR] = "port error",
+ [IB_EVENT_LID_CHANGE] = "LID change",
+ [IB_EVENT_PKEY_CHANGE] = "P_key change",
+ [IB_EVENT_SM_CHANGE] = "SM change",
+ [IB_EVENT_SRQ_ERR] = "SRQ error",
+ [IB_EVENT_SRQ_LIMIT_REACHED] = "SRQ limit reached",
+ [IB_EVENT_QP_LAST_WQE_REACHED] = "last WQE reached",
+ [IB_EVENT_CLIENT_REREGISTER] = "client reregister",
+ [IB_EVENT_GID_CHANGE] = "GID changed",
+};
+
+const char *ib_event_msg(enum ib_event_type event)
+{
+ size_t index = event;
+
+ return (index < ARRAY_SIZE(ib_events) && ib_events[index]) ?
+ ib_events[index] : "unrecognized event";
+}
+EXPORT_SYMBOL(ib_event_msg);
+
+static const char * const wc_statuses[] = {
+ [IB_WC_SUCCESS] = "success",
+ [IB_WC_LOC_LEN_ERR] = "local length error",
+ [IB_WC_LOC_QP_OP_ERR] = "local QP operation error",
+ [IB_WC_LOC_EEC_OP_ERR] = "local EE context operation error",
+ [IB_WC_LOC_PROT_ERR] = "local protection error",
+ [IB_WC_WR_FLUSH_ERR] = "WR flushed",
+ [IB_WC_MW_BIND_ERR] = "memory management operation error",
+ [IB_WC_BAD_RESP_ERR] = "bad response error",
+ [IB_WC_LOC_ACCESS_ERR] = "local access error",
+ [IB_WC_REM_INV_REQ_ERR] = "invalid request error",
+ [IB_WC_REM_ACCESS_ERR] = "remote access error",
+ [IB_WC_REM_OP_ERR] = "remote operation error",
+ [IB_WC_RETRY_EXC_ERR] = "transport retry counter exceeded",
+ [IB_WC_RNR_RETRY_EXC_ERR] = "RNR retry counter exceeded",
+ [IB_WC_LOC_RDD_VIOL_ERR] = "local RDD violation error",
+ [IB_WC_REM_INV_RD_REQ_ERR] = "remote invalid RD request",
+ [IB_WC_REM_ABORT_ERR] = "operation aborted",
+ [IB_WC_INV_EECN_ERR] = "invalid EE context number",
+ [IB_WC_INV_EEC_STATE_ERR] = "invalid EE context state",
+ [IB_WC_FATAL_ERR] = "fatal error",
+ [IB_WC_RESP_TIMEOUT_ERR] = "response timeout error",
+ [IB_WC_GENERAL_ERR] = "general error",
+};
+
+const char *ib_wc_status_msg(enum ib_wc_status status)
+{
+ size_t index = status;
+
+ return (index < ARRAY_SIZE(wc_statuses) && wc_statuses[index]) ?
+ wc_statuses[index] : "unrecognized status";
+}
+EXPORT_SYMBOL(ib_wc_status_msg);
+
__attribute_const__ int ib_rate_to_mult(enum ib_rate rate)
{
switch (rate) {
@@ -192,17 +257,16 @@ struct ib_ah *ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
}
EXPORT_SYMBOL(ib_create_ah);
-int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, struct ib_wc *wc,
- struct ib_grh *grh, struct ib_ah_attr *ah_attr)
+int ib_init_ah_from_wc(struct ib_device *device, u8 port_num,
+ const struct ib_wc *wc, const struct ib_grh *grh,
+ struct ib_ah_attr *ah_attr)
{
u32 flow_class;
u16 gid_index;
int ret;
- int is_eth = (rdma_port_get_link_layer(device, port_num) ==
- IB_LINK_LAYER_ETHERNET);
memset(ah_attr, 0, sizeof *ah_attr);
- if (is_eth) {
+ if (rdma_cap_eth_ah(device, port_num)) {
if (!(wc->wc_flags & IB_WC_GRH))
return -EPROTOTYPE;
@@ -244,8 +308,8 @@ int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, struct ib_wc *wc,
}
EXPORT_SYMBOL(ib_init_ah_from_wc);
-struct ib_ah *ib_create_ah_from_wc(struct ib_pd *pd, struct ib_wc *wc,
- struct ib_grh *grh, u8 port_num)
+struct ib_ah *ib_create_ah_from_wc(struct ib_pd *pd, const struct ib_wc *wc,
+ const struct ib_grh *grh, u8 port_num)
{
struct ib_ah_attr ah_attr;
int ret;
@@ -871,7 +935,7 @@ int ib_resolve_eth_l2_attrs(struct ib_qp *qp,
union ib_gid sgid;
if ((*qp_attr_mask & IB_QP_AV) &&
- (rdma_port_get_link_layer(qp->device, qp_attr->ah_attr.port_num) == IB_LINK_LAYER_ETHERNET)) {
+ (rdma_cap_eth_ah(qp->device, qp_attr->ah_attr.port_num))) {
ret = ib_query_gid(qp->device, qp_attr->ah_attr.port_num,
qp_attr->ah_attr.grh.sgid_index, &sgid);
if (ret)
@@ -1012,11 +1076,12 @@ EXPORT_SYMBOL(ib_destroy_qp);
struct ib_cq *ib_create_cq(struct ib_device *device,
ib_comp_handler comp_handler,
void (*event_handler)(struct ib_event *, void *),
- void *cq_context, int cqe, int comp_vector)
+ void *cq_context,
+ const struct ib_cq_init_attr *cq_attr)
{
struct ib_cq *cq;
- cq = device->create_cq(device, cqe, comp_vector, NULL, NULL);
+ cq = device->create_cq(device, cq_attr, NULL, NULL);
if (!IS_ERR(cq)) {
cq->device = device;
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c
index bdf3507810cb..25c3f0085563 100644
--- a/drivers/infiniband/hw/amso1100/c2_provider.c
+++ b/drivers/infiniband/hw/amso1100/c2_provider.c
@@ -63,13 +63,16 @@
#include "c2_provider.h"
#include "c2_user.h"
-static int c2_query_device(struct ib_device *ibdev,
- struct ib_device_attr *props)
+static int c2_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
struct c2_dev *c2dev = to_c2dev(ibdev);
pr_debug("%s:%u\n", __func__, __LINE__);
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
+
*props = c2dev->props;
return 0;
}
@@ -286,13 +289,18 @@ static int c2_destroy_qp(struct ib_qp *ib_qp)
return 0;
}
-static struct ib_cq *c2_create_cq(struct ib_device *ibdev, int entries, int vector,
+static struct ib_cq *c2_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
struct ib_ucontext *context,
struct ib_udata *udata)
{
+ int entries = attr->cqe;
struct c2_cq *cq;
int err;
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
+
cq = kmalloc(sizeof(*cq), GFP_KERNEL);
if (!cq) {
pr_debug("%s: Unable to allocate CQ\n", __func__);
@@ -582,9 +590,13 @@ static int c2_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
static int c2_process_mad(struct ib_device *ibdev,
int mad_flags,
u8 port_num,
- struct ib_wc *in_wc,
- struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in_mad,
+ size_t in_mad_size,
+ struct ib_mad_hdr *out_mad,
+ size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
pr_debug("%s:%u\n", __func__, __LINE__);
return -ENOSYS;
@@ -757,6 +769,23 @@ static struct net_device *c2_pseudo_netdev_init(struct c2_dev *c2dev)
return netdev;
}
+static int c2_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = c2_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IWARP;
+
+ return 0;
+}
+
int c2_register_device(struct c2_dev *dev)
{
int ret = -ENOMEM;
@@ -820,6 +849,7 @@ int c2_register_device(struct c2_dev *dev)
dev->ibdev.reg_phys_mr = c2_reg_phys_mr;
dev->ibdev.reg_user_mr = c2_reg_user_mr;
dev->ibdev.dereg_mr = c2_dereg_mr;
+ dev->ibdev.get_port_immutable = c2_port_immutable;
dev->ibdev.alloc_fmr = NULL;
dev->ibdev.unmap_fmr = NULL;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c
index 811b24a539c0..b1b73232f217 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c
@@ -85,9 +85,13 @@ static int iwch_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
static int iwch_process_mad(struct ib_device *ibdev,
int mad_flags,
u8 port_num,
- struct ib_wc *in_wc,
- struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in_mad,
+ size_t in_mad_size,
+ struct ib_mad_hdr *out_mad,
+ size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
return -ENOSYS;
}
@@ -138,10 +142,12 @@ static int iwch_destroy_cq(struct ib_cq *ib_cq)
return 0;
}
-static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int vector,
- struct ib_ucontext *ib_context,
- struct ib_udata *udata)
+static struct ib_cq *iwch_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *ib_context,
+ struct ib_udata *udata)
{
+ int entries = attr->cqe;
struct iwch_dev *rhp;
struct iwch_cq *chp;
struct iwch_create_cq_resp uresp;
@@ -151,6 +157,9 @@ static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int ve
size_t resplen;
PDBG("%s ib_dev %p entries %d\n", __func__, ibdev, entries);
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
+
rhp = to_iwch_dev(ibdev);
chp = kzalloc(sizeof(*chp), GFP_KERNEL);
if (!chp)
@@ -1145,13 +1154,17 @@ static u64 fw_vers_string_to_u64(struct iwch_dev *iwch_dev)
(fw_mic & 0xffff);
}
-static int iwch_query_device(struct ib_device *ibdev,
- struct ib_device_attr *props)
+static int iwch_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
struct iwch_dev *dev;
+
PDBG("%s ibdev %p\n", __func__, ibdev);
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
+
dev = to_iwch_dev(ibdev);
memset(props, 0, sizeof *props);
memcpy(&props->sys_image_guid, dev->rdev.t3cdev_p->lldev->dev_addr, 6);
@@ -1343,6 +1356,23 @@ static struct device_attribute *iwch_class_attributes[] = {
&dev_attr_board_id,
};
+static int iwch_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = iwch_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IWARP;
+
+ return 0;
+}
+
int iwch_register_device(struct iwch_dev *dev)
{
int ret;
@@ -1420,6 +1450,7 @@ int iwch_register_device(struct iwch_dev *dev)
dev->ibdev.post_recv = iwch_post_receive;
dev->ibdev.get_protocol_stats = iwch_get_mib;
dev->ibdev.uverbs_abi_ver = IWCH_UVERBS_ABI_VERSION;
+ dev->ibdev.get_port_immutable = iwch_port_immutable;
dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
if (!dev->ibdev.iwcm)
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 57176ddd4c50..3ad8dc798f52 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -583,6 +583,22 @@ static void c4iw_record_pm_msg(struct c4iw_ep *ep,
sizeof(ep->com.mapped_remote_addr));
}
+static int get_remote_addr(struct c4iw_ep *parent_ep, struct c4iw_ep *child_ep)
+{
+ int ret;
+
+ print_addr(&parent_ep->com, __func__, "get_remote_addr parent_ep ");
+ print_addr(&child_ep->com, __func__, "get_remote_addr child_ep ");
+
+ ret = iwpm_get_remote_info(&parent_ep->com.mapped_local_addr,
+ &child_ep->com.mapped_remote_addr,
+ &child_ep->com.remote_addr, RDMA_NL_C4IW);
+ if (ret)
+ PDBG("Unable to find remote peer addr info - err %d\n", ret);
+
+ return ret;
+}
+
static void best_mtu(const unsigned short *mtus, unsigned short mtu,
unsigned int *idx, int use_ts, int ipv6)
{
@@ -675,7 +691,7 @@ static int send_connect(struct c4iw_ep *ep)
if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) {
opt2 |= T5_OPT_2_VALID_F;
opt2 |= CONG_CNTRL_V(CONG_ALG_TAHOE);
- opt2 |= CONG_CNTRL_VALID; /* OPT_2_ISS for T5 */
+ opt2 |= T5_ISS_F;
}
t4_set_arp_err_handler(skb, ep, act_open_req_arp_failure);
@@ -2042,9 +2058,12 @@ static int act_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
status, status2errno(status));
if (is_neg_adv(status)) {
- dev_warn(&dev->rdev.lldi.pdev->dev,
- "Connection problems for atid %u status %u (%s)\n",
- atid, status, neg_adv_str(status));
+ PDBG("%s Connection problems for atid %u status %u (%s)\n",
+ __func__, atid, status, neg_adv_str(status));
+ ep->stats.connect_neg_adv++;
+ mutex_lock(&dev->rdev.stats.lock);
+ dev->rdev.stats.neg_adv++;
+ mutex_unlock(&dev->rdev.stats.lock);
return 0;
}
@@ -2214,7 +2233,7 @@ static void accept_cr(struct c4iw_ep *ep, struct sk_buff *skb,
u32 isn = (prandom_u32() & ~7UL) - 1;
opt2 |= T5_OPT_2_VALID_F;
opt2 |= CONG_CNTRL_V(CONG_ALG_TAHOE);
- opt2 |= CONG_CNTRL_VALID; /* OPT_2_ISS for T5 */
+ opt2 |= T5_ISS_F;
rpl5 = (void *)rpl;
memset(&rpl5->iss, 0, roundup(sizeof(*rpl5)-sizeof(*rpl), 16));
if (peer2peer)
@@ -2352,27 +2371,57 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
state_set(&child_ep->com, CONNECTING);
child_ep->com.dev = dev;
child_ep->com.cm_id = NULL;
+
+ /*
+ * The mapped_local and mapped_remote addresses get setup with
+ * the actual 4-tuple. The local address will be based on the
+ * actual local address of the connection, but on the port number
+ * of the parent listening endpoint. The remote address is
+ * setup based on a query to the IWPM since we don't know what it
+ * originally was before mapping. If no mapping was done, then
+ * mapped_remote == remote, and mapped_local == local.
+ */
if (iptype == 4) {
struct sockaddr_in *sin = (struct sockaddr_in *)
- &child_ep->com.local_addr;
+ &child_ep->com.mapped_local_addr;
+
sin->sin_family = PF_INET;
sin->sin_port = local_port;
sin->sin_addr.s_addr = *(__be32 *)local_ip;
- sin = (struct sockaddr_in *)&child_ep->com.remote_addr;
+
+ sin = (struct sockaddr_in *)&child_ep->com.local_addr;
+ sin->sin_family = PF_INET;
+ sin->sin_port = ((struct sockaddr_in *)
+ &parent_ep->com.local_addr)->sin_port;
+ sin->sin_addr.s_addr = *(__be32 *)local_ip;
+
+ sin = (struct sockaddr_in *)&child_ep->com.mapped_remote_addr;
sin->sin_family = PF_INET;
sin->sin_port = peer_port;
sin->sin_addr.s_addr = *(__be32 *)peer_ip;
} else {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)
- &child_ep->com.local_addr;
+ &child_ep->com.mapped_local_addr;
+
sin6->sin6_family = PF_INET6;
sin6->sin6_port = local_port;
memcpy(sin6->sin6_addr.s6_addr, local_ip, 16);
- sin6 = (struct sockaddr_in6 *)&child_ep->com.remote_addr;
+
+ sin6 = (struct sockaddr_in6 *)&child_ep->com.local_addr;
+ sin6->sin6_family = PF_INET6;
+ sin6->sin6_port = ((struct sockaddr_in6 *)
+ &parent_ep->com.local_addr)->sin6_port;
+ memcpy(sin6->sin6_addr.s6_addr, local_ip, 16);
+
+ sin6 = (struct sockaddr_in6 *)&child_ep->com.mapped_remote_addr;
sin6->sin6_family = PF_INET6;
sin6->sin6_port = peer_port;
memcpy(sin6->sin6_addr.s6_addr, peer_ip, 16);
}
+ memcpy(&child_ep->com.remote_addr, &child_ep->com.mapped_remote_addr,
+ sizeof(child_ep->com.remote_addr));
+ get_remote_addr(parent_ep, child_ep);
+
c4iw_get_ep(&parent_ep->com);
child_ep->parent_ep = parent_ep;
child_ep->tos = PASS_OPEN_TOS_G(ntohl(req->tos_stid));
@@ -2520,9 +2569,13 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
ep = lookup_tid(t, tid);
if (is_neg_adv(req->status)) {
- dev_warn(&dev->rdev.lldi.pdev->dev,
- "Negative advice on abort - tid %u status %d (%s)\n",
- ep->hwtid, req->status, neg_adv_str(req->status));
+ PDBG("%s Negative advice on abort- tid %u status %d (%s)\n",
+ __func__, ep->hwtid, req->status,
+ neg_adv_str(req->status));
+ ep->stats.abort_neg_adv++;
+ mutex_lock(&dev->rdev.stats.lock);
+ dev->rdev.stats.neg_adv++;
+ mutex_unlock(&dev->rdev.stats.lock);
return 0;
}
PDBG("%s ep %p tid %u state %u\n", __func__, ep, ep->hwtid,
@@ -3571,7 +3624,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb,
* TP will ignore any value > 0 for MSS index.
*/
req->tcb.opt0 = cpu_to_be64(MSS_IDX_V(0xF));
- req->cookie = (unsigned long)skb;
+ req->cookie = (uintptr_t)skb;
set_wr_txq(req_skb, CPL_PRIORITY_CONTROL, port_id);
ret = cxgb4_ofld_send(dev->rdev.lldi.ports[0], req_skb);
@@ -3931,9 +3984,11 @@ static int peer_abort_intr(struct c4iw_dev *dev, struct sk_buff *skb)
return 0;
}
if (is_neg_adv(req->status)) {
- dev_warn(&dev->rdev.lldi.pdev->dev,
- "Negative advice on abort - tid %u status %d (%s)\n",
- ep->hwtid, req->status, neg_adv_str(req->status));
+ PDBG("%s Negative advice on abort- tid %u status %d (%s)\n",
+ __func__, ep->hwtid, req->status,
+ neg_adv_str(req->status));
+ ep->stats.abort_neg_adv++;
+ dev->rdev.stats.neg_adv++;
kfree_skb(skb);
return 0;
}
diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c
index ab7692ac2044..c7aab48f07cd 100644
--- a/drivers/infiniband/hw/cxgb4/cq.c
+++ b/drivers/infiniband/hw/cxgb4/cq.c
@@ -55,7 +55,7 @@ static int destroy_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
FW_RI_RES_WR_NRES_V(1) |
FW_WR_COMPL_F);
res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16));
- res_wr->cookie = (unsigned long) &wr_wait;
+ res_wr->cookie = (uintptr_t)&wr_wait;
res = res_wr->res;
res->u.cq.restype = FW_RI_RES_TYPE_CQ;
res->u.cq.op = FW_RI_RES_OP_RESET;
@@ -125,7 +125,7 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
FW_RI_RES_WR_NRES_V(1) |
FW_WR_COMPL_F);
res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16));
- res_wr->cookie = (unsigned long) &wr_wait;
+ res_wr->cookie = (uintptr_t)&wr_wait;
res = res_wr->res;
res->u.cq.restype = FW_RI_RES_TYPE_CQ;
res->u.cq.op = FW_RI_RES_OP_WRITE;
@@ -158,10 +158,15 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
cq->gen = 1;
cq->gts = rdev->lldi.gts_reg;
cq->rdev = rdev;
- if (user) {
- cq->ugts = (u64)pci_resource_start(rdev->lldi.pdev, 2) +
- (cq->cqid << rdev->cqshift);
- cq->ugts &= PAGE_MASK;
+
+ cq->bar2_va = c4iw_bar2_addrs(rdev, cq->cqid, T4_BAR2_QTYPE_INGRESS,
+ &cq->bar2_qid,
+ user ? &cq->bar2_pa : NULL);
+ if (user && !cq->bar2_va) {
+ pr_warn(MOD "%s: cqid %u not in BAR2 range.\n",
+ pci_name(rdev->lldi.pdev), cq->cqid);
+ ret = -EINVAL;
+ goto err4;
}
return 0;
err4:
@@ -859,10 +864,13 @@ int c4iw_destroy_cq(struct ib_cq *ib_cq)
return 0;
}
-struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
- int vector, struct ib_ucontext *ib_context,
+struct ib_cq *c4iw_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *ib_context,
struct ib_udata *udata)
{
+ int entries = attr->cqe;
+ int vector = attr->comp_vector;
struct c4iw_dev *rhp;
struct c4iw_cq *chp;
struct c4iw_create_cq_resp uresp;
@@ -872,6 +880,8 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
struct c4iw_mm_entry *mm, *mm2;
PDBG("%s ib_dev %p entries %d\n", __func__, ibdev, entries);
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
rhp = to_c4iw_dev(ibdev);
@@ -964,14 +974,13 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
insert_mmap(ucontext, mm);
mm2->key = uresp.gts_key;
- mm2->addr = chp->cq.ugts;
+ mm2->addr = chp->cq.bar2_pa;
mm2->len = PAGE_SIZE;
insert_mmap(ucontext, mm2);
}
PDBG("%s cqid 0x%0x chp %p size %u memsize %zu, dma_addr 0x%0llx\n",
__func__, chp->cq.cqid, chp, chp->cq.size,
- chp->cq.memsize,
- (unsigned long long) chp->cq.dma_addr);
+ chp->cq.memsize, (unsigned long long) chp->cq.dma_addr);
return &chp->ibcq;
err5:
kfree(mm2);
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index 8fb295e4a9ab..1a297391b54c 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -93,6 +93,7 @@ static struct ibnl_client_cbs c4iw_nl_cb_table[] = {
[RDMA_NL_IWPM_ADD_MAPPING] = {.dump = iwpm_add_mapping_cb},
[RDMA_NL_IWPM_QUERY_MAPPING] = {.dump = iwpm_add_and_query_mapping_cb},
[RDMA_NL_IWPM_HANDLE_ERR] = {.dump = iwpm_mapping_error_cb},
+ [RDMA_NL_IWPM_REMOTE_INFO] = {.dump = iwpm_remote_info_cb},
[RDMA_NL_IWPM_MAPINFO] = {.dump = iwpm_mapping_info_cb},
[RDMA_NL_IWPM_MAPINFO_NUM] = {.dump = iwpm_ack_mapping_info_cb}
};
@@ -151,7 +152,7 @@ static int wr_log_show(struct seq_file *seq, void *v)
int prev_ts_set = 0;
int idx, end;
-#define ts2ns(ts) div64_ul((ts) * dev->rdev.lldi.cclk_ps, 1000)
+#define ts2ns(ts) div64_u64((ts) * dev->rdev.lldi.cclk_ps, 1000)
idx = atomic_read(&dev->rdev.wr_log_idx) &
(dev->rdev.wr_log_size - 1);
@@ -489,6 +490,7 @@ static int stats_show(struct seq_file *seq, void *v)
dev->rdev.stats.act_ofld_conn_fails);
seq_printf(seq, "PAS_OFLD_CONN_FAILS: %10llu\n",
dev->rdev.stats.pas_ofld_conn_fails);
+ seq_printf(seq, "NEG_ADV_RCVD: %10llu\n", dev->rdev.stats.neg_adv);
seq_printf(seq, "AVAILABLE IRD: %10u\n", dev->avail_ird);
return 0;
}
@@ -560,10 +562,13 @@ static int dump_ep(int id, void *p, void *data)
cc = snprintf(epd->buf + epd->pos, space,
"ep %p cm_id %p qp %p state %d flags 0x%lx "
"history 0x%lx hwtid %d atid %d "
+ "conn_na %u abort_na %u "
"%pI4:%d/%d <-> %pI4:%d/%d\n",
ep, ep->com.cm_id, ep->com.qp,
(int)ep->com.state, ep->com.flags,
ep->com.history, ep->hwtid, ep->atid,
+ ep->stats.connect_neg_adv,
+ ep->stats.abort_neg_adv,
&lsin->sin_addr, ntohs(lsin->sin_port),
ntohs(mapped_lsin->sin_port),
&rsin->sin_addr, ntohs(rsin->sin_port),
@@ -581,10 +586,13 @@ static int dump_ep(int id, void *p, void *data)
cc = snprintf(epd->buf + epd->pos, space,
"ep %p cm_id %p qp %p state %d flags 0x%lx "
"history 0x%lx hwtid %d atid %d "
+ "conn_na %u abort_na %u "
"%pI6:%d/%d <-> %pI6:%d/%d\n",
ep, ep->com.cm_id, ep->com.qp,
(int)ep->com.state, ep->com.flags,
ep->com.history, ep->hwtid, ep->atid,
+ ep->stats.connect_neg_adv,
+ ep->stats.abort_neg_adv,
&lsin6->sin6_addr, ntohs(lsin6->sin6_port),
ntohs(mapped_lsin6->sin6_port),
&rsin6->sin6_addr, ntohs(rsin6->sin6_port),
@@ -765,12 +773,29 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev)
c4iw_init_dev_ucontext(rdev, &rdev->uctx);
/*
- * qpshift is the number of bits to shift the qpid left in order
- * to get the correct address of the doorbell for that qp.
+ * This implementation assumes udb_density == ucq_density! Eventually
+ * we might need to support this but for now fail the open. Also the
+ * cqid and qpid range must match for now.
*/
- rdev->qpshift = PAGE_SHIFT - ilog2(rdev->lldi.udb_density);
+ if (rdev->lldi.udb_density != rdev->lldi.ucq_density) {
+ pr_err(MOD "%s: unsupported udb/ucq densities %u/%u\n",
+ pci_name(rdev->lldi.pdev), rdev->lldi.udb_density,
+ rdev->lldi.ucq_density);
+ err = -EINVAL;
+ goto err1;
+ }
+ if (rdev->lldi.vr->qp.start != rdev->lldi.vr->cq.start ||
+ rdev->lldi.vr->qp.size != rdev->lldi.vr->cq.size) {
+ pr_err(MOD "%s: unsupported qp and cq id ranges "
+ "qp start %u size %u cq start %u size %u\n",
+ pci_name(rdev->lldi.pdev), rdev->lldi.vr->qp.start,
+ rdev->lldi.vr->qp.size, rdev->lldi.vr->cq.size,
+ rdev->lldi.vr->cq.size);
+ err = -EINVAL;
+ goto err1;
+ }
+
rdev->qpmask = rdev->lldi.udb_density - 1;
- rdev->cqshift = PAGE_SHIFT - ilog2(rdev->lldi.ucq_density);
rdev->cqmask = rdev->lldi.ucq_density - 1;
PDBG("%s dev %s stag start 0x%0x size 0x%0x num stags %d "
"pbl start 0x%0x size 0x%0x rq start 0x%0x size 0x%0x "
@@ -784,14 +809,12 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev)
rdev->lldi.vr->qp.size,
rdev->lldi.vr->cq.start,
rdev->lldi.vr->cq.size);
- PDBG("udb len 0x%x udb base %llx db_reg %p gts_reg %p qpshift %lu "
- "qpmask 0x%x cqshift %lu cqmask 0x%x\n",
+ PDBG("udb len 0x%x udb base %p db_reg %p gts_reg %p "
+ "qpmask 0x%x cqmask 0x%x\n",
(unsigned)pci_resource_len(rdev->lldi.pdev, 2),
- (u64)pci_resource_start(rdev->lldi.pdev, 2),
- rdev->lldi.db_reg,
- rdev->lldi.gts_reg,
- rdev->qpshift, rdev->qpmask,
- rdev->cqshift, rdev->cqmask);
+ (void *)pci_resource_start(rdev->lldi.pdev, 2),
+ rdev->lldi.db_reg, rdev->lldi.gts_reg,
+ rdev->qpmask, rdev->cqmask);
if (c4iw_num_stags(rdev) == 0) {
err = -EINVAL;
@@ -1355,7 +1378,7 @@ static void recover_lost_dbs(struct uld_ctx *ctx, struct qp_list *qp_list)
t4_sq_host_wq_pidx(&qp->wq),
t4_sq_wq_size(&qp->wq));
if (ret) {
- pr_err(KERN_ERR MOD "%s: Fatal error - "
+ pr_err(MOD "%s: Fatal error - "
"DB overflow recovery failed - "
"error syncing SQ qid %u\n",
pci_name(ctx->lldi.pdev), qp->wq.sq.qid);
@@ -1371,7 +1394,7 @@ static void recover_lost_dbs(struct uld_ctx *ctx, struct qp_list *qp_list)
t4_rq_wq_size(&qp->wq));
if (ret) {
- pr_err(KERN_ERR MOD "%s: Fatal error - "
+ pr_err(MOD "%s: Fatal error - "
"DB overflow recovery failed - "
"error syncing RQ qid %u\n",
pci_name(ctx->lldi.pdev), qp->wq.rq.qid);
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index d87e1650f643..cc77844fada3 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -137,6 +137,7 @@ struct c4iw_stats {
u64 tcam_full;
u64 act_ofld_conn_fails;
u64 pas_ofld_conn_fails;
+ u64 neg_adv;
};
struct c4iw_hw_queue {
@@ -164,9 +165,7 @@ struct wr_log_entry {
struct c4iw_rdev {
struct c4iw_resource resource;
- unsigned long qpshift;
u32 qpmask;
- unsigned long cqshift;
u32 cqmask;
struct c4iw_dev_ucontext uctx;
struct gen_pool *pbl_pool;
@@ -814,6 +813,11 @@ struct c4iw_listen_ep {
int backlog;
};
+struct c4iw_ep_stats {
+ unsigned connect_neg_adv;
+ unsigned abort_neg_adv;
+};
+
struct c4iw_ep {
struct c4iw_ep_common com;
struct c4iw_ep *parent_ep;
@@ -846,6 +850,7 @@ struct c4iw_ep {
unsigned int retry_count;
int snd_win;
int rcv_win;
+ struct c4iw_ep_stats stats;
};
static inline void print_addr(struct c4iw_ep_common *epc, const char *func,
@@ -985,10 +990,10 @@ int c4iw_reregister_phys_mem(struct ib_mr *mr,
int acc, u64 *iova_start);
int c4iw_dereg_mr(struct ib_mr *ib_mr);
int c4iw_destroy_cq(struct ib_cq *ib_cq);
-struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
- int vector,
- struct ib_ucontext *ib_context,
- struct ib_udata *udata);
+struct ib_cq *c4iw_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *ib_context,
+ struct ib_udata *udata);
int c4iw_resize_cq(struct ib_cq *cq, int cqe, struct ib_udata *udata);
int c4iw_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags);
int c4iw_destroy_qp(struct ib_qp *ib_qp);
@@ -1025,6 +1030,9 @@ void c4iw_ev_dispatch(struct c4iw_dev *dev, struct t4_cqe *err_cqe);
extern struct cxgb4_client t4c_client;
extern c4iw_handler_func c4iw_handlers[NUM_CPL_CMDS];
+void __iomem *c4iw_bar2_addrs(struct c4iw_rdev *rdev, unsigned int qid,
+ enum cxgb4_bar2_qtype qtype,
+ unsigned int *pbar2_qid, u64 *pbar2_pa);
extern void c4iw_log_wr_stats(struct t4_wq *wq, struct t4_cqe *cqe);
extern int c4iw_wr_log;
extern int db_fc_threshold;
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index 3ef0cf9f5c44..cff815b91707 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -144,7 +144,7 @@ static int _c4iw_write_mem_inline(struct c4iw_rdev *rdev, u32 addr, u32 len,
if (i == (num_wqe-1)) {
req->wr.wr_hi = cpu_to_be32(FW_WR_OP_V(FW_ULPTX_WR) |
FW_WR_COMPL_F);
- req->wr.wr_lo = (__force __be64)(unsigned long) &wr_wait;
+ req->wr.wr_lo = (__force __be64)&wr_wait;
} else
req->wr.wr_hi = cpu_to_be32(FW_WR_OP_V(FW_ULPTX_WR));
req->wr.wr_mid = cpu_to_be32(
@@ -676,12 +676,12 @@ struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc)
mhp->attr.zbva = 0;
mhp->attr.va_fbo = 0;
mhp->attr.page_size = 0;
- mhp->attr.len = ~0UL;
+ mhp->attr.len = ~0ULL;
mhp->attr.pbl_size = 0;
ret = write_tpt_entry(&rhp->rdev, 0, &stag, 1, php->pdid,
FW_RI_STAG_NSMR, mhp->attr.perms,
- mhp->attr.mw_bind_enable, 0, 0, ~0UL, 0, 0, 0);
+ mhp->attr.mw_bind_enable, 0, 0, ~0ULL, 0, 0, 0);
if (ret)
goto err1;
diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c
index 66bd6a2ad83b..62c816af46e4 100644
--- a/drivers/infiniband/hw/cxgb4/provider.c
+++ b/drivers/infiniband/hw/cxgb4/provider.c
@@ -80,9 +80,13 @@ static int c4iw_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
}
static int c4iw_process_mad(struct ib_device *ibdev, int mad_flags,
- u8 port_num, struct ib_wc *in_wc,
- struct ib_grh *in_grh, struct ib_mad *in_mad,
- struct ib_mad *out_mad)
+ u8 port_num, const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in_mad,
+ size_t in_mad_size,
+ struct ib_mad_hdr *out_mad,
+ size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
return -ENOSYS;
}
@@ -301,13 +305,17 @@ static int c4iw_query_gid(struct ib_device *ibdev, u8 port, int index,
return 0;
}
-static int c4iw_query_device(struct ib_device *ibdev,
- struct ib_device_attr *props)
+static int c4iw_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
struct c4iw_dev *dev;
+
PDBG("%s ibdev %p\n", __func__, ibdev);
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
+
dev = to_c4iw_dev(ibdev);
memset(props, 0, sizeof *props);
memcpy(&props->sys_image_guid, dev->rdev.lldi.ports[0]->dev_addr, 6);
@@ -465,6 +473,23 @@ static struct device_attribute *c4iw_class_attributes[] = {
&dev_attr_board_id,
};
+static int c4iw_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = c4iw_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IWARP;
+
+ return 0;
+}
+
int c4iw_register_device(struct c4iw_dev *dev)
{
int ret;
@@ -542,6 +567,7 @@ int c4iw_register_device(struct c4iw_dev *dev)
dev->ibdev.post_recv = c4iw_post_receive;
dev->ibdev.get_protocol_stats = c4iw_get_mib;
dev->ibdev.uverbs_abi_ver = C4IW_UVERBS_ABI_VERSION;
+ dev->ibdev.get_port_immutable = c4iw_port_immutable;
dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
if (!dev->ibdev.iwcm)
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index 15cae5a31018..6517e1208ccb 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -165,6 +165,29 @@ static int destroy_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
return 0;
}
+/*
+ * Determine the BAR2 virtual address and qid. If pbar2_pa is not NULL,
+ * then this is a user mapping so compute the page-aligned physical address
+ * for mapping.
+ */
+void __iomem *c4iw_bar2_addrs(struct c4iw_rdev *rdev, unsigned int qid,
+ enum cxgb4_bar2_qtype qtype,
+ unsigned int *pbar2_qid, u64 *pbar2_pa)
+{
+ u64 bar2_qoffset;
+ int ret;
+
+ ret = cxgb4_bar2_sge_qregs(rdev->lldi.ports[0], qid, qtype,
+ pbar2_pa ? 1 : 0,
+ &bar2_qoffset, pbar2_qid);
+ if (ret)
+ return NULL;
+
+ if (pbar2_pa)
+ *pbar2_pa = (rdev->bar2_pa + bar2_qoffset) & PAGE_MASK;
+ return rdev->bar2_kva + bar2_qoffset;
+}
+
static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
struct t4_cq *rcq, struct t4_cq *scq,
struct c4iw_dev_ucontext *uctx)
@@ -236,25 +259,23 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
dma_unmap_addr_set(&wq->rq, mapping, wq->rq.dma_addr);
wq->db = rdev->lldi.db_reg;
- wq->gts = rdev->lldi.gts_reg;
- if (user || is_t5(rdev->lldi.adapter_type)) {
- u32 off;
- off = (wq->sq.qid << rdev->qpshift) & PAGE_MASK;
- if (user) {
- wq->sq.udb = (u64 __iomem *)(rdev->bar2_pa + off);
- } else {
- off += 128 * (wq->sq.qid & rdev->qpmask) + 8;
- wq->sq.udb = (u64 __iomem *)(rdev->bar2_kva + off);
- }
- off = (wq->rq.qid << rdev->qpshift) & PAGE_MASK;
- if (user) {
- wq->rq.udb = (u64 __iomem *)(rdev->bar2_pa + off);
- } else {
- off += 128 * (wq->rq.qid & rdev->qpmask) + 8;
- wq->rq.udb = (u64 __iomem *)(rdev->bar2_kva + off);
- }
+ wq->sq.bar2_va = c4iw_bar2_addrs(rdev, wq->sq.qid, T4_BAR2_QTYPE_EGRESS,
+ &wq->sq.bar2_qid,
+ user ? &wq->sq.bar2_pa : NULL);
+ wq->rq.bar2_va = c4iw_bar2_addrs(rdev, wq->rq.qid, T4_BAR2_QTYPE_EGRESS,
+ &wq->rq.bar2_qid,
+ user ? &wq->rq.bar2_pa : NULL);
+
+ /*
+ * User mode must have bar2 access.
+ */
+ if (user && (!wq->sq.bar2_va || !wq->rq.bar2_va)) {
+ pr_warn(MOD "%s: sqid %u or rqid %u not in BAR2 range.\n",
+ pci_name(rdev->lldi.pdev), wq->sq.qid, wq->rq.qid);
+ goto free_dma;
}
+
wq->rdev = rdev;
wq->rq.msn = 1;
@@ -275,7 +296,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
FW_RI_RES_WR_NRES_V(2) |
FW_WR_COMPL_F);
res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16));
- res_wr->cookie = (unsigned long) &wr_wait;
+ res_wr->cookie = (uintptr_t)&wr_wait;
res = res_wr->res;
res->u.sqrq.restype = FW_RI_RES_TYPE_SQ;
res->u.sqrq.op = FW_RI_RES_OP_WRITE;
@@ -336,10 +357,9 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
if (ret)
goto free_dma;
- PDBG("%s sqid 0x%x rqid 0x%x kdb 0x%p squdb 0x%lx rqudb 0x%lx\n",
+ PDBG("%s sqid 0x%x rqid 0x%x kdb 0x%p sq_bar2_addr %p rq_bar2_addr %p\n",
__func__, wq->sq.qid, wq->rq.qid, wq->db,
- (__force unsigned long) wq->sq.udb,
- (__force unsigned long) wq->rq.udb);
+ wq->sq.bar2_va, wq->rq.bar2_va);
return 0;
free_dma:
@@ -1209,7 +1229,7 @@ static int rdma_fini(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
wqe->flowid_len16 = cpu_to_be32(
FW_WR_FLOWID_V(ep->hwtid) |
FW_WR_LEN16_V(DIV_ROUND_UP(sizeof(*wqe), 16)));
- wqe->cookie = (unsigned long) &ep->com.wr_wait;
+ wqe->cookie = (uintptr_t)&ep->com.wr_wait;
wqe->u.fini.type = FW_RI_TYPE_FINI;
ret = c4iw_ofld_send(&rhp->rdev, skb);
@@ -1279,7 +1299,7 @@ static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp)
FW_WR_FLOWID_V(qhp->ep->hwtid) |
FW_WR_LEN16_V(DIV_ROUND_UP(sizeof(*wqe), 16)));
- wqe->cookie = (unsigned long) &qhp->ep->com.wr_wait;
+ wqe->cookie = (uintptr_t)&qhp->ep->com.wr_wait;
wqe->u.init.type = FW_RI_TYPE_INIT;
wqe->u.init.mpareqbit_p2ptype =
@@ -1766,11 +1786,11 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
mm2->len = PAGE_ALIGN(qhp->wq.rq.memsize);
insert_mmap(ucontext, mm2);
mm3->key = uresp.sq_db_gts_key;
- mm3->addr = (__force unsigned long) qhp->wq.sq.udb;
+ mm3->addr = (__force unsigned long)qhp->wq.sq.bar2_pa;
mm3->len = PAGE_SIZE;
insert_mmap(ucontext, mm3);
mm4->key = uresp.rq_db_gts_key;
- mm4->addr = (__force unsigned long) qhp->wq.rq.udb;
+ mm4->addr = (__force unsigned long)qhp->wq.rq.bar2_pa;
mm4->len = PAGE_SIZE;
insert_mmap(ucontext, mm4);
if (mm5) {
diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h
index 871cdcac7be2..274a7ab13bef 100644
--- a/drivers/infiniband/hw/cxgb4/t4.h
+++ b/drivers/infiniband/hw/cxgb4/t4.h
@@ -33,6 +33,7 @@
#include "t4_hw.h"
#include "t4_regs.h"
+#include "t4_values.h"
#include "t4_msg.h"
#include "t4fw_ri_api.h"
@@ -290,8 +291,10 @@ struct t4_sq {
unsigned long phys_addr;
struct t4_swsqe *sw_sq;
struct t4_swsqe *oldest_read;
- u64 __iomem *udb;
+ void __iomem *bar2_va;
+ u64 bar2_pa;
size_t memsize;
+ u32 bar2_qid;
u32 qid;
u16 in_use;
u16 size;
@@ -314,8 +317,10 @@ struct t4_rq {
dma_addr_t dma_addr;
DEFINE_DMA_UNMAP_ADDR(mapping);
struct t4_swrqe *sw_rq;
- u64 __iomem *udb;
+ void __iomem *bar2_va;
+ u64 bar2_pa;
size_t memsize;
+ u32 bar2_qid;
u32 qid;
u32 msn;
u32 rqt_hwaddr;
@@ -332,7 +337,6 @@ struct t4_wq {
struct t4_sq sq;
struct t4_rq rq;
void __iomem *db;
- void __iomem *gts;
struct c4iw_rdev *rdev;
int flushed;
};
@@ -457,15 +461,18 @@ static inline void t4_ring_sq_db(struct t4_wq *wq, u16 inc, u8 t5,
/* Flush host queue memory writes. */
wmb();
- if (t5) {
- if (inc == 1 && wqe) {
+ if (wq->sq.bar2_va) {
+ if (inc == 1 && wq->sq.bar2_qid == 0 && wqe) {
PDBG("%s: WC wq->sq.pidx = %d\n",
__func__, wq->sq.pidx);
- pio_copy(wq->sq.udb + 7, (void *)wqe);
+ pio_copy((u64 __iomem *)
+ (wq->sq.bar2_va + SGE_UDB_WCDOORBELL),
+ (u64 *)wqe);
} else {
PDBG("%s: DB wq->sq.pidx = %d\n",
__func__, wq->sq.pidx);
- writel(PIDX_T5_V(inc), wq->sq.udb);
+ writel(PIDX_T5_V(inc) | QID_V(wq->sq.bar2_qid),
+ wq->sq.bar2_va + SGE_UDB_KDOORBELL);
}
/* Flush user doorbell area writes. */
@@ -481,15 +488,18 @@ static inline void t4_ring_rq_db(struct t4_wq *wq, u16 inc, u8 t5,
/* Flush host queue memory writes. */
wmb();
- if (t5) {
- if (inc == 1 && wqe) {
+ if (wq->rq.bar2_va) {
+ if (inc == 1 && wq->rq.bar2_qid == 0 && wqe) {
PDBG("%s: WC wq->rq.pidx = %d\n",
__func__, wq->rq.pidx);
- pio_copy(wq->rq.udb + 7, (void *)wqe);
+ pio_copy((u64 __iomem *)
+ (wq->rq.bar2_va + SGE_UDB_WCDOORBELL),
+ (void *)wqe);
} else {
PDBG("%s: DB wq->rq.pidx = %d\n",
__func__, wq->rq.pidx);
- writel(PIDX_T5_V(inc), wq->rq.udb);
+ writel(PIDX_T5_V(inc) | QID_V(wq->rq.bar2_qid),
+ wq->rq.bar2_va + SGE_UDB_KDOORBELL);
}
/* Flush user doorbell area writes. */
@@ -534,11 +544,14 @@ struct t4_cq {
DEFINE_DMA_UNMAP_ADDR(mapping);
struct t4_cqe *sw_queue;
void __iomem *gts;
+ void __iomem *bar2_va;
+ u64 bar2_pa;
+ u32 bar2_qid;
struct c4iw_rdev *rdev;
- u64 ugts;
size_t memsize;
__be64 bits_type_ts;
u32 cqid;
+ u32 qid_mask;
int vector;
u16 size; /* including status page */
u16 cidx;
@@ -551,6 +564,15 @@ struct t4_cq {
unsigned long flags;
};
+static inline void write_gts(struct t4_cq *cq, u32 val)
+{
+ if (cq->bar2_va)
+ writel(val | INGRESSQID_V(cq->bar2_qid),
+ cq->bar2_va + SGE_UDB_GTS);
+ else
+ writel(val | INGRESSQID_V(cq->cqid), cq->gts);
+}
+
static inline int t4_clear_cq_armed(struct t4_cq *cq)
{
return test_and_clear_bit(CQ_ARMED, &cq->flags);
@@ -562,14 +584,12 @@ static inline int t4_arm_cq(struct t4_cq *cq, int se)
set_bit(CQ_ARMED, &cq->flags);
while (cq->cidx_inc > CIDXINC_M) {
- val = SEINTARM_V(0) | CIDXINC_V(CIDXINC_M) | TIMERREG_V(7) |
- INGRESSQID_V(cq->cqid);
- writel(val, cq->gts);
+ val = SEINTARM_V(0) | CIDXINC_V(CIDXINC_M) | TIMERREG_V(7);
+ write_gts(cq, val);
cq->cidx_inc -= CIDXINC_M;
}
- val = SEINTARM_V(se) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(6) |
- INGRESSQID_V(cq->cqid);
- writel(val, cq->gts);
+ val = SEINTARM_V(se) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(6);
+ write_gts(cq, val);
cq->cidx_inc = 0;
return 0;
}
@@ -600,9 +620,8 @@ static inline void t4_hwcq_consume(struct t4_cq *cq)
if (++cq->cidx_inc == (cq->size >> 4) || cq->cidx_inc == CIDXINC_M) {
u32 val;
- val = SEINTARM_V(0) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(7) |
- INGRESSQID_V(cq->cqid);
- writel(val, cq->gts);
+ val = SEINTARM_V(0) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(7);
+ write_gts(cq, val);
cq->cidx_inc = 0;
}
if (++cq->cidx == cq->size) {
diff --git a/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h b/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h
index 5e53327fc647..343e8daf2270 100644
--- a/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h
+++ b/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h
@@ -848,6 +848,8 @@ enum { /* TCP congestion control algorithms */
#define CONG_CNTRL_V(x) ((x) << CONG_CNTRL_S)
#define CONG_CNTRL_G(x) (((x) >> CONG_CNTRL_S) & CONG_CNTRL_M)
-#define CONG_CNTRL_VALID (1 << 18)
+#define T5_ISS_S 18
+#define T5_ISS_V(x) ((x) << T5_ISS_S)
+#define T5_ISS_F T5_ISS_V(1U)
#endif /* _T4FW_RI_API_H_ */
diff --git a/drivers/infiniband/hw/ehca/ehca_cq.c b/drivers/infiniband/hw/ehca/ehca_cq.c
index 8cc837537768..9b68b175069b 100644
--- a/drivers/infiniband/hw/ehca/ehca_cq.c
+++ b/drivers/infiniband/hw/ehca/ehca_cq.c
@@ -113,10 +113,12 @@ struct ehca_qp *ehca_cq_get_qp(struct ehca_cq *cq, int real_qp_num)
return ret;
}
-struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
+struct ib_cq *ehca_create_cq(struct ib_device *device,
+ const struct ib_cq_init_attr *attr,
struct ib_ucontext *context,
struct ib_udata *udata)
{
+ int cqe = attr->cqe;
static const u32 additional_cqe = 20;
struct ib_cq *cq;
struct ehca_cq *my_cq;
@@ -131,6 +133,9 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
int ipz_rc, i;
unsigned long flags;
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
+
if (cqe >= 0xFFFFFFFF - 64 - additional_cqe)
return ERR_PTR(-EINVAL);
diff --git a/drivers/infiniband/hw/ehca/ehca_hca.c b/drivers/infiniband/hw/ehca/ehca_hca.c
index 9ed4d2588304..e8b1bb65797a 100644
--- a/drivers/infiniband/hw/ehca/ehca_hca.c
+++ b/drivers/infiniband/hw/ehca/ehca_hca.c
@@ -50,7 +50,8 @@ static unsigned int limit_uint(unsigned int value)
return min_t(unsigned int, value, INT_MAX);
}
-int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props)
+int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
int i, ret = 0;
struct ehca_shca *shca = container_of(ibdev, struct ehca_shca,
@@ -71,6 +72,9 @@ int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props)
IB_DEVICE_PORT_ACTIVE_EVENT, HCA_CAP_PORT_ACTIVE_EVENT,
};
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
+
rblock = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
if (!rblock) {
ehca_err(&shca->ib_device, "Can't allocate rblock memory.");
diff --git a/drivers/infiniband/hw/ehca/ehca_iverbs.h b/drivers/infiniband/hw/ehca/ehca_iverbs.h
index 22f79afa7fc1..80e6a3d5df3e 100644
--- a/drivers/infiniband/hw/ehca/ehca_iverbs.h
+++ b/drivers/infiniband/hw/ehca/ehca_iverbs.h
@@ -44,11 +44,15 @@
#include "ehca_classes.h"
-int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props);
+int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+ struct ib_udata *uhw);
int ehca_query_port(struct ib_device *ibdev, u8 port,
struct ib_port_attr *props);
+enum rdma_protocol_type
+ehca_query_protocol(struct ib_device *device, u8 port_num);
+
int ehca_query_sma_attr(struct ehca_shca *shca, u8 port,
struct ehca_sma_attr *attr);
@@ -126,7 +130,8 @@ int ehca_destroy_eq(struct ehca_shca *shca, struct ehca_eq *eq);
void *ehca_poll_eq(struct ehca_shca *shca, struct ehca_eq *eq);
-struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
+struct ib_cq *ehca_create_cq(struct ib_device *device,
+ const struct ib_cq_init_attr *attr,
struct ib_ucontext *context,
struct ib_udata *udata);
@@ -188,9 +193,10 @@ int ehca_dealloc_ucontext(struct ib_ucontext *context);
int ehca_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
int ehca_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad,
- struct ib_mad *out_mad);
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index);
void ehca_poll_eqs(unsigned long data);
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index cd8d290a09fc..8246418cd4e0 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -46,6 +46,7 @@
#include <linux/notifier.h>
#include <linux/memory.h>
+#include <rdma/ib_mad.h>
#include "ehca_classes.h"
#include "ehca_iverbs.h"
#include "ehca_mrmw.h"
@@ -431,6 +432,24 @@ init_node_guid1:
return ret;
}
+static int ehca_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = ehca_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IBA_IB;
+ immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+ return 0;
+}
+
static int ehca_init_device(struct ehca_shca *shca)
{
int ret;
@@ -510,6 +529,7 @@ static int ehca_init_device(struct ehca_shca *shca)
shca->ib_device.process_mad = ehca_process_mad;
shca->ib_device.mmap = ehca_mmap;
shca->ib_device.dma_ops = &ehca_dma_mapping_ops;
+ shca->ib_device.get_port_immutable = ehca_port_immutable;
if (EHCA_BMASK_GET(HCA_CAP_SRQ, shca->hca_cap)) {
shca->ib_device.uverbs_cmd_mask |=
@@ -534,6 +554,7 @@ static int ehca_create_aqp1(struct ehca_shca *shca, u32 port)
struct ib_cq *ibcq;
struct ib_qp *ibqp;
struct ib_qp_init_attr qp_init_attr;
+ struct ib_cq_init_attr cq_attr = {};
int ret;
if (sport->ibcq_aqp1) {
@@ -541,7 +562,9 @@ static int ehca_create_aqp1(struct ehca_shca *shca, u32 port)
return -EPERM;
}
- ibcq = ib_create_cq(&shca->ib_device, NULL, NULL, (void *)(-1), 10, 0);
+ cq_attr.cqe = 10;
+ ibcq = ib_create_cq(&shca->ib_device, NULL, NULL, (void *)(-1),
+ &cq_attr);
if (IS_ERR(ibcq)) {
ehca_err(&shca->ib_device, "Cannot create AQP1 CQ.");
return PTR_ERR(ibcq);
diff --git a/drivers/infiniband/hw/ehca/ehca_mcast.c b/drivers/infiniband/hw/ehca/ehca_mcast.c
index 120aedf9f989..cec181532924 100644
--- a/drivers/infiniband/hw/ehca/ehca_mcast.c
+++ b/drivers/infiniband/hw/ehca/ehca_mcast.c
@@ -77,7 +77,7 @@ int ehca_attach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
return -EINVAL;
}
- memcpy(&my_gid.raw, gid->raw, sizeof(union ib_gid));
+ memcpy(&my_gid, gid->raw, sizeof(union ib_gid));
subnet_prefix = be64_to_cpu(my_gid.global.subnet_prefix);
interface_id = be64_to_cpu(my_gid.global.interface_id);
@@ -114,7 +114,7 @@ int ehca_detach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
return -EINVAL;
}
- memcpy(&my_gid.raw, gid->raw, sizeof(union ib_gid));
+ memcpy(&my_gid, gid->raw, sizeof(union ib_gid));
subnet_prefix = be64_to_cpu(my_gid.global.subnet_prefix);
interface_id = be64_to_cpu(my_gid.global.interface_id);
diff --git a/drivers/infiniband/hw/ehca/ehca_sqp.c b/drivers/infiniband/hw/ehca/ehca_sqp.c
index dba8f9f8b996..12b5bc23832b 100644
--- a/drivers/infiniband/hw/ehca/ehca_sqp.c
+++ b/drivers/infiniband/hw/ehca/ehca_sqp.c
@@ -140,10 +140,10 @@ struct vertcfl {
} __attribute__ ((packed));
static int ehca_process_perf(struct ib_device *ibdev, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad *in_mad, struct ib_mad *out_mad)
{
- struct ib_perf *in_perf = (struct ib_perf *)in_mad;
+ const struct ib_perf *in_perf = (const struct ib_perf *)in_mad;
struct ib_perf *out_perf = (struct ib_perf *)out_mad;
struct ib_class_port_info *poi =
(struct ib_class_port_info *)out_perf->data;
@@ -187,8 +187,8 @@ static int ehca_process_perf(struct ib_device *ibdev, u8 port_num,
/* if request was globally routed, copy route info */
if (in_grh) {
- struct vertcfl *vertcfl =
- (struct vertcfl *)&in_grh->version_tclass_flow;
+ const struct vertcfl *vertcfl =
+ (const struct vertcfl *)&in_grh->version_tclass_flow;
memcpy(poi->redirect_gid, in_grh->dgid.raw,
sizeof(poi->redirect_gid));
tcslfl->tc = vertcfl->tc;
@@ -217,10 +217,17 @@ perf_reply:
}
int ehca_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
int ret;
+ const struct ib_mad *in_mad = (const struct ib_mad *)in;
+ struct ib_mad *out_mad = (struct ib_mad *)out;
+
+ BUG_ON(in_mad_size != sizeof(*in_mad) ||
+ *out_mad_size != sizeof(*out_mad));
if (!port_num || port_num > ibdev->phys_port_cnt || !in_wc)
return IB_MAD_RESULT_FAILURE;
diff --git a/drivers/infiniband/hw/ipath/Kconfig b/drivers/infiniband/hw/ipath/Kconfig
index 1d9bb115cbf6..8fe54ff00580 100644
--- a/drivers/infiniband/hw/ipath/Kconfig
+++ b/drivers/infiniband/hw/ipath/Kconfig
@@ -9,3 +9,6 @@ config INFINIBAND_IPATH
as IP-over-InfiniBand as well as with userspace applications
(in conjunction with InfiniBand userspace access).
For QLogic PCIe QLE based cards, use the QIB driver instead.
+
+ If you have this hardware you will need to boot with PAT disabled
+ on your x86-64 systems, use the nopat kernel parameter.
diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c
index 0416c6c0e126..e9dd9112e718 100644
--- a/drivers/infiniband/hw/ipath/ipath_cq.c
+++ b/drivers/infiniband/hw/ipath/ipath_cq.c
@@ -188,7 +188,7 @@ static void send_complete(unsigned long data)
/**
* ipath_create_cq - create a completion queue
* @ibdev: the device this completion queue is attached to
- * @entries: the minimum size of the completion queue
+ * @attr: creation attributes
* @context: unused by the InfiniPath driver
* @udata: unused by the InfiniPath driver
*
@@ -197,16 +197,21 @@ static void send_complete(unsigned long data)
*
* Called by ib_create_cq() in the generic verbs code.
*/
-struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, int comp_vector,
+struct ib_cq *ipath_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
struct ib_ucontext *context,
struct ib_udata *udata)
{
+ int entries = attr->cqe;
struct ipath_ibdev *dev = to_idev(ibdev);
struct ipath_cq *cq;
struct ipath_cq_wc *wc;
struct ib_cq *ret;
u32 sz;
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
+
if (entries < 1 || entries > ib_ipath_max_cqes) {
ret = ERR_PTR(-EINVAL);
goto done;
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index bd0caedafe99..2d7e503d13cb 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -42,6 +42,9 @@
#include <linux/bitmap.h>
#include <linux/slab.h>
#include <linux/module.h>
+#ifdef CONFIG_X86_64
+#include <asm/pat.h>
+#endif
#include "ipath_kernel.h"
#include "ipath_verbs.h"
@@ -395,6 +398,14 @@ static int ipath_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
unsigned long long addr;
u32 bar0 = 0, bar1 = 0;
+#ifdef CONFIG_X86_64
+ if (WARN(pat_enabled(),
+ "ipath needs PAT disabled, boot with nopat kernel parameter\n")) {
+ ret = -ENODEV;
+ goto bail;
+ }
+#endif
+
dd = ipath_alloc_devdata(pdev);
if (IS_ERR(dd)) {
ret = PTR_ERR(dd);
@@ -542,6 +553,7 @@ static int ipath_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dd->ipath_kregbase = __ioremap(addr, len,
(_PAGE_NO_CACHE|_PAGE_WRITETHRU));
#else
+ /* XXX: split this properly to enable on PAT */
dd->ipath_kregbase = ioremap_nocache(addr, len);
#endif
@@ -587,12 +599,8 @@ static int ipath_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
ret = ipath_enable_wc(dd);
- if (ret) {
- ipath_dev_err(dd, "Write combining not enabled "
- "(err %d): performance may be poor\n",
- -ret);
+ if (ret)
ret = 0;
- }
ipath_verify_pioperf(dd);
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h
index e08db7020cd4..f0f947122779 100644
--- a/drivers/infiniband/hw/ipath/ipath_kernel.h
+++ b/drivers/infiniband/hw/ipath/ipath_kernel.h
@@ -463,9 +463,7 @@ struct ipath_devdata {
/* offset in HT config space of slave/primary interface block */
u8 ipath_ht_slave_off;
/* for write combining settings */
- unsigned long ipath_wc_cookie;
- unsigned long ipath_wc_base;
- unsigned long ipath_wc_len;
+ int wc_cookie;
/* ref count for each pkey */
atomic_t ipath_pkeyrefs[4];
/* shadow copy of struct page *'s for exp tid pages */
diff --git a/drivers/infiniband/hw/ipath/ipath_mad.c b/drivers/infiniband/hw/ipath/ipath_mad.c
index e890e5ba0e01..948188e37f95 100644
--- a/drivers/infiniband/hw/ipath/ipath_mad.c
+++ b/drivers/infiniband/hw/ipath/ipath_mad.c
@@ -1257,7 +1257,7 @@ static int recv_pma_set_portcounters_ext(struct ib_pma_mad *pmp,
}
static int process_subn(struct ib_device *ibdev, int mad_flags,
- u8 port_num, struct ib_mad *in_mad,
+ u8 port_num, const struct ib_mad *in_mad,
struct ib_mad *out_mad)
{
struct ib_smp *smp = (struct ib_smp *)out_mad;
@@ -1389,7 +1389,7 @@ bail:
}
static int process_perf(struct ib_device *ibdev, u8 port_num,
- struct ib_mad *in_mad,
+ const struct ib_mad *in_mad,
struct ib_mad *out_mad)
{
struct ib_pma_mad *pmp = (struct ib_pma_mad *)out_mad;
@@ -1490,10 +1490,17 @@ bail:
* This is called by the ib_mad module.
*/
int ipath_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
int ret;
+ const struct ib_mad *in_mad = (const struct ib_mad *)in;
+ struct ib_mad *out_mad = (struct ib_mad *)out;
+
+ BUG_ON(in_mad_size != sizeof(*in_mad) ||
+ *out_mad_size != sizeof(*out_mad));
switch (in_mad->mad_hdr.mgmt_class) {
case IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE:
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
index 44ea9390417c..48253b839a6f 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -1495,11 +1495,14 @@ bail:
return 0;
}
-static int ipath_query_device(struct ib_device *ibdev,
- struct ib_device_attr *props)
+static int ipath_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
struct ipath_ibdev *dev = to_idev(ibdev);
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
+
memset(props, 0, sizeof(*props));
props->device_cap_flags = IB_DEVICE_BAD_PKEY_CNTR |
@@ -1980,6 +1983,24 @@ static int disable_timer(struct ipath_devdata *dd)
return 0;
}
+static int ipath_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = ipath_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IBA_IB;
+ immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+ return 0;
+}
+
/**
* ipath_register_ib_device - register our device with the infiniband core
* @dd: the device data structure
@@ -2179,6 +2200,7 @@ int ipath_register_ib_device(struct ipath_devdata *dd)
dev->process_mad = ipath_process_mad;
dev->mmap = ipath_mmap;
dev->dma_ops = &ipath_dma_mapping_ops;
+ dev->get_port_immutable = ipath_port_immutable;
snprintf(dev->node_desc, sizeof(dev->node_desc),
IPATH_IDSTR " %s", init_utsname()->nodename);
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h
index ae6cff4abffc..ec167e545e15 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.h
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.h
@@ -701,9 +701,11 @@ static inline void ipath_schedule_send(struct ipath_qp *qp)
int ipath_process_mad(struct ib_device *ibdev,
int mad_flags,
u8 port_num,
- struct ib_wc *in_wc,
- struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad);
+ const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index);
/*
* Compare the lower 24 bits of the two values.
@@ -807,7 +809,8 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig);
int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
-struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, int comp_vector,
+struct ib_cq *ipath_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
struct ib_ucontext *context,
struct ib_udata *udata);
diff --git a/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
index 4ad0b932df1f..7b6e4c843e19 100644
--- a/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
+++ b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
@@ -37,7 +37,6 @@
*/
#include <linux/pci.h>
-#include <asm/mtrr.h>
#include <asm/processor.h>
#include "ipath_kernel.h"
@@ -122,27 +121,14 @@ int ipath_enable_wc(struct ipath_devdata *dd)
}
if (!ret) {
- int cookie;
- ipath_cdbg(VERBOSE, "Setting mtrr for chip to WC "
- "(addr %llx, len=0x%llx)\n",
- (unsigned long long) pioaddr,
- (unsigned long long) piolen);
- cookie = mtrr_add(pioaddr, piolen, MTRR_TYPE_WRCOMB, 0);
- if (cookie < 0) {
- {
- dev_info(&dd->pcidev->dev,
- "mtrr_add() WC for PIO bufs "
- "failed (%d)\n",
- cookie);
- ret = -EINVAL;
- }
- } else {
- ipath_cdbg(VERBOSE, "Set mtrr for chip to WC, "
- "cookie is %d\n", cookie);
- dd->ipath_wc_cookie = cookie;
- dd->ipath_wc_base = (unsigned long) pioaddr;
- dd->ipath_wc_len = (unsigned long) piolen;
- }
+ dd->wc_cookie = arch_phys_wc_add(pioaddr, piolen);
+ if (dd->wc_cookie < 0) {
+ ipath_dev_err(dd, "Seting mtrr failed on PIO buffers\n");
+ ret = -ENODEV;
+ } else if (dd->wc_cookie == 0)
+ ipath_cdbg(VERBOSE, "Set mtrr for chip to WC not needed\n");
+ else
+ ipath_cdbg(VERBOSE, "Set mtrr for chip to WC\n");
}
return ret;
@@ -154,16 +140,5 @@ int ipath_enable_wc(struct ipath_devdata *dd)
*/
void ipath_disable_wc(struct ipath_devdata *dd)
{
- if (dd->ipath_wc_cookie) {
- int r;
- ipath_cdbg(VERBOSE, "undoing WCCOMB on pio buffers\n");
- r = mtrr_del(dd->ipath_wc_cookie, dd->ipath_wc_base,
- dd->ipath_wc_len);
- if (r < 0)
- dev_info(&dd->pcidev->dev,
- "mtrr_del(%lx, %lx, %lx) failed: %d\n",
- dd->ipath_wc_cookie, dd->ipath_wc_base,
- dd->ipath_wc_len, r);
- dd->ipath_wc_cookie = 0; /* even on failure */
- }
+ arch_phys_wc_del(dd->wc_cookie);
}
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index 0176caa5792c..36eb3d012b6d 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -166,10 +166,14 @@ err_buf:
return err;
}
-struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector,
+#define CQ_CREATE_FLAGS_SUPPORTED IB_CQ_FLAGS_TIMESTAMP_COMPLETION
+struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
struct ib_ucontext *context,
struct ib_udata *udata)
{
+ int entries = attr->cqe;
+ int vector = attr->comp_vector;
struct mlx4_ib_dev *dev = to_mdev(ibdev);
struct mlx4_ib_cq *cq;
struct mlx4_uar *uar;
@@ -178,6 +182,9 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector
if (entries < 1 || entries > dev->dev->caps.max_cqes)
return ERR_PTR(-EINVAL);
+ if (attr->flags & ~CQ_CREATE_FLAGS_SUPPORTED)
+ return ERR_PTR(-EINVAL);
+
cq = kmalloc(sizeof *cq, GFP_KERNEL);
if (!cq)
return ERR_PTR(-ENOMEM);
@@ -188,6 +195,7 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector
spin_lock_init(&cq->lock);
cq->resize_buf = NULL;
cq->resize_umem = NULL;
+ cq->create_flags = attr->flags;
INIT_LIST_HEAD(&cq->send_qp_list);
INIT_LIST_HEAD(&cq->recv_qp_list);
@@ -231,7 +239,8 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector
vector = dev->eq_table[vector % ibdev->num_comp_vectors];
err = mlx4_cq_alloc(dev->dev, entries, &cq->buf.mtt, uar,
- cq->db.dma, &cq->mcq, vector, 0, 0);
+ cq->db.dma, &cq->mcq, vector, 0,
+ !!(cq->create_flags & IB_CQ_FLAGS_TIMESTAMP_COMPLETION));
if (err)
goto err_dbmap;
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index 9cd2b002d7ae..3e2dee46caa2 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -111,8 +111,9 @@ __be64 mlx4_ib_get_new_demux_tid(struct mlx4_ib_demux_ctx *ctx)
}
int mlx4_MAD_IFC(struct mlx4_ib_dev *dev, int mad_ifc_flags,
- int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
- void *in_mad, void *response_mad)
+ int port, const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const void *in_mad, void *response_mad)
{
struct mlx4_cmd_mailbox *inmailbox, *outmailbox;
void *inbox;
@@ -220,7 +221,7 @@ static void update_sm_ah(struct mlx4_ib_dev *dev, u8 port_num, u16 lid, u8 sl)
* Snoop SM MADs for port info, GUID info, and P_Key table sets, so we can
* synthesize LID change, Client-Rereg, GID change, and P_Key change events.
*/
-static void smp_snoop(struct ib_device *ibdev, u8 port_num, struct ib_mad *mad,
+static void smp_snoop(struct ib_device *ibdev, u8 port_num, const struct ib_mad *mad,
u16 prev_lid)
{
struct ib_port_info *pinfo;
@@ -356,7 +357,7 @@ static void node_desc_override(struct ib_device *dev,
}
}
-static void forward_trap(struct mlx4_ib_dev *dev, u8 port_num, struct ib_mad *mad)
+static void forward_trap(struct mlx4_ib_dev *dev, u8 port_num, const struct ib_mad *mad)
{
int qpn = mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED;
struct ib_mad_send_buf *send_buf;
@@ -366,7 +367,8 @@ static void forward_trap(struct mlx4_ib_dev *dev, u8 port_num, struct ib_mad *ma
if (agent) {
send_buf = ib_create_send_mad(agent, qpn, 0, 0, IB_MGMT_MAD_HDR,
- IB_MGMT_MAD_DATA, GFP_ATOMIC);
+ IB_MGMT_MAD_DATA, GFP_ATOMIC,
+ IB_MGMT_BASE_VERSION);
if (IS_ERR(send_buf))
return;
/*
@@ -722,8 +724,8 @@ static int mlx4_ib_demux_mad(struct ib_device *ibdev, u8 port,
}
static int ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad *in_mad, struct ib_mad *out_mad)
{
u16 slid, prev_lid = 0;
int err;
@@ -825,8 +827,8 @@ static void edit_counter(struct mlx4_counter *cnt,
}
static int iboe_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad *in_mad, struct ib_mad *out_mad)
{
struct mlx4_cmd_mailbox *mailbox;
struct mlx4_ib_dev *dev = to_mdev(ibdev);
@@ -866,9 +868,17 @@ static int iboe_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
}
int mlx4_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
+ const struct ib_mad *in_mad = (const struct ib_mad *)in;
+ struct ib_mad *out_mad = (struct ib_mad *)out;
+
+ BUG_ON(in_mad_size != sizeof(*in_mad) ||
+ *out_mad_size != sizeof(*out_mad));
+
switch (rdma_port_get_link_layer(ibdev, port_num)) {
case IB_LINK_LAYER_INFINIBAND:
return ib_process_mad(ibdev, mad_flags, port_num, in_wc,
@@ -1773,6 +1783,7 @@ static int create_pv_resources(struct ib_device *ibdev, int slave, int port,
int create_tun, struct mlx4_ib_demux_pv_ctx *ctx)
{
int ret, cq_size;
+ struct ib_cq_init_attr cq_attr = {};
if (ctx->state != DEMUX_PV_STATE_DOWN)
return -EEXIST;
@@ -1801,8 +1812,9 @@ static int create_pv_resources(struct ib_device *ibdev, int slave, int port,
if (ctx->has_smi)
cq_size *= 2;
+ cq_attr.cqe = cq_size;
ctx->cq = ib_create_cq(ctx->ib_dev, mlx4_ib_tunnel_comp_handler,
- NULL, ctx, cq_size, 0);
+ NULL, ctx, &cq_attr);
if (IS_ERR(ctx->cq)) {
ret = PTR_ERR(ctx->cq);
pr_err("Couldn't create tunnel CQ (%d)\n", ret);
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 57070c529dfb..166da787780c 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -132,14 +132,35 @@ static int num_ib_ports(struct mlx4_dev *dev)
}
static int mlx4_ib_query_device(struct ib_device *ibdev,
- struct ib_device_attr *props)
+ struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
struct mlx4_ib_dev *dev = to_mdev(ibdev);
struct ib_smp *in_mad = NULL;
struct ib_smp *out_mad = NULL;
int err = -ENOMEM;
int have_ib_ports;
+ struct mlx4_uverbs_ex_query_device cmd;
+ struct mlx4_uverbs_ex_query_device_resp resp = {.comp_mask = 0};
+ struct mlx4_clock_params clock_params;
+ if (uhw->inlen) {
+ if (uhw->inlen < sizeof(cmd))
+ return -EINVAL;
+
+ err = ib_copy_from_udata(&cmd, uhw, sizeof(cmd));
+ if (err)
+ return err;
+
+ if (cmd.comp_mask)
+ return -EINVAL;
+
+ if (cmd.reserved)
+ return -EINVAL;
+ }
+
+ resp.response_length = offsetof(typeof(resp), response_length) +
+ sizeof(resp.response_length);
in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL);
out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
if (!in_mad || !out_mad)
@@ -229,7 +250,24 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
props->max_total_mcast_qp_attach = props->max_mcast_qp_attach *
props->max_mcast_grp;
props->max_map_per_fmr = dev->dev->caps.max_fmr_maps;
+ props->hca_core_clock = dev->dev->caps.hca_core_clock * 1000UL;
+ props->timestamp_mask = 0xFFFFFFFFFFFFULL;
+
+ err = mlx4_get_internal_clock_params(dev->dev, &clock_params);
+ if (err)
+ goto out;
+
+ if (uhw->outlen >= resp.response_length + sizeof(resp.hca_core_clock_offset)) {
+ resp.hca_core_clock_offset = clock_params.offset % PAGE_SIZE;
+ resp.response_length += sizeof(resp.hca_core_clock_offset);
+ resp.comp_mask |= QUERY_DEVICE_RESP_MASK_TIMESTAMP;
+ }
+ if (uhw->outlen) {
+ err = ib_copy_to_udata(uhw, &resp, resp.response_length);
+ if (err)
+ goto out;
+ }
out:
kfree(in_mad);
kfree(out_mad);
@@ -712,8 +750,24 @@ static int mlx4_ib_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
dev->dev->caps.num_uars,
PAGE_SIZE, vma->vm_page_prot))
return -EAGAIN;
- } else
+ } else if (vma->vm_pgoff == 3) {
+ struct mlx4_clock_params params;
+ int ret = mlx4_get_internal_clock_params(dev->dev, &params);
+
+ if (ret)
+ return ret;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (io_remap_pfn_range(vma, vma->vm_start,
+ (pci_resource_start(dev->dev->persist->pdev,
+ params.bar) +
+ params.offset)
+ >> PAGE_SHIFT,
+ PAGE_SIZE, vma->vm_page_prot))
+ return -EAGAIN;
+ } else {
return -EINVAL;
+ }
return 0;
}
@@ -758,6 +812,7 @@ static struct ib_xrcd *mlx4_ib_alloc_xrcd(struct ib_device *ibdev,
struct ib_udata *udata)
{
struct mlx4_ib_xrcd *xrcd;
+ struct ib_cq_init_attr cq_attr = {};
int err;
if (!(to_mdev(ibdev)->dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC))
@@ -777,7 +832,8 @@ static struct ib_xrcd *mlx4_ib_alloc_xrcd(struct ib_device *ibdev,
goto err2;
}
- xrcd->cq = ib_create_cq(ibdev, NULL, NULL, xrcd, 1, 0);
+ cq_attr.cqe = 1;
+ xrcd->cq = ib_create_cq(ibdev, NULL, NULL, xrcd, &cq_attr);
if (IS_ERR(xrcd->cq)) {
err = PTR_ERR(xrcd->cq);
goto err3;
@@ -1185,7 +1241,6 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp,
&mflow->reg_id[i].id);
if (err)
goto err_create_flow;
- i++;
if (is_bonded) {
/* Application always sees one port so the mirror rule
* must be on port #2
@@ -1200,6 +1255,7 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp,
j++;
}
+ i++;
}
if (i < ARRAY_SIZE(type) && flow_attr->type == IB_FLOW_ATTR_NORMAL) {
@@ -1207,7 +1263,7 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp,
&mflow->reg_id[i].id);
if (err)
goto err_create_flow;
- i++;
+
if (is_bonded) {
flow_attr->port = 2;
err = mlx4_ib_tunnel_steer_add(qp, flow_attr,
@@ -1218,6 +1274,7 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp,
j++;
}
/* function to create mirror rule */
+ i++;
}
return &mflow->ibflow;
@@ -1569,8 +1626,7 @@ static void reset_gids_task(struct work_struct *work)
MLX4_CMD_TIME_CLASS_B,
MLX4_CMD_WRAPPED);
if (err)
- pr_warn(KERN_WARNING
- "set port %d command failed\n", gw->port);
+ pr_warn("set port %d command failed\n", gw->port);
}
mlx4_free_cmd_mailbox(dev, mailbox);
@@ -2115,6 +2171,29 @@ static void mlx4_ib_free_eqs(struct mlx4_dev *dev, struct mlx4_ib_dev *ibdev)
kfree(ibdev->eq_table);
}
+static int mlx4_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = mlx4_ib_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+
+ if (mlx4_ib_port_link_layer(ibdev, port_num) == IB_LINK_LAYER_INFINIBAND)
+ immutable->core_cap_flags = RDMA_CORE_PORT_IBA_IB;
+ else
+ immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE;
+
+ immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+ return 0;
+}
+
static void *mlx4_ib_add(struct mlx4_dev *dev)
{
struct mlx4_ib_dev *ibdev;
@@ -2242,6 +2321,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
ibdev->ib_dev.attach_mcast = mlx4_ib_mcg_attach;
ibdev->ib_dev.detach_mcast = mlx4_ib_mcg_detach;
ibdev->ib_dev.process_mad = mlx4_ib_process_mad;
+ ibdev->ib_dev.get_port_immutable = mlx4_port_immutable;
if (!mlx4_is_slave(ibdev->dev)) {
ibdev->ib_dev.alloc_fmr = mlx4_ib_fmr_alloc;
@@ -2279,6 +2359,10 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
(1ull << IB_USER_VERBS_EX_CMD_DESTROY_FLOW);
}
+ ibdev->ib_dev.uverbs_ex_cmd_mask |=
+ (1ull << IB_USER_VERBS_EX_CMD_QUERY_DEVICE) |
+ (1ull << IB_USER_VERBS_EX_CMD_CREATE_CQ);
+
mlx4_ib_alloc_eqs(dev, ibdev);
spin_lock_init(&iboe->lock);
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index fce3934372a1..7933adfff662 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -110,6 +110,7 @@ struct mlx4_ib_cq {
struct mutex resize_mutex;
struct ib_umem *umem;
struct ib_umem *resize_umem;
+ int create_flags;
/* List of qps that it serves.*/
struct list_head send_qp_list;
struct list_head recv_qp_list;
@@ -555,6 +556,21 @@ struct mlx4_ib_qp_tunnel_init_attr {
u8 port;
};
+struct mlx4_uverbs_ex_query_device {
+ __u32 comp_mask;
+ __u32 reserved;
+};
+
+enum query_device_resp_mask {
+ QUERY_DEVICE_RESP_MASK_TIMESTAMP = 1UL << 0,
+};
+
+struct mlx4_uverbs_ex_query_device_resp {
+ __u32 comp_mask;
+ __u32 response_length;
+ __u64 hca_core_clock_offset;
+};
+
static inline struct mlx4_ib_dev *to_mdev(struct ib_device *ibdev)
{
return container_of(ibdev, struct mlx4_ib_dev, ib_dev);
@@ -668,7 +684,8 @@ void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list);
int mlx4_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period);
int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata);
-struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector,
+struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
struct ib_ucontext *context,
struct ib_udata *udata);
int mlx4_ib_destroy_cq(struct ib_cq *cq);
@@ -706,11 +723,13 @@ int mlx4_ib_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
struct ib_recv_wr **bad_wr);
int mlx4_MAD_IFC(struct mlx4_ib_dev *dev, int mad_ifc_flags,
- int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
- void *in_mad, void *response_mad);
+ int port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const void *in_mad, void *response_mad);
int mlx4_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad);
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index);
int mlx4_ib_mad_init(struct mlx4_ib_dev *dev);
void mlx4_ib_mad_cleanup(struct mlx4_ib_dev *dev);
diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c
index 2ee6b1051975..09fbae618d35 100644
--- a/drivers/infiniband/hw/mlx5/cq.c
+++ b/drivers/infiniband/hw/mlx5/cq.c
@@ -736,10 +736,13 @@ static void destroy_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq)
mlx5_db_free(dev->mdev, &cq->db);
}
-struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
- int vector, struct ib_ucontext *context,
+struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *context,
struct ib_udata *udata)
{
+ int entries = attr->cqe;
+ int vector = attr->comp_vector;
struct mlx5_create_cq_mbox_in *cqb = NULL;
struct mlx5_ib_dev *dev = to_mdev(ibdev);
struct mlx5_ib_cq *cq;
@@ -750,6 +753,9 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
int eqn;
int err;
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
+
if (entries < 0)
return ERR_PTR(-EINVAL);
diff --git a/drivers/infiniband/hw/mlx5/mad.c b/drivers/infiniband/hw/mlx5/mad.c
index 9cf9a37bb5ff..8e45714fa369 100644
--- a/drivers/infiniband/hw/mlx5/mad.c
+++ b/drivers/infiniband/hw/mlx5/mad.c
@@ -41,8 +41,8 @@ enum {
};
int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
- u8 port, struct ib_wc *in_wc, struct ib_grh *in_grh,
- void *in_mad, void *response_mad)
+ u8 port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const void *in_mad, void *response_mad)
{
u8 op_modifier = 0;
@@ -58,11 +58,18 @@ int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
}
int mlx5_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
u16 slid;
int err;
+ const struct ib_mad *in_mad = (const struct ib_mad *)in;
+ struct ib_mad *out_mad = (struct ib_mad *)out;
+
+ BUG_ON(in_mad_size != sizeof(*in_mad) ||
+ *out_mad_size != sizeof(*out_mad));
slid = in_wc ? in_wc->slid : be16_to_cpu(IB_LID_PERMISSIVE);
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index 57c9809e8b87..c6cb26e0c866 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -63,7 +63,8 @@ static char mlx5_version[] =
DRIVER_VERSION " (" DRIVER_RELDATE ")\n";
static int mlx5_ib_query_device(struct ib_device *ibdev,
- struct ib_device_attr *props)
+ struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
struct mlx5_ib_dev *dev = to_mdev(ibdev);
struct ib_smp *in_mad = NULL;
@@ -74,6 +75,9 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
int max_sq_sg;
u64 flags;
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
+
gen = &dev->mdev->caps.gen;
in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL);
out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL);
@@ -910,6 +914,7 @@ static int get_port_caps(struct mlx5_ib_dev *dev)
struct mlx5_general_caps *gen;
int err = -ENOMEM;
int port;
+ struct ib_udata uhw = {.inlen = 0, .outlen = 0};
gen = &dev->mdev->caps.gen;
pprops = kmalloc(sizeof(*pprops), GFP_KERNEL);
@@ -920,7 +925,7 @@ static int get_port_caps(struct mlx5_ib_dev *dev)
if (!dprops)
goto out;
- err = mlx5_ib_query_device(&dev->ib_dev, dprops);
+ err = mlx5_ib_query_device(&dev->ib_dev, dprops, &uhw);
if (err) {
mlx5_ib_warn(dev, "query_device failed %d\n", err);
goto out;
@@ -971,6 +976,7 @@ static int create_umr_res(struct mlx5_ib_dev *dev)
struct ib_cq *cq;
struct ib_qp *qp;
struct ib_mr *mr;
+ struct ib_cq_init_attr cq_attr = {};
int ret;
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
@@ -994,8 +1000,9 @@ static int create_umr_res(struct mlx5_ib_dev *dev)
goto error_1;
}
- cq = ib_create_cq(&dev->ib_dev, mlx5_umr_cq_handler, NULL, NULL, 128,
- 0);
+ cq_attr.cqe = 128;
+ cq = ib_create_cq(&dev->ib_dev, mlx5_umr_cq_handler, NULL, NULL,
+ &cq_attr);
if (IS_ERR(cq)) {
mlx5_ib_dbg(dev, "Couldn't create CQ for sync UMR QP\n");
ret = PTR_ERR(cq);
@@ -1087,6 +1094,7 @@ static int create_dev_resources(struct mlx5_ib_resources *devr)
{
struct ib_srq_init_attr attr;
struct mlx5_ib_dev *dev;
+ struct ib_cq_init_attr cq_attr = {.cqe = 1};
int ret = 0;
dev = container_of(devr, struct mlx5_ib_dev, devr);
@@ -1100,7 +1108,7 @@ static int create_dev_resources(struct mlx5_ib_resources *devr)
devr->p0->uobject = NULL;
atomic_set(&devr->p0->usecnt, 0);
- devr->c0 = mlx5_ib_create_cq(&dev->ib_dev, 1, 0, NULL, NULL);
+ devr->c0 = mlx5_ib_create_cq(&dev->ib_dev, &cq_attr, NULL, NULL);
if (IS_ERR(devr->c0)) {
ret = PTR_ERR(devr->c0);
goto error1;
@@ -1182,6 +1190,24 @@ static void destroy_dev_resources(struct mlx5_ib_resources *devr)
mlx5_ib_dealloc_pd(devr->p0);
}
+static int mlx5_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = mlx5_ib_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IBA_IB;
+ immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+ return 0;
+}
+
static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
{
struct mlx5_ib_dev *dev;
@@ -1285,6 +1311,7 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
dev->ib_dev.alloc_fast_reg_page_list = mlx5_ib_alloc_fast_reg_page_list;
dev->ib_dev.free_fast_reg_page_list = mlx5_ib_free_fast_reg_page_list;
dev->ib_dev.check_mr_status = mlx5_ib_check_mr_status;
+ dev->ib_dev.get_port_immutable = mlx5_port_immutable;
mlx5_ib_internal_query_odp_caps(dev);
diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
index dff1cfcdf476..178314e764da 100644
--- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
+++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
@@ -525,8 +525,8 @@ void __mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq)
void mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq);
void mlx5_ib_free_srq_wqe(struct mlx5_ib_srq *srq, int wqe_index);
int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
- u8 port, struct ib_wc *in_wc, struct ib_grh *in_grh,
- void *in_mad, void *response_mad);
+ u8 port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const void *in_mad, void *response_mad);
struct ib_ah *create_ib_ah(struct ib_ah_attr *ah_attr,
struct mlx5_ib_ah *ah);
struct ib_ah *mlx5_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr);
@@ -556,8 +556,9 @@ int mlx5_ib_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
void *mlx5_get_send_wqe(struct mlx5_ib_qp *qp, int n);
int mlx5_ib_read_user_wqe(struct mlx5_ib_qp *qp, int send, int wqe_index,
void *buffer, u32 length);
-struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
- int vector, struct ib_ucontext *context,
+struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *context,
struct ib_udata *udata);
int mlx5_ib_destroy_cq(struct ib_cq *cq);
int mlx5_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc);
@@ -586,8 +587,10 @@ int mlx5_ib_map_phys_fmr(struct ib_fmr *ibfmr, u64 *page_list,
int mlx5_ib_unmap_fmr(struct list_head *fmr_list);
int mlx5_ib_fmr_dealloc(struct ib_fmr *ibfmr);
int mlx5_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad);
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index);
struct ib_xrcd *mlx5_ib_alloc_xrcd(struct ib_device *ibdev,
struct ib_ucontext *context,
struct ib_udata *udata);
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 4d7024b899cb..d35f62d4f4c5 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -1392,7 +1392,7 @@ static int mlx5_set_path(struct mlx5_ib_dev *dev, const struct ib_ah_attr *ah,
if (ah->ah_flags & IB_AH_GRH) {
if (ah->grh.sgid_index >= gen->port[port - 1].gid_table_len) {
- pr_err(KERN_ERR "sgid_index (%u) too large. max is %d\n",
+ pr_err("sgid_index (%u) too large. max is %d\n",
ah->grh.sgid_index, gen->port[port - 1].gid_table_len);
return -EINVAL;
}
diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.c b/drivers/infiniband/hw/mthca/mthca_cmd.c
index 9d3e5c1ac60e..c7f49bbb0c72 100644
--- a/drivers/infiniband/hw/mthca/mthca_cmd.c
+++ b/drivers/infiniband/hw/mthca/mthca_cmd.c
@@ -1858,8 +1858,8 @@ int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn)
}
int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey,
- int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
- void *in_mad, void *response_mad)
+ int port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const void *in_mad, void *response_mad)
{
struct mthca_mailbox *inmailbox, *outmailbox;
void *inbox;
diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.h b/drivers/infiniband/hw/mthca/mthca_cmd.h
index f952244c54de..d2e5b194b938 100644
--- a/drivers/infiniband/hw/mthca/mthca_cmd.h
+++ b/drivers/infiniband/hw/mthca/mthca_cmd.h
@@ -312,8 +312,8 @@ int mthca_QUERY_QP(struct mthca_dev *dev, u32 num, int is_ee,
struct mthca_mailbox *mailbox);
int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn);
int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey,
- int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
- void *in_mad, void *response_mad);
+ int port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const void *in_mad, void *response_mad);
int mthca_READ_MGM(struct mthca_dev *dev, int index,
struct mthca_mailbox *mailbox);
int mthca_WRITE_MGM(struct mthca_dev *dev, int index,
diff --git a/drivers/infiniband/hw/mthca/mthca_dev.h b/drivers/infiniband/hw/mthca/mthca_dev.h
index 7e6a6d64ad4e..4393a022867b 100644
--- a/drivers/infiniband/hw/mthca/mthca_dev.h
+++ b/drivers/infiniband/hw/mthca/mthca_dev.h
@@ -576,10 +576,11 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid);
int mthca_process_mad(struct ib_device *ibdev,
int mad_flags,
u8 port_num,
- struct ib_wc *in_wc,
- struct ib_grh *in_grh,
- struct ib_mad *in_mad,
- struct ib_mad *out_mad);
+ const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index);
int mthca_create_agents(struct mthca_dev *dev);
void mthca_free_agents(struct mthca_dev *dev);
diff --git a/drivers/infiniband/hw/mthca/mthca_mad.c b/drivers/infiniband/hw/mthca/mthca_mad.c
index 8881fa376e06..6b2418b74c99 100644
--- a/drivers/infiniband/hw/mthca/mthca_mad.c
+++ b/drivers/infiniband/hw/mthca/mthca_mad.c
@@ -104,7 +104,7 @@ static void update_sm_ah(struct mthca_dev *dev,
*/
static void smp_snoop(struct ib_device *ibdev,
u8 port_num,
- struct ib_mad *mad,
+ const struct ib_mad *mad,
u16 prev_lid)
{
struct ib_event event;
@@ -160,7 +160,7 @@ static void node_desc_override(struct ib_device *dev,
static void forward_trap(struct mthca_dev *dev,
u8 port_num,
- struct ib_mad *mad)
+ const struct ib_mad *mad)
{
int qpn = mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED;
struct ib_mad_send_buf *send_buf;
@@ -170,7 +170,8 @@ static void forward_trap(struct mthca_dev *dev,
if (agent) {
send_buf = ib_create_send_mad(agent, qpn, 0, 0, IB_MGMT_MAD_HDR,
- IB_MGMT_MAD_DATA, GFP_ATOMIC);
+ IB_MGMT_MAD_DATA, GFP_ATOMIC,
+ IB_MGMT_BASE_VERSION);
if (IS_ERR(send_buf))
return;
/*
@@ -195,15 +196,21 @@ static void forward_trap(struct mthca_dev *dev,
int mthca_process_mad(struct ib_device *ibdev,
int mad_flags,
u8 port_num,
- struct ib_wc *in_wc,
- struct ib_grh *in_grh,
- struct ib_mad *in_mad,
- struct ib_mad *out_mad)
+ const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
int err;
u16 slid = in_wc ? in_wc->slid : be16_to_cpu(IB_LID_PERMISSIVE);
u16 prev_lid = 0;
struct ib_port_attr pattr;
+ const struct ib_mad *in_mad = (const struct ib_mad *)in;
+ struct ib_mad *out_mad = (struct ib_mad *)out;
+
+ BUG_ON(in_mad_size != sizeof(*in_mad) ||
+ *out_mad_size != sizeof(*out_mad));
/* Forward locally generated traps to the SM */
if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP &&
diff --git a/drivers/infiniband/hw/mthca/mthca_profile.c b/drivers/infiniband/hw/mthca/mthca_profile.c
index 8edb28a9a0e7..15d064479ef6 100644
--- a/drivers/infiniband/hw/mthca/mthca_profile.c
+++ b/drivers/infiniband/hw/mthca/mthca_profile.c
@@ -77,7 +77,6 @@ s64 mthca_make_profile(struct mthca_dev *dev,
u64 mem_base, mem_avail;
s64 total_size = 0;
struct mthca_resource *profile;
- struct mthca_resource tmp;
int i, j;
profile = kzalloc(MTHCA_RES_NUM * sizeof *profile, GFP_KERNEL);
@@ -136,11 +135,8 @@ s64 mthca_make_profile(struct mthca_dev *dev,
*/
for (i = MTHCA_RES_NUM; i > 0; --i)
for (j = 1; j < i; ++j) {
- if (profile[j].size > profile[j - 1].size) {
- tmp = profile[j];
- profile[j] = profile[j - 1];
- profile[j - 1] = tmp;
- }
+ if (profile[j].size > profile[j - 1].size)
+ swap(profile[j], profile[j - 1]);
}
for (i = 0; i < MTHCA_RES_NUM; ++i) {
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c
index 415f8e1a54db..93ae51dcf2ff 100644
--- a/drivers/infiniband/hw/mthca/mthca_provider.c
+++ b/drivers/infiniband/hw/mthca/mthca_provider.c
@@ -57,14 +57,17 @@ static void init_query_mad(struct ib_smp *mad)
mad->method = IB_MGMT_METHOD_GET;
}
-static int mthca_query_device(struct ib_device *ibdev,
- struct ib_device_attr *props)
+static int mthca_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
struct ib_smp *in_mad = NULL;
struct ib_smp *out_mad = NULL;
int err = -ENOMEM;
struct mthca_dev *mdev = to_mdev(ibdev);
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
+
in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL);
out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
if (!in_mad || !out_mad)
@@ -641,16 +644,20 @@ static int mthca_destroy_qp(struct ib_qp *qp)
return 0;
}
-static struct ib_cq *mthca_create_cq(struct ib_device *ibdev, int entries,
- int comp_vector,
+static struct ib_cq *mthca_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
struct ib_ucontext *context,
struct ib_udata *udata)
{
+ int entries = attr->cqe;
struct mthca_create_cq ucmd;
struct mthca_cq *cq;
int nent;
int err;
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
+
if (entries < 1 || entries > to_mdev(ibdev)->limits.max_cqes)
return ERR_PTR(-EINVAL);
@@ -1244,6 +1251,24 @@ out:
return err;
}
+static int mthca_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = mthca_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IBA_IB;
+ immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+ return 0;
+}
+
int mthca_register_device(struct mthca_dev *dev)
{
int ret;
@@ -1323,6 +1348,7 @@ int mthca_register_device(struct mthca_dev *dev)
dev->ib_dev.reg_phys_mr = mthca_reg_phys_mr;
dev->ib_dev.reg_user_mr = mthca_reg_user_mr;
dev->ib_dev.dereg_mr = mthca_dereg_mr;
+ dev->ib_dev.get_port_immutable = mthca_port_immutable;
if (dev->mthca_flags & MTHCA_FLAG_FMR) {
dev->ib_dev.alloc_fmr = mthca_alloc_fmr;
diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c
index 3b2a6dc8ea99..9f9d5c563a61 100644
--- a/drivers/infiniband/hw/nes/nes.c
+++ b/drivers/infiniband/hw/nes/nes.c
@@ -116,6 +116,7 @@ static struct ibnl_client_cbs nes_nl_cb_table[] = {
[RDMA_NL_IWPM_REG_PID] = {.dump = iwpm_register_pid_cb},
[RDMA_NL_IWPM_ADD_MAPPING] = {.dump = iwpm_add_mapping_cb},
[RDMA_NL_IWPM_QUERY_MAPPING] = {.dump = iwpm_add_and_query_mapping_cb},
+ [RDMA_NL_IWPM_REMOTE_INFO] = {.dump = iwpm_remote_info_cb},
[RDMA_NL_IWPM_HANDLE_ERR] = {.dump = iwpm_mapping_error_cb},
[RDMA_NL_IWPM_MAPINFO] = {.dump = iwpm_mapping_info_cb},
[RDMA_NL_IWPM_MAPINFO_NUM] = {.dump = iwpm_ack_mapping_info_cb}
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index 6f09a72e78d7..9047af429906 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -596,27 +596,52 @@ static void nes_form_reg_msg(struct nes_vnic *nesvnic,
memcpy(pm_msg->if_name, nesvnic->netdev->name, IWPM_IFNAME_SIZE);
}
+static void record_sockaddr_info(struct sockaddr_storage *addr_info,
+ nes_addr_t *ip_addr, u16 *port_num)
+{
+ struct sockaddr_in *in_addr = (struct sockaddr_in *)addr_info;
+
+ if (in_addr->sin_family == AF_INET) {
+ *ip_addr = ntohl(in_addr->sin_addr.s_addr);
+ *port_num = ntohs(in_addr->sin_port);
+ }
+}
+
/*
* nes_record_pm_msg - Save the received mapping info
*/
static void nes_record_pm_msg(struct nes_cm_info *cm_info,
struct iwpm_sa_data *pm_msg)
{
- struct sockaddr_in *mapped_loc_addr =
- (struct sockaddr_in *)&pm_msg->mapped_loc_addr;
- struct sockaddr_in *mapped_rem_addr =
- (struct sockaddr_in *)&pm_msg->mapped_rem_addr;
-
- if (mapped_loc_addr->sin_family == AF_INET) {
- cm_info->mapped_loc_addr =
- ntohl(mapped_loc_addr->sin_addr.s_addr);
- cm_info->mapped_loc_port = ntohs(mapped_loc_addr->sin_port);
- }
- if (mapped_rem_addr->sin_family == AF_INET) {
- cm_info->mapped_rem_addr =
- ntohl(mapped_rem_addr->sin_addr.s_addr);
- cm_info->mapped_rem_port = ntohs(mapped_rem_addr->sin_port);
- }
+ record_sockaddr_info(&pm_msg->mapped_loc_addr,
+ &cm_info->mapped_loc_addr, &cm_info->mapped_loc_port);
+
+ record_sockaddr_info(&pm_msg->mapped_rem_addr,
+ &cm_info->mapped_rem_addr, &cm_info->mapped_rem_port);
+}
+
+/*
+ * nes_get_reminfo - Get the address info of the remote connecting peer
+ */
+static int nes_get_remote_addr(struct nes_cm_node *cm_node)
+{
+ struct sockaddr_storage mapped_loc_addr, mapped_rem_addr;
+ struct sockaddr_storage remote_addr;
+ int ret;
+
+ nes_create_sockaddr(htonl(cm_node->mapped_loc_addr),
+ htons(cm_node->mapped_loc_port), &mapped_loc_addr);
+ nes_create_sockaddr(htonl(cm_node->mapped_rem_addr),
+ htons(cm_node->mapped_rem_port), &mapped_rem_addr);
+
+ ret = iwpm_get_remote_info(&mapped_loc_addr, &mapped_rem_addr,
+ &remote_addr, RDMA_NL_NES);
+ if (ret)
+ nes_debug(NES_DBG_CM, "Unable to find remote peer address info\n");
+ else
+ record_sockaddr_info(&remote_addr, &cm_node->rem_addr,
+ &cm_node->rem_port);
+ return ret;
}
/**
@@ -1566,9 +1591,14 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
return NULL;
/* set our node specific transport info */
- cm_node->loc_addr = cm_info->loc_addr;
+ if (listener) {
+ cm_node->loc_addr = listener->loc_addr;
+ cm_node->loc_port = listener->loc_port;
+ } else {
+ cm_node->loc_addr = cm_info->loc_addr;
+ cm_node->loc_port = cm_info->loc_port;
+ }
cm_node->rem_addr = cm_info->rem_addr;
- cm_node->loc_port = cm_info->loc_port;
cm_node->rem_port = cm_info->rem_port;
cm_node->mapped_loc_addr = cm_info->mapped_loc_addr;
@@ -1586,6 +1616,8 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
&cm_node->loc_addr, cm_node->loc_port,
&cm_node->rem_addr, cm_node->rem_port);
cm_node->listener = listener;
+ if (listener)
+ cm_node->tos = listener->tos;
cm_node->netdev = nesvnic->netdev;
cm_node->cm_id = cm_info->cm_id;
memcpy(cm_node->loc_mac, nesvnic->netdev->dev_addr, ETH_ALEN);
@@ -2151,6 +2183,7 @@ static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
cm_node->state = NES_CM_STATE_ESTABLISHED;
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
+ nes_get_remote_addr(cm_node);
handle_rcv_mpa(cm_node, skb);
} else { /* rcvd ACK only */
dev_kfree_skb_any(skb);
@@ -2907,6 +2940,9 @@ static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_nod
nesqp->nesqp_context->misc2 |= cpu_to_le32(64 << NES_QPCONTEXT_MISC2_TTL_SHIFT);
+ nesqp->nesqp_context->misc2 |= cpu_to_le32(
+ cm_node->tos << NES_QPCONTEXT_MISC2_TOS_SHIFT);
+
nesqp->nesqp_context->mss |= cpu_to_le32(((u32)cm_node->tcp_cntxt.mss) << 16);
nesqp->nesqp_context->tcp_state_flow_label |= cpu_to_le32(
@@ -3581,6 +3617,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
cm_node->ord_size = 1;
cm_node->apbvt_set = apbvt_set;
+ cm_node->tos = cm_id->tos;
nesqp->cm_node = cm_node;
cm_node->nesqp = nesqp;
nes_add_ref(&nesqp->ibqp);
@@ -3635,6 +3672,7 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog)
}
cm_id->provider_data = cm_node;
+ cm_node->tos = cm_id->tos;
if (!cm_node->reused_node) {
if (nes_create_mapinfo(&cm_info))
diff --git a/drivers/infiniband/hw/nes/nes_cm.h b/drivers/infiniband/hw/nes/nes_cm.h
index f522cf639789..32a6420c2940 100644
--- a/drivers/infiniband/hw/nes/nes_cm.h
+++ b/drivers/infiniband/hw/nes/nes_cm.h
@@ -303,6 +303,7 @@ struct nes_cm_listener {
int backlog;
enum nes_cm_listener_state listener_state;
u32 reused_node;
+ u8 tos;
};
/* per connection node and node state information */
@@ -352,6 +353,7 @@ struct nes_cm_node {
struct list_head reset_entry;
struct nes_qp *nesqp;
atomic_t passive_state;
+ u8 tos;
};
/* structure for client or CM to fill when making CM api calls. */
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index c0d0296e7a00..fbc43e5f717b 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -512,12 +512,16 @@ static void nes_free_fast_reg_page_list(struct ib_fast_reg_page_list *pifrpl)
/**
* nes_query_device
*/
-static int nes_query_device(struct ib_device *ibdev, struct ib_device_attr *props)
+static int nes_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
struct nes_vnic *nesvnic = to_nesvnic(ibdev);
struct nes_device *nesdev = nesvnic->nesdev;
struct nes_ib_device *nesibdev = nesvnic->nesibdev;
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
+
memset(props, 0, sizeof(*props));
memcpy(&props->sys_image_guid, nesvnic->netdev->dev_addr, 6);
@@ -606,7 +610,6 @@ static int nes_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr
return 0;
}
-
/**
* nes_query_pkey
*/
@@ -1527,10 +1530,12 @@ static int nes_destroy_qp(struct ib_qp *ibqp)
/**
* nes_create_cq
*/
-static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
- int comp_vector,
- struct ib_ucontext *context, struct ib_udata *udata)
+static struct ib_cq *nes_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *context,
+ struct ib_udata *udata)
{
+ int entries = attr->cqe;
u64 u64temp;
struct nes_vnic *nesvnic = to_nesvnic(ibdev);
struct nes_device *nesdev = nesvnic->nesdev;
@@ -1550,6 +1555,9 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
unsigned long flags;
int ret;
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
+
if (entries > nesadapter->max_cqe)
return ERR_PTR(-EINVAL);
@@ -3222,8 +3230,10 @@ static int nes_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
* nes_process_mad
*/
static int nes_process_mad(struct ib_device *ibdev, int mad_flags,
- u8 port_num, struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ u8 port_num, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
nes_debug(NES_DBG_INIT, "\n");
return -ENOSYS;
@@ -3828,6 +3838,22 @@ static int nes_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags notify_
return 0;
}
+static int nes_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = nes_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IWARP;
+
+ return 0;
+}
/**
* nes_init_ofa_device
@@ -3928,6 +3954,7 @@ struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev)
nesibdev->ibdev.iwcm->reject = nes_reject;
nesibdev->ibdev.iwcm->create_listen = nes_create_listen;
nesibdev->ibdev.iwcm->destroy_listen = nes_destroy_listen;
+ nesibdev->ibdev.get_port_immutable = nes_port_immutable;
return nesibdev;
}
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma.h b/drivers/infiniband/hw/ocrdma/ocrdma.h
index c9780d919769..b396344fae16 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma.h
@@ -40,7 +40,7 @@
#include <be_roce.h>
#include "ocrdma_sli.h"
-#define OCRDMA_ROCE_DRV_VERSION "10.4.205.0u"
+#define OCRDMA_ROCE_DRV_VERSION "10.6.0.0"
#define OCRDMA_ROCE_DRV_DESC "Emulex OneConnect RoCE Driver"
#define OCRDMA_NODE_DESC "Emulex OneConnect RoCE HCA"
@@ -515,6 +515,8 @@ static inline int ocrdma_resolve_dmac(struct ocrdma_dev *dev,
memcpy(&in6, ah_attr->grh.dgid.raw, sizeof(in6));
if (rdma_is_multicast_addr(&in6))
rdma_get_mcast_mac(&in6, mac_addr);
+ else if (rdma_link_local_addr(&in6))
+ rdma_get_ll_mac(&in6, mac_addr);
else
memcpy(mac_addr, ah_attr->dmac, ETH_ALEN);
return 0;
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_ah.c b/drivers/infiniband/hw/ocrdma/ocrdma_ah.c
index d812904f3984..4bafa15708d0 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_ah.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_ah.c
@@ -56,7 +56,13 @@ static inline int set_av_attr(struct ocrdma_dev *dev, struct ocrdma_ah *ah,
vlan_tag = attr->vlan_id;
if (!vlan_tag || (vlan_tag > 0xFFF))
vlan_tag = dev->pvid;
- if (vlan_tag && (vlan_tag < 0x1000)) {
+ if (vlan_tag || dev->pfc_state) {
+ if (!vlan_tag) {
+ pr_err("ocrdma%d:Using VLAN with PFC is recommended\n",
+ dev->id);
+ pr_err("ocrdma%d:Using VLAN 0 for this connection\n",
+ dev->id);
+ }
eth.eth_type = cpu_to_be16(0x8100);
eth.roce_eth_type = cpu_to_be16(OCRDMA_ROCE_ETH_TYPE);
vlan_tag |= (dev->sl & 0x07) << OCRDMA_VID_PCP_SHIFT;
@@ -121,7 +127,9 @@ struct ib_ah *ocrdma_create_ah(struct ib_pd *ibpd, struct ib_ah_attr *attr)
goto av_conf_err;
}
- if (pd->uctx) {
+ if ((pd->uctx) &&
+ (!rdma_is_multicast_addr((struct in6_addr *)attr->grh.dgid.raw)) &&
+ (!rdma_link_local_addr((struct in6_addr *)attr->grh.dgid.raw))) {
status = rdma_addr_find_dmac_by_grh(&sgid, &attr->grh.dgid,
attr->dmac, &attr->vlan_id);
if (status) {
@@ -196,12 +204,19 @@ int ocrdma_modify_ah(struct ib_ah *ibah, struct ib_ah_attr *attr)
int ocrdma_process_mad(struct ib_device *ibdev,
int process_mad_flags,
u8 port_num,
- struct ib_wc *in_wc,
- struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
int status;
struct ocrdma_dev *dev;
+ const struct ib_mad *in_mad = (const struct ib_mad *)in;
+ struct ib_mad *out_mad = (struct ib_mad *)out;
+
+ BUG_ON(in_mad_size != sizeof(*in_mad) ||
+ *out_mad_size != sizeof(*out_mad));
switch (in_mad->mad_hdr.mgmt_class) {
case IB_MGMT_CLASS_PERF_MGMT:
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_ah.h b/drivers/infiniband/hw/ocrdma/ocrdma_ah.h
index 726a87cf22dc..cf366fe03cb8 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_ah.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_ah.h
@@ -42,7 +42,9 @@ int ocrdma_modify_ah(struct ib_ah *, struct ib_ah_attr *);
int ocrdma_process_mad(struct ib_device *,
int process_mad_flags,
u8 port_num,
- struct ib_wc *in_wc,
- struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad);
+ const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index);
#endif /* __OCRDMA_AH_H__ */
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
index 0c9e95909a64..47615ff33bc6 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
@@ -933,12 +933,18 @@ static irqreturn_t ocrdma_irq_handler(int irq, void *handle)
struct ocrdma_eqe eqe;
struct ocrdma_eqe *ptr;
u16 cq_id;
+ u8 mcode;
int budget = eq->cq_cnt;
do {
ptr = ocrdma_get_eqe(eq);
eqe = *ptr;
ocrdma_le32_to_cpu(&eqe, sizeof(eqe));
+ mcode = (eqe.id_valid & OCRDMA_EQE_MAJOR_CODE_MASK)
+ >> OCRDMA_EQE_MAJOR_CODE_SHIFT;
+ if (mcode == OCRDMA_MAJOR_CODE_SENTINAL)
+ pr_err("EQ full on eqid = 0x%x, eqe = 0x%x\n",
+ eq->q.id, eqe.id_valid);
if ((eqe.id_valid & OCRDMA_EQE_VALID_MASK) == 0)
break;
@@ -1434,27 +1440,30 @@ static int ocrdma_mbx_alloc_pd_range(struct ocrdma_dev *dev)
struct ocrdma_alloc_pd_range_rsp *rsp;
/* Pre allocate the DPP PDs */
- cmd = ocrdma_init_emb_mqe(OCRDMA_CMD_ALLOC_PD_RANGE, sizeof(*cmd));
- if (!cmd)
- return -ENOMEM;
- cmd->pd_count = dev->attr.max_dpp_pds;
- cmd->enable_dpp_rsvd |= OCRDMA_ALLOC_PD_ENABLE_DPP;
- status = ocrdma_mbx_cmd(dev, (struct ocrdma_mqe *)cmd);
- if (status)
- goto mbx_err;
- rsp = (struct ocrdma_alloc_pd_range_rsp *)cmd;
-
- if ((rsp->dpp_page_pdid & OCRDMA_ALLOC_PD_RSP_DPP) && rsp->pd_count) {
- dev->pd_mgr->dpp_page_index = rsp->dpp_page_pdid >>
- OCRDMA_ALLOC_PD_RSP_DPP_PAGE_SHIFT;
- dev->pd_mgr->pd_dpp_start = rsp->dpp_page_pdid &
- OCRDMA_ALLOC_PD_RNG_RSP_START_PDID_MASK;
- dev->pd_mgr->max_dpp_pd = rsp->pd_count;
- pd_bitmap_size = BITS_TO_LONGS(rsp->pd_count) * sizeof(long);
- dev->pd_mgr->pd_dpp_bitmap = kzalloc(pd_bitmap_size,
- GFP_KERNEL);
+ if (dev->attr.max_dpp_pds) {
+ cmd = ocrdma_init_emb_mqe(OCRDMA_CMD_ALLOC_PD_RANGE,
+ sizeof(*cmd));
+ if (!cmd)
+ return -ENOMEM;
+ cmd->pd_count = dev->attr.max_dpp_pds;
+ cmd->enable_dpp_rsvd |= OCRDMA_ALLOC_PD_ENABLE_DPP;
+ status = ocrdma_mbx_cmd(dev, (struct ocrdma_mqe *)cmd);
+ rsp = (struct ocrdma_alloc_pd_range_rsp *)cmd;
+
+ if (!status && (rsp->dpp_page_pdid & OCRDMA_ALLOC_PD_RSP_DPP) &&
+ rsp->pd_count) {
+ dev->pd_mgr->dpp_page_index = rsp->dpp_page_pdid >>
+ OCRDMA_ALLOC_PD_RSP_DPP_PAGE_SHIFT;
+ dev->pd_mgr->pd_dpp_start = rsp->dpp_page_pdid &
+ OCRDMA_ALLOC_PD_RNG_RSP_START_PDID_MASK;
+ dev->pd_mgr->max_dpp_pd = rsp->pd_count;
+ pd_bitmap_size =
+ BITS_TO_LONGS(rsp->pd_count) * sizeof(long);
+ dev->pd_mgr->pd_dpp_bitmap = kzalloc(pd_bitmap_size,
+ GFP_KERNEL);
+ }
+ kfree(cmd);
}
- kfree(cmd);
cmd = ocrdma_init_emb_mqe(OCRDMA_CMD_ALLOC_PD_RANGE, sizeof(*cmd));
if (!cmd)
@@ -1462,10 +1471,8 @@ static int ocrdma_mbx_alloc_pd_range(struct ocrdma_dev *dev)
cmd->pd_count = dev->attr.max_pd - dev->attr.max_dpp_pds;
status = ocrdma_mbx_cmd(dev, (struct ocrdma_mqe *)cmd);
- if (status)
- goto mbx_err;
rsp = (struct ocrdma_alloc_pd_range_rsp *)cmd;
- if (rsp->pd_count) {
+ if (!status && rsp->pd_count) {
dev->pd_mgr->pd_norm_start = rsp->dpp_page_pdid &
OCRDMA_ALLOC_PD_RNG_RSP_START_PDID_MASK;
dev->pd_mgr->max_normal_pd = rsp->pd_count;
@@ -1473,15 +1480,13 @@ static int ocrdma_mbx_alloc_pd_range(struct ocrdma_dev *dev)
dev->pd_mgr->pd_norm_bitmap = kzalloc(pd_bitmap_size,
GFP_KERNEL);
}
+ kfree(cmd);
if (dev->pd_mgr->pd_norm_bitmap || dev->pd_mgr->pd_dpp_bitmap) {
/* Enable PD resource manager */
dev->pd_mgr->pd_prealloc_valid = true;
- } else {
- return -ENOMEM;
+ return 0;
}
-mbx_err:
- kfree(cmd);
return status;
}
@@ -2406,7 +2411,7 @@ int ocrdma_mbx_query_qp(struct ocrdma_dev *dev, struct ocrdma_qp *qp,
struct ocrdma_query_qp *cmd;
struct ocrdma_query_qp_rsp *rsp;
- cmd = ocrdma_init_emb_mqe(OCRDMA_CMD_QUERY_QP, sizeof(*cmd));
+ cmd = ocrdma_init_emb_mqe(OCRDMA_CMD_QUERY_QP, sizeof(*rsp));
if (!cmd)
return status;
cmd->qp_id = qp->id;
@@ -2428,7 +2433,7 @@ static int ocrdma_set_av_params(struct ocrdma_qp *qp,
int status;
struct ib_ah_attr *ah_attr = &attrs->ah_attr;
union ib_gid sgid, zgid;
- u32 vlan_id;
+ u32 vlan_id = 0xFFFF;
u8 mac_addr[6];
struct ocrdma_dev *dev = get_ocrdma_dev(qp->ibqp.device);
@@ -2468,12 +2473,22 @@ static int ocrdma_set_av_params(struct ocrdma_qp *qp,
cmd->params.vlan_dmac_b4_to_b5 = mac_addr[4] | (mac_addr[5] << 8);
if (attr_mask & IB_QP_VID) {
vlan_id = attrs->vlan_id;
+ } else if (dev->pfc_state) {
+ vlan_id = 0;
+ pr_err("ocrdma%d:Using VLAN with PFC is recommended\n",
+ dev->id);
+ pr_err("ocrdma%d:Using VLAN 0 for this connection\n",
+ dev->id);
+ }
+
+ if (vlan_id < 0x1000) {
cmd->params.vlan_dmac_b4_to_b5 |=
vlan_id << OCRDMA_QP_PARAMS_VLAN_SHIFT;
cmd->flags |= OCRDMA_QP_PARA_VLAN_EN_VALID;
cmd->params.rnt_rc_sl_fl |=
(dev->sl & 0x07) << OCRDMA_QP_PARAMS_SL_SHIFT;
}
+
return 0;
}
@@ -2519,8 +2534,10 @@ static int ocrdma_set_qp_params(struct ocrdma_qp *qp,
cmd->flags |= OCRDMA_QP_PARA_DST_QPN_VALID;
}
if (attr_mask & IB_QP_PATH_MTU) {
- if (attrs->path_mtu < IB_MTU_256 ||
+ if (attrs->path_mtu < IB_MTU_512 ||
attrs->path_mtu > IB_MTU_4096) {
+ pr_err("ocrdma%d: IB MTU %d is not supported\n",
+ dev->id, ib_mtu_enum_to_int(attrs->path_mtu));
status = -EINVAL;
goto pmtu_err;
}
@@ -3147,9 +3164,9 @@ void ocrdma_cleanup_hw(struct ocrdma_dev *dev)
ocrdma_free_pd_pool(dev);
ocrdma_mbx_delete_ah_tbl(dev);
- /* cleanup the eqs */
- ocrdma_destroy_eqs(dev);
-
/* cleanup the control path */
ocrdma_destroy_mq(dev);
+
+ /* cleanup the eqs */
+ ocrdma_destroy_eqs(dev);
}
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
index 7a2b59aca004..8a1398b253a2 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
@@ -30,6 +30,7 @@
#include <rdma/ib_verbs.h>
#include <rdma/ib_user_verbs.h>
#include <rdma/ib_addr.h>
+#include <rdma/ib_mad.h>
#include <linux/netdevice.h>
#include <net/addrconf.h>
@@ -202,6 +203,24 @@ static enum rdma_link_layer ocrdma_link_layer(struct ib_device *device,
return IB_LINK_LAYER_ETHERNET;
}
+static int ocrdma_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = ocrdma_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE;
+ immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+ return 0;
+}
+
static int ocrdma_register_device(struct ocrdma_dev *dev)
{
strlcpy(dev->ibdev.name, "ocrdma%d", IB_DEVICE_NAME_MAX);
@@ -286,6 +305,7 @@ static int ocrdma_register_device(struct ocrdma_dev *dev)
dev->ibdev.dma_device = &dev->nic_info.pdev->dev;
dev->ibdev.process_mad = ocrdma_process_mad;
+ dev->ibdev.get_port_immutable = ocrdma_port_immutable;
if (ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R) {
dev->ibdev.uverbs_cmd_mask |=
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_sli.h b/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
index 243c87c8bd65..02ad0aee99af 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
@@ -1176,6 +1176,8 @@ struct ocrdma_query_qp_rsp {
struct ocrdma_mqe_hdr hdr;
struct ocrdma_mbx_rsp rsp;
struct ocrdma_qp_params params;
+ u32 dpp_credits_cqid;
+ u32 rbq_id;
};
enum {
@@ -1624,12 +1626,19 @@ struct ocrdma_delete_ah_tbl_rsp {
enum {
OCRDMA_EQE_VALID_SHIFT = 0,
OCRDMA_EQE_VALID_MASK = BIT(0),
+ OCRDMA_EQE_MAJOR_CODE_MASK = 0x0E,
+ OCRDMA_EQE_MAJOR_CODE_SHIFT = 0x01,
OCRDMA_EQE_FOR_CQE_MASK = 0xFFFE,
OCRDMA_EQE_RESOURCE_ID_SHIFT = 16,
OCRDMA_EQE_RESOURCE_ID_MASK = 0xFFFF <<
OCRDMA_EQE_RESOURCE_ID_SHIFT,
};
+enum major_code {
+ OCRDMA_MAJOR_CODE_COMPLETION = 0x00,
+ OCRDMA_MAJOR_CODE_SENTINAL = 0x01
+};
+
struct ocrdma_eqe {
u32 id_valid;
};
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
index 877175563634..5bb61eb58f2c 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
@@ -61,10 +61,14 @@ int ocrdma_query_gid(struct ib_device *ibdev, u8 port,
return 0;
}
-int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr)
+int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr,
+ struct ib_udata *uhw)
{
struct ocrdma_dev *dev = get_ocrdma_dev(ibdev);
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
+
memset(attr, 0, sizeof *attr);
memcpy(&attr->fw_ver, &dev->attr.fw_ver[0],
min(sizeof(dev->attr.fw_ver), sizeof(attr->fw_ver)));
@@ -365,7 +369,7 @@ static struct ocrdma_pd *_ocrdma_alloc_pd(struct ocrdma_dev *dev,
if (!pd)
return ERR_PTR(-ENOMEM);
- if (udata && uctx) {
+ if (udata && uctx && dev->attr.max_dpp_pds) {
pd->dpp_enabled =
ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R;
pd->num_dpp_qp =
@@ -375,7 +379,12 @@ static struct ocrdma_pd *_ocrdma_alloc_pd(struct ocrdma_dev *dev,
if (dev->pd_mgr->pd_prealloc_valid) {
status = ocrdma_get_pd_num(dev, pd);
- return (status == 0) ? pd : ERR_PTR(status);
+ if (status == 0) {
+ return pd;
+ } else {
+ kfree(pd);
+ return ERR_PTR(status);
+ }
}
retry:
@@ -679,7 +688,6 @@ err:
ocrdma_release_ucontext_pd(uctx);
} else {
status = _ocrdma_dealloc_pd(dev, pd);
- kfree(pd);
}
exit:
return ERR_PTR(status);
@@ -1000,10 +1008,12 @@ err:
return status;
}
-struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev, int entries, int vector,
+struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
struct ib_ucontext *ib_ctx,
struct ib_udata *udata)
{
+ int entries = attr->cqe;
struct ocrdma_cq *cq;
struct ocrdma_dev *dev = get_ocrdma_dev(ibdev);
struct ocrdma_ucontext *uctx = NULL;
@@ -1011,6 +1021,9 @@ struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev, int entries, int vector,
int status;
struct ocrdma_create_cq_ureq ureq;
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
+
if (udata) {
if (ib_copy_from_udata(&ureq, udata, sizeof(ureq)))
return ERR_PTR(-EFAULT);
@@ -1721,18 +1734,20 @@ int ocrdma_destroy_qp(struct ib_qp *ibqp)
struct ocrdma_qp *qp;
struct ocrdma_dev *dev;
struct ib_qp_attr attrs;
- int attr_mask = IB_QP_STATE;
+ int attr_mask;
unsigned long flags;
qp = get_ocrdma_qp(ibqp);
dev = get_ocrdma_dev(ibqp->device);
- attrs.qp_state = IB_QPS_ERR;
pd = qp->pd;
/* change the QP state to ERROR */
- _ocrdma_modify_qp(ibqp, &attrs, attr_mask);
-
+ if (qp->state != OCRDMA_QPS_RST) {
+ attrs.qp_state = IB_QPS_ERR;
+ attr_mask = IB_QP_STATE;
+ _ocrdma_modify_qp(ibqp, &attrs, attr_mask);
+ }
/* ensure that CQEs for newly created QP (whose id may be same with
* one which just getting destroyed are same), dont get
* discarded until the old CQEs are discarded.
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h
index b8f7853fd36c..b15c608efa7b 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.h
@@ -36,11 +36,15 @@ int ocrdma_post_recv(struct ib_qp *, struct ib_recv_wr *,
int ocrdma_poll_cq(struct ib_cq *, int num_entries, struct ib_wc *wc);
int ocrdma_arm_cq(struct ib_cq *, enum ib_cq_notify_flags flags);
-int ocrdma_query_device(struct ib_device *, struct ib_device_attr *props);
+int ocrdma_query_device(struct ib_device *, struct ib_device_attr *props,
+ struct ib_udata *uhw);
int ocrdma_query_port(struct ib_device *, u8 port, struct ib_port_attr *props);
int ocrdma_modify_port(struct ib_device *, u8 port, int mask,
struct ib_port_modify *props);
+enum rdma_protocol_type
+ocrdma_query_protocol(struct ib_device *device, u8 port_num);
+
void ocrdma_get_guid(struct ocrdma_dev *, u8 *guid);
int ocrdma_query_gid(struct ib_device *, u8 port,
int index, union ib_gid *gid);
@@ -56,8 +60,10 @@ struct ib_pd *ocrdma_alloc_pd(struct ib_device *,
struct ib_ucontext *, struct ib_udata *);
int ocrdma_dealloc_pd(struct ib_pd *pd);
-struct ib_cq *ocrdma_create_cq(struct ib_device *, int entries, int vector,
- struct ib_ucontext *, struct ib_udata *);
+struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *ib_ctx,
+ struct ib_udata *udata);
int ocrdma_resize_cq(struct ib_cq *, int cqe, struct ib_udata *);
int ocrdma_destroy_cq(struct ib_cq *);
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index ffd48bfc4923..7df16f74bb45 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -903,7 +903,7 @@ struct qib_devdata {
/* PCI Device ID (here for NodeInfo) */
u16 deviceid;
/* for write combining settings */
- unsigned long wc_cookie;
+ int wc_cookie;
unsigned long wc_base;
unsigned long wc_len;
@@ -1136,7 +1136,6 @@ extern struct qib_devdata *qib_lookup(int unit);
extern u32 qib_cpulist_count;
extern unsigned long *qib_cpulist;
-extern unsigned qib_wc_pat;
extern unsigned qib_cc_table_size;
int qib_init(struct qib_devdata *, int);
int init_chip_wc_pat(struct qib_devdata *dd, u32);
diff --git a/drivers/infiniband/hw/qib/qib_cq.c b/drivers/infiniband/hw/qib/qib_cq.c
index ab4e11cfab15..2b45d0b02300 100644
--- a/drivers/infiniband/hw/qib/qib_cq.c
+++ b/drivers/infiniband/hw/qib/qib_cq.c
@@ -203,7 +203,7 @@ static void send_complete(struct kthread_work *work)
/**
* qib_create_cq - create a completion queue
* @ibdev: the device this completion queue is attached to
- * @entries: the minimum size of the completion queue
+ * @attr: creation attributes
* @context: unused by the QLogic_IB driver
* @udata: user data for libibverbs.so
*
@@ -212,16 +212,21 @@ static void send_complete(struct kthread_work *work)
*
* Called by ib_create_cq() in the generic verbs code.
*/
-struct ib_cq *qib_create_cq(struct ib_device *ibdev, int entries,
- int comp_vector, struct ib_ucontext *context,
+struct ib_cq *qib_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *context,
struct ib_udata *udata)
{
+ int entries = attr->cqe;
struct qib_ibdev *dev = to_idev(ibdev);
struct qib_cq *cq;
struct qib_cq_wc *wc;
struct ib_cq *ret;
u32 sz;
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
+
if (entries < 1 || entries > ib_qib_max_cqes) {
ret = ERR_PTR(-EINVAL);
goto done;
diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c
index 9ea6c440a00c..725881890c4a 100644
--- a/drivers/infiniband/hw/qib/qib_file_ops.c
+++ b/drivers/infiniband/hw/qib/qib_file_ops.c
@@ -835,7 +835,8 @@ static int mmap_piobufs(struct vm_area_struct *vma,
vma->vm_flags &= ~VM_MAYREAD;
vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND;
- if (qib_wc_pat)
+ /* We used PAT if wc_cookie == 0 */
+ if (!dd->wc_cookie)
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
ret = io_remap_pfn_range(vma, vma->vm_start, phys >> PAGE_SHIFT,
diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c
index 0d2ba59af30a..4b927809d1a1 100644
--- a/drivers/infiniband/hw/qib/qib_iba6120.c
+++ b/drivers/infiniband/hw/qib/qib_iba6120.c
@@ -3315,11 +3315,9 @@ static int init_6120_variables(struct qib_devdata *dd)
qib_6120_config_ctxts(dd);
qib_set_ctxtcnt(dd);
- if (qib_wc_pat) {
- ret = init_chip_wc_pat(dd, 0);
- if (ret)
- goto bail;
- }
+ ret = init_chip_wc_pat(dd, 0);
+ if (ret)
+ goto bail;
set_6120_baseaddrs(dd); /* set chip access pointers now */
ret = 0;
diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c
index 22affda8af88..00b2af211157 100644
--- a/drivers/infiniband/hw/qib/qib_iba7220.c
+++ b/drivers/infiniband/hw/qib/qib_iba7220.c
@@ -4126,11 +4126,9 @@ static int qib_init_7220_variables(struct qib_devdata *dd)
qib_7220_config_ctxts(dd);
qib_set_ctxtcnt(dd); /* needed for PAT setup */
- if (qib_wc_pat) {
- ret = init_chip_wc_pat(dd, 0);
- if (ret)
- goto bail;
- }
+ ret = init_chip_wc_pat(dd, 0);
+ if (ret)
+ goto bail;
set_7220_baseaddrs(dd); /* set chip access pointers now */
ret = 0;
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index ef97b71c8f7d..6c8ff10101c0 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -5502,7 +5502,8 @@ static void try_7322_ipg(struct qib_pportdata *ppd)
goto retry;
send_buf = ib_create_send_mad(agent, 0, 0, 0, IB_MGMT_MAD_HDR,
- IB_MGMT_MAD_DATA, GFP_ATOMIC);
+ IB_MGMT_MAD_DATA, GFP_ATOMIC,
+ IB_MGMT_BASE_VERSION);
if (IS_ERR(send_buf))
goto retry;
@@ -6429,6 +6430,7 @@ static int qib_init_7322_variables(struct qib_devdata *dd)
unsigned features, pidx, sbufcnt;
int ret, mtu;
u32 sbufs, updthresh;
+ resource_size_t vl15off;
/* pport structs are contiguous, allocated after devdata */
ppd = (struct qib_pportdata *)(dd + 1);
@@ -6677,29 +6679,27 @@ static int qib_init_7322_variables(struct qib_devdata *dd)
qib_7322_config_ctxts(dd);
qib_set_ctxtcnt(dd);
- if (qib_wc_pat) {
- resource_size_t vl15off;
- /*
- * We do not set WC on the VL15 buffers to avoid
- * a rare problem with unaligned writes from
- * interrupt-flushed store buffers, so we need
- * to map those separately here. We can't solve
- * this for the rarely used mtrr case.
- */
- ret = init_chip_wc_pat(dd, 0);
- if (ret)
- goto bail;
+ /*
+ * We do not set WC on the VL15 buffers to avoid
+ * a rare problem with unaligned writes from
+ * interrupt-flushed store buffers, so we need
+ * to map those separately here. We can't solve
+ * this for the rarely used mtrr case.
+ */
+ ret = init_chip_wc_pat(dd, 0);
+ if (ret)
+ goto bail;
- /* vl15 buffers start just after the 4k buffers */
- vl15off = dd->physaddr + (dd->piobufbase >> 32) +
- dd->piobcnt4k * dd->align4k;
- dd->piovl15base = ioremap_nocache(vl15off,
- NUM_VL15_BUFS * dd->align4k);
- if (!dd->piovl15base) {
- ret = -ENOMEM;
- goto bail;
- }
+ /* vl15 buffers start just after the 4k buffers */
+ vl15off = dd->physaddr + (dd->piobufbase >> 32) +
+ dd->piobcnt4k * dd->align4k;
+ dd->piovl15base = ioremap_nocache(vl15off,
+ NUM_VL15_BUFS * dd->align4k);
+ if (!dd->piovl15base) {
+ ret = -ENOMEM;
+ goto bail;
}
+
qib_7322_set_baseaddrs(dd); /* set chip access pointers now */
ret = 0;
diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c
index 2ee36953e234..7e00470adc30 100644
--- a/drivers/infiniband/hw/qib/qib_init.c
+++ b/drivers/infiniband/hw/qib/qib_init.c
@@ -91,15 +91,6 @@ MODULE_PARM_DESC(krcvqs, "number of kernel receive queues per IB port");
unsigned qib_cc_table_size;
module_param_named(cc_table_size, qib_cc_table_size, uint, S_IRUGO);
MODULE_PARM_DESC(cc_table_size, "Congestion control table entries 0 (CCA disabled - default), min = 128, max = 1984");
-/*
- * qib_wc_pat parameter:
- * 0 is WC via MTRR
- * 1 is WC via PAT
- * If PAT initialization fails, code reverts back to MTRR
- */
-unsigned qib_wc_pat = 1; /* default (1) is to use PAT, not MTRR */
-module_param_named(wc_pat, qib_wc_pat, uint, S_IRUGO);
-MODULE_PARM_DESC(wc_pat, "enable write-combining via PAT mechanism");
static void verify_interrupt(unsigned long);
@@ -1377,8 +1368,7 @@ static void cleanup_device_data(struct qib_devdata *dd)
spin_unlock(&dd->pport[pidx].cc_shadow_lock);
}
- if (!qib_wc_pat)
- qib_disable_wc(dd);
+ qib_disable_wc(dd);
if (dd->pioavailregs_dma) {
dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE,
@@ -1547,14 +1537,12 @@ static int qib_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
goto bail;
}
- if (!qib_wc_pat) {
- ret = qib_enable_wc(dd);
- if (ret) {
- qib_dev_err(dd,
- "Write combining not enabled (err %d): performance may be poor\n",
- -ret);
- ret = 0;
- }
+ ret = qib_enable_wc(dd);
+ if (ret) {
+ qib_dev_err(dd,
+ "Write combining not enabled (err %d): performance may be poor\n",
+ -ret);
+ ret = 0;
}
qib_verify_pioperf(dd);
diff --git a/drivers/infiniband/hw/qib/qib_mad.c b/drivers/infiniband/hw/qib/qib_mad.c
index 395f4046dba2..05e3242d8442 100644
--- a/drivers/infiniband/hw/qib/qib_mad.c
+++ b/drivers/infiniband/hw/qib/qib_mad.c
@@ -83,7 +83,8 @@ static void qib_send_trap(struct qib_ibport *ibp, void *data, unsigned len)
return;
send_buf = ib_create_send_mad(agent, 0, 0, 0, IB_MGMT_MAD_HDR,
- IB_MGMT_MAD_DATA, GFP_ATOMIC);
+ IB_MGMT_MAD_DATA, GFP_ATOMIC,
+ IB_MGMT_BASE_VERSION);
if (IS_ERR(send_buf))
return;
@@ -1854,7 +1855,7 @@ static int pma_set_portcounters_ext(struct ib_pma_mad *pmp,
}
static int process_subn(struct ib_device *ibdev, int mad_flags,
- u8 port, struct ib_mad *in_mad,
+ u8 port, const struct ib_mad *in_mad,
struct ib_mad *out_mad)
{
struct ib_smp *smp = (struct ib_smp *)out_mad;
@@ -2006,7 +2007,7 @@ bail:
}
static int process_perf(struct ib_device *ibdev, u8 port,
- struct ib_mad *in_mad,
+ const struct ib_mad *in_mad,
struct ib_mad *out_mad)
{
struct ib_pma_mad *pmp = (struct ib_pma_mad *)out_mad;
@@ -2299,7 +2300,7 @@ static int check_cc_key(struct qib_ibport *ibp,
}
static int process_cc(struct ib_device *ibdev, int mad_flags,
- u8 port, struct ib_mad *in_mad,
+ u8 port, const struct ib_mad *in_mad,
struct ib_mad *out_mad)
{
struct ib_cc_mad *ccp = (struct ib_cc_mad *)out_mad;
@@ -2400,12 +2401,19 @@ bail:
* This is called by the ib_mad module.
*/
int qib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad)
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index)
{
int ret;
struct qib_ibport *ibp = to_iport(ibdev, port);
struct qib_pportdata *ppd = ppd_from_ibp(ibp);
+ const struct ib_mad *in_mad = (const struct ib_mad *)in;
+ struct ib_mad *out_mad = (struct ib_mad *)out;
+
+ BUG_ON(in_mad_size != sizeof(*in_mad) ||
+ *out_mad_size != sizeof(*out_mad));
switch (in_mad->mad_hdr.mgmt_class) {
case IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE:
diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c
index 4a3599890ea5..a05d1a372208 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.c
+++ b/drivers/infiniband/hw/qib/qib_verbs.c
@@ -1550,12 +1550,14 @@ full:
}
}
-static int qib_query_device(struct ib_device *ibdev,
- struct ib_device_attr *props)
+static int qib_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
struct qib_devdata *dd = dd_from_ibdev(ibdev);
struct qib_ibdev *dev = to_idev(ibdev);
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
memset(props, 0, sizeof(*props));
props->device_cap_flags = IB_DEVICE_BAD_PKEY_CNTR |
@@ -2040,6 +2042,24 @@ static void init_ibport(struct qib_pportdata *ppd)
RCU_INIT_POINTER(ibp->qp1, NULL);
}
+static int qib_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = qib_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IBA_IB;
+ immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+ return 0;
+}
+
/**
* qib_register_ib_device - register our device with the infiniband core
* @dd: the device data structure
@@ -2227,6 +2247,7 @@ int qib_register_ib_device(struct qib_devdata *dd)
ibdev->process_mad = qib_process_mad;
ibdev->mmap = qib_mmap;
ibdev->dma_ops = &qib_dma_mapping_ops;
+ ibdev->get_port_immutable = qib_port_immutable;
snprintf(ibdev->node_desc, sizeof(ibdev->node_desc),
"Intel Infiniband HCA %s", init_utsname()->nodename);
diff --git a/drivers/infiniband/hw/qib/qib_verbs.h b/drivers/infiniband/hw/qib/qib_verbs.h
index bfc8948fdd35..1635572752ce 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.h
+++ b/drivers/infiniband/hw/qib/qib_verbs.h
@@ -872,8 +872,10 @@ void qib_cap_mask_chg(struct qib_ibport *ibp);
void qib_sys_guid_chg(struct qib_ibport *ibp);
void qib_node_desc_chg(struct qib_ibport *ibp);
int qib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
- struct ib_wc *in_wc, struct ib_grh *in_grh,
- struct ib_mad *in_mad, struct ib_mad *out_mad);
+ const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+ const struct ib_mad_hdr *in, size_t in_mad_size,
+ struct ib_mad_hdr *out, size_t *out_mad_size,
+ u16 *out_mad_pkey_index);
int qib_create_agents(struct qib_ibdev *dev);
void qib_free_agents(struct qib_ibdev *dev);
@@ -1007,8 +1009,9 @@ void qib_cq_enter(struct qib_cq *cq, struct ib_wc *entry, int sig);
int qib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
-struct ib_cq *qib_create_cq(struct ib_device *ibdev, int entries,
- int comp_vector, struct ib_ucontext *context,
+struct ib_cq *qib_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *context,
struct ib_udata *udata);
int qib_destroy_cq(struct ib_cq *ibcq);
diff --git a/drivers/infiniband/hw/qib/qib_wc_x86_64.c b/drivers/infiniband/hw/qib/qib_wc_x86_64.c
index 81b225f2300a..edd0ddbd4481 100644
--- a/drivers/infiniband/hw/qib/qib_wc_x86_64.c
+++ b/drivers/infiniband/hw/qib/qib_wc_x86_64.c
@@ -116,21 +116,10 @@ int qib_enable_wc(struct qib_devdata *dd)
}
if (!ret) {
- int cookie;
-
- cookie = mtrr_add(pioaddr, piolen, MTRR_TYPE_WRCOMB, 0);
- if (cookie < 0) {
- {
- qib_devinfo(dd->pcidev,
- "mtrr_add() WC for PIO bufs failed (%d)\n",
- cookie);
- ret = -EINVAL;
- }
- } else {
- dd->wc_cookie = cookie;
- dd->wc_base = (unsigned long) pioaddr;
- dd->wc_len = (unsigned long) piolen;
- }
+ dd->wc_cookie = arch_phys_wc_add(pioaddr, piolen);
+ if (dd->wc_cookie < 0)
+ /* use error from routine */
+ ret = dd->wc_cookie;
}
return ret;
@@ -142,18 +131,7 @@ int qib_enable_wc(struct qib_devdata *dd)
*/
void qib_disable_wc(struct qib_devdata *dd)
{
- if (dd->wc_cookie) {
- int r;
-
- r = mtrr_del(dd->wc_cookie, dd->wc_base,
- dd->wc_len);
- if (r < 0)
- qib_devinfo(dd->pcidev,
- "mtrr_del(%lx, %lx, %lx) failed: %d\n",
- dd->wc_cookie, dd->wc_base,
- dd->wc_len, r);
- dd->wc_cookie = 0; /* even on failure */
- }
+ arch_phys_wc_del(dd->wc_cookie);
}
/**
diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c
index 0d0f98695d53..34c49b8105fe 100644
--- a/drivers/infiniband/hw/usnic/usnic_ib_main.c
+++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c
@@ -300,6 +300,22 @@ static struct notifier_block usnic_ib_inetaddr_notifier = {
};
/* End of inet section*/
+static int usnic_port_immutable(struct ib_device *ibdev, u8 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr attr;
+ int err;
+
+ err = usnic_ib_query_port(ibdev, port_num, &attr);
+ if (err)
+ return err;
+
+ immutable->pkey_tbl_len = attr.pkey_tbl_len;
+ immutable->gid_tbl_len = attr.gid_tbl_len;
+
+ return 0;
+}
+
/* Start of PF discovery section */
static void *usnic_ib_device_add(struct pci_dev *dev)
{
@@ -383,6 +399,7 @@ static void *usnic_ib_device_add(struct pci_dev *dev)
us_ibdev->ib_dev.poll_cq = usnic_ib_poll_cq;
us_ibdev->ib_dev.req_notify_cq = usnic_ib_req_notify_cq;
us_ibdev->ib_dev.get_dma_mr = usnic_ib_get_dma_mr;
+ us_ibdev->ib_dev.get_port_immutable = usnic_port_immutable;
if (ib_register_device(&us_ibdev->ib_dev, NULL))
diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c
index 53bd6a2d9cdb..7df43827cb29 100644
--- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.c
+++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.c
@@ -248,7 +248,8 @@ enum rdma_link_layer usnic_ib_port_link_layer(struct ib_device *device,
}
int usnic_ib_query_device(struct ib_device *ibdev,
- struct ib_device_attr *props)
+ struct ib_device_attr *props,
+ struct ib_udata *uhw)
{
struct usnic_ib_dev *us_ibdev = to_usdev(ibdev);
union ib_gid gid;
@@ -257,6 +258,9 @@ int usnic_ib_query_device(struct ib_device *ibdev,
int qp_per_vf;
usnic_dbg("\n");
+ if (uhw->inlen || uhw->outlen)
+ return -EINVAL;
+
mutex_lock(&us_ibdev->usdev_lock);
us_ibdev->netdev->ethtool_ops->get_drvinfo(us_ibdev->netdev, &info);
us_ibdev->netdev->ethtool_ops->get_settings(us_ibdev->netdev, &cmd);
@@ -570,13 +574,17 @@ int usnic_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
return status;
}
-struct ib_cq *usnic_ib_create_cq(struct ib_device *ibdev, int entries,
- int vector, struct ib_ucontext *context,
- struct ib_udata *udata)
+struct ib_cq *usnic_ib_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *context,
+ struct ib_udata *udata)
{
struct ib_cq *cq;
usnic_dbg("\n");
+ if (attr->flags)
+ return ERR_PTR(-EINVAL);
+
cq = kzalloc(sizeof(*cq), GFP_KERNEL);
if (!cq)
return ERR_PTR(-EBUSY);
diff --git a/drivers/infiniband/hw/usnic/usnic_ib_verbs.h b/drivers/infiniband/hw/usnic/usnic_ib_verbs.h
index bb864f5aed70..0bd04efa16f3 100644
--- a/drivers/infiniband/hw/usnic/usnic_ib_verbs.h
+++ b/drivers/infiniband/hw/usnic/usnic_ib_verbs.h
@@ -24,9 +24,12 @@
enum rdma_link_layer usnic_ib_port_link_layer(struct ib_device *device,
u8 port_num);
int usnic_ib_query_device(struct ib_device *ibdev,
- struct ib_device_attr *props);
+ struct ib_device_attr *props,
+ struct ib_udata *uhw);
int usnic_ib_query_port(struct ib_device *ibdev, u8 port,
struct ib_port_attr *props);
+enum rdma_protocol_type
+usnic_ib_query_protocol(struct ib_device *device, u8 port_num);
int usnic_ib_query_qp(struct ib_qp *qp, struct ib_qp_attr *qp_attr,
int qp_attr_mask,
struct ib_qp_init_attr *qp_init_attr);
@@ -44,9 +47,10 @@ struct ib_qp *usnic_ib_create_qp(struct ib_pd *pd,
int usnic_ib_destroy_qp(struct ib_qp *qp);
int usnic_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int attr_mask, struct ib_udata *udata);
-struct ib_cq *usnic_ib_create_cq(struct ib_device *ibdev, int entries,
- int vector, struct ib_ucontext *context,
- struct ib_udata *udata);
+struct ib_cq *usnic_ib_create_cq(struct ib_device *ibdev,
+ const struct ib_cq_init_attr *attr,
+ struct ib_ucontext *context,
+ struct ib_udata *udata);
int usnic_ib_destroy_cq(struct ib_cq *cq);
struct ib_mr *usnic_ib_reg_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt_addr, int access_flags,
diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c
index 417de1f32960..cb2337f0532b 100644
--- a/drivers/infiniband/hw/usnic/usnic_uiom.c
+++ b/drivers/infiniband/hw/usnic/usnic_uiom.c
@@ -472,11 +472,10 @@ struct usnic_uiom_pd *usnic_uiom_alloc_pd(void)
return ERR_PTR(-ENOMEM);
pd->domain = domain = iommu_domain_alloc(&pci_bus_type);
- if (IS_ERR_OR_NULL(domain)) {
- usnic_err("Failed to allocate IOMMU domain with err %ld\n",
- PTR_ERR(pd->domain));
+ if (!domain) {
+ usnic_err("Failed to allocate IOMMU domain");
kfree(pd);
- return ERR_PTR(domain ? PTR_ERR(domain) : -ENOMEM);
+ return ERR_PTR(-ENOMEM);
}
iommu_set_fault_handler(pd->domain, usnic_uiom_dma_fault, NULL);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 56959adb6c7d..cf32a778e7d0 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -386,8 +386,8 @@ static int ipoib_cm_nonsrq_init_rx(struct net_device *dev, struct ib_cm_id *cm_i
rx->rx_ring[i].mapping,
GFP_KERNEL)) {
ipoib_warn(priv, "failed to allocate receive buffer %d\n", i);
- ret = -ENOMEM;
- goto err_count;
+ ret = -ENOMEM;
+ goto err_count;
}
ret = ipoib_cm_post_receive_nonsrq(dev, rx, &t->wr, t->sge, i);
if (ret) {
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 9e1b203d756d..da149c278cb8 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -1128,7 +1128,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
{
struct ipoib_neigh_table *ntbl = &priv->ntbl;
struct ipoib_neigh_hash *htbl;
- struct ipoib_neigh **buckets;
+ struct ipoib_neigh __rcu **buckets;
u32 size;
clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
@@ -1146,7 +1146,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
htbl->size = size;
htbl->mask = (size - 1);
htbl->buckets = buckets;
- ntbl->htbl = htbl;
+ RCU_INIT_POINTER(ntbl->htbl, htbl);
htbl->ntbl = ntbl;
atomic_set(&ntbl->entries, 0);
@@ -1685,9 +1685,7 @@ static void ipoib_add_one(struct ib_device *device)
struct net_device *dev;
struct ipoib_dev_priv *priv;
int s, e, p;
-
- if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
- return;
+ int count = 0;
dev_list = kmalloc(sizeof *dev_list, GFP_KERNEL);
if (!dev_list)
@@ -1704,15 +1702,21 @@ static void ipoib_add_one(struct ib_device *device)
}
for (p = s; p <= e; ++p) {
- if (rdma_port_get_link_layer(device, p) != IB_LINK_LAYER_INFINIBAND)
+ if (!rdma_protocol_ib(device, p))
continue;
dev = ipoib_add_port("ib%d", device, p);
if (!IS_ERR(dev)) {
priv = netdev_priv(dev);
list_add_tail(&priv->list, dev_list);
+ count++;
}
}
+ if (!count) {
+ kfree(dev_list);
+ return;
+ }
+
ib_set_client_data(device, &ipoib_client, dev_list);
}
@@ -1721,9 +1725,6 @@ static void ipoib_remove_one(struct ib_device *device)
struct ipoib_dev_priv *priv, *tmp;
struct list_head *dev_list;
- if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
- return;
-
dev_list = ib_get_client_data(device, &ipoib_client);
if (!dev_list)
return;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
index e5cc43074196..9e6ee82a8fd7 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
@@ -141,6 +141,7 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
.sq_sig_type = IB_SIGNAL_ALL_WR,
.qp_type = IB_QPT_UD
};
+ struct ib_cq_init_attr cq_attr = {};
int ret, size;
int i;
@@ -178,14 +179,17 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
} else
goto out_free_wq;
- priv->recv_cq = ib_create_cq(priv->ca, ipoib_ib_completion, NULL, dev, size, 0);
+ cq_attr.cqe = size;
+ priv->recv_cq = ib_create_cq(priv->ca, ipoib_ib_completion, NULL,
+ dev, &cq_attr);
if (IS_ERR(priv->recv_cq)) {
printk(KERN_WARNING "%s: failed to create receive CQ\n", ca->name);
goto out_cm_dev_cleanup;
}
+ cq_attr.cqe = ipoib_sendq_size;
priv->send_cq = ib_create_cq(priv->ca, ipoib_send_comp_handler, NULL,
- dev, ipoib_sendq_size, 0);
+ dev, &cq_attr);
if (IS_ERR(priv->send_cq)) {
printk(KERN_WARNING "%s: failed to create send CQ\n", ca->name);
goto out_free_recv_cq;
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index cc2dd35ffbc0..5c9f565ea0e8 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -51,19 +51,22 @@ static void iser_cq_callback(struct ib_cq *cq, void *cq_context);
static void iser_cq_event_callback(struct ib_event *cause, void *context)
{
- iser_err("got cq event %d \n", cause->event);
+ iser_err("cq event %s (%d)\n",
+ ib_event_msg(cause->event), cause->event);
}
static void iser_qp_event_callback(struct ib_event *cause, void *context)
{
- iser_err("got qp event %d\n",cause->event);
+ iser_err("qp event %s (%d)\n",
+ ib_event_msg(cause->event), cause->event);
}
static void iser_event_handler(struct ib_event_handler *handler,
struct ib_event *event)
{
- iser_err("async event %d on device %s port %d\n", event->event,
- event->device->name, event->element.port_num);
+ iser_err("async event %s (%d) on device %s port %d\n",
+ ib_event_msg(event->event), event->event,
+ event->device->name, event->element.port_num);
}
/**
@@ -123,14 +126,17 @@ static int iser_create_device_ib_res(struct iser_device *device)
goto pd_err;
for (i = 0; i < device->comps_used; i++) {
+ struct ib_cq_init_attr cq_attr = {};
struct iser_comp *comp = &device->comps[i];
comp->device = device;
+ cq_attr.cqe = max_cqe;
+ cq_attr.comp_vector = i;
comp->cq = ib_create_cq(device->ib_device,
iser_cq_callback,
iser_cq_event_callback,
(void *)comp,
- max_cqe, i);
+ &cq_attr);
if (IS_ERR(comp->cq)) {
comp->cq = NULL;
goto cq_err;
@@ -873,8 +879,9 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
int ret = 0;
iser_conn = (struct iser_conn *)cma_id->context;
- iser_info("event %d status %d conn %p id %p\n",
- event->event, event->status, cma_id->context, cma_id);
+ iser_info("%s (%d): status %d conn %p id %p\n",
+ rdma_event_msg(event->event), event->event,
+ event->status, cma_id->context, cma_id);
mutex_lock(&iser_conn->state_mutex);
switch (event->event) {
@@ -913,7 +920,8 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
}
break;
default:
- iser_err("Unexpected RDMA CM event (%d)\n", event->event);
+ iser_err("Unexpected RDMA CM event: %s (%d)\n",
+ rdma_event_msg(event->event), event->event);
break;
}
mutex_unlock(&iser_conn->state_mutex);
@@ -1173,10 +1181,13 @@ static void iser_handle_wc(struct ib_wc *wc)
}
} else {
if (wc->status != IB_WC_WR_FLUSH_ERR)
- iser_err("wr id %llx status %d vend_err %x\n",
- wc->wr_id, wc->status, wc->vendor_err);
+ iser_err("%s (%d): wr id %llx vend_err %x\n",
+ ib_wc_status_msg(wc->status), wc->status,
+ wc->wr_id, wc->vendor_err);
else
- iser_dbg("flush error: wr id %llx\n", wc->wr_id);
+ iser_dbg("%s (%d): wr id %llx\n",
+ ib_wc_status_msg(wc->status), wc->status,
+ wc->wr_id);
if (wc->wr_id == ISER_BEACON_WRID)
/* all flush errors were consumed */
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index 327529ee85eb..f3b7a34e10d8 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -65,6 +65,8 @@ static int
isert_rdma_accept(struct isert_conn *isert_conn);
struct rdma_cm_id *isert_setup_id(struct isert_np *isert_np);
+static void isert_release_work(struct work_struct *work);
+
static inline bool
isert_prot_cmd(struct isert_conn *conn, struct se_cmd *cmd)
{
@@ -78,7 +80,9 @@ isert_qp_event_callback(struct ib_event *e, void *context)
{
struct isert_conn *isert_conn = context;
- isert_err("conn %p event: %d\n", isert_conn, e->event);
+ isert_err("%s (%d): conn %p\n",
+ ib_event_msg(e->event), e->event, isert_conn);
+
switch (e->event) {
case IB_EVENT_COMM_EST:
rdma_notify(isert_conn->cm_id, IB_EVENT_COMM_EST);
@@ -316,15 +320,18 @@ isert_alloc_comps(struct isert_device *device,
max_cqe = min(ISER_MAX_CQ_LEN, attr->max_cqe);
for (i = 0; i < device->comps_used; i++) {
+ struct ib_cq_init_attr cq_attr = {};
struct isert_comp *comp = &device->comps[i];
comp->device = device;
INIT_WORK(&comp->work, isert_cq_work);
+ cq_attr.cqe = max_cqe;
+ cq_attr.comp_vector = i;
comp->cq = ib_create_cq(device->ib_device,
isert_cq_callback,
isert_cq_event_callback,
(void *)comp,
- max_cqe, i);
+ &cq_attr);
if (IS_ERR(comp->cq)) {
isert_err("Unable to allocate cq\n");
ret = PTR_ERR(comp->cq);
@@ -547,11 +554,11 @@ isert_create_pi_ctx(struct fast_reg_descriptor *desc,
return 0;
err_prot_mr:
- ib_dereg_mr(desc->pi_ctx->prot_mr);
+ ib_dereg_mr(pi_ctx->prot_mr);
err_prot_frpl:
- ib_free_fast_reg_page_list(desc->pi_ctx->prot_frpl);
+ ib_free_fast_reg_page_list(pi_ctx->prot_frpl);
err_pi_ctx:
- kfree(desc->pi_ctx);
+ kfree(pi_ctx);
return ret;
}
@@ -648,6 +655,7 @@ isert_init_conn(struct isert_conn *isert_conn)
mutex_init(&isert_conn->mutex);
spin_lock_init(&isert_conn->pool_lock);
INIT_LIST_HEAD(&isert_conn->fr_pool);
+ INIT_WORK(&isert_conn->release_work, isert_release_work);
}
static void
@@ -897,7 +905,8 @@ static int
isert_np_cma_handler(struct isert_np *isert_np,
enum rdma_cm_event_type event)
{
- isert_dbg("isert np %p, handling event %d\n", isert_np, event);
+ isert_dbg("%s (%d): isert np %p\n",
+ rdma_event_msg(event), event, isert_np);
switch (event) {
case RDMA_CM_EVENT_DEVICE_REMOVAL:
@@ -925,6 +934,7 @@ isert_disconnected_handler(struct rdma_cm_id *cma_id,
{
struct isert_np *isert_np = cma_id->context;
struct isert_conn *isert_conn;
+ bool terminating = false;
if (isert_np->np_cm_id == cma_id)
return isert_np_cma_handler(cma_id->context, event);
@@ -932,12 +942,25 @@ isert_disconnected_handler(struct rdma_cm_id *cma_id,
isert_conn = cma_id->qp->qp_context;
mutex_lock(&isert_conn->mutex);
+ terminating = (isert_conn->state == ISER_CONN_TERMINATING);
isert_conn_terminate(isert_conn);
mutex_unlock(&isert_conn->mutex);
isert_info("conn %p completing wait\n", isert_conn);
complete(&isert_conn->wait);
+ if (terminating)
+ goto out;
+
+ mutex_lock(&isert_np->np_accept_mutex);
+ if (!list_empty(&isert_conn->accept_node)) {
+ list_del_init(&isert_conn->accept_node);
+ isert_put_conn(isert_conn);
+ queue_work(isert_release_wq, &isert_conn->release_work);
+ }
+ mutex_unlock(&isert_np->np_accept_mutex);
+
+out:
return 0;
}
@@ -957,7 +980,8 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
{
int ret = 0;
- isert_info("event %d status %d id %p np %p\n", event->event,
+ isert_info("%s (%d): status %d id %p np %p\n",
+ rdma_event_msg(event->event), event->event,
event->status, cma_id, cma_id->context);
switch (event->event) {
@@ -2091,10 +2115,13 @@ isert_handle_wc(struct ib_wc *wc)
}
} else {
if (wc->status != IB_WC_WR_FLUSH_ERR)
- isert_err("wr id %llx status %d vend_err %x\n",
- wc->wr_id, wc->status, wc->vendor_err);
+ isert_err("%s (%d): wr id %llx vend_err %x\n",
+ ib_wc_status_msg(wc->status), wc->status,
+ wc->wr_id, wc->vendor_err);
else
- isert_dbg("flush error: wr id %llx\n", wc->wr_id);
+ isert_dbg("%s (%d): wr id %llx\n",
+ ib_wc_status_msg(wc->status), wc->status,
+ wc->wr_id);
if (wc->wr_id != ISER_FASTREG_LI_WRID)
isert_cq_comp_err(isert_conn, wc);
@@ -2380,7 +2407,6 @@ isert_build_rdma_wr(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd,
page_off = offset % PAGE_SIZE;
send_wr->sg_list = ib_sge;
- send_wr->num_sge = sg_nents;
send_wr->wr_id = (uintptr_t)&isert_cmd->tx_desc;
/*
* Perform mapping of TCM scatterlist memory ib_sge dma_addr.
@@ -2400,14 +2426,17 @@ isert_build_rdma_wr(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd,
ib_sge->addr, ib_sge->length, ib_sge->lkey);
page_off = 0;
data_left -= ib_sge->length;
+ if (!data_left)
+ break;
ib_sge++;
isert_dbg("Incrementing ib_sge pointer to %p\n", ib_sge);
}
+ send_wr->num_sge = ++i;
isert_dbg("Set outgoing sg_list: %p num_sg: %u from TCM SGLs\n",
send_wr->sg_list, send_wr->num_sge);
- return sg_nents;
+ return send_wr->num_sge;
}
static int
@@ -3366,7 +3395,6 @@ static void isert_wait_conn(struct iscsi_conn *conn)
isert_wait4flush(isert_conn);
isert_wait4logout(isert_conn);
- INIT_WORK(&isert_conn->release_work, isert_release_work);
queue_work(isert_release_wq, &isert_conn->release_work);
}
@@ -3374,6 +3402,7 @@ static void isert_free_conn(struct iscsi_conn *conn)
{
struct isert_conn *isert_conn = conn->context;
+ isert_wait4flush(isert_conn);
isert_put_conn(isert_conn);
}
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 918814cd0f80..eada8f758ad4 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -59,9 +59,10 @@
#define DRV_RELDATE "July 1, 2013"
MODULE_AUTHOR("Roland Dreier");
-MODULE_DESCRIPTION("InfiniBand SCSI RDMA Protocol initiator "
- "v" DRV_VERSION " (" DRV_RELDATE ")");
+MODULE_DESCRIPTION("InfiniBand SCSI RDMA Protocol initiator");
MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_INFO(release_date, DRV_RELDATE);
static unsigned int srp_sg_tablesize;
static unsigned int cmd_sg_entries;
@@ -253,7 +254,8 @@ static void srp_free_iu(struct srp_host *host, struct srp_iu *iu)
static void srp_qp_event(struct ib_event *event, void *context)
{
- pr_debug("QP event %d\n", event->event);
+ pr_debug("QP event %s (%d)\n",
+ ib_event_msg(event->event), event->event);
}
static int srp_init_qp(struct srp_target_port *target,
@@ -465,14 +467,13 @@ static struct srp_fr_pool *srp_alloc_fr_pool(struct srp_target_port *target)
*/
static void srp_destroy_qp(struct srp_rdma_ch *ch)
{
- struct srp_target_port *target = ch->target;
static struct ib_qp_attr attr = { .qp_state = IB_QPS_ERR };
static struct ib_recv_wr wr = { .wr_id = SRP_LAST_WR_ID };
struct ib_recv_wr *bad_wr;
int ret;
/* Destroying a QP and reusing ch->done is only safe if not connected */
- WARN_ON_ONCE(target->connected);
+ WARN_ON_ONCE(ch->connected);
ret = ib_modify_qp(ch->qp, &attr, IB_QP_STATE);
WARN_ONCE(ret, "ib_cm_init_qp_attr() returned %d\n", ret);
@@ -499,6 +500,7 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch)
struct ib_fmr_pool *fmr_pool = NULL;
struct srp_fr_pool *fr_pool = NULL;
const int m = 1 + dev->use_fast_reg;
+ struct ib_cq_init_attr cq_attr = {};
int ret;
init_attr = kzalloc(sizeof *init_attr, GFP_KERNEL);
@@ -506,15 +508,19 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch)
return -ENOMEM;
/* + 1 for SRP_LAST_WR_ID */
+ cq_attr.cqe = target->queue_size + 1;
+ cq_attr.comp_vector = ch->comp_vector;
recv_cq = ib_create_cq(dev->dev, srp_recv_completion, NULL, ch,
- target->queue_size + 1, ch->comp_vector);
+ &cq_attr);
if (IS_ERR(recv_cq)) {
ret = PTR_ERR(recv_cq);
goto err;
}
+ cq_attr.cqe = m * target->queue_size;
+ cq_attr.comp_vector = ch->comp_vector;
send_cq = ib_create_cq(dev->dev, srp_send_completion, NULL, ch,
- m * target->queue_size, ch->comp_vector);
+ &cq_attr);
if (IS_ERR(send_cq)) {
ret = PTR_ERR(send_cq);
goto err_recv_cq;
@@ -781,7 +787,7 @@ static int srp_send_req(struct srp_rdma_ch *ch, bool multich)
shost_printk(KERN_DEBUG, target->scsi_host,
PFX "Topspin/Cisco initiator port ID workaround "
"activated for target GUID %016llx\n",
- (unsigned long long) be64_to_cpu(target->ioc_guid));
+ be64_to_cpu(target->ioc_guid));
memset(req->priv.initiator_port_id, 0, 8);
memcpy(req->priv.initiator_port_id + 8,
&target->srp_host->srp_dev->dev->node_guid, 8);
@@ -811,35 +817,19 @@ static bool srp_queue_remove_work(struct srp_target_port *target)
return changed;
}
-static bool srp_change_conn_state(struct srp_target_port *target,
- bool connected)
-{
- bool changed = false;
-
- spin_lock_irq(&target->lock);
- if (target->connected != connected) {
- target->connected = connected;
- changed = true;
- }
- spin_unlock_irq(&target->lock);
-
- return changed;
-}
-
static void srp_disconnect_target(struct srp_target_port *target)
{
struct srp_rdma_ch *ch;
int i;
- if (srp_change_conn_state(target, false)) {
- /* XXX should send SRP_I_LOGOUT request */
+ /* XXX should send SRP_I_LOGOUT request */
- for (i = 0; i < target->ch_count; i++) {
- ch = &target->ch[i];
- if (ch->cm_id && ib_send_cm_dreq(ch->cm_id, NULL, 0)) {
- shost_printk(KERN_DEBUG, target->scsi_host,
- PFX "Sending CM DREQ failed\n");
- }
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ ch->connected = false;
+ if (ch->cm_id && ib_send_cm_dreq(ch->cm_id, NULL, 0)) {
+ shost_printk(KERN_DEBUG, target->scsi_host,
+ PFX "Sending CM DREQ failed\n");
}
}
}
@@ -852,7 +842,7 @@ static void srp_free_req_data(struct srp_target_port *target,
struct srp_request *req;
int i;
- if (!ch->target || !ch->req_ring)
+ if (!ch->req_ring)
return;
for (i = 0; i < target->req_ring_size; ++i) {
@@ -986,14 +976,26 @@ static void srp_rport_delete(struct srp_rport *rport)
srp_queue_remove_work(target);
}
+/**
+ * srp_connected_ch() - number of connected channels
+ * @target: SRP target port.
+ */
+static int srp_connected_ch(struct srp_target_port *target)
+{
+ int i, c = 0;
+
+ for (i = 0; i < target->ch_count; i++)
+ c += target->ch[i].connected;
+
+ return c;
+}
+
static int srp_connect_ch(struct srp_rdma_ch *ch, bool multich)
{
struct srp_target_port *target = ch->target;
int ret;
- WARN_ON_ONCE(!multich && target->connected);
-
- target->qp_in_error = false;
+ WARN_ON_ONCE(!multich && srp_connected_ch(target) > 0);
ret = srp_lookup_path(ch);
if (ret)
@@ -1016,7 +1018,7 @@ static int srp_connect_ch(struct srp_rdma_ch *ch, bool multich)
*/
switch (ch->status) {
case 0:
- srp_change_conn_state(target, true);
+ ch->connected = true;
return 0;
case SRP_PORT_REDIRECT:
@@ -1214,14 +1216,10 @@ static int srp_rport_reconnect(struct srp_rport *rport)
*/
for (i = 0; i < target->ch_count; i++) {
ch = &target->ch[i];
- if (!ch->target)
- break;
ret += srp_new_cm_id(ch);
}
for (i = 0; i < target->ch_count; i++) {
ch = &target->ch[i];
- if (!ch->target)
- break;
for (j = 0; j < target->req_ring_size; ++j) {
struct srp_request *req = &ch->req_ring[j];
@@ -1230,8 +1228,6 @@ static int srp_rport_reconnect(struct srp_rport *rport)
}
for (i = 0; i < target->ch_count; i++) {
ch = &target->ch[i];
- if (!ch->target)
- break;
/*
* Whether or not creating a new CM ID succeeded, create a new
* QP. This guarantees that all completion callback function
@@ -1243,13 +1239,13 @@ static int srp_rport_reconnect(struct srp_rport *rport)
for (j = 0; j < target->queue_size; ++j)
list_add(&ch->tx_ring[j]->list, &ch->free_tx);
}
+
+ target->qp_in_error = false;
+
for (i = 0; i < target->ch_count; i++) {
ch = &target->ch[i];
- if (ret || !ch->target) {
- if (i > 1)
- ret = 0;
+ if (ret)
break;
- }
ret = srp_connect_ch(ch, multich);
multich = true;
}
@@ -1842,7 +1838,7 @@ static void srp_process_aer_req(struct srp_rdma_ch *ch,
s32 delta = be32_to_cpu(req->req_lim_delta);
shost_printk(KERN_ERR, target->scsi_host, PFX
- "ignoring AER for LUN %llu\n", be64_to_cpu(req->lun));
+ "ignoring AER for LUN %llu\n", scsilun_to_int(&req->lun));
if (srp_response_common(ch, delta, &rsp, sizeof(rsp)))
shost_printk(KERN_ERR, target->scsi_host, PFX
@@ -1929,20 +1925,21 @@ static void srp_handle_qp_err(u64 wr_id, enum ib_wc_status wc_status,
return;
}
- if (target->connected && !target->qp_in_error) {
+ if (ch->connected && !target->qp_in_error) {
if (wr_id & LOCAL_INV_WR_ID_MASK) {
shost_printk(KERN_ERR, target->scsi_host, PFX
- "LOCAL_INV failed with status %d\n",
- wc_status);
+ "LOCAL_INV failed with status %s (%d)\n",
+ ib_wc_status_msg(wc_status), wc_status);
} else if (wr_id & FAST_REG_WR_ID_MASK) {
shost_printk(KERN_ERR, target->scsi_host, PFX
- "FAST_REG_MR failed status %d\n",
- wc_status);
+ "FAST_REG_MR failed status %s (%d)\n",
+ ib_wc_status_msg(wc_status), wc_status);
} else {
shost_printk(KERN_ERR, target->scsi_host,
- PFX "failed %s status %d for iu %p\n",
+ PFX "failed %s status %s (%d) for iu %p\n",
send_err ? "send" : "receive",
- wc_status, (void *)(uintptr_t)wr_id);
+ ib_wc_status_msg(wc_status), wc_status,
+ (void *)(uintptr_t)wr_id);
}
queue_work(system_long_wq, &target->tl_err_work);
}
@@ -2034,7 +2031,7 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd)
memset(cmd, 0, sizeof *cmd);
cmd->opcode = SRP_CMD;
- cmd->lun = cpu_to_be64((u64) scmnd->device->lun << 48);
+ int_to_scsilun(scmnd->device->lun, &cmd->lun);
cmd->tag = tag;
memcpy(cmd->cdb, scmnd->cmnd, scmnd->cmd_len);
@@ -2367,7 +2364,7 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
case IB_CM_DREQ_RECEIVED:
shost_printk(KERN_WARNING, target->scsi_host,
PFX "DREQ received - connection closed\n");
- srp_change_conn_state(target, false);
+ ch->connected = false;
if (ib_send_cm_drep(cm_id, NULL, 0))
shost_printk(KERN_ERR, target->scsi_host,
PFX "Sending CM DREP failed\n");
@@ -2414,8 +2411,8 @@ srp_change_queue_depth(struct scsi_device *sdev, int qdepth)
return scsi_change_queue_depth(sdev, qdepth);
}
-static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag,
- unsigned int lun, u8 func)
+static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
+ u8 func)
{
struct srp_target_port *target = ch->target;
struct srp_rport *rport = target->rport;
@@ -2423,7 +2420,7 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag,
struct srp_iu *iu;
struct srp_tsk_mgmt *tsk_mgmt;
- if (!target->connected || target->qp_in_error)
+ if (!ch->connected || target->qp_in_error)
return -1;
init_completion(&ch->tsk_mgmt_done);
@@ -2449,7 +2446,7 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag,
memset(tsk_mgmt, 0, sizeof *tsk_mgmt);
tsk_mgmt->opcode = SRP_TSK_MGMT;
- tsk_mgmt->lun = cpu_to_be64((u64) lun << 48);
+ int_to_scsilun(lun, &tsk_mgmt->lun);
tsk_mgmt->tag = req_tag | SRP_TAG_TSK_MGMT;
tsk_mgmt->tsk_mgmt_func = func;
tsk_mgmt->task_tag = req_tag;
@@ -2563,8 +2560,7 @@ static ssize_t show_id_ext(struct device *dev, struct device_attribute *attr,
{
struct srp_target_port *target = host_to_target(class_to_shost(dev));
- return sprintf(buf, "0x%016llx\n",
- (unsigned long long) be64_to_cpu(target->id_ext));
+ return sprintf(buf, "0x%016llx\n", be64_to_cpu(target->id_ext));
}
static ssize_t show_ioc_guid(struct device *dev, struct device_attribute *attr,
@@ -2572,8 +2568,7 @@ static ssize_t show_ioc_guid(struct device *dev, struct device_attribute *attr,
{
struct srp_target_port *target = host_to_target(class_to_shost(dev));
- return sprintf(buf, "0x%016llx\n",
- (unsigned long long) be64_to_cpu(target->ioc_guid));
+ return sprintf(buf, "0x%016llx\n", be64_to_cpu(target->ioc_guid));
}
static ssize_t show_service_id(struct device *dev,
@@ -2581,8 +2576,7 @@ static ssize_t show_service_id(struct device *dev,
{
struct srp_target_port *target = host_to_target(class_to_shost(dev));
- return sprintf(buf, "0x%016llx\n",
- (unsigned long long) be64_to_cpu(target->service_id));
+ return sprintf(buf, "0x%016llx\n", be64_to_cpu(target->service_id));
}
static ssize_t show_pkey(struct device *dev, struct device_attribute *attr,
@@ -2773,7 +2767,7 @@ static int srp_add_target(struct srp_host *host, struct srp_target_port *target)
target->state = SRP_TARGET_SCANNING;
sprintf(target->target_name, "SRP.T10:%016llX",
- (unsigned long long) be64_to_cpu(target->id_ext));
+ be64_to_cpu(target->id_ext));
if (scsi_add_host(target->scsi_host, host->srp_dev->dev->dma_device))
return -ENODEV;
@@ -2797,7 +2791,8 @@ static int srp_add_target(struct srp_host *host, struct srp_target_port *target)
scsi_scan_target(&target->scsi_host->shost_gendev,
0, target->scsi_id, SCAN_WILD_CARD, 0);
- if (!target->connected || target->qp_in_error) {
+ if (srp_connected_ch(target) < target->ch_count ||
+ target->qp_in_error) {
shost_printk(KERN_INFO, target->scsi_host,
PFX "SCSI scan failed - removing SCSI host\n");
srp_queue_remove_work(target);
@@ -3146,7 +3141,7 @@ static ssize_t srp_create_target(struct device *dev,
target_host->transportt = ib_srp_transport_template;
target_host->max_channel = 0;
target_host->max_id = 1;
- target_host->max_lun = SRP_MAX_LUN;
+ target_host->max_lun = -1LL;
target_host->max_cmd_len = sizeof ((struct srp_cmd *) (void *) 0L)->cdb;
target = host_to_target(target_host);
@@ -3172,11 +3167,11 @@ static ssize_t srp_create_target(struct device *dev,
ret = srp_parse_options(buf, target);
if (ret)
- goto err;
+ goto out;
ret = scsi_init_shared_tag_map(target_host, target_host->can_queue);
if (ret)
- goto err;
+ goto out;
target->req_ring_size = target->queue_size - SRP_TSK_MGMT_SQ_SIZE;
@@ -3187,7 +3182,7 @@ static ssize_t srp_create_target(struct device *dev,
be64_to_cpu(target->ioc_guid),
be64_to_cpu(target->initiator_ext));
ret = -EEXIST;
- goto err;
+ goto out;
}
if (!srp_dev->has_fmr && !srp_dev->has_fr && !target->allow_ext_sg &&
@@ -3208,7 +3203,7 @@ static ssize_t srp_create_target(struct device *dev,
spin_lock_init(&target->lock);
ret = ib_query_gid(ibdev, host->port, 0, &target->sgid);
if (ret)
- goto err;
+ goto out;
ret = -ENOMEM;
target->ch_count = max_t(unsigned, num_online_nodes(),
@@ -3219,7 +3214,7 @@ static ssize_t srp_create_target(struct device *dev,
target->ch = kcalloc(target->ch_count, sizeof(*target->ch),
GFP_KERNEL);
if (!target->ch)
- goto err;
+ goto out;
node_idx = 0;
for_each_online_node(node) {
@@ -3315,9 +3310,6 @@ err_disconnect:
}
kfree(target->ch);
-
-err:
- scsi_host_put(target_host);
goto out;
}
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index a611556406ac..17ee3f80ba55 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -54,7 +54,6 @@ enum {
SRP_DLID_REDIRECT = 2,
SRP_STALE_CONN = 3,
- SRP_MAX_LUN = 512,
SRP_DEF_SG_TABLESIZE = 12,
SRP_DEFAULT_QUEUE_SIZE = 1 << 6,
@@ -170,6 +169,7 @@ struct srp_rdma_ch {
struct completion tsk_mgmt_done;
u8 tsk_mgmt_status;
+ bool connected;
};
/**
@@ -214,7 +214,6 @@ struct srp_target_port {
__be16 pkey;
u32 rq_tmo_jiffies;
- bool connected;
int zero_req_lim;
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 9b84b4c0a000..4556cd11288e 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -41,6 +41,7 @@
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/atomic.h>
+#include <scsi/scsi_proto.h>
#include <scsi/scsi_tcq.h>
#include <target/configfs_macros.h>
#include <target/target_core_base.h>
@@ -476,7 +477,8 @@ static void srpt_mad_recv_handler(struct ib_mad_agent *mad_agent,
rsp = ib_create_send_mad(mad_agent, mad_wc->wc->src_qp,
mad_wc->wc->pkey_index, 0,
IB_MGMT_DEVICE_HDR, IB_MGMT_DEVICE_DATA,
- GFP_KERNEL);
+ GFP_KERNEL,
+ IB_MGMT_BASE_VERSION);
if (IS_ERR(rsp))
goto err_rsp;
@@ -2080,6 +2082,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
struct srpt_port *sport = ch->sport;
struct srpt_device *sdev = sport->sdev;
u32 srp_sq_size = sport->port_attrib.srp_sq_size;
+ struct ib_cq_init_attr cq_attr = {};
int ret;
WARN_ON(ch->rq_size < 1);
@@ -2090,8 +2093,9 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
goto out;
retry:
+ cq_attr.cqe = ch->rq_size + srp_sq_size;
ch->cq = ib_create_cq(sdev->device, srpt_completion, NULL, ch,
- ch->rq_size + srp_sq_size, 0);
+ &cq_attr);
if (IS_ERR(ch->cq)) {
ret = PTR_ERR(ch->cq);
pr_err("failed to create CQ cqe= %d ret= %d\n",
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.h b/drivers/infiniband/ulp/srpt/ib_srpt.h
index 3dae156905de..d85c0c205625 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.h
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.h
@@ -245,7 +245,7 @@ struct srpt_send_ioctx {
u8 n_rdma;
u8 n_rbuf;
bool queue_status_only;
- u8 sense_data[SCSI_SENSE_BUFFERSIZE];
+ u8 sense_data[TRANSPORT_SENSE_BUFFER];
};
/**
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index f362883c94e3..1d247bcf2ae2 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -747,6 +747,63 @@ static void joydev_cleanup(struct joydev *joydev)
input_close_device(handle);
}
+static bool joydev_dev_is_absolute_mouse(struct input_dev *dev)
+{
+ DECLARE_BITMAP(jd_scratch, KEY_CNT);
+
+ BUILD_BUG_ON(ABS_CNT > KEY_CNT || EV_CNT > KEY_CNT);
+
+ /*
+ * Virtualization (VMware, etc) and remote management (HP
+ * ILO2) solutions use absolute coordinates for their virtual
+ * pointing devices so that there is one-to-one relationship
+ * between pointer position on the host screen and virtual
+ * guest screen, and so their mice use ABS_X, ABS_Y and 3
+ * primary button events. This clashes with what joydev
+ * considers to be joysticks (a device with at minimum ABS_X
+ * axis).
+ *
+ * Here we are trying to separate absolute mice from
+ * joysticks. A device is, for joystick detection purposes,
+ * considered to be an absolute mouse if the following is
+ * true:
+ *
+ * 1) Event types are exactly EV_ABS, EV_KEY and EV_SYN.
+ * 2) Absolute events are exactly ABS_X and ABS_Y.
+ * 3) Keys are exactly BTN_LEFT, BTN_RIGHT and BTN_MIDDLE.
+ * 4) Device is not on "Amiga" bus.
+ */
+
+ bitmap_zero(jd_scratch, EV_CNT);
+ __set_bit(EV_ABS, jd_scratch);
+ __set_bit(EV_KEY, jd_scratch);
+ __set_bit(EV_SYN, jd_scratch);
+ if (!bitmap_equal(jd_scratch, dev->evbit, EV_CNT))
+ return false;
+
+ bitmap_zero(jd_scratch, ABS_CNT);
+ __set_bit(ABS_X, jd_scratch);
+ __set_bit(ABS_Y, jd_scratch);
+ if (!bitmap_equal(dev->absbit, jd_scratch, ABS_CNT))
+ return false;
+
+ bitmap_zero(jd_scratch, KEY_CNT);
+ __set_bit(BTN_LEFT, jd_scratch);
+ __set_bit(BTN_RIGHT, jd_scratch);
+ __set_bit(BTN_MIDDLE, jd_scratch);
+
+ if (!bitmap_equal(dev->keybit, jd_scratch, KEY_CNT))
+ return false;
+
+ /*
+ * Amiga joystick (amijoy) historically uses left/middle/right
+ * button events.
+ */
+ if (dev->id.bustype == BUS_AMIGA)
+ return false;
+
+ return true;
+}
static bool joydev_match(struct input_handler *handler, struct input_dev *dev)
{
@@ -758,6 +815,10 @@ static bool joydev_match(struct input_handler *handler, struct input_dev *dev)
if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_DIGI, dev->keybit))
return false;
+ /* Avoid absolute mice */
+ if (joydev_dev_is_absolute_mouse(dev))
+ return false;
+
return true;
}
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 106fbac7f8c5..e8eb60c6d83e 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -677,7 +677,7 @@ config KEYBOARD_W90P910
config KEYBOARD_CROS_EC
tristate "ChromeOS EC keyboard"
select INPUT_MATRIXKMAP
- depends on MFD_CROS_EC
+ depends on CROS_EC_PROTO
help
Say Y here to enable the matrix keyboard used by ChromeOS devices
and implemented on the ChromeOS EC. You must enable one bus option
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index b50c5b8b8a4d..b01966dc7eb3 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -148,19 +148,28 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
{
- int ret;
- struct cros_ec_command msg = {
- .command = EC_CMD_MKBP_STATE,
- .insize = ckdev->cols,
- };
+ int ret = 0;
+ struct cros_ec_command *msg;
- ret = cros_ec_cmd_xfer(ckdev->ec, &msg);
- if (ret < 0)
- return ret;
+ msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
- memcpy(kb_state, msg.indata, ckdev->cols);
+ msg->version = 0;
+ msg->command = EC_CMD_MKBP_STATE;
+ msg->insize = ckdev->cols;
+ msg->outsize = 0;
- return 0;
+ ret = cros_ec_cmd_xfer(ckdev->ec, msg);
+ if (ret < 0) {
+ dev_err(ckdev->dev, "Error transferring EC message %d\n", ret);
+ goto exit;
+ }
+
+ memcpy(kb_state, msg->data, ckdev->cols);
+exit:
+ kfree(msg);
+ return ret;
}
static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
@@ -266,7 +275,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
ckdev->dev = dev;
dev_set_drvdata(&pdev->dev, ckdev);
- idev->name = ec->ec_name;
+ idev->name = CROS_EC_DEV_NAME;
idev->phys = ec->phys_name;
__set_bit(EV_REP, idev->evbit);
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 7462d2fc8cfe..d7820d1152d2 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -156,7 +156,7 @@ config MOUSE_PS2_VMMOUSE
Say Y here if you are running under control of VMware hypervisor
(ESXi, Workstation or Fusion). Also make sure that when you enable
this option, you remove the xf86-input-vmmouse user-space driver
- or upgrade it to at least xf86-input-vmmouse 13.0.1, which doesn't
+ or upgrade it to at least xf86-input-vmmouse 13.1.0, which doesn't
load in the presence of an in-kernel vmmouse driver.
If unsure, say N.
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index e6708f6efb4d..a353b7de6d22 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -941,6 +941,11 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
case V7_PACKET_ID_TWO:
mt[1].x &= ~0x000F;
mt[1].y |= 0x000F;
+ /* Detect false-postive touches where x & y report max value */
+ if (mt[1].y == 0x7ff && mt[1].x == 0xff0) {
+ mt[1].x = 0;
+ /* y gets set to 0 at the end of this function */
+ }
break;
case V7_PACKET_ID_MULTI:
@@ -1058,9 +1063,8 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
right = (packet[1] & 0x02) >> 1;
middle = (packet[1] & 0x04) >> 2;
- /* Divide 2 since trackpoint's speed is too fast */
- input_report_rel(dev2, REL_X, (char)x / 2);
- input_report_rel(dev2, REL_Y, -((char)y / 2));
+ input_report_rel(dev2, REL_X, (char)x);
+ input_report_rel(dev2, REL_Y, -((char)y));
input_report_key(dev2, BTN_LEFT, left);
input_report_key(dev2, BTN_RIGHT, right);
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 991dc6b20a58..ce3d40004458 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -315,7 +315,7 @@ static void elantech_report_semi_mt_data(struct input_dev *dev,
unsigned int x2, unsigned int y2)
{
elantech_set_slot(dev, 0, num_fingers != 0, x1, y1);
- elantech_set_slot(dev, 1, num_fingers == 2, x2, y2);
+ elantech_set_slot(dev, 1, num_fingers >= 2, x2, y2);
}
/*
@@ -1376,10 +1376,11 @@ static bool elantech_is_signature_valid(const unsigned char *param)
return true;
/*
- * Some models have a revision higher then 20. Meaning param[2] may
- * be 10 or 20, skip the rates check for these.
+ * Some hw_version >= 4 models have a revision higher then 20. Meaning
+ * that param[2] may be 10 or 20, skip the rates check for these.
*/
- if (param[0] == 0x46 && (param[1] & 0xef) == 0x0f && param[2] < 40)
+ if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f &&
+ param[2] < 40)
return true;
for (i = 0; i < ARRAY_SIZE(rates); i++)
@@ -1555,6 +1556,7 @@ static int elantech_set_properties(struct elantech_data *etd)
case 9:
case 10:
case 13:
+ case 14:
etd->hw_version = 4;
break;
default:
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 630af73e98c4..35c8d0ceabee 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -151,6 +151,11 @@ static const struct min_max_quirk min_max_pnpid_table[] = {
1024, 5112, 2024, 4832
},
{
+ (const char * const []){"LEN2000", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
+ 1024, 5113, 2021, 4832
+ },
+ {
(const char * const []){"LEN2001", NULL},
{ANY_BOARD_ID, ANY_BOARD_ID},
1024, 5022, 2508, 4832
@@ -191,7 +196,7 @@ static const char * const topbuttonpad_pnp_ids[] = {
"LEN0045",
"LEN0047",
"LEN0049",
- "LEN2000",
+ "LEN2000", /* S540 */
"LEN2001", /* Edge E431 */
"LEN2002", /* Edge E531 */
"LEN2003",
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
index 2d5ff86b343f..e4c31256a74d 100644
--- a/drivers/input/touchscreen/stmpe-ts.c
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -164,7 +164,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data)
STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
/* start polling for touch_det to detect release */
- schedule_delayed_work(&ts->work, HZ / 50);
+ schedule_delayed_work(&ts->work, msecs_to_jiffies(50));
return IRQ_HANDLED;
}
diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
index aecb9ad2e701..642f4a53de50 100644
--- a/drivers/input/touchscreen/sx8654.c
+++ b/drivers/input/touchscreen/sx8654.c
@@ -187,7 +187,7 @@ static int sx8654_probe(struct i2c_client *client,
return -ENOMEM;
input = devm_input_allocate_device(&client->dev);
- if (!sx8654)
+ if (!input)
return -ENOMEM;
input->name = "SX8654 I2C Touchscreen";
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 1ae4e547b419..40f37a2b4a8a 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -339,6 +339,7 @@ config SPAPR_TCE_IOMMU
Enables bits of IOMMU API required by VFIO. The iommu_ops
is not implemented as it is not necessary for VFIO.
+# ARM IOMMU support
config ARM_SMMU
bool "ARM Ltd. System MMU (SMMU) Support"
depends on (ARM64 || ARM) && MMU
@@ -352,4 +353,16 @@ config ARM_SMMU
Say Y here if your SoC includes an IOMMU device implementing
the ARM SMMU architecture.
+config ARM_SMMU_V3
+ bool "ARM Ltd. System MMU Version 3 (SMMUv3) Support"
+ depends on ARM64 && PCI
+ select IOMMU_API
+ select IOMMU_IO_PGTABLE_LPAE
+ help
+ Support for implementations of the ARM System MMU architecture
+ version 3 providing translation support to a PCIe root complex.
+
+ Say Y here if your system includes an IOMMU device implementing
+ the ARM SMMUv3 architecture.
+
endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 080ffab4ed1c..c6dcc513d711 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
+obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
obj-$(CONFIG_DMAR_TABLE) += dmar.o
obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o
obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index e43d48956dea..d3e5e9abe3b6 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -34,6 +34,7 @@
#include <linux/irq.h>
#include <linux/msi.h>
#include <linux/dma-contiguous.h>
+#include <linux/irqdomain.h>
#include <asm/irq_remapping.h>
#include <asm/io_apic.h>
#include <asm/apic.h>
@@ -64,10 +65,6 @@
static DEFINE_RWLOCK(amd_iommu_devtable_lock);
-/* A list of preallocated protection domains */
-static LIST_HEAD(iommu_pd_list);
-static DEFINE_SPINLOCK(iommu_pd_list_lock);
-
/* List of all available dev_data structures */
static LIST_HEAD(dev_data_list);
static DEFINE_SPINLOCK(dev_data_list_lock);
@@ -119,7 +116,7 @@ struct iommu_cmd {
struct kmem_cache *amd_iommu_irq_cache;
static void update_domain(struct protection_domain *domain);
-static int __init alloc_passthrough_domain(void);
+static int alloc_passthrough_domain(void);
/****************************************************************************
*
@@ -234,31 +231,38 @@ static bool pdev_pri_erratum(struct pci_dev *pdev, u32 erratum)
}
/*
- * In this function the list of preallocated protection domains is traversed to
- * find the domain for a specific device
+ * This function actually applies the mapping to the page table of the
+ * dma_ops domain.
*/
-static struct dma_ops_domain *find_protection_domain(u16 devid)
+static void alloc_unity_mapping(struct dma_ops_domain *dma_dom,
+ struct unity_map_entry *e)
{
- struct dma_ops_domain *entry, *ret = NULL;
- unsigned long flags;
- u16 alias = amd_iommu_alias_table[devid];
-
- if (list_empty(&iommu_pd_list))
- return NULL;
-
- spin_lock_irqsave(&iommu_pd_list_lock, flags);
+ u64 addr;
- list_for_each_entry(entry, &iommu_pd_list, list) {
- if (entry->target_dev == devid ||
- entry->target_dev == alias) {
- ret = entry;
- break;
- }
+ for (addr = e->address_start; addr < e->address_end;
+ addr += PAGE_SIZE) {
+ if (addr < dma_dom->aperture_size)
+ __set_bit(addr >> PAGE_SHIFT,
+ dma_dom->aperture[0]->bitmap);
}
+}
+
+/*
+ * Inits the unity mappings required for a specific device
+ */
+static void init_unity_mappings_for_device(struct device *dev,
+ struct dma_ops_domain *dma_dom)
+{
+ struct unity_map_entry *e;
+ u16 devid;
- spin_unlock_irqrestore(&iommu_pd_list_lock, flags);
+ devid = get_device_id(dev);
- return ret;
+ list_for_each_entry(e, &amd_iommu_unity_map, list) {
+ if (!(devid >= e->devid_start && devid <= e->devid_end))
+ continue;
+ alloc_unity_mapping(dma_dom, e);
+ }
}
/*
@@ -290,11 +294,23 @@ static bool check_device(struct device *dev)
static void init_iommu_group(struct device *dev)
{
+ struct dma_ops_domain *dma_domain;
+ struct iommu_domain *domain;
struct iommu_group *group;
group = iommu_group_get_for_dev(dev);
- if (!IS_ERR(group))
- iommu_group_put(group);
+ if (IS_ERR(group))
+ return;
+
+ domain = iommu_group_default_domain(group);
+ if (!domain)
+ goto out;
+
+ dma_domain = to_pdomain(domain)->priv;
+
+ init_unity_mappings_for_device(dev, dma_domain);
+out:
+ iommu_group_put(group);
}
static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
@@ -434,64 +450,15 @@ static void iommu_uninit_device(struct device *dev)
/* Unlink from alias, it may change if another device is re-plugged */
dev_data->alias_data = NULL;
+ /* Remove dma-ops */
+ dev->archdata.dma_ops = NULL;
+
/*
* We keep dev_data around for unplugged devices and reuse it when the
* device is re-plugged - not doing so would introduce a ton of races.
*/
}
-void __init amd_iommu_uninit_devices(void)
-{
- struct iommu_dev_data *dev_data, *n;
- struct pci_dev *pdev = NULL;
-
- for_each_pci_dev(pdev) {
-
- if (!check_device(&pdev->dev))
- continue;
-
- iommu_uninit_device(&pdev->dev);
- }
-
- /* Free all of our dev_data structures */
- list_for_each_entry_safe(dev_data, n, &dev_data_list, dev_data_list)
- free_dev_data(dev_data);
-}
-
-int __init amd_iommu_init_devices(void)
-{
- struct pci_dev *pdev = NULL;
- int ret = 0;
-
- for_each_pci_dev(pdev) {
-
- if (!check_device(&pdev->dev))
- continue;
-
- ret = iommu_init_device(&pdev->dev);
- if (ret == -ENOTSUPP)
- iommu_ignore_device(&pdev->dev);
- else if (ret)
- goto out_free;
- }
-
- /*
- * Initialize IOMMU groups only after iommu_init_device() has
- * had a chance to populate any IVRS defined aliases.
- */
- for_each_pci_dev(pdev) {
- if (check_device(&pdev->dev))
- init_iommu_group(&pdev->dev);
- }
-
- return 0;
-
-out_free:
-
- amd_iommu_uninit_devices();
-
- return ret;
-}
#ifdef CONFIG_AMD_IOMMU_STATS
/*
@@ -1463,94 +1430,6 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom,
return unmapped;
}
-/*
- * This function checks if a specific unity mapping entry is needed for
- * this specific IOMMU.
- */
-static int iommu_for_unity_map(struct amd_iommu *iommu,
- struct unity_map_entry *entry)
-{
- u16 bdf, i;
-
- for (i = entry->devid_start; i <= entry->devid_end; ++i) {
- bdf = amd_iommu_alias_table[i];
- if (amd_iommu_rlookup_table[bdf] == iommu)
- return 1;
- }
-
- return 0;
-}
-
-/*
- * This function actually applies the mapping to the page table of the
- * dma_ops domain.
- */
-static int dma_ops_unity_map(struct dma_ops_domain *dma_dom,
- struct unity_map_entry *e)
-{
- u64 addr;
- int ret;
-
- for (addr = e->address_start; addr < e->address_end;
- addr += PAGE_SIZE) {
- ret = iommu_map_page(&dma_dom->domain, addr, addr, e->prot,
- PAGE_SIZE);
- if (ret)
- return ret;
- /*
- * if unity mapping is in aperture range mark the page
- * as allocated in the aperture
- */
- if (addr < dma_dom->aperture_size)
- __set_bit(addr >> PAGE_SHIFT,
- dma_dom->aperture[0]->bitmap);
- }
-
- return 0;
-}
-
-/*
- * Init the unity mappings for a specific IOMMU in the system
- *
- * Basically iterates over all unity mapping entries and applies them to
- * the default domain DMA of that IOMMU if necessary.
- */
-static int iommu_init_unity_mappings(struct amd_iommu *iommu)
-{
- struct unity_map_entry *entry;
- int ret;
-
- list_for_each_entry(entry, &amd_iommu_unity_map, list) {
- if (!iommu_for_unity_map(iommu, entry))
- continue;
- ret = dma_ops_unity_map(iommu->default_dom, entry);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Inits the unity mappings required for a specific device
- */
-static int init_unity_mappings_for_device(struct dma_ops_domain *dma_dom,
- u16 devid)
-{
- struct unity_map_entry *e;
- int ret;
-
- list_for_each_entry(e, &amd_iommu_unity_map, list) {
- if (!(devid >= e->devid_start && devid <= e->devid_end))
- continue;
- ret = dma_ops_unity_map(dma_dom, e);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
/****************************************************************************
*
* The next functions belong to the address allocator for the dma_ops
@@ -1704,14 +1583,16 @@ static unsigned long dma_ops_area_alloc(struct device *dev,
unsigned long next_bit = dom->next_address % APERTURE_RANGE_SIZE;
int max_index = dom->aperture_size >> APERTURE_RANGE_SHIFT;
int i = start >> APERTURE_RANGE_SHIFT;
- unsigned long boundary_size;
+ unsigned long boundary_size, mask;
unsigned long address = -1;
unsigned long limit;
next_bit >>= PAGE_SHIFT;
- boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
- PAGE_SIZE) >> PAGE_SHIFT;
+ mask = dma_get_seg_boundary(dev);
+
+ boundary_size = mask + 1 ? ALIGN(mask + 1, PAGE_SIZE) >> PAGE_SHIFT :
+ 1UL << (BITS_PER_LONG - PAGE_SHIFT);
for (;i < max_index; ++i) {
unsigned long offset = dom->aperture[i]->offset >> PAGE_SHIFT;
@@ -1869,9 +1750,15 @@ static void free_pt_##LVL (unsigned long __pt) \
pt = (u64 *)__pt; \
\
for (i = 0; i < 512; ++i) { \
+ /* PTE present? */ \
if (!IOMMU_PTE_PRESENT(pt[i])) \
continue; \
\
+ /* Large PTE? */ \
+ if (PM_PTE_LEVEL(pt[i]) == 0 || \
+ PM_PTE_LEVEL(pt[i]) == 7) \
+ continue; \
+ \
p = (unsigned long)IOMMU_PTE_PAGE(pt[i]); \
FN(p); \
} \
@@ -2008,7 +1895,6 @@ static struct dma_ops_domain *dma_ops_domain_alloc(void)
goto free_dma_dom;
dma_dom->need_flush = false;
- dma_dom->target_dev = 0xffff;
add_domain_to_list(&dma_dom->domain);
@@ -2373,110 +2259,67 @@ static void detach_device(struct device *dev)
dev_data->ats.enabled = false;
}
-/*
- * Find out the protection domain structure for a given PCI device. This
- * will give us the pointer to the page table root for example.
- */
-static struct protection_domain *domain_for_device(struct device *dev)
+static int amd_iommu_add_device(struct device *dev)
{
struct iommu_dev_data *dev_data;
- struct protection_domain *dom = NULL;
- unsigned long flags;
-
- dev_data = get_dev_data(dev);
-
- if (dev_data->domain)
- return dev_data->domain;
-
- if (dev_data->alias_data != NULL) {
- struct iommu_dev_data *alias_data = dev_data->alias_data;
-
- read_lock_irqsave(&amd_iommu_devtable_lock, flags);
- if (alias_data->domain != NULL) {
- __attach_device(dev_data, alias_data->domain);
- dom = alias_data->domain;
- }
- read_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
- }
-
- return dom;
-}
-
-static int device_change_notifier(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct dma_ops_domain *dma_domain;
- struct protection_domain *domain;
- struct iommu_dev_data *dev_data;
- struct device *dev = data;
+ struct iommu_domain *domain;
struct amd_iommu *iommu;
- unsigned long flags;
u16 devid;
+ int ret;
- if (!check_device(dev))
+ if (!check_device(dev) || get_dev_data(dev))
return 0;
- devid = get_device_id(dev);
- iommu = amd_iommu_rlookup_table[devid];
- dev_data = get_dev_data(dev);
-
- switch (action) {
- case BUS_NOTIFY_ADD_DEVICE:
+ devid = get_device_id(dev);
+ iommu = amd_iommu_rlookup_table[devid];
- iommu_init_device(dev);
- init_iommu_group(dev);
+ ret = iommu_init_device(dev);
+ if (ret) {
+ if (ret != -ENOTSUPP)
+ pr_err("Failed to initialize device %s - trying to proceed anyway\n",
+ dev_name(dev));
- /*
- * dev_data is still NULL and
- * got initialized in iommu_init_device
- */
- dev_data = get_dev_data(dev);
+ iommu_ignore_device(dev);
+ dev->archdata.dma_ops = &nommu_dma_ops;
+ goto out;
+ }
+ init_iommu_group(dev);
- if (iommu_pass_through || dev_data->iommu_v2) {
- dev_data->passthrough = true;
- attach_device(dev, pt_domain);
- break;
- }
+ dev_data = get_dev_data(dev);
- domain = domain_for_device(dev);
+ BUG_ON(!dev_data);
- /* allocate a protection domain if a device is added */
- dma_domain = find_protection_domain(devid);
- if (!dma_domain) {
- dma_domain = dma_ops_domain_alloc();
- if (!dma_domain)
- goto out;
- dma_domain->target_dev = devid;
-
- spin_lock_irqsave(&iommu_pd_list_lock, flags);
- list_add_tail(&dma_domain->list, &iommu_pd_list);
- spin_unlock_irqrestore(&iommu_pd_list_lock, flags);
- }
+ if (dev_data->iommu_v2)
+ iommu_request_dm_for_dev(dev);
+ /* Domains are initialized for this device - have a look what we ended up with */
+ domain = iommu_get_domain_for_dev(dev);
+ if (domain->type == IOMMU_DOMAIN_IDENTITY) {
+ dev_data->passthrough = true;
+ dev->archdata.dma_ops = &nommu_dma_ops;
+ } else {
dev->archdata.dma_ops = &amd_iommu_dma_ops;
-
- break;
- case BUS_NOTIFY_REMOVED_DEVICE:
-
- iommu_uninit_device(dev);
-
- default:
- goto out;
}
+out:
iommu_completion_wait(iommu);
-out:
return 0;
}
-static struct notifier_block device_nb = {
- .notifier_call = device_change_notifier,
-};
-
-void amd_iommu_init_notifier(void)
+static void amd_iommu_remove_device(struct device *dev)
{
- bus_register_notifier(&pci_bus_type, &device_nb);
+ struct amd_iommu *iommu;
+ u16 devid;
+
+ if (!check_device(dev))
+ return;
+
+ devid = get_device_id(dev);
+ iommu = amd_iommu_rlookup_table[devid];
+
+ iommu_uninit_device(dev);
+ iommu_completion_wait(iommu);
}
/*****************************************************************************
@@ -2495,28 +2338,20 @@ void amd_iommu_init_notifier(void)
static struct protection_domain *get_domain(struct device *dev)
{
struct protection_domain *domain;
- struct dma_ops_domain *dma_dom;
- u16 devid = get_device_id(dev);
+ struct iommu_domain *io_domain;
if (!check_device(dev))
return ERR_PTR(-EINVAL);
- domain = domain_for_device(dev);
- if (domain != NULL && !dma_ops_domain(domain))
- return ERR_PTR(-EBUSY);
-
- if (domain != NULL)
- return domain;
+ io_domain = iommu_get_domain_for_dev(dev);
+ if (!io_domain)
+ return NULL;
- /* Device not bound yet - bind it */
- dma_dom = find_protection_domain(devid);
- if (!dma_dom)
- dma_dom = amd_iommu_rlookup_table[devid]->default_dom;
- attach_device(dev, &dma_dom->domain);
- DUMP_printk("Using protection domain %d for device %s\n",
- dma_dom->domain.id, dev_name(dev));
+ domain = to_pdomain(io_domain);
+ if (!dma_ops_domain(domain))
+ return ERR_PTR(-EBUSY);
- return &dma_dom->domain;
+ return domain;
}
static void update_device_table(struct protection_domain *domain)
@@ -2930,6 +2765,7 @@ static void *alloc_coherent(struct device *dev, size_t size,
size = PAGE_ALIGN(size);
dma_mask = dev->coherent_dma_mask;
flag &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
+ flag |= __GFP_ZERO;
page = alloc_pages(flag | __GFP_NOWARN, get_order(size));
if (!page) {
@@ -3011,54 +2847,6 @@ static int amd_iommu_dma_supported(struct device *dev, u64 mask)
return check_device(dev);
}
-/*
- * The function for pre-allocating protection domains.
- *
- * If the driver core informs the DMA layer if a driver grabs a device
- * we don't need to preallocate the protection domains anymore.
- * For now we have to.
- */
-static void __init prealloc_protection_domains(void)
-{
- struct iommu_dev_data *dev_data;
- struct dma_ops_domain *dma_dom;
- struct pci_dev *dev = NULL;
- u16 devid;
-
- for_each_pci_dev(dev) {
-
- /* Do we handle this device? */
- if (!check_device(&dev->dev))
- continue;
-
- dev_data = get_dev_data(&dev->dev);
- if (!amd_iommu_force_isolation && dev_data->iommu_v2) {
- /* Make sure passthrough domain is allocated */
- alloc_passthrough_domain();
- dev_data->passthrough = true;
- attach_device(&dev->dev, pt_domain);
- pr_info("AMD-Vi: Using passthrough domain for device %s\n",
- dev_name(&dev->dev));
- }
-
- /* Is there already any domain for it? */
- if (domain_for_device(&dev->dev))
- continue;
-
- devid = get_device_id(&dev->dev);
-
- dma_dom = dma_ops_domain_alloc();
- if (!dma_dom)
- continue;
- init_unity_mappings_for_device(dma_dom, devid);
- dma_dom->target_dev = devid;
-
- attach_device(&dev->dev, &dma_dom->domain);
-
- list_add_tail(&dma_dom->list, &iommu_pd_list);
- }
-}
-
static struct dma_map_ops amd_iommu_dma_ops = {
.alloc = alloc_coherent,
.free = free_coherent,
@@ -3069,76 +2857,16 @@ static struct dma_map_ops amd_iommu_dma_ops = {
.dma_supported = amd_iommu_dma_supported,
};
-static unsigned device_dma_ops_init(void)
+int __init amd_iommu_init_api(void)
{
- struct iommu_dev_data *dev_data;
- struct pci_dev *pdev = NULL;
- unsigned unhandled = 0;
-
- for_each_pci_dev(pdev) {
- if (!check_device(&pdev->dev)) {
-
- iommu_ignore_device(&pdev->dev);
-
- unhandled += 1;
- continue;
- }
-
- dev_data = get_dev_data(&pdev->dev);
-
- if (!dev_data->passthrough)
- pdev->dev.archdata.dma_ops = &amd_iommu_dma_ops;
- else
- pdev->dev.archdata.dma_ops = &nommu_dma_ops;
- }
-
- return unhandled;
-}
-
-/*
- * The function which clues the AMD IOMMU driver into dma_ops.
- */
-
-void __init amd_iommu_init_api(void)
-{
- bus_set_iommu(&pci_bus_type, &amd_iommu_ops);
+ return bus_set_iommu(&pci_bus_type, &amd_iommu_ops);
}
int __init amd_iommu_init_dma_ops(void)
{
- struct amd_iommu *iommu;
- int ret, unhandled;
-
- /*
- * first allocate a default protection domain for every IOMMU we
- * found in the system. Devices not assigned to any other
- * protection domain will be assigned to the default one.
- */
- for_each_iommu(iommu) {
- iommu->default_dom = dma_ops_domain_alloc();
- if (iommu->default_dom == NULL)
- return -ENOMEM;
- iommu->default_dom->domain.flags |= PD_DEFAULT_MASK;
- ret = iommu_init_unity_mappings(iommu);
- if (ret)
- goto free_domains;
- }
-
- /*
- * Pre-allocate the protection domains for each device.
- */
- prealloc_protection_domains();
-
iommu_detected = 1;
swiotlb = 0;
- /* Make the driver finally visible to the drivers */
- unhandled = device_dma_ops_init();
- if (unhandled && max_pfn > MAX_DMA32_PFN) {
- /* There are unhandled devices - initialize swiotlb for them */
- swiotlb = 1;
- }
-
amd_iommu_stats_init();
if (amd_iommu_unmap_flush)
@@ -3147,14 +2875,6 @@ int __init amd_iommu_init_dma_ops(void)
pr_info("AMD-Vi: Lazy IO/TLB flushing enabled\n");
return 0;
-
-free_domains:
-
- for_each_iommu(iommu) {
- dma_ops_domain_free(iommu->default_dom);
- }
-
- return ret;
}
/*****************************************************************************
@@ -3221,7 +2941,7 @@ out_err:
return NULL;
}
-static int __init alloc_passthrough_domain(void)
+static int alloc_passthrough_domain(void)
{
if (pt_domain != NULL)
return 0;
@@ -3239,30 +2959,46 @@ static int __init alloc_passthrough_domain(void)
static struct iommu_domain *amd_iommu_domain_alloc(unsigned type)
{
struct protection_domain *pdomain;
+ struct dma_ops_domain *dma_domain;
- /* We only support unmanaged domains for now */
- if (type != IOMMU_DOMAIN_UNMANAGED)
- return NULL;
-
- pdomain = protection_domain_alloc();
- if (!pdomain)
- goto out_free;
+ switch (type) {
+ case IOMMU_DOMAIN_UNMANAGED:
+ pdomain = protection_domain_alloc();
+ if (!pdomain)
+ return NULL;
- pdomain->mode = PAGE_MODE_3_LEVEL;
- pdomain->pt_root = (void *)get_zeroed_page(GFP_KERNEL);
- if (!pdomain->pt_root)
- goto out_free;
+ pdomain->mode = PAGE_MODE_3_LEVEL;
+ pdomain->pt_root = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!pdomain->pt_root) {
+ protection_domain_free(pdomain);
+ return NULL;
+ }
- pdomain->domain.geometry.aperture_start = 0;
- pdomain->domain.geometry.aperture_end = ~0ULL;
- pdomain->domain.geometry.force_aperture = true;
+ pdomain->domain.geometry.aperture_start = 0;
+ pdomain->domain.geometry.aperture_end = ~0ULL;
+ pdomain->domain.geometry.force_aperture = true;
- return &pdomain->domain;
+ break;
+ case IOMMU_DOMAIN_DMA:
+ dma_domain = dma_ops_domain_alloc();
+ if (!dma_domain) {
+ pr_err("AMD-Vi: Failed to allocate\n");
+ return NULL;
+ }
+ pdomain = &dma_domain->domain;
+ break;
+ case IOMMU_DOMAIN_IDENTITY:
+ pdomain = protection_domain_alloc();
+ if (!pdomain)
+ return NULL;
-out_free:
- protection_domain_free(pdomain);
+ pdomain->mode = PAGE_MODE_NONE;
+ break;
+ default:
+ return NULL;
+ }
- return NULL;
+ return &pdomain->domain;
}
static void amd_iommu_domain_free(struct iommu_domain *dom)
@@ -3412,6 +3148,47 @@ static bool amd_iommu_capable(enum iommu_cap cap)
return false;
}
+static void amd_iommu_get_dm_regions(struct device *dev,
+ struct list_head *head)
+{
+ struct unity_map_entry *entry;
+ u16 devid;
+
+ devid = get_device_id(dev);
+
+ list_for_each_entry(entry, &amd_iommu_unity_map, list) {
+ struct iommu_dm_region *region;
+
+ if (devid < entry->devid_start || devid > entry->devid_end)
+ continue;
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region) {
+ pr_err("Out of memory allocating dm-regions for %s\n",
+ dev_name(dev));
+ return;
+ }
+
+ region->start = entry->address_start;
+ region->length = entry->address_end - entry->address_start;
+ if (entry->prot & IOMMU_PROT_IR)
+ region->prot |= IOMMU_READ;
+ if (entry->prot & IOMMU_PROT_IW)
+ region->prot |= IOMMU_WRITE;
+
+ list_add_tail(&region->list, head);
+ }
+}
+
+static void amd_iommu_put_dm_regions(struct device *dev,
+ struct list_head *head)
+{
+ struct iommu_dm_region *entry, *next;
+
+ list_for_each_entry_safe(entry, next, head, list)
+ kfree(entry);
+}
+
static const struct iommu_ops amd_iommu_ops = {
.capable = amd_iommu_capable,
.domain_alloc = amd_iommu_domain_alloc,
@@ -3422,6 +3199,10 @@ static const struct iommu_ops amd_iommu_ops = {
.unmap = amd_iommu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = amd_iommu_iova_to_phys,
+ .add_device = amd_iommu_add_device,
+ .remove_device = amd_iommu_remove_device,
+ .get_dm_regions = amd_iommu_get_dm_regions,
+ .put_dm_regions = amd_iommu_put_dm_regions,
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
};
@@ -3851,6 +3632,21 @@ union irte {
} fields;
};
+struct irq_2_irte {
+ u16 devid; /* Device ID for IRTE table */
+ u16 index; /* Index into IRTE table*/
+};
+
+struct amd_ir_data {
+ struct irq_2_irte irq_2_irte;
+ union irte irte_entry;
+ union {
+ struct msi_msg msi_entry;
+ };
+};
+
+static struct irq_chip amd_ir_chip;
+
#define DTE_IRQ_PHYS_ADDR_MASK (((1ULL << 45)-1) << 6)
#define DTE_IRQ_REMAP_INTCTL (2ULL << 60)
#define DTE_IRQ_TABLE_LEN (8ULL << 1)
@@ -3944,7 +3740,7 @@ out_unlock:
return table;
}
-static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count)
+static int alloc_irq_index(u16 devid, int count)
{
struct irq_remap_table *table;
unsigned long flags;
@@ -3966,18 +3762,10 @@ static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count)
c = 0;
if (c == count) {
- struct irq_2_irte *irte_info;
-
for (; c != 0; --c)
table->table[index - c + 1] = IRTE_ALLOCATED;
index -= count - 1;
-
- cfg->remapped = 1;
- irte_info = &cfg->irq_2_irte;
- irte_info->devid = devid;
- irte_info->index = index;
-
goto out;
}
}
@@ -3990,22 +3778,6 @@ out:
return index;
}
-static int get_irte(u16 devid, int index, union irte *irte)
-{
- struct irq_remap_table *table;
- unsigned long flags;
-
- table = get_irq_table(devid, false);
- if (!table)
- return -ENOMEM;
-
- spin_lock_irqsave(&table->lock, flags);
- irte->val = table->table[index];
- spin_unlock_irqrestore(&table->lock, flags);
-
- return 0;
-}
-
static int modify_irte(u16 devid, int index, union irte irte)
{
struct irq_remap_table *table;
@@ -4052,243 +3824,316 @@ static void free_irte(u16 devid, int index)
iommu_completion_wait(iommu);
}
-static int setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry,
- unsigned int destination, int vector,
- struct io_apic_irq_attr *attr)
+static int get_devid(struct irq_alloc_info *info)
{
- struct irq_remap_table *table;
- struct irq_2_irte *irte_info;
- struct irq_cfg *cfg;
- union irte irte;
- int ioapic_id;
- int index;
- int devid;
- int ret;
+ int devid = -1;
- cfg = irq_cfg(irq);
- if (!cfg)
- return -EINVAL;
-
- irte_info = &cfg->irq_2_irte;
- ioapic_id = mpc_ioapic_id(attr->ioapic);
- devid = get_ioapic_devid(ioapic_id);
-
- if (devid < 0)
- return devid;
-
- table = get_irq_table(devid, true);
- if (table == NULL)
- return -ENOMEM;
-
- index = attr->ioapic_pin;
-
- /* Setup IRQ remapping info */
- cfg->remapped = 1;
- irte_info->devid = devid;
- irte_info->index = index;
+ switch (info->type) {
+ case X86_IRQ_ALLOC_TYPE_IOAPIC:
+ devid = get_ioapic_devid(info->ioapic_id);
+ break;
+ case X86_IRQ_ALLOC_TYPE_HPET:
+ devid = get_hpet_devid(info->hpet_id);
+ break;
+ case X86_IRQ_ALLOC_TYPE_MSI:
+ case X86_IRQ_ALLOC_TYPE_MSIX:
+ devid = get_device_id(&info->msi_dev->dev);
+ break;
+ default:
+ BUG_ON(1);
+ break;
+ }
- /* Setup IRTE for IOMMU */
- irte.val = 0;
- irte.fields.vector = vector;
- irte.fields.int_type = apic->irq_delivery_mode;
- irte.fields.destination = destination;
- irte.fields.dm = apic->irq_dest_mode;
- irte.fields.valid = 1;
-
- ret = modify_irte(devid, index, irte);
- if (ret)
- return ret;
+ return devid;
+}
- /* Setup IOAPIC entry */
- memset(entry, 0, sizeof(*entry));
+static struct irq_domain *get_ir_irq_domain(struct irq_alloc_info *info)
+{
+ struct amd_iommu *iommu;
+ int devid;
- entry->vector = index;
- entry->mask = 0;
- entry->trigger = attr->trigger;
- entry->polarity = attr->polarity;
+ if (!info)
+ return NULL;
- /*
- * Mask level triggered irqs.
- */
- if (attr->trigger)
- entry->mask = 1;
+ devid = get_devid(info);
+ if (devid >= 0) {
+ iommu = amd_iommu_rlookup_table[devid];
+ if (iommu)
+ return iommu->ir_domain;
+ }
- return 0;
+ return NULL;
}
-static int set_affinity(struct irq_data *data, const struct cpumask *mask,
- bool force)
+static struct irq_domain *get_irq_domain(struct irq_alloc_info *info)
{
- struct irq_2_irte *irte_info;
- unsigned int dest, irq;
- struct irq_cfg *cfg;
- union irte irte;
- int err;
-
- if (!config_enabled(CONFIG_SMP))
- return -1;
-
- cfg = irqd_cfg(data);
- irq = data->irq;
- irte_info = &cfg->irq_2_irte;
+ struct amd_iommu *iommu;
+ int devid;
- if (!cpumask_intersects(mask, cpu_online_mask))
- return -EINVAL;
+ if (!info)
+ return NULL;
- if (get_irte(irte_info->devid, irte_info->index, &irte))
- return -EBUSY;
+ switch (info->type) {
+ case X86_IRQ_ALLOC_TYPE_MSI:
+ case X86_IRQ_ALLOC_TYPE_MSIX:
+ devid = get_device_id(&info->msi_dev->dev);
+ if (devid >= 0) {
+ iommu = amd_iommu_rlookup_table[devid];
+ if (iommu)
+ return iommu->msi_domain;
+ }
+ break;
+ default:
+ break;
+ }
- if (assign_irq_vector(irq, cfg, mask))
- return -EBUSY;
+ return NULL;
+}
- err = apic->cpu_mask_to_apicid_and(cfg->domain, mask, &dest);
- if (err) {
- if (assign_irq_vector(irq, cfg, data->affinity))
- pr_err("AMD-Vi: Failed to recover vector for irq %d\n", irq);
- return err;
- }
+struct irq_remap_ops amd_iommu_irq_ops = {
+ .prepare = amd_iommu_prepare,
+ .enable = amd_iommu_enable,
+ .disable = amd_iommu_disable,
+ .reenable = amd_iommu_reenable,
+ .enable_faulting = amd_iommu_enable_faulting,
+ .get_ir_irq_domain = get_ir_irq_domain,
+ .get_irq_domain = get_irq_domain,
+};
- irte.fields.vector = cfg->vector;
- irte.fields.destination = dest;
+static void irq_remapping_prepare_irte(struct amd_ir_data *data,
+ struct irq_cfg *irq_cfg,
+ struct irq_alloc_info *info,
+ int devid, int index, int sub_handle)
+{
+ struct irq_2_irte *irte_info = &data->irq_2_irte;
+ struct msi_msg *msg = &data->msi_entry;
+ union irte *irte = &data->irte_entry;
+ struct IO_APIC_route_entry *entry;
- modify_irte(irte_info->devid, irte_info->index, irte);
+ data->irq_2_irte.devid = devid;
+ data->irq_2_irte.index = index + sub_handle;
- if (cfg->move_in_progress)
- send_cleanup_vector(cfg);
+ /* Setup IRTE for IOMMU */
+ irte->val = 0;
+ irte->fields.vector = irq_cfg->vector;
+ irte->fields.int_type = apic->irq_delivery_mode;
+ irte->fields.destination = irq_cfg->dest_apicid;
+ irte->fields.dm = apic->irq_dest_mode;
+ irte->fields.valid = 1;
+
+ switch (info->type) {
+ case X86_IRQ_ALLOC_TYPE_IOAPIC:
+ /* Setup IOAPIC entry */
+ entry = info->ioapic_entry;
+ info->ioapic_entry = NULL;
+ memset(entry, 0, sizeof(*entry));
+ entry->vector = index;
+ entry->mask = 0;
+ entry->trigger = info->ioapic_trigger;
+ entry->polarity = info->ioapic_polarity;
+ /* Mask level triggered irqs. */
+ if (info->ioapic_trigger)
+ entry->mask = 1;
+ break;
- cpumask_copy(data->affinity, mask);
+ case X86_IRQ_ALLOC_TYPE_HPET:
+ case X86_IRQ_ALLOC_TYPE_MSI:
+ case X86_IRQ_ALLOC_TYPE_MSIX:
+ msg->address_hi = MSI_ADDR_BASE_HI;
+ msg->address_lo = MSI_ADDR_BASE_LO;
+ msg->data = irte_info->index;
+ break;
- return 0;
+ default:
+ BUG_ON(1);
+ break;
+ }
}
-static int free_irq(int irq)
+static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
{
- struct irq_2_irte *irte_info;
+ struct irq_alloc_info *info = arg;
+ struct irq_data *irq_data;
+ struct amd_ir_data *data;
struct irq_cfg *cfg;
+ int i, ret, devid;
+ int index = -1;
- cfg = irq_cfg(irq);
- if (!cfg)
+ if (!info)
+ return -EINVAL;
+ if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_MSI &&
+ info->type != X86_IRQ_ALLOC_TYPE_MSIX)
return -EINVAL;
- irte_info = &cfg->irq_2_irte;
-
- free_irte(irte_info->devid, irte_info->index);
+ /*
+ * With IRQ remapping enabled, don't need contiguous CPU vectors
+ * to support multiple MSI interrupts.
+ */
+ if (info->type == X86_IRQ_ALLOC_TYPE_MSI)
+ info->flags &= ~X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
- return 0;
-}
+ devid = get_devid(info);
+ if (devid < 0)
+ return -EINVAL;
-static void compose_msi_msg(struct pci_dev *pdev,
- unsigned int irq, unsigned int dest,
- struct msi_msg *msg, u8 hpet_id)
-{
- struct irq_2_irte *irte_info;
- struct irq_cfg *cfg;
- union irte irte;
+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+ if (ret < 0)
+ return ret;
- cfg = irq_cfg(irq);
- if (!cfg)
- return;
+ ret = -ENOMEM;
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ goto out_free_parent;
- irte_info = &cfg->irq_2_irte;
+ if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC) {
+ if (get_irq_table(devid, true))
+ index = info->ioapic_pin;
+ else
+ ret = -ENOMEM;
+ } else {
+ index = alloc_irq_index(devid, nr_irqs);
+ }
+ if (index < 0) {
+ pr_warn("Failed to allocate IRTE\n");
+ kfree(data);
+ goto out_free_parent;
+ }
- irte.val = 0;
- irte.fields.vector = cfg->vector;
- irte.fields.int_type = apic->irq_delivery_mode;
- irte.fields.destination = dest;
- irte.fields.dm = apic->irq_dest_mode;
- irte.fields.valid = 1;
+ for (i = 0; i < nr_irqs; i++) {
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+ cfg = irqd_cfg(irq_data);
+ if (!irq_data || !cfg) {
+ ret = -EINVAL;
+ goto out_free_data;
+ }
- modify_irte(irte_info->devid, irte_info->index, irte);
+ if (i > 0) {
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ goto out_free_data;
+ }
+ irq_data->hwirq = (devid << 16) + i;
+ irq_data->chip_data = data;
+ irq_data->chip = &amd_ir_chip;
+ irq_remapping_prepare_irte(data, cfg, info, devid, index, i);
+ irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT);
+ }
+ return 0;
- msg->address_hi = MSI_ADDR_BASE_HI;
- msg->address_lo = MSI_ADDR_BASE_LO;
- msg->data = irte_info->index;
+out_free_data:
+ for (i--; i >= 0; i--) {
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+ if (irq_data)
+ kfree(irq_data->chip_data);
+ }
+ for (i = 0; i < nr_irqs; i++)
+ free_irte(devid, index + i);
+out_free_parent:
+ irq_domain_free_irqs_common(domain, virq, nr_irqs);
+ return ret;
}
-static int msi_alloc_irq(struct pci_dev *pdev, int irq, int nvec)
+static void irq_remapping_free(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs)
{
- struct irq_cfg *cfg;
- int index;
- u16 devid;
-
- if (!pdev)
- return -EINVAL;
+ struct irq_2_irte *irte_info;
+ struct irq_data *irq_data;
+ struct amd_ir_data *data;
+ int i;
- cfg = irq_cfg(irq);
- if (!cfg)
- return -EINVAL;
+ for (i = 0; i < nr_irqs; i++) {
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+ if (irq_data && irq_data->chip_data) {
+ data = irq_data->chip_data;
+ irte_info = &data->irq_2_irte;
+ free_irte(irte_info->devid, irte_info->index);
+ kfree(data);
+ }
+ }
+ irq_domain_free_irqs_common(domain, virq, nr_irqs);
+}
- devid = get_device_id(&pdev->dev);
- index = alloc_irq_index(cfg, devid, nvec);
+static void irq_remapping_activate(struct irq_domain *domain,
+ struct irq_data *irq_data)
+{
+ struct amd_ir_data *data = irq_data->chip_data;
+ struct irq_2_irte *irte_info = &data->irq_2_irte;
- return index < 0 ? MAX_IRQS_PER_TABLE : index;
+ modify_irte(irte_info->devid, irte_info->index, data->irte_entry);
}
-static int msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
- int index, int offset)
+static void irq_remapping_deactivate(struct irq_domain *domain,
+ struct irq_data *irq_data)
{
- struct irq_2_irte *irte_info;
- struct irq_cfg *cfg;
- u16 devid;
+ struct amd_ir_data *data = irq_data->chip_data;
+ struct irq_2_irte *irte_info = &data->irq_2_irte;
+ union irte entry;
- if (!pdev)
- return -EINVAL;
+ entry.val = 0;
+ modify_irte(irte_info->devid, irte_info->index, data->irte_entry);
+}
- cfg = irq_cfg(irq);
- if (!cfg)
- return -EINVAL;
+static struct irq_domain_ops amd_ir_domain_ops = {
+ .alloc = irq_remapping_alloc,
+ .free = irq_remapping_free,
+ .activate = irq_remapping_activate,
+ .deactivate = irq_remapping_deactivate,
+};
- if (index >= MAX_IRQS_PER_TABLE)
- return 0;
+static int amd_ir_set_affinity(struct irq_data *data,
+ const struct cpumask *mask, bool force)
+{
+ struct amd_ir_data *ir_data = data->chip_data;
+ struct irq_2_irte *irte_info = &ir_data->irq_2_irte;
+ struct irq_cfg *cfg = irqd_cfg(data);
+ struct irq_data *parent = data->parent_data;
+ int ret;
+
+ ret = parent->chip->irq_set_affinity(parent, mask, force);
+ if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
+ return ret;
- devid = get_device_id(&pdev->dev);
- irte_info = &cfg->irq_2_irte;
+ /*
+ * Atomically updates the IRTE with the new destination, vector
+ * and flushes the interrupt entry cache.
+ */
+ ir_data->irte_entry.fields.vector = cfg->vector;
+ ir_data->irte_entry.fields.destination = cfg->dest_apicid;
+ modify_irte(irte_info->devid, irte_info->index, ir_data->irte_entry);
- cfg->remapped = 1;
- irte_info->devid = devid;
- irte_info->index = index + offset;
+ /*
+ * After this point, all the interrupts will start arriving
+ * at the new destination. So, time to cleanup the previous
+ * vector allocation.
+ */
+ send_cleanup_vector(cfg);
- return 0;
+ return IRQ_SET_MASK_OK_DONE;
}
-static int alloc_hpet_msi(unsigned int irq, unsigned int id)
+static void ir_compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg)
{
- struct irq_2_irte *irte_info;
- struct irq_cfg *cfg;
- int index, devid;
+ struct amd_ir_data *ir_data = irq_data->chip_data;
- cfg = irq_cfg(irq);
- if (!cfg)
- return -EINVAL;
+ *msg = ir_data->msi_entry;
+}
- irte_info = &cfg->irq_2_irte;
- devid = get_hpet_devid(id);
- if (devid < 0)
- return devid;
+static struct irq_chip amd_ir_chip = {
+ .irq_ack = ir_ack_apic_edge,
+ .irq_set_affinity = amd_ir_set_affinity,
+ .irq_compose_msi_msg = ir_compose_msi_msg,
+};
- index = alloc_irq_index(cfg, devid, 1);
- if (index < 0)
- return index;
+int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
+{
+ iommu->ir_domain = irq_domain_add_tree(NULL, &amd_ir_domain_ops, iommu);
+ if (!iommu->ir_domain)
+ return -ENOMEM;
- cfg->remapped = 1;
- irte_info->devid = devid;
- irte_info->index = index;
+ iommu->ir_domain->parent = arch_get_ir_parent_domain();
+ iommu->msi_domain = arch_create_msi_irq_domain(iommu->ir_domain);
return 0;
}
-
-struct irq_remap_ops amd_iommu_irq_ops = {
- .prepare = amd_iommu_prepare,
- .enable = amd_iommu_enable,
- .disable = amd_iommu_disable,
- .reenable = amd_iommu_reenable,
- .enable_faulting = amd_iommu_enable_faulting,
- .setup_ioapic_entry = setup_ioapic_entry,
- .set_affinity = set_affinity,
- .free_irq = free_irq,
- .compose_msi_msg = compose_msi_msg,
- .msi_alloc_irq = msi_alloc_irq,
- .msi_setup_irq = msi_setup_irq,
- .alloc_hpet_msi = alloc_hpet_msi,
-};
#endif
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 450ef5001a65..dbda9ae68c5d 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -226,6 +226,7 @@ static enum iommu_init_state init_state = IOMMU_START_STATE;
static int amd_iommu_enable_interrupts(void);
static int __init iommu_go_to_state(enum iommu_init_state state);
+static void init_device_table_dma(void);
static inline void update_last_devid(u16 devid)
{
@@ -1124,6 +1125,10 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
if (ret)
return ret;
+ ret = amd_iommu_create_irq_domain(iommu);
+ if (ret)
+ return ret;
+
/*
* Make sure IOMMU is not considered to translate itself. The IVRS
* table tells us so, but this is a lie!
@@ -1385,9 +1390,15 @@ static int __init amd_iommu_init_pci(void)
break;
}
- ret = amd_iommu_init_devices();
+ init_device_table_dma();
+
+ for_each_iommu(iommu)
+ iommu_flush_all_caches(iommu);
- print_iommu_info();
+ ret = amd_iommu_init_api();
+
+ if (!ret)
+ print_iommu_info();
return ret;
}
@@ -1825,8 +1836,6 @@ static bool __init check_ioapic_information(void)
static void __init free_dma_resources(void)
{
- amd_iommu_uninit_devices();
-
free_pages((unsigned long)amd_iommu_pd_alloc_bitmap,
get_order(MAX_DOMAIN_ID/8));
@@ -2019,27 +2028,10 @@ static bool detect_ivrs(void)
static int amd_iommu_init_dma(void)
{
- struct amd_iommu *iommu;
- int ret;
-
if (iommu_pass_through)
- ret = amd_iommu_init_passthrough();
+ return amd_iommu_init_passthrough();
else
- ret = amd_iommu_init_dma_ops();
-
- if (ret)
- return ret;
-
- init_device_table_dma();
-
- for_each_iommu(iommu)
- iommu_flush_all_caches(iommu);
-
- amd_iommu_init_api();
-
- amd_iommu_init_notifier();
-
- return 0;
+ return amd_iommu_init_dma_ops();
}
/****************************************************************************
diff --git a/drivers/iommu/amd_iommu_proto.h b/drivers/iommu/amd_iommu_proto.h
index 72b0fd455e24..0bd9eb374462 100644
--- a/drivers/iommu/amd_iommu_proto.h
+++ b/drivers/iommu/amd_iommu_proto.h
@@ -30,7 +30,7 @@ extern void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu);
extern int amd_iommu_init_devices(void);
extern void amd_iommu_uninit_devices(void);
extern void amd_iommu_init_notifier(void);
-extern void amd_iommu_init_api(void);
+extern int amd_iommu_init_api(void);
/* Needed for interrupt remapping */
extern int amd_iommu_prepare(void);
@@ -62,6 +62,15 @@ extern u8 amd_iommu_pc_get_max_counters(u16 devid);
extern int amd_iommu_pc_get_set_reg_val(u16 devid, u8 bank, u8 cntr, u8 fxn,
u64 *value, bool is_write);
+#ifdef CONFIG_IRQ_REMAP
+extern int amd_iommu_create_irq_domain(struct amd_iommu *iommu);
+#else
+static inline int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
+{
+ return 0;
+}
+#endif
+
#define PPR_SUCCESS 0x0
#define PPR_INVALID 0x1
#define PPR_FAILURE 0xf
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index 05030e523771..f65908841be0 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -398,6 +398,7 @@ struct amd_iommu_fault {
struct iommu_domain;
+struct irq_domain;
/*
* This structure contains generic data for IOMMU protection domains
@@ -446,8 +447,6 @@ struct aperture_range {
* Data container for a dma_ops specific protection domain
*/
struct dma_ops_domain {
- struct list_head list;
-
/* generic protection domain information */
struct protection_domain domain;
@@ -462,12 +461,6 @@ struct dma_ops_domain {
/* This will be set to true when TLB needs to be flushed */
bool need_flush;
-
- /*
- * if this is a preallocated domain, keep the device for which it was
- * preallocated in this variable
- */
- u16 target_dev;
};
/*
@@ -552,9 +545,6 @@ struct amd_iommu {
/* if one, we need to send a completion wait command */
bool need_sync;
- /* default dma_ops domain for that IOMMU */
- struct dma_ops_domain *default_dom;
-
/* IOMMU sysfs device */
struct device *iommu_dev;
@@ -579,6 +569,10 @@ struct amd_iommu {
/* The maximum PC banks and counters/bank (PCSup=1) */
u8 max_banks;
u8 max_counters;
+#ifdef CONFIG_IRQ_REMAP
+ struct irq_domain *ir_domain;
+ struct irq_domain *msi_domain;
+#endif
};
struct devid_map {
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index a1cbba9056fd..3465faf1809e 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -266,6 +266,7 @@ static void put_pasid_state(struct pasid_state *pasid_state)
static void put_pasid_state_wait(struct pasid_state *pasid_state)
{
+ atomic_dec(&pasid_state->count);
wait_event(pasid_state->wq, !atomic_read(&pasid_state->count));
free_pasid_state(pasid_state);
}
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
new file mode 100644
index 000000000000..f14130121298
--- /dev/null
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -0,0 +1,2670 @@
+/*
+ * IOMMU API for ARM architected SMMUv3 implementations.
+ *
+ * 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/>.
+ *
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ *
+ * This driver is powered by bad coffee and bombay mix.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include "io-pgtable.h"
+
+/* MMIO registers */
+#define ARM_SMMU_IDR0 0x0
+#define IDR0_ST_LVL_SHIFT 27
+#define IDR0_ST_LVL_MASK 0x3
+#define IDR0_ST_LVL_2LVL (1 << IDR0_ST_LVL_SHIFT)
+#define IDR0_STALL_MODEL (3 << 24)
+#define IDR0_TTENDIAN_SHIFT 21
+#define IDR0_TTENDIAN_MASK 0x3
+#define IDR0_TTENDIAN_LE (2 << IDR0_TTENDIAN_SHIFT)
+#define IDR0_TTENDIAN_BE (3 << IDR0_TTENDIAN_SHIFT)
+#define IDR0_TTENDIAN_MIXED (0 << IDR0_TTENDIAN_SHIFT)
+#define IDR0_CD2L (1 << 19)
+#define IDR0_VMID16 (1 << 18)
+#define IDR0_PRI (1 << 16)
+#define IDR0_SEV (1 << 14)
+#define IDR0_MSI (1 << 13)
+#define IDR0_ASID16 (1 << 12)
+#define IDR0_ATS (1 << 10)
+#define IDR0_HYP (1 << 9)
+#define IDR0_COHACC (1 << 4)
+#define IDR0_TTF_SHIFT 2
+#define IDR0_TTF_MASK 0x3
+#define IDR0_TTF_AARCH64 (2 << IDR0_TTF_SHIFT)
+#define IDR0_S1P (1 << 1)
+#define IDR0_S2P (1 << 0)
+
+#define ARM_SMMU_IDR1 0x4
+#define IDR1_TABLES_PRESET (1 << 30)
+#define IDR1_QUEUES_PRESET (1 << 29)
+#define IDR1_REL (1 << 28)
+#define IDR1_CMDQ_SHIFT 21
+#define IDR1_CMDQ_MASK 0x1f
+#define IDR1_EVTQ_SHIFT 16
+#define IDR1_EVTQ_MASK 0x1f
+#define IDR1_PRIQ_SHIFT 11
+#define IDR1_PRIQ_MASK 0x1f
+#define IDR1_SSID_SHIFT 6
+#define IDR1_SSID_MASK 0x1f
+#define IDR1_SID_SHIFT 0
+#define IDR1_SID_MASK 0x3f
+
+#define ARM_SMMU_IDR5 0x14
+#define IDR5_STALL_MAX_SHIFT 16
+#define IDR5_STALL_MAX_MASK 0xffff
+#define IDR5_GRAN64K (1 << 6)
+#define IDR5_GRAN16K (1 << 5)
+#define IDR5_GRAN4K (1 << 4)
+#define IDR5_OAS_SHIFT 0
+#define IDR5_OAS_MASK 0x7
+#define IDR5_OAS_32_BIT (0 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_36_BIT (1 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_40_BIT (2 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_42_BIT (3 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_44_BIT (4 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_48_BIT (5 << IDR5_OAS_SHIFT)
+
+#define ARM_SMMU_CR0 0x20
+#define CR0_CMDQEN (1 << 3)
+#define CR0_EVTQEN (1 << 2)
+#define CR0_PRIQEN (1 << 1)
+#define CR0_SMMUEN (1 << 0)
+
+#define ARM_SMMU_CR0ACK 0x24
+
+#define ARM_SMMU_CR1 0x28
+#define CR1_SH_NSH 0
+#define CR1_SH_OSH 2
+#define CR1_SH_ISH 3
+#define CR1_CACHE_NC 0
+#define CR1_CACHE_WB 1
+#define CR1_CACHE_WT 2
+#define CR1_TABLE_SH_SHIFT 10
+#define CR1_TABLE_OC_SHIFT 8
+#define CR1_TABLE_IC_SHIFT 6
+#define CR1_QUEUE_SH_SHIFT 4
+#define CR1_QUEUE_OC_SHIFT 2
+#define CR1_QUEUE_IC_SHIFT 0
+
+#define ARM_SMMU_CR2 0x2c
+#define CR2_PTM (1 << 2)
+#define CR2_RECINVSID (1 << 1)
+#define CR2_E2H (1 << 0)
+
+#define ARM_SMMU_IRQ_CTRL 0x50
+#define IRQ_CTRL_EVTQ_IRQEN (1 << 2)
+#define IRQ_CTRL_GERROR_IRQEN (1 << 0)
+
+#define ARM_SMMU_IRQ_CTRLACK 0x54
+
+#define ARM_SMMU_GERROR 0x60
+#define GERROR_SFM_ERR (1 << 8)
+#define GERROR_MSI_GERROR_ABT_ERR (1 << 7)
+#define GERROR_MSI_PRIQ_ABT_ERR (1 << 6)
+#define GERROR_MSI_EVTQ_ABT_ERR (1 << 5)
+#define GERROR_MSI_CMDQ_ABT_ERR (1 << 4)
+#define GERROR_PRIQ_ABT_ERR (1 << 3)
+#define GERROR_EVTQ_ABT_ERR (1 << 2)
+#define GERROR_CMDQ_ERR (1 << 0)
+#define GERROR_ERR_MASK 0xfd
+
+#define ARM_SMMU_GERRORN 0x64
+
+#define ARM_SMMU_GERROR_IRQ_CFG0 0x68
+#define ARM_SMMU_GERROR_IRQ_CFG1 0x70
+#define ARM_SMMU_GERROR_IRQ_CFG2 0x74
+
+#define ARM_SMMU_STRTAB_BASE 0x80
+#define STRTAB_BASE_RA (1UL << 62)
+#define STRTAB_BASE_ADDR_SHIFT 6
+#define STRTAB_BASE_ADDR_MASK 0x3ffffffffffUL
+
+#define ARM_SMMU_STRTAB_BASE_CFG 0x88
+#define STRTAB_BASE_CFG_LOG2SIZE_SHIFT 0
+#define STRTAB_BASE_CFG_LOG2SIZE_MASK 0x3f
+#define STRTAB_BASE_CFG_SPLIT_SHIFT 6
+#define STRTAB_BASE_CFG_SPLIT_MASK 0x1f
+#define STRTAB_BASE_CFG_FMT_SHIFT 16
+#define STRTAB_BASE_CFG_FMT_MASK 0x3
+#define STRTAB_BASE_CFG_FMT_LINEAR (0 << STRTAB_BASE_CFG_FMT_SHIFT)
+#define STRTAB_BASE_CFG_FMT_2LVL (1 << STRTAB_BASE_CFG_FMT_SHIFT)
+
+#define ARM_SMMU_CMDQ_BASE 0x90
+#define ARM_SMMU_CMDQ_PROD 0x98
+#define ARM_SMMU_CMDQ_CONS 0x9c
+
+#define ARM_SMMU_EVTQ_BASE 0xa0
+#define ARM_SMMU_EVTQ_PROD 0x100a8
+#define ARM_SMMU_EVTQ_CONS 0x100ac
+#define ARM_SMMU_EVTQ_IRQ_CFG0 0xb0
+#define ARM_SMMU_EVTQ_IRQ_CFG1 0xb8
+#define ARM_SMMU_EVTQ_IRQ_CFG2 0xbc
+
+#define ARM_SMMU_PRIQ_BASE 0xc0
+#define ARM_SMMU_PRIQ_PROD 0x100c8
+#define ARM_SMMU_PRIQ_CONS 0x100cc
+#define ARM_SMMU_PRIQ_IRQ_CFG0 0xd0
+#define ARM_SMMU_PRIQ_IRQ_CFG1 0xd8
+#define ARM_SMMU_PRIQ_IRQ_CFG2 0xdc
+
+/* Common MSI config fields */
+#define MSI_CFG0_SH_SHIFT 60
+#define MSI_CFG0_SH_NSH (0UL << MSI_CFG0_SH_SHIFT)
+#define MSI_CFG0_SH_OSH (2UL << MSI_CFG0_SH_SHIFT)
+#define MSI_CFG0_SH_ISH (3UL << MSI_CFG0_SH_SHIFT)
+#define MSI_CFG0_MEMATTR_SHIFT 56
+#define MSI_CFG0_MEMATTR_DEVICE_nGnRE (0x1 << MSI_CFG0_MEMATTR_SHIFT)
+#define MSI_CFG0_ADDR_SHIFT 2
+#define MSI_CFG0_ADDR_MASK 0x3fffffffffffUL
+
+#define Q_IDX(q, p) ((p) & ((1 << (q)->max_n_shift) - 1))
+#define Q_WRP(q, p) ((p) & (1 << (q)->max_n_shift))
+#define Q_OVERFLOW_FLAG (1 << 31)
+#define Q_OVF(q, p) ((p) & Q_OVERFLOW_FLAG)
+#define Q_ENT(q, p) ((q)->base + \
+ Q_IDX(q, p) * (q)->ent_dwords)
+
+#define Q_BASE_RWA (1UL << 62)
+#define Q_BASE_ADDR_SHIFT 5
+#define Q_BASE_ADDR_MASK 0xfffffffffffUL
+#define Q_BASE_LOG2SIZE_SHIFT 0
+#define Q_BASE_LOG2SIZE_MASK 0x1fUL
+
+/*
+ * Stream table.
+ *
+ * Linear: Enough to cover 1 << IDR1.SIDSIZE entries
+ * 2lvl: 8k L1 entries, 256 lazy entries per table (each table covers a PCI bus)
+ */
+#define STRTAB_L1_SZ_SHIFT 16
+#define STRTAB_SPLIT 8
+
+#define STRTAB_L1_DESC_DWORDS 1
+#define STRTAB_L1_DESC_SPAN_SHIFT 0
+#define STRTAB_L1_DESC_SPAN_MASK 0x1fUL
+#define STRTAB_L1_DESC_L2PTR_SHIFT 6
+#define STRTAB_L1_DESC_L2PTR_MASK 0x3ffffffffffUL
+
+#define STRTAB_STE_DWORDS 8
+#define STRTAB_STE_0_V (1UL << 0)
+#define STRTAB_STE_0_CFG_SHIFT 1
+#define STRTAB_STE_0_CFG_MASK 0x7UL
+#define STRTAB_STE_0_CFG_ABORT (0UL << STRTAB_STE_0_CFG_SHIFT)
+#define STRTAB_STE_0_CFG_BYPASS (4UL << STRTAB_STE_0_CFG_SHIFT)
+#define STRTAB_STE_0_CFG_S1_TRANS (5UL << STRTAB_STE_0_CFG_SHIFT)
+#define STRTAB_STE_0_CFG_S2_TRANS (6UL << STRTAB_STE_0_CFG_SHIFT)
+
+#define STRTAB_STE_0_S1FMT_SHIFT 4
+#define STRTAB_STE_0_S1FMT_LINEAR (0UL << STRTAB_STE_0_S1FMT_SHIFT)
+#define STRTAB_STE_0_S1CTXPTR_SHIFT 6
+#define STRTAB_STE_0_S1CTXPTR_MASK 0x3ffffffffffUL
+#define STRTAB_STE_0_S1CDMAX_SHIFT 59
+#define STRTAB_STE_0_S1CDMAX_MASK 0x1fUL
+
+#define STRTAB_STE_1_S1C_CACHE_NC 0UL
+#define STRTAB_STE_1_S1C_CACHE_WBRA 1UL
+#define STRTAB_STE_1_S1C_CACHE_WT 2UL
+#define STRTAB_STE_1_S1C_CACHE_WB 3UL
+#define STRTAB_STE_1_S1C_SH_NSH 0UL
+#define STRTAB_STE_1_S1C_SH_OSH 2UL
+#define STRTAB_STE_1_S1C_SH_ISH 3UL
+#define STRTAB_STE_1_S1CIR_SHIFT 2
+#define STRTAB_STE_1_S1COR_SHIFT 4
+#define STRTAB_STE_1_S1CSH_SHIFT 6
+
+#define STRTAB_STE_1_S1STALLD (1UL << 27)
+
+#define STRTAB_STE_1_EATS_ABT 0UL
+#define STRTAB_STE_1_EATS_TRANS 1UL
+#define STRTAB_STE_1_EATS_S1CHK 2UL
+#define STRTAB_STE_1_EATS_SHIFT 28
+
+#define STRTAB_STE_1_STRW_NSEL1 0UL
+#define STRTAB_STE_1_STRW_EL2 2UL
+#define STRTAB_STE_1_STRW_SHIFT 30
+
+#define STRTAB_STE_2_S2VMID_SHIFT 0
+#define STRTAB_STE_2_S2VMID_MASK 0xffffUL
+#define STRTAB_STE_2_VTCR_SHIFT 32
+#define STRTAB_STE_2_VTCR_MASK 0x7ffffUL
+#define STRTAB_STE_2_S2AA64 (1UL << 51)
+#define STRTAB_STE_2_S2ENDI (1UL << 52)
+#define STRTAB_STE_2_S2PTW (1UL << 54)
+#define STRTAB_STE_2_S2R (1UL << 58)
+
+#define STRTAB_STE_3_S2TTB_SHIFT 4
+#define STRTAB_STE_3_S2TTB_MASK 0xfffffffffffUL
+
+/* Context descriptor (stage-1 only) */
+#define CTXDESC_CD_DWORDS 8
+#define CTXDESC_CD_0_TCR_T0SZ_SHIFT 0
+#define ARM64_TCR_T0SZ_SHIFT 0
+#define ARM64_TCR_T0SZ_MASK 0x1fUL
+#define CTXDESC_CD_0_TCR_TG0_SHIFT 6
+#define ARM64_TCR_TG0_SHIFT 14
+#define ARM64_TCR_TG0_MASK 0x3UL
+#define CTXDESC_CD_0_TCR_IRGN0_SHIFT 8
+#define ARM64_TCR_IRGN0_SHIFT 24
+#define ARM64_TCR_IRGN0_MASK 0x3UL
+#define CTXDESC_CD_0_TCR_ORGN0_SHIFT 10
+#define ARM64_TCR_ORGN0_SHIFT 26
+#define ARM64_TCR_ORGN0_MASK 0x3UL
+#define CTXDESC_CD_0_TCR_SH0_SHIFT 12
+#define ARM64_TCR_SH0_SHIFT 12
+#define ARM64_TCR_SH0_MASK 0x3UL
+#define CTXDESC_CD_0_TCR_EPD0_SHIFT 14
+#define ARM64_TCR_EPD0_SHIFT 7
+#define ARM64_TCR_EPD0_MASK 0x1UL
+#define CTXDESC_CD_0_TCR_EPD1_SHIFT 30
+#define ARM64_TCR_EPD1_SHIFT 23
+#define ARM64_TCR_EPD1_MASK 0x1UL
+
+#define CTXDESC_CD_0_ENDI (1UL << 15)
+#define CTXDESC_CD_0_V (1UL << 31)
+
+#define CTXDESC_CD_0_TCR_IPS_SHIFT 32
+#define ARM64_TCR_IPS_SHIFT 32
+#define ARM64_TCR_IPS_MASK 0x7UL
+#define CTXDESC_CD_0_TCR_TBI0_SHIFT 38
+#define ARM64_TCR_TBI0_SHIFT 37
+#define ARM64_TCR_TBI0_MASK 0x1UL
+
+#define CTXDESC_CD_0_AA64 (1UL << 41)
+#define CTXDESC_CD_0_R (1UL << 45)
+#define CTXDESC_CD_0_A (1UL << 46)
+#define CTXDESC_CD_0_ASET_SHIFT 47
+#define CTXDESC_CD_0_ASET_SHARED (0UL << CTXDESC_CD_0_ASET_SHIFT)
+#define CTXDESC_CD_0_ASET_PRIVATE (1UL << CTXDESC_CD_0_ASET_SHIFT)
+#define CTXDESC_CD_0_ASID_SHIFT 48
+#define CTXDESC_CD_0_ASID_MASK 0xffffUL
+
+#define CTXDESC_CD_1_TTB0_SHIFT 4
+#define CTXDESC_CD_1_TTB0_MASK 0xfffffffffffUL
+
+#define CTXDESC_CD_3_MAIR_SHIFT 0
+
+/* Convert between AArch64 (CPU) TCR format and SMMU CD format */
+#define ARM_SMMU_TCR2CD(tcr, fld) \
+ (((tcr) >> ARM64_TCR_##fld##_SHIFT & ARM64_TCR_##fld##_MASK) \
+ << CTXDESC_CD_0_TCR_##fld##_SHIFT)
+
+/* Command queue */
+#define CMDQ_ENT_DWORDS 2
+#define CMDQ_MAX_SZ_SHIFT 8
+
+#define CMDQ_ERR_SHIFT 24
+#define CMDQ_ERR_MASK 0x7f
+#define CMDQ_ERR_CERROR_NONE_IDX 0
+#define CMDQ_ERR_CERROR_ILL_IDX 1
+#define CMDQ_ERR_CERROR_ABT_IDX 2
+
+#define CMDQ_0_OP_SHIFT 0
+#define CMDQ_0_OP_MASK 0xffUL
+#define CMDQ_0_SSV (1UL << 11)
+
+#define CMDQ_PREFETCH_0_SID_SHIFT 32
+#define CMDQ_PREFETCH_1_SIZE_SHIFT 0
+#define CMDQ_PREFETCH_1_ADDR_MASK ~0xfffUL
+
+#define CMDQ_CFGI_0_SID_SHIFT 32
+#define CMDQ_CFGI_0_SID_MASK 0xffffffffUL
+#define CMDQ_CFGI_1_LEAF (1UL << 0)
+#define CMDQ_CFGI_1_RANGE_SHIFT 0
+#define CMDQ_CFGI_1_RANGE_MASK 0x1fUL
+
+#define CMDQ_TLBI_0_VMID_SHIFT 32
+#define CMDQ_TLBI_0_ASID_SHIFT 48
+#define CMDQ_TLBI_1_LEAF (1UL << 0)
+#define CMDQ_TLBI_1_ADDR_MASK ~0xfffUL
+
+#define CMDQ_PRI_0_SSID_SHIFT 12
+#define CMDQ_PRI_0_SSID_MASK 0xfffffUL
+#define CMDQ_PRI_0_SID_SHIFT 32
+#define CMDQ_PRI_0_SID_MASK 0xffffffffUL
+#define CMDQ_PRI_1_GRPID_SHIFT 0
+#define CMDQ_PRI_1_GRPID_MASK 0x1ffUL
+#define CMDQ_PRI_1_RESP_SHIFT 12
+#define CMDQ_PRI_1_RESP_DENY (0UL << CMDQ_PRI_1_RESP_SHIFT)
+#define CMDQ_PRI_1_RESP_FAIL (1UL << CMDQ_PRI_1_RESP_SHIFT)
+#define CMDQ_PRI_1_RESP_SUCC (2UL << CMDQ_PRI_1_RESP_SHIFT)
+
+#define CMDQ_SYNC_0_CS_SHIFT 12
+#define CMDQ_SYNC_0_CS_NONE (0UL << CMDQ_SYNC_0_CS_SHIFT)
+#define CMDQ_SYNC_0_CS_SEV (2UL << CMDQ_SYNC_0_CS_SHIFT)
+
+/* Event queue */
+#define EVTQ_ENT_DWORDS 4
+#define EVTQ_MAX_SZ_SHIFT 7
+
+#define EVTQ_0_ID_SHIFT 0
+#define EVTQ_0_ID_MASK 0xffUL
+
+/* PRI queue */
+#define PRIQ_ENT_DWORDS 2
+#define PRIQ_MAX_SZ_SHIFT 8
+
+#define PRIQ_0_SID_SHIFT 0
+#define PRIQ_0_SID_MASK 0xffffffffUL
+#define PRIQ_0_SSID_SHIFT 32
+#define PRIQ_0_SSID_MASK 0xfffffUL
+#define PRIQ_0_OF (1UL << 57)
+#define PRIQ_0_PERM_PRIV (1UL << 58)
+#define PRIQ_0_PERM_EXEC (1UL << 59)
+#define PRIQ_0_PERM_READ (1UL << 60)
+#define PRIQ_0_PERM_WRITE (1UL << 61)
+#define PRIQ_0_PRG_LAST (1UL << 62)
+#define PRIQ_0_SSID_V (1UL << 63)
+
+#define PRIQ_1_PRG_IDX_SHIFT 0
+#define PRIQ_1_PRG_IDX_MASK 0x1ffUL
+#define PRIQ_1_ADDR_SHIFT 12
+#define PRIQ_1_ADDR_MASK 0xfffffffffffffUL
+
+/* High-level queue structures */
+#define ARM_SMMU_POLL_TIMEOUT_US 100
+
+static bool disable_bypass;
+module_param_named(disable_bypass, disable_bypass, bool, S_IRUGO);
+MODULE_PARM_DESC(disable_bypass,
+ "Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU.");
+
+enum pri_resp {
+ PRI_RESP_DENY,
+ PRI_RESP_FAIL,
+ PRI_RESP_SUCC,
+};
+
+struct arm_smmu_cmdq_ent {
+ /* Common fields */
+ u8 opcode;
+ bool substream_valid;
+
+ /* Command-specific fields */
+ union {
+ #define CMDQ_OP_PREFETCH_CFG 0x1
+ struct {
+ u32 sid;
+ u8 size;
+ u64 addr;
+ } prefetch;
+
+ #define CMDQ_OP_CFGI_STE 0x3
+ #define CMDQ_OP_CFGI_ALL 0x4
+ struct {
+ u32 sid;
+ union {
+ bool leaf;
+ u8 span;
+ };
+ } cfgi;
+
+ #define CMDQ_OP_TLBI_NH_ASID 0x11
+ #define CMDQ_OP_TLBI_NH_VA 0x12
+ #define CMDQ_OP_TLBI_EL2_ALL 0x20
+ #define CMDQ_OP_TLBI_S12_VMALL 0x28
+ #define CMDQ_OP_TLBI_S2_IPA 0x2a
+ #define CMDQ_OP_TLBI_NSNH_ALL 0x30
+ struct {
+ u16 asid;
+ u16 vmid;
+ bool leaf;
+ u64 addr;
+ } tlbi;
+
+ #define CMDQ_OP_PRI_RESP 0x41
+ struct {
+ u32 sid;
+ u32 ssid;
+ u16 grpid;
+ enum pri_resp resp;
+ } pri;
+
+ #define CMDQ_OP_CMD_SYNC 0x46
+ };
+};
+
+struct arm_smmu_queue {
+ int irq; /* Wired interrupt */
+
+ __le64 *base;
+ dma_addr_t base_dma;
+ u64 q_base;
+
+ size_t ent_dwords;
+ u32 max_n_shift;
+ u32 prod;
+ u32 cons;
+
+ u32 __iomem *prod_reg;
+ u32 __iomem *cons_reg;
+};
+
+struct arm_smmu_cmdq {
+ struct arm_smmu_queue q;
+ spinlock_t lock;
+};
+
+struct arm_smmu_evtq {
+ struct arm_smmu_queue q;
+ u32 max_stalls;
+};
+
+struct arm_smmu_priq {
+ struct arm_smmu_queue q;
+};
+
+/* High-level stream table and context descriptor structures */
+struct arm_smmu_strtab_l1_desc {
+ u8 span;
+
+ __le64 *l2ptr;
+ dma_addr_t l2ptr_dma;
+};
+
+struct arm_smmu_s1_cfg {
+ __le64 *cdptr;
+ dma_addr_t cdptr_dma;
+
+ struct arm_smmu_ctx_desc {
+ u16 asid;
+ u64 ttbr;
+ u64 tcr;
+ u64 mair;
+ } cd;
+};
+
+struct arm_smmu_s2_cfg {
+ u16 vmid;
+ u64 vttbr;
+ u64 vtcr;
+};
+
+struct arm_smmu_strtab_ent {
+ bool valid;
+
+ bool bypass; /* Overrides s1/s2 config */
+ struct arm_smmu_s1_cfg *s1_cfg;
+ struct arm_smmu_s2_cfg *s2_cfg;
+};
+
+struct arm_smmu_strtab_cfg {
+ __le64 *strtab;
+ dma_addr_t strtab_dma;
+ struct arm_smmu_strtab_l1_desc *l1_desc;
+ unsigned int num_l1_ents;
+
+ u64 strtab_base;
+ u32 strtab_base_cfg;
+};
+
+/* An SMMUv3 instance */
+struct arm_smmu_device {
+ struct device *dev;
+ void __iomem *base;
+
+#define ARM_SMMU_FEAT_2_LVL_STRTAB (1 << 0)
+#define ARM_SMMU_FEAT_2_LVL_CDTAB (1 << 1)
+#define ARM_SMMU_FEAT_TT_LE (1 << 2)
+#define ARM_SMMU_FEAT_TT_BE (1 << 3)
+#define ARM_SMMU_FEAT_PRI (1 << 4)
+#define ARM_SMMU_FEAT_ATS (1 << 5)
+#define ARM_SMMU_FEAT_SEV (1 << 6)
+#define ARM_SMMU_FEAT_MSI (1 << 7)
+#define ARM_SMMU_FEAT_COHERENCY (1 << 8)
+#define ARM_SMMU_FEAT_TRANS_S1 (1 << 9)
+#define ARM_SMMU_FEAT_TRANS_S2 (1 << 10)
+#define ARM_SMMU_FEAT_STALLS (1 << 11)
+#define ARM_SMMU_FEAT_HYP (1 << 12)
+ u32 features;
+
+ struct arm_smmu_cmdq cmdq;
+ struct arm_smmu_evtq evtq;
+ struct arm_smmu_priq priq;
+
+ int gerr_irq;
+
+ unsigned long ias; /* IPA */
+ unsigned long oas; /* PA */
+
+#define ARM_SMMU_MAX_ASIDS (1 << 16)
+ unsigned int asid_bits;
+ DECLARE_BITMAP(asid_map, ARM_SMMU_MAX_ASIDS);
+
+#define ARM_SMMU_MAX_VMIDS (1 << 16)
+ unsigned int vmid_bits;
+ DECLARE_BITMAP(vmid_map, ARM_SMMU_MAX_VMIDS);
+
+ unsigned int ssid_bits;
+ unsigned int sid_bits;
+
+ struct arm_smmu_strtab_cfg strtab_cfg;
+ struct list_head list;
+};
+
+/* SMMU private data for an IOMMU group */
+struct arm_smmu_group {
+ struct arm_smmu_device *smmu;
+ struct arm_smmu_domain *domain;
+ int num_sids;
+ u32 *sids;
+ struct arm_smmu_strtab_ent ste;
+};
+
+/* SMMU private data for an IOMMU domain */
+enum arm_smmu_domain_stage {
+ ARM_SMMU_DOMAIN_S1 = 0,
+ ARM_SMMU_DOMAIN_S2,
+ ARM_SMMU_DOMAIN_NESTED,
+};
+
+struct arm_smmu_domain {
+ struct arm_smmu_device *smmu;
+ struct mutex init_mutex; /* Protects smmu pointer */
+
+ struct io_pgtable_ops *pgtbl_ops;
+ spinlock_t pgtbl_lock;
+
+ enum arm_smmu_domain_stage stage;
+ union {
+ struct arm_smmu_s1_cfg s1_cfg;
+ struct arm_smmu_s2_cfg s2_cfg;
+ };
+
+ struct iommu_domain domain;
+};
+
+/* Our list of SMMU instances */
+static DEFINE_SPINLOCK(arm_smmu_devices_lock);
+static LIST_HEAD(arm_smmu_devices);
+
+static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
+{
+ return container_of(dom, struct arm_smmu_domain, domain);
+}
+
+/* Low-level queue manipulation functions */
+static bool queue_full(struct arm_smmu_queue *q)
+{
+ return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
+ Q_WRP(q, q->prod) != Q_WRP(q, q->cons);
+}
+
+static bool queue_empty(struct arm_smmu_queue *q)
+{
+ return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
+ Q_WRP(q, q->prod) == Q_WRP(q, q->cons);
+}
+
+static void queue_sync_cons(struct arm_smmu_queue *q)
+{
+ q->cons = readl_relaxed(q->cons_reg);
+}
+
+static void queue_inc_cons(struct arm_smmu_queue *q)
+{
+ u32 cons = (Q_WRP(q, q->cons) | Q_IDX(q, q->cons)) + 1;
+
+ q->cons = Q_OVF(q, q->cons) | Q_WRP(q, cons) | Q_IDX(q, cons);
+ writel(q->cons, q->cons_reg);
+}
+
+static int queue_sync_prod(struct arm_smmu_queue *q)
+{
+ int ret = 0;
+ u32 prod = readl_relaxed(q->prod_reg);
+
+ if (Q_OVF(q, prod) != Q_OVF(q, q->prod))
+ ret = -EOVERFLOW;
+
+ q->prod = prod;
+ return ret;
+}
+
+static void queue_inc_prod(struct arm_smmu_queue *q)
+{
+ u32 prod = (Q_WRP(q, q->prod) | Q_IDX(q, q->prod)) + 1;
+
+ q->prod = Q_OVF(q, q->prod) | Q_WRP(q, prod) | Q_IDX(q, prod);
+ writel(q->prod, q->prod_reg);
+}
+
+static bool __queue_cons_before(struct arm_smmu_queue *q, u32 until)
+{
+ if (Q_WRP(q, q->cons) == Q_WRP(q, until))
+ return Q_IDX(q, q->cons) < Q_IDX(q, until);
+
+ return Q_IDX(q, q->cons) >= Q_IDX(q, until);
+}
+
+static int queue_poll_cons(struct arm_smmu_queue *q, u32 until, bool wfe)
+{
+ ktime_t timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US);
+
+ while (queue_sync_cons(q), __queue_cons_before(q, until)) {
+ if (ktime_compare(ktime_get(), timeout) > 0)
+ return -ETIMEDOUT;
+
+ if (wfe) {
+ wfe();
+ } else {
+ cpu_relax();
+ udelay(1);
+ }
+ }
+
+ return 0;
+}
+
+static void queue_write(__le64 *dst, u64 *src, size_t n_dwords)
+{
+ int i;
+
+ for (i = 0; i < n_dwords; ++i)
+ *dst++ = cpu_to_le64(*src++);
+}
+
+static int queue_insert_raw(struct arm_smmu_queue *q, u64 *ent)
+{
+ if (queue_full(q))
+ return -ENOSPC;
+
+ queue_write(Q_ENT(q, q->prod), ent, q->ent_dwords);
+ queue_inc_prod(q);
+ return 0;
+}
+
+static void queue_read(__le64 *dst, u64 *src, size_t n_dwords)
+{
+ int i;
+
+ for (i = 0; i < n_dwords; ++i)
+ *dst++ = le64_to_cpu(*src++);
+}
+
+static int queue_remove_raw(struct arm_smmu_queue *q, u64 *ent)
+{
+ if (queue_empty(q))
+ return -EAGAIN;
+
+ queue_read(ent, Q_ENT(q, q->cons), q->ent_dwords);
+ queue_inc_cons(q);
+ return 0;
+}
+
+/* High-level queue accessors */
+static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
+{
+ memset(cmd, 0, CMDQ_ENT_DWORDS << 3);
+ cmd[0] |= (ent->opcode & CMDQ_0_OP_MASK) << CMDQ_0_OP_SHIFT;
+
+ switch (ent->opcode) {
+ case CMDQ_OP_TLBI_EL2_ALL:
+ case CMDQ_OP_TLBI_NSNH_ALL:
+ break;
+ case CMDQ_OP_PREFETCH_CFG:
+ cmd[0] |= (u64)ent->prefetch.sid << CMDQ_PREFETCH_0_SID_SHIFT;
+ cmd[1] |= ent->prefetch.size << CMDQ_PREFETCH_1_SIZE_SHIFT;
+ cmd[1] |= ent->prefetch.addr & CMDQ_PREFETCH_1_ADDR_MASK;
+ break;
+ case CMDQ_OP_CFGI_STE:
+ cmd[0] |= (u64)ent->cfgi.sid << CMDQ_CFGI_0_SID_SHIFT;
+ cmd[1] |= ent->cfgi.leaf ? CMDQ_CFGI_1_LEAF : 0;
+ break;
+ case CMDQ_OP_CFGI_ALL:
+ /* Cover the entire SID range */
+ cmd[1] |= CMDQ_CFGI_1_RANGE_MASK << CMDQ_CFGI_1_RANGE_SHIFT;
+ break;
+ case CMDQ_OP_TLBI_NH_VA:
+ cmd[0] |= (u64)ent->tlbi.asid << CMDQ_TLBI_0_ASID_SHIFT;
+ /* Fallthrough */
+ case CMDQ_OP_TLBI_S2_IPA:
+ cmd[0] |= (u64)ent->tlbi.vmid << CMDQ_TLBI_0_VMID_SHIFT;
+ cmd[1] |= ent->tlbi.leaf ? CMDQ_TLBI_1_LEAF : 0;
+ cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_ADDR_MASK;
+ break;
+ case CMDQ_OP_TLBI_NH_ASID:
+ cmd[0] |= (u64)ent->tlbi.asid << CMDQ_TLBI_0_ASID_SHIFT;
+ /* Fallthrough */
+ case CMDQ_OP_TLBI_S12_VMALL:
+ cmd[0] |= (u64)ent->tlbi.vmid << CMDQ_TLBI_0_VMID_SHIFT;
+ break;
+ case CMDQ_OP_PRI_RESP:
+ cmd[0] |= ent->substream_valid ? CMDQ_0_SSV : 0;
+ cmd[0] |= ent->pri.ssid << CMDQ_PRI_0_SSID_SHIFT;
+ cmd[0] |= (u64)ent->pri.sid << CMDQ_PRI_0_SID_SHIFT;
+ cmd[1] |= ent->pri.grpid << CMDQ_PRI_1_GRPID_SHIFT;
+ switch (ent->pri.resp) {
+ case PRI_RESP_DENY:
+ cmd[1] |= CMDQ_PRI_1_RESP_DENY;
+ break;
+ case PRI_RESP_FAIL:
+ cmd[1] |= CMDQ_PRI_1_RESP_FAIL;
+ break;
+ case PRI_RESP_SUCC:
+ cmd[1] |= CMDQ_PRI_1_RESP_SUCC;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case CMDQ_OP_CMD_SYNC:
+ cmd[0] |= CMDQ_SYNC_0_CS_SEV;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu)
+{
+ static const char *cerror_str[] = {
+ [CMDQ_ERR_CERROR_NONE_IDX] = "No error",
+ [CMDQ_ERR_CERROR_ILL_IDX] = "Illegal command",
+ [CMDQ_ERR_CERROR_ABT_IDX] = "Abort on command fetch",
+ };
+
+ int i;
+ u64 cmd[CMDQ_ENT_DWORDS];
+ struct arm_smmu_queue *q = &smmu->cmdq.q;
+ u32 cons = readl_relaxed(q->cons_reg);
+ u32 idx = cons >> CMDQ_ERR_SHIFT & CMDQ_ERR_MASK;
+ struct arm_smmu_cmdq_ent cmd_sync = {
+ .opcode = CMDQ_OP_CMD_SYNC,
+ };
+
+ dev_err(smmu->dev, "CMDQ error (cons 0x%08x): %s\n", cons,
+ cerror_str[idx]);
+
+ switch (idx) {
+ case CMDQ_ERR_CERROR_ILL_IDX:
+ break;
+ case CMDQ_ERR_CERROR_ABT_IDX:
+ dev_err(smmu->dev, "retrying command fetch\n");
+ case CMDQ_ERR_CERROR_NONE_IDX:
+ return;
+ }
+
+ /*
+ * We may have concurrent producers, so we need to be careful
+ * not to touch any of the shadow cmdq state.
+ */
+ queue_read(cmd, Q_ENT(q, idx), q->ent_dwords);
+ dev_err(smmu->dev, "skipping command in error state:\n");
+ for (i = 0; i < ARRAY_SIZE(cmd); ++i)
+ dev_err(smmu->dev, "\t0x%016llx\n", (unsigned long long)cmd[i]);
+
+ /* Convert the erroneous command into a CMD_SYNC */
+ if (arm_smmu_cmdq_build_cmd(cmd, &cmd_sync)) {
+ dev_err(smmu->dev, "failed to convert to CMD_SYNC\n");
+ return;
+ }
+
+ queue_write(cmd, Q_ENT(q, idx), q->ent_dwords);
+}
+
+static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
+ struct arm_smmu_cmdq_ent *ent)
+{
+ u32 until;
+ u64 cmd[CMDQ_ENT_DWORDS];
+ bool wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV);
+ struct arm_smmu_queue *q = &smmu->cmdq.q;
+
+ if (arm_smmu_cmdq_build_cmd(cmd, ent)) {
+ dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n",
+ ent->opcode);
+ return;
+ }
+
+ spin_lock(&smmu->cmdq.lock);
+ while (until = q->prod + 1, queue_insert_raw(q, cmd) == -ENOSPC) {
+ /*
+ * Keep the queue locked, otherwise the producer could wrap
+ * twice and we could see a future consumer pointer that looks
+ * like it's behind us.
+ */
+ if (queue_poll_cons(q, until, wfe))
+ dev_err_ratelimited(smmu->dev, "CMDQ timeout\n");
+ }
+
+ if (ent->opcode == CMDQ_OP_CMD_SYNC && queue_poll_cons(q, until, wfe))
+ dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n");
+ spin_unlock(&smmu->cmdq.lock);
+}
+
+/* Context descriptor manipulation functions */
+static u64 arm_smmu_cpu_tcr_to_cd(u64 tcr)
+{
+ u64 val = 0;
+
+ /* Repack the TCR. Just care about TTBR0 for now */
+ val |= ARM_SMMU_TCR2CD(tcr, T0SZ);
+ val |= ARM_SMMU_TCR2CD(tcr, TG0);
+ val |= ARM_SMMU_TCR2CD(tcr, IRGN0);
+ val |= ARM_SMMU_TCR2CD(tcr, ORGN0);
+ val |= ARM_SMMU_TCR2CD(tcr, SH0);
+ val |= ARM_SMMU_TCR2CD(tcr, EPD0);
+ val |= ARM_SMMU_TCR2CD(tcr, EPD1);
+ val |= ARM_SMMU_TCR2CD(tcr, IPS);
+ val |= ARM_SMMU_TCR2CD(tcr, TBI0);
+
+ return val;
+}
+
+static void arm_smmu_write_ctx_desc(struct arm_smmu_device *smmu,
+ struct arm_smmu_s1_cfg *cfg)
+{
+ u64 val;
+
+ /*
+ * We don't need to issue any invalidation here, as we'll invalidate
+ * the STE when installing the new entry anyway.
+ */
+ val = arm_smmu_cpu_tcr_to_cd(cfg->cd.tcr) |
+#ifdef __BIG_ENDIAN
+ CTXDESC_CD_0_ENDI |
+#endif
+ CTXDESC_CD_0_R | CTXDESC_CD_0_A | CTXDESC_CD_0_ASET_PRIVATE |
+ CTXDESC_CD_0_AA64 | (u64)cfg->cd.asid << CTXDESC_CD_0_ASID_SHIFT |
+ CTXDESC_CD_0_V;
+ cfg->cdptr[0] = cpu_to_le64(val);
+
+ val = cfg->cd.ttbr & CTXDESC_CD_1_TTB0_MASK << CTXDESC_CD_1_TTB0_SHIFT;
+ cfg->cdptr[1] = cpu_to_le64(val);
+
+ cfg->cdptr[3] = cpu_to_le64(cfg->cd.mair << CTXDESC_CD_3_MAIR_SHIFT);
+}
+
+/* Stream table manipulation functions */
+static void
+arm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc)
+{
+ u64 val = 0;
+
+ val |= (desc->span & STRTAB_L1_DESC_SPAN_MASK)
+ << STRTAB_L1_DESC_SPAN_SHIFT;
+ val |= desc->l2ptr_dma &
+ STRTAB_L1_DESC_L2PTR_MASK << STRTAB_L1_DESC_L2PTR_SHIFT;
+
+ *dst = cpu_to_le64(val);
+}
+
+static void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid)
+{
+ struct arm_smmu_cmdq_ent cmd = {
+ .opcode = CMDQ_OP_CFGI_STE,
+ .cfgi = {
+ .sid = sid,
+ .leaf = true,
+ },
+ };
+
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+ cmd.opcode = CMDQ_OP_CMD_SYNC;
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+}
+
+static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
+ __le64 *dst, struct arm_smmu_strtab_ent *ste)
+{
+ /*
+ * This is hideously complicated, but we only really care about
+ * three cases at the moment:
+ *
+ * 1. Invalid (all zero) -> bypass (init)
+ * 2. Bypass -> translation (attach)
+ * 3. Translation -> bypass (detach)
+ *
+ * Given that we can't update the STE atomically and the SMMU
+ * doesn't read the thing in a defined order, that leaves us
+ * with the following maintenance requirements:
+ *
+ * 1. Update Config, return (init time STEs aren't live)
+ * 2. Write everything apart from dword 0, sync, write dword 0, sync
+ * 3. Update Config, sync
+ */
+ u64 val = le64_to_cpu(dst[0]);
+ bool ste_live = false;
+ struct arm_smmu_cmdq_ent prefetch_cmd = {
+ .opcode = CMDQ_OP_PREFETCH_CFG,
+ .prefetch = {
+ .sid = sid,
+ },
+ };
+
+ if (val & STRTAB_STE_0_V) {
+ u64 cfg;
+
+ cfg = val & STRTAB_STE_0_CFG_MASK << STRTAB_STE_0_CFG_SHIFT;
+ switch (cfg) {
+ case STRTAB_STE_0_CFG_BYPASS:
+ break;
+ case STRTAB_STE_0_CFG_S1_TRANS:
+ case STRTAB_STE_0_CFG_S2_TRANS:
+ ste_live = true;
+ break;
+ default:
+ BUG(); /* STE corruption */
+ }
+ }
+
+ /* Nuke the existing Config, as we're going to rewrite it */
+ val &= ~(STRTAB_STE_0_CFG_MASK << STRTAB_STE_0_CFG_SHIFT);
+
+ if (ste->valid)
+ val |= STRTAB_STE_0_V;
+ else
+ val &= ~STRTAB_STE_0_V;
+
+ if (ste->bypass) {
+ val |= disable_bypass ? STRTAB_STE_0_CFG_ABORT
+ : STRTAB_STE_0_CFG_BYPASS;
+ dst[0] = cpu_to_le64(val);
+ dst[2] = 0; /* Nuke the VMID */
+ if (ste_live)
+ arm_smmu_sync_ste_for_sid(smmu, sid);
+ return;
+ }
+
+ if (ste->s1_cfg) {
+ BUG_ON(ste_live);
+ dst[1] = cpu_to_le64(
+ STRTAB_STE_1_S1C_CACHE_WBRA
+ << STRTAB_STE_1_S1CIR_SHIFT |
+ STRTAB_STE_1_S1C_CACHE_WBRA
+ << STRTAB_STE_1_S1COR_SHIFT |
+ STRTAB_STE_1_S1C_SH_ISH << STRTAB_STE_1_S1CSH_SHIFT |
+ STRTAB_STE_1_S1STALLD |
+#ifdef CONFIG_PCI_ATS
+ STRTAB_STE_1_EATS_TRANS << STRTAB_STE_1_EATS_SHIFT |
+#endif
+ STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT);
+
+ val |= (ste->s1_cfg->cdptr_dma & STRTAB_STE_0_S1CTXPTR_MASK
+ << STRTAB_STE_0_S1CTXPTR_SHIFT) |
+ STRTAB_STE_0_CFG_S1_TRANS;
+
+ }
+
+ if (ste->s2_cfg) {
+ BUG_ON(ste_live);
+ dst[2] = cpu_to_le64(
+ ste->s2_cfg->vmid << STRTAB_STE_2_S2VMID_SHIFT |
+ (ste->s2_cfg->vtcr & STRTAB_STE_2_VTCR_MASK)
+ << STRTAB_STE_2_VTCR_SHIFT |
+#ifdef __BIG_ENDIAN
+ STRTAB_STE_2_S2ENDI |
+#endif
+ STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 |
+ STRTAB_STE_2_S2R);
+
+ dst[3] = cpu_to_le64(ste->s2_cfg->vttbr &
+ STRTAB_STE_3_S2TTB_MASK << STRTAB_STE_3_S2TTB_SHIFT);
+
+ val |= STRTAB_STE_0_CFG_S2_TRANS;
+ }
+
+ arm_smmu_sync_ste_for_sid(smmu, sid);
+ dst[0] = cpu_to_le64(val);
+ arm_smmu_sync_ste_for_sid(smmu, sid);
+
+ /* It's likely that we'll want to use the new STE soon */
+ arm_smmu_cmdq_issue_cmd(smmu, &prefetch_cmd);
+}
+
+static void arm_smmu_init_bypass_stes(u64 *strtab, unsigned int nent)
+{
+ unsigned int i;
+ struct arm_smmu_strtab_ent ste = {
+ .valid = true,
+ .bypass = true,
+ };
+
+ for (i = 0; i < nent; ++i) {
+ arm_smmu_write_strtab_ent(NULL, -1, strtab, &ste);
+ strtab += STRTAB_STE_DWORDS;
+ }
+}
+
+static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
+{
+ size_t size;
+ void *strtab;
+ struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+ struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];
+
+ if (desc->l2ptr)
+ return 0;
+
+ size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);
+ strtab = &cfg->strtab[sid >> STRTAB_SPLIT << STRTAB_L1_DESC_DWORDS];
+
+ desc->span = STRTAB_SPLIT + 1;
+ desc->l2ptr = dma_zalloc_coherent(smmu->dev, size, &desc->l2ptr_dma,
+ GFP_KERNEL);
+ if (!desc->l2ptr) {
+ dev_err(smmu->dev,
+ "failed to allocate l2 stream table for SID %u\n",
+ sid);
+ return -ENOMEM;
+ }
+
+ arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT);
+ arm_smmu_write_strtab_l1_desc(strtab, desc);
+ return 0;
+}
+
+/* IRQ and event handlers */
+static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
+{
+ int i;
+ struct arm_smmu_device *smmu = dev;
+ struct arm_smmu_queue *q = &smmu->evtq.q;
+ u64 evt[EVTQ_ENT_DWORDS];
+
+ while (!queue_remove_raw(q, evt)) {
+ u8 id = evt[0] >> EVTQ_0_ID_SHIFT & EVTQ_0_ID_MASK;
+
+ dev_info(smmu->dev, "event 0x%02x received:\n", id);
+ for (i = 0; i < ARRAY_SIZE(evt); ++i)
+ dev_info(smmu->dev, "\t0x%016llx\n",
+ (unsigned long long)evt[i]);
+ }
+
+ /* Sync our overflow flag, as we believe we're up to speed */
+ q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t arm_smmu_evtq_handler(int irq, void *dev)
+{
+ irqreturn_t ret = IRQ_WAKE_THREAD;
+ struct arm_smmu_device *smmu = dev;
+ struct arm_smmu_queue *q = &smmu->evtq.q;
+
+ /*
+ * Not much we can do on overflow, so scream and pretend we're
+ * trying harder.
+ */
+ if (queue_sync_prod(q) == -EOVERFLOW)
+ dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n");
+ else if (queue_empty(q))
+ ret = IRQ_NONE;
+
+ return ret;
+}
+
+static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
+{
+ struct arm_smmu_device *smmu = dev;
+ struct arm_smmu_queue *q = &smmu->priq.q;
+ u64 evt[PRIQ_ENT_DWORDS];
+
+ while (!queue_remove_raw(q, evt)) {
+ u32 sid, ssid;
+ u16 grpid;
+ bool ssv, last;
+
+ sid = evt[0] >> PRIQ_0_SID_SHIFT & PRIQ_0_SID_MASK;
+ ssv = evt[0] & PRIQ_0_SSID_V;
+ ssid = ssv ? evt[0] >> PRIQ_0_SSID_SHIFT & PRIQ_0_SSID_MASK : 0;
+ last = evt[0] & PRIQ_0_PRG_LAST;
+ grpid = evt[1] >> PRIQ_1_PRG_IDX_SHIFT & PRIQ_1_PRG_IDX_MASK;
+
+ dev_info(smmu->dev, "unexpected PRI request received:\n");
+ dev_info(smmu->dev,
+ "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n",
+ sid, ssid, grpid, last ? "L" : "",
+ evt[0] & PRIQ_0_PERM_PRIV ? "" : "un",
+ evt[0] & PRIQ_0_PERM_READ ? "R" : "",
+ evt[0] & PRIQ_0_PERM_WRITE ? "W" : "",
+ evt[0] & PRIQ_0_PERM_EXEC ? "X" : "",
+ evt[1] & PRIQ_1_ADDR_MASK << PRIQ_1_ADDR_SHIFT);
+
+ if (last) {
+ struct arm_smmu_cmdq_ent cmd = {
+ .opcode = CMDQ_OP_PRI_RESP,
+ .substream_valid = ssv,
+ .pri = {
+ .sid = sid,
+ .ssid = ssid,
+ .grpid = grpid,
+ .resp = PRI_RESP_DENY,
+ },
+ };
+
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+ }
+ }
+
+ /* Sync our overflow flag, as we believe we're up to speed */
+ q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t arm_smmu_priq_handler(int irq, void *dev)
+{
+ irqreturn_t ret = IRQ_WAKE_THREAD;
+ struct arm_smmu_device *smmu = dev;
+ struct arm_smmu_queue *q = &smmu->priq.q;
+
+ /* PRIQ overflow indicates a programming error */
+ if (queue_sync_prod(q) == -EOVERFLOW)
+ dev_err(smmu->dev, "PRIQ overflow detected -- requests lost\n");
+ else if (queue_empty(q))
+ ret = IRQ_NONE;
+
+ return ret;
+}
+
+static irqreturn_t arm_smmu_cmdq_sync_handler(int irq, void *dev)
+{
+ /* We don't actually use CMD_SYNC interrupts for anything */
+ return IRQ_HANDLED;
+}
+
+static int arm_smmu_device_disable(struct arm_smmu_device *smmu);
+
+static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
+{
+ u32 gerror, gerrorn;
+ struct arm_smmu_device *smmu = dev;
+
+ gerror = readl_relaxed(smmu->base + ARM_SMMU_GERROR);
+ gerrorn = readl_relaxed(smmu->base + ARM_SMMU_GERRORN);
+
+ gerror ^= gerrorn;
+ if (!(gerror & GERROR_ERR_MASK))
+ return IRQ_NONE; /* No errors pending */
+
+ dev_warn(smmu->dev,
+ "unexpected global error reported (0x%08x), this could be serious\n",
+ gerror);
+
+ if (gerror & GERROR_SFM_ERR) {
+ dev_err(smmu->dev, "device has entered Service Failure Mode!\n");
+ arm_smmu_device_disable(smmu);
+ }
+
+ if (gerror & GERROR_MSI_GERROR_ABT_ERR)
+ dev_warn(smmu->dev, "GERROR MSI write aborted\n");
+
+ if (gerror & GERROR_MSI_PRIQ_ABT_ERR) {
+ dev_warn(smmu->dev, "PRIQ MSI write aborted\n");
+ arm_smmu_priq_handler(irq, smmu->dev);
+ }
+
+ if (gerror & GERROR_MSI_EVTQ_ABT_ERR) {
+ dev_warn(smmu->dev, "EVTQ MSI write aborted\n");
+ arm_smmu_evtq_handler(irq, smmu->dev);
+ }
+
+ if (gerror & GERROR_MSI_CMDQ_ABT_ERR) {
+ dev_warn(smmu->dev, "CMDQ MSI write aborted\n");
+ arm_smmu_cmdq_sync_handler(irq, smmu->dev);
+ }
+
+ if (gerror & GERROR_PRIQ_ABT_ERR)
+ dev_err(smmu->dev, "PRIQ write aborted -- events may have been lost\n");
+
+ if (gerror & GERROR_EVTQ_ABT_ERR)
+ dev_err(smmu->dev, "EVTQ write aborted -- events may have been lost\n");
+
+ if (gerror & GERROR_CMDQ_ERR)
+ arm_smmu_cmdq_skip_err(smmu);
+
+ writel(gerror, smmu->base + ARM_SMMU_GERRORN);
+ return IRQ_HANDLED;
+}
+
+/* IO_PGTABLE API */
+static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
+{
+ struct arm_smmu_cmdq_ent cmd;
+
+ cmd.opcode = CMDQ_OP_CMD_SYNC;
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+}
+
+static void arm_smmu_tlb_sync(void *cookie)
+{
+ struct arm_smmu_domain *smmu_domain = cookie;
+ __arm_smmu_tlb_sync(smmu_domain->smmu);
+}
+
+static void arm_smmu_tlb_inv_context(void *cookie)
+{
+ struct arm_smmu_domain *smmu_domain = cookie;
+ struct arm_smmu_device *smmu = smmu_domain->smmu;
+ struct arm_smmu_cmdq_ent cmd;
+
+ if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+ cmd.opcode = CMDQ_OP_TLBI_NH_ASID;
+ cmd.tlbi.asid = smmu_domain->s1_cfg.cd.asid;
+ cmd.tlbi.vmid = 0;
+ } else {
+ cmd.opcode = CMDQ_OP_TLBI_S12_VMALL;
+ cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid;
+ }
+
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+ __arm_smmu_tlb_sync(smmu);
+}
+
+static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
+ bool leaf, void *cookie)
+{
+ struct arm_smmu_domain *smmu_domain = cookie;
+ struct arm_smmu_device *smmu = smmu_domain->smmu;
+ struct arm_smmu_cmdq_ent cmd = {
+ .tlbi = {
+ .leaf = leaf,
+ .addr = iova,
+ },
+ };
+
+ if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+ cmd.opcode = CMDQ_OP_TLBI_NH_VA;
+ cmd.tlbi.asid = smmu_domain->s1_cfg.cd.asid;
+ } else {
+ cmd.opcode = CMDQ_OP_TLBI_S2_IPA;
+ cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid;
+ }
+
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+}
+
+static void arm_smmu_flush_pgtable(void *addr, size_t size, void *cookie)
+{
+ struct arm_smmu_domain *smmu_domain = cookie;
+ struct arm_smmu_device *smmu = smmu_domain->smmu;
+ unsigned long offset = (unsigned long)addr & ~PAGE_MASK;
+
+ if (smmu->features & ARM_SMMU_FEAT_COHERENCY) {
+ dsb(ishst);
+ } else {
+ dma_addr_t dma_addr;
+ struct device *dev = smmu->dev;
+
+ dma_addr = dma_map_page(dev, virt_to_page(addr), offset, size,
+ DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, dma_addr))
+ dev_err(dev, "failed to flush pgtable at %p\n", addr);
+ else
+ dma_unmap_page(dev, dma_addr, size, DMA_TO_DEVICE);
+ }
+}
+
+static struct iommu_gather_ops arm_smmu_gather_ops = {
+ .tlb_flush_all = arm_smmu_tlb_inv_context,
+ .tlb_add_flush = arm_smmu_tlb_inv_range_nosync,
+ .tlb_sync = arm_smmu_tlb_sync,
+ .flush_pgtable = arm_smmu_flush_pgtable,
+};
+
+/* IOMMU API */
+static bool arm_smmu_capable(enum iommu_cap cap)
+{
+ switch (cap) {
+ case IOMMU_CAP_CACHE_COHERENCY:
+ return true;
+ case IOMMU_CAP_INTR_REMAP:
+ return true; /* MSIs are just memory writes */
+ case IOMMU_CAP_NOEXEC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
+{
+ struct arm_smmu_domain *smmu_domain;
+
+ if (type != IOMMU_DOMAIN_UNMANAGED)
+ return NULL;
+
+ /*
+ * Allocate the domain and initialise some of its data structures.
+ * We can't really do anything meaningful until we've added a
+ * master.
+ */
+ smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL);
+ if (!smmu_domain)
+ return NULL;
+
+ mutex_init(&smmu_domain->init_mutex);
+ spin_lock_init(&smmu_domain->pgtbl_lock);
+ return &smmu_domain->domain;
+}
+
+static int arm_smmu_bitmap_alloc(unsigned long *map, int span)
+{
+ int idx, size = 1 << span;
+
+ do {
+ idx = find_first_zero_bit(map, size);
+ if (idx == size)
+ return -ENOSPC;
+ } while (test_and_set_bit(idx, map));
+
+ return idx;
+}
+
+static void arm_smmu_bitmap_free(unsigned long *map, int idx)
+{
+ clear_bit(idx, map);
+}
+
+static void arm_smmu_domain_free(struct iommu_domain *domain)
+{
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct arm_smmu_device *smmu = smmu_domain->smmu;
+
+ if (smmu_domain->pgtbl_ops)
+ free_io_pgtable_ops(smmu_domain->pgtbl_ops);
+
+ /* Free the CD and ASID, if we allocated them */
+ if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+ struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
+
+ if (cfg->cdptr) {
+ dma_free_coherent(smmu_domain->smmu->dev,
+ CTXDESC_CD_DWORDS << 3,
+ cfg->cdptr,
+ cfg->cdptr_dma);
+
+ arm_smmu_bitmap_free(smmu->asid_map, cfg->cd.asid);
+ }
+ } else {
+ struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
+ if (cfg->vmid)
+ arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
+ }
+
+ kfree(smmu_domain);
+}
+
+static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
+ struct io_pgtable_cfg *pgtbl_cfg)
+{
+ int ret;
+ u16 asid;
+ struct arm_smmu_device *smmu = smmu_domain->smmu;
+ struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
+
+ asid = arm_smmu_bitmap_alloc(smmu->asid_map, smmu->asid_bits);
+ if (IS_ERR_VALUE(asid))
+ return asid;
+
+ cfg->cdptr = dma_zalloc_coherent(smmu->dev, CTXDESC_CD_DWORDS << 3,
+ &cfg->cdptr_dma, GFP_KERNEL);
+ if (!cfg->cdptr) {
+ dev_warn(smmu->dev, "failed to allocate context descriptor\n");
+ goto out_free_asid;
+ }
+
+ cfg->cd.asid = asid;
+ cfg->cd.ttbr = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
+ cfg->cd.tcr = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
+ cfg->cd.mair = pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
+ return 0;
+
+out_free_asid:
+ arm_smmu_bitmap_free(smmu->asid_map, asid);
+ return ret;
+}
+
+static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
+ struct io_pgtable_cfg *pgtbl_cfg)
+{
+ u16 vmid;
+ struct arm_smmu_device *smmu = smmu_domain->smmu;
+ struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
+
+ vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
+ if (IS_ERR_VALUE(vmid))
+ return vmid;
+
+ cfg->vmid = vmid;
+ cfg->vttbr = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
+ cfg->vtcr = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
+ return 0;
+}
+
+static struct iommu_ops arm_smmu_ops;
+
+static int arm_smmu_domain_finalise(struct iommu_domain *domain)
+{
+ int ret;
+ unsigned long ias, oas;
+ enum io_pgtable_fmt fmt;
+ struct io_pgtable_cfg pgtbl_cfg;
+ struct io_pgtable_ops *pgtbl_ops;
+ int (*finalise_stage_fn)(struct arm_smmu_domain *,
+ struct io_pgtable_cfg *);
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct arm_smmu_device *smmu = smmu_domain->smmu;
+
+ /* Restrict the stage to what we can actually support */
+ if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
+ smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
+ if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
+ smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+
+ switch (smmu_domain->stage) {
+ case ARM_SMMU_DOMAIN_S1:
+ ias = VA_BITS;
+ oas = smmu->ias;
+ fmt = ARM_64_LPAE_S1;
+ finalise_stage_fn = arm_smmu_domain_finalise_s1;
+ break;
+ case ARM_SMMU_DOMAIN_NESTED:
+ case ARM_SMMU_DOMAIN_S2:
+ ias = smmu->ias;
+ oas = smmu->oas;
+ fmt = ARM_64_LPAE_S2;
+ finalise_stage_fn = arm_smmu_domain_finalise_s2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pgtbl_cfg = (struct io_pgtable_cfg) {
+ .pgsize_bitmap = arm_smmu_ops.pgsize_bitmap,
+ .ias = ias,
+ .oas = oas,
+ .tlb = &arm_smmu_gather_ops,
+ };
+
+ pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
+ if (!pgtbl_ops)
+ return -ENOMEM;
+
+ arm_smmu_ops.pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
+ smmu_domain->pgtbl_ops = pgtbl_ops;
+
+ ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
+ if (IS_ERR_VALUE(ret))
+ free_io_pgtable_ops(pgtbl_ops);
+
+ return ret;
+}
+
+static struct arm_smmu_group *arm_smmu_group_get(struct device *dev)
+{
+ struct iommu_group *group;
+ struct arm_smmu_group *smmu_group;
+
+ group = iommu_group_get(dev);
+ if (!group)
+ return NULL;
+
+ smmu_group = iommu_group_get_iommudata(group);
+ iommu_group_put(group);
+ return smmu_group;
+}
+
+static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
+{
+ __le64 *step;
+ struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+ if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+ struct arm_smmu_strtab_l1_desc *l1_desc;
+ int idx;
+
+ /* Two-level walk */
+ idx = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS;
+ l1_desc = &cfg->l1_desc[idx];
+ idx = (sid & ((1 << STRTAB_SPLIT) - 1)) * STRTAB_STE_DWORDS;
+ step = &l1_desc->l2ptr[idx];
+ } else {
+ /* Simple linear lookup */
+ step = &cfg->strtab[sid * STRTAB_STE_DWORDS];
+ }
+
+ return step;
+}
+
+static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group)
+{
+ int i;
+ struct arm_smmu_domain *smmu_domain = smmu_group->domain;
+ struct arm_smmu_strtab_ent *ste = &smmu_group->ste;
+ struct arm_smmu_device *smmu = smmu_group->smmu;
+
+ if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+ ste->s1_cfg = &smmu_domain->s1_cfg;
+ ste->s2_cfg = NULL;
+ arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
+ } else {
+ ste->s1_cfg = NULL;
+ ste->s2_cfg = &smmu_domain->s2_cfg;
+ }
+
+ for (i = 0; i < smmu_group->num_sids; ++i) {
+ u32 sid = smmu_group->sids[i];
+ __le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
+
+ arm_smmu_write_strtab_ent(smmu, sid, step, ste);
+ }
+
+ return 0;
+}
+
+static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ int ret = 0;
+ struct arm_smmu_device *smmu;
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
+
+ if (!smmu_group)
+ return -ENOENT;
+
+ /* Already attached to a different domain? */
+ if (smmu_group->domain && smmu_group->domain != smmu_domain)
+ return -EEXIST;
+
+ smmu = smmu_group->smmu;
+ mutex_lock(&smmu_domain->init_mutex);
+
+ if (!smmu_domain->smmu) {
+ smmu_domain->smmu = smmu;
+ ret = arm_smmu_domain_finalise(domain);
+ if (ret) {
+ smmu_domain->smmu = NULL;
+ goto out_unlock;
+ }
+ } else if (smmu_domain->smmu != smmu) {
+ dev_err(dev,
+ "cannot attach to SMMU %s (upstream of %s)\n",
+ dev_name(smmu_domain->smmu->dev),
+ dev_name(smmu->dev));
+ ret = -ENXIO;
+ goto out_unlock;
+ }
+
+ /* Group already attached to this domain? */
+ if (smmu_group->domain)
+ goto out_unlock;
+
+ smmu_group->domain = smmu_domain;
+ smmu_group->ste.bypass = false;
+
+ ret = arm_smmu_install_ste_for_group(smmu_group);
+ if (IS_ERR_VALUE(ret))
+ smmu_group->domain = NULL;
+
+out_unlock:
+ mutex_unlock(&smmu_domain->init_mutex);
+ return ret;
+}
+
+static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
+
+ BUG_ON(!smmu_domain);
+ BUG_ON(!smmu_group);
+
+ mutex_lock(&smmu_domain->init_mutex);
+ BUG_ON(smmu_group->domain != smmu_domain);
+
+ smmu_group->ste.bypass = true;
+ if (IS_ERR_VALUE(arm_smmu_install_ste_for_group(smmu_group)))
+ dev_warn(dev, "failed to install bypass STE\n");
+
+ smmu_group->domain = NULL;
+ mutex_unlock(&smmu_domain->init_mutex);
+}
+
+static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t paddr, size_t size, int prot)
+{
+ int ret;
+ unsigned long flags;
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+
+ if (!ops)
+ return -ENODEV;
+
+ spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+ ret = ops->map(ops, iova, paddr, size, prot);
+ spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+ return ret;
+}
+
+static size_t
+arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
+{
+ size_t ret;
+ unsigned long flags;
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+
+ if (!ops)
+ return 0;
+
+ spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+ ret = ops->unmap(ops, iova, size);
+ spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+ return ret;
+}
+
+static phys_addr_t
+arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
+{
+ phys_addr_t ret;
+ unsigned long flags;
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+ struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+
+ if (!ops)
+ return 0;
+
+ spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+ ret = ops->iova_to_phys(ops, iova);
+ spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+
+ return ret;
+}
+
+static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *sidp)
+{
+ *(u32 *)sidp = alias;
+ return 0; /* Continue walking */
+}
+
+static void __arm_smmu_release_pci_iommudata(void *data)
+{
+ kfree(data);
+}
+
+static struct arm_smmu_device *arm_smmu_get_for_pci_dev(struct pci_dev *pdev)
+{
+ struct device_node *of_node;
+ struct arm_smmu_device *curr, *smmu = NULL;
+ struct pci_bus *bus = pdev->bus;
+
+ /* Walk up to the root bus */
+ while (!pci_is_root_bus(bus))
+ bus = bus->parent;
+
+ /* Follow the "iommus" phandle from the host controller */
+ of_node = of_parse_phandle(bus->bridge->parent->of_node, "iommus", 0);
+ if (!of_node)
+ return NULL;
+
+ /* See if we can find an SMMU corresponding to the phandle */
+ spin_lock(&arm_smmu_devices_lock);
+ list_for_each_entry(curr, &arm_smmu_devices, list) {
+ if (curr->dev->of_node == of_node) {
+ smmu = curr;
+ break;
+ }
+ }
+ spin_unlock(&arm_smmu_devices_lock);
+ of_node_put(of_node);
+ return smmu;
+}
+
+static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
+{
+ unsigned long limit = smmu->strtab_cfg.num_l1_ents;
+
+ if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
+ limit *= 1UL << STRTAB_SPLIT;
+
+ return sid < limit;
+}
+
+static int arm_smmu_add_device(struct device *dev)
+{
+ int i, ret;
+ u32 sid, *sids;
+ struct pci_dev *pdev;
+ struct iommu_group *group;
+ struct arm_smmu_group *smmu_group;
+ struct arm_smmu_device *smmu;
+
+ /* We only support PCI, for now */
+ if (!dev_is_pci(dev))
+ return -ENODEV;
+
+ pdev = to_pci_dev(dev);
+ group = iommu_group_get_for_dev(dev);
+ if (IS_ERR(group))
+ return PTR_ERR(group);
+
+ smmu_group = iommu_group_get_iommudata(group);
+ if (!smmu_group) {
+ smmu = arm_smmu_get_for_pci_dev(pdev);
+ if (!smmu) {
+ ret = -ENOENT;
+ goto out_put_group;
+ }
+
+ smmu_group = kzalloc(sizeof(*smmu_group), GFP_KERNEL);
+ if (!smmu_group) {
+ ret = -ENOMEM;
+ goto out_put_group;
+ }
+
+ smmu_group->ste.valid = true;
+ smmu_group->smmu = smmu;
+ iommu_group_set_iommudata(group, smmu_group,
+ __arm_smmu_release_pci_iommudata);
+ } else {
+ smmu = smmu_group->smmu;
+ }
+
+ /* Assume SID == RID until firmware tells us otherwise */
+ pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
+ for (i = 0; i < smmu_group->num_sids; ++i) {
+ /* If we already know about this SID, then we're done */
+ if (smmu_group->sids[i] == sid)
+ return 0;
+ }
+
+ /* Check the SID is in range of the SMMU and our stream table */
+ if (!arm_smmu_sid_in_range(smmu, sid)) {
+ ret = -ERANGE;
+ goto out_put_group;
+ }
+
+ /* Ensure l2 strtab is initialised */
+ if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+ ret = arm_smmu_init_l2_strtab(smmu, sid);
+ if (ret)
+ goto out_put_group;
+ }
+
+ /* Resize the SID array for the group */
+ smmu_group->num_sids++;
+ sids = krealloc(smmu_group->sids, smmu_group->num_sids * sizeof(*sids),
+ GFP_KERNEL);
+ if (!sids) {
+ smmu_group->num_sids--;
+ ret = -ENOMEM;
+ goto out_put_group;
+ }
+
+ /* Add the new SID */
+ sids[smmu_group->num_sids - 1] = sid;
+ smmu_group->sids = sids;
+ return 0;
+
+out_put_group:
+ iommu_group_put(group);
+ return ret;
+}
+
+static void arm_smmu_remove_device(struct device *dev)
+{
+ iommu_group_remove_device(dev);
+}
+
+static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
+ enum iommu_attr attr, void *data)
+{
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+
+ switch (attr) {
+ case DOMAIN_ATTR_NESTING:
+ *(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
+ return 0;
+ default:
+ return -ENODEV;
+ }
+}
+
+static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
+ enum iommu_attr attr, void *data)
+{
+ int ret = 0;
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+
+ mutex_lock(&smmu_domain->init_mutex);
+
+ switch (attr) {
+ case DOMAIN_ATTR_NESTING:
+ if (smmu_domain->smmu) {
+ ret = -EPERM;
+ goto out_unlock;
+ }
+
+ if (*(int *)data)
+ smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
+ else
+ smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+
+ break;
+ default:
+ ret = -ENODEV;
+ }
+
+out_unlock:
+ mutex_unlock(&smmu_domain->init_mutex);
+ return ret;
+}
+
+static struct iommu_ops arm_smmu_ops = {
+ .capable = arm_smmu_capable,
+ .domain_alloc = arm_smmu_domain_alloc,
+ .domain_free = arm_smmu_domain_free,
+ .attach_dev = arm_smmu_attach_dev,
+ .detach_dev = arm_smmu_detach_dev,
+ .map = arm_smmu_map,
+ .unmap = arm_smmu_unmap,
+ .iova_to_phys = arm_smmu_iova_to_phys,
+ .add_device = arm_smmu_add_device,
+ .remove_device = arm_smmu_remove_device,
+ .domain_get_attr = arm_smmu_domain_get_attr,
+ .domain_set_attr = arm_smmu_domain_set_attr,
+ .pgsize_bitmap = -1UL, /* Restricted during device attach */
+};
+
+/* Probing and initialisation functions */
+static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
+ struct arm_smmu_queue *q,
+ unsigned long prod_off,
+ unsigned long cons_off,
+ size_t dwords)
+{
+ size_t qsz = ((1 << q->max_n_shift) * dwords) << 3;
+
+ q->base = dma_alloc_coherent(smmu->dev, qsz, &q->base_dma, GFP_KERNEL);
+ if (!q->base) {
+ dev_err(smmu->dev, "failed to allocate queue (0x%zx bytes)\n",
+ qsz);
+ return -ENOMEM;
+ }
+
+ q->prod_reg = smmu->base + prod_off;
+ q->cons_reg = smmu->base + cons_off;
+ q->ent_dwords = dwords;
+
+ q->q_base = Q_BASE_RWA;
+ q->q_base |= q->base_dma & Q_BASE_ADDR_MASK << Q_BASE_ADDR_SHIFT;
+ q->q_base |= (q->max_n_shift & Q_BASE_LOG2SIZE_MASK)
+ << Q_BASE_LOG2SIZE_SHIFT;
+
+ q->prod = q->cons = 0;
+ return 0;
+}
+
+static void arm_smmu_free_one_queue(struct arm_smmu_device *smmu,
+ struct arm_smmu_queue *q)
+{
+ size_t qsz = ((1 << q->max_n_shift) * q->ent_dwords) << 3;
+
+ dma_free_coherent(smmu->dev, qsz, q->base, q->base_dma);
+}
+
+static void arm_smmu_free_queues(struct arm_smmu_device *smmu)
+{
+ arm_smmu_free_one_queue(smmu, &smmu->cmdq.q);
+ arm_smmu_free_one_queue(smmu, &smmu->evtq.q);
+
+ if (smmu->features & ARM_SMMU_FEAT_PRI)
+ arm_smmu_free_one_queue(smmu, &smmu->priq.q);
+}
+
+static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
+{
+ int ret;
+
+ /* cmdq */
+ spin_lock_init(&smmu->cmdq.lock);
+ ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, ARM_SMMU_CMDQ_PROD,
+ ARM_SMMU_CMDQ_CONS, CMDQ_ENT_DWORDS);
+ if (ret)
+ goto out;
+
+ /* evtq */
+ ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, ARM_SMMU_EVTQ_PROD,
+ ARM_SMMU_EVTQ_CONS, EVTQ_ENT_DWORDS);
+ if (ret)
+ goto out_free_cmdq;
+
+ /* priq */
+ if (!(smmu->features & ARM_SMMU_FEAT_PRI))
+ return 0;
+
+ ret = arm_smmu_init_one_queue(smmu, &smmu->priq.q, ARM_SMMU_PRIQ_PROD,
+ ARM_SMMU_PRIQ_CONS, PRIQ_ENT_DWORDS);
+ if (ret)
+ goto out_free_evtq;
+
+ return 0;
+
+out_free_evtq:
+ arm_smmu_free_one_queue(smmu, &smmu->evtq.q);
+out_free_cmdq:
+ arm_smmu_free_one_queue(smmu, &smmu->cmdq.q);
+out:
+ return ret;
+}
+
+static void arm_smmu_free_l2_strtab(struct arm_smmu_device *smmu)
+{
+ int i;
+ size_t size;
+ struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+ size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);
+ for (i = 0; i < cfg->num_l1_ents; ++i) {
+ struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[i];
+
+ if (!desc->l2ptr)
+ continue;
+
+ dma_free_coherent(smmu->dev, size, desc->l2ptr,
+ desc->l2ptr_dma);
+ }
+}
+
+static int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu)
+{
+ unsigned int i;
+ struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+ size_t size = sizeof(*cfg->l1_desc) * cfg->num_l1_ents;
+ void *strtab = smmu->strtab_cfg.strtab;
+
+ cfg->l1_desc = devm_kzalloc(smmu->dev, size, GFP_KERNEL);
+ if (!cfg->l1_desc) {
+ dev_err(smmu->dev, "failed to allocate l1 stream table desc\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < cfg->num_l1_ents; ++i) {
+ arm_smmu_write_strtab_l1_desc(strtab, &cfg->l1_desc[i]);
+ strtab += STRTAB_L1_DESC_DWORDS << 3;
+ }
+
+ return 0;
+}
+
+static int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu)
+{
+ void *strtab;
+ u64 reg;
+ u32 size;
+ int ret;
+ struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+ /* Calculate the L1 size, capped to the SIDSIZE */
+ size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3);
+ size = min(size, smmu->sid_bits - STRTAB_SPLIT);
+ if (size + STRTAB_SPLIT < smmu->sid_bits)
+ dev_warn(smmu->dev,
+ "2-level strtab only covers %u/%u bits of SID\n",
+ size + STRTAB_SPLIT, smmu->sid_bits);
+
+ cfg->num_l1_ents = 1 << size;
+ size = cfg->num_l1_ents * (STRTAB_L1_DESC_DWORDS << 3);
+ strtab = dma_zalloc_coherent(smmu->dev, size, &cfg->strtab_dma,
+ GFP_KERNEL);
+ if (!strtab) {
+ dev_err(smmu->dev,
+ "failed to allocate l1 stream table (%u bytes)\n",
+ size);
+ return -ENOMEM;
+ }
+ cfg->strtab = strtab;
+
+ /* Configure strtab_base_cfg for 2 levels */
+ reg = STRTAB_BASE_CFG_FMT_2LVL;
+ reg |= (size & STRTAB_BASE_CFG_LOG2SIZE_MASK)
+ << STRTAB_BASE_CFG_LOG2SIZE_SHIFT;
+ reg |= (STRTAB_SPLIT & STRTAB_BASE_CFG_SPLIT_MASK)
+ << STRTAB_BASE_CFG_SPLIT_SHIFT;
+ cfg->strtab_base_cfg = reg;
+
+ ret = arm_smmu_init_l1_strtab(smmu);
+ if (ret)
+ dma_free_coherent(smmu->dev,
+ cfg->num_l1_ents *
+ (STRTAB_L1_DESC_DWORDS << 3),
+ strtab,
+ cfg->strtab_dma);
+ return ret;
+}
+
+static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu)
+{
+ void *strtab;
+ u64 reg;
+ u32 size;
+ struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+ size = (1 << smmu->sid_bits) * (STRTAB_STE_DWORDS << 3);
+ strtab = dma_zalloc_coherent(smmu->dev, size, &cfg->strtab_dma,
+ GFP_KERNEL);
+ if (!strtab) {
+ dev_err(smmu->dev,
+ "failed to allocate linear stream table (%u bytes)\n",
+ size);
+ return -ENOMEM;
+ }
+ cfg->strtab = strtab;
+ cfg->num_l1_ents = 1 << smmu->sid_bits;
+
+ /* Configure strtab_base_cfg for a linear table covering all SIDs */
+ reg = STRTAB_BASE_CFG_FMT_LINEAR;
+ reg |= (smmu->sid_bits & STRTAB_BASE_CFG_LOG2SIZE_MASK)
+ << STRTAB_BASE_CFG_LOG2SIZE_SHIFT;
+ cfg->strtab_base_cfg = reg;
+
+ arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents);
+ return 0;
+}
+
+static int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
+{
+ u64 reg;
+ int ret;
+
+ if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
+ ret = arm_smmu_init_strtab_2lvl(smmu);
+ else
+ ret = arm_smmu_init_strtab_linear(smmu);
+
+ if (ret)
+ return ret;
+
+ /* Set the strtab base address */
+ reg = smmu->strtab_cfg.strtab_dma &
+ STRTAB_BASE_ADDR_MASK << STRTAB_BASE_ADDR_SHIFT;
+ reg |= STRTAB_BASE_RA;
+ smmu->strtab_cfg.strtab_base = reg;
+
+ /* Allocate the first VMID for stage-2 bypass STEs */
+ set_bit(0, smmu->vmid_map);
+ return 0;
+}
+
+static void arm_smmu_free_strtab(struct arm_smmu_device *smmu)
+{
+ struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+ u32 size = cfg->num_l1_ents;
+
+ if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+ arm_smmu_free_l2_strtab(smmu);
+ size *= STRTAB_L1_DESC_DWORDS << 3;
+ } else {
+ size *= STRTAB_STE_DWORDS * 3;
+ }
+
+ dma_free_coherent(smmu->dev, size, cfg->strtab, cfg->strtab_dma);
+}
+
+static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
+{
+ int ret;
+
+ ret = arm_smmu_init_queues(smmu);
+ if (ret)
+ return ret;
+
+ ret = arm_smmu_init_strtab(smmu);
+ if (ret)
+ goto out_free_queues;
+
+ return 0;
+
+out_free_queues:
+ arm_smmu_free_queues(smmu);
+ return ret;
+}
+
+static void arm_smmu_free_structures(struct arm_smmu_device *smmu)
+{
+ arm_smmu_free_strtab(smmu);
+ arm_smmu_free_queues(smmu);
+}
+
+static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
+ unsigned int reg_off, unsigned int ack_off)
+{
+ u32 reg;
+
+ writel_relaxed(val, smmu->base + reg_off);
+ return readl_relaxed_poll_timeout(smmu->base + ack_off, reg, reg == val,
+ 1, ARM_SMMU_POLL_TIMEOUT_US);
+}
+
+static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
+{
+ int ret, irq;
+
+ /* Disable IRQs first */
+ ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL,
+ ARM_SMMU_IRQ_CTRLACK);
+ if (ret) {
+ dev_err(smmu->dev, "failed to disable irqs\n");
+ return ret;
+ }
+
+ /* Clear the MSI address regs */
+ writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
+ writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
+
+ /* Request wired interrupt lines */
+ irq = smmu->evtq.q.irq;
+ if (irq) {
+ ret = devm_request_threaded_irq(smmu->dev, irq,
+ arm_smmu_evtq_handler,
+ arm_smmu_evtq_thread,
+ 0, "arm-smmu-v3-evtq", smmu);
+ if (IS_ERR_VALUE(ret))
+ dev_warn(smmu->dev, "failed to enable evtq irq\n");
+ }
+
+ irq = smmu->cmdq.q.irq;
+ if (irq) {
+ ret = devm_request_irq(smmu->dev, irq,
+ arm_smmu_cmdq_sync_handler, 0,
+ "arm-smmu-v3-cmdq-sync", smmu);
+ if (IS_ERR_VALUE(ret))
+ dev_warn(smmu->dev, "failed to enable cmdq-sync irq\n");
+ }
+
+ irq = smmu->gerr_irq;
+ if (irq) {
+ ret = devm_request_irq(smmu->dev, irq, arm_smmu_gerror_handler,
+ 0, "arm-smmu-v3-gerror", smmu);
+ if (IS_ERR_VALUE(ret))
+ dev_warn(smmu->dev, "failed to enable gerror irq\n");
+ }
+
+ if (smmu->features & ARM_SMMU_FEAT_PRI) {
+ writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
+
+ irq = smmu->priq.q.irq;
+ if (irq) {
+ ret = devm_request_threaded_irq(smmu->dev, irq,
+ arm_smmu_priq_handler,
+ arm_smmu_priq_thread,
+ 0, "arm-smmu-v3-priq",
+ smmu);
+ if (IS_ERR_VALUE(ret))
+ dev_warn(smmu->dev,
+ "failed to enable priq irq\n");
+ }
+ }
+
+ /* Enable interrupt generation on the SMMU */
+ ret = arm_smmu_write_reg_sync(smmu,
+ IRQ_CTRL_EVTQ_IRQEN |
+ IRQ_CTRL_GERROR_IRQEN,
+ ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK);
+ if (ret)
+ dev_warn(smmu->dev, "failed to enable irqs\n");
+
+ return 0;
+}
+
+static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
+{
+ int ret;
+
+ ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_CR0, ARM_SMMU_CR0ACK);
+ if (ret)
+ dev_err(smmu->dev, "failed to clear cr0\n");
+
+ return ret;
+}
+
+static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
+{
+ int ret;
+ u32 reg, enables;
+ struct arm_smmu_cmdq_ent cmd;
+
+ /* Clear CR0 and sync (disables SMMU and queue processing) */
+ reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
+ if (reg & CR0_SMMUEN)
+ dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
+
+ ret = arm_smmu_device_disable(smmu);
+ if (ret)
+ return ret;
+
+ /* CR1 (table and queue memory attributes) */
+ reg = (CR1_SH_ISH << CR1_TABLE_SH_SHIFT) |
+ (CR1_CACHE_WB << CR1_TABLE_OC_SHIFT) |
+ (CR1_CACHE_WB << CR1_TABLE_IC_SHIFT) |
+ (CR1_SH_ISH << CR1_QUEUE_SH_SHIFT) |
+ (CR1_CACHE_WB << CR1_QUEUE_OC_SHIFT) |
+ (CR1_CACHE_WB << CR1_QUEUE_IC_SHIFT);
+ writel_relaxed(reg, smmu->base + ARM_SMMU_CR1);
+
+ /* CR2 (random crap) */
+ reg = CR2_PTM | CR2_RECINVSID | CR2_E2H;
+ writel_relaxed(reg, smmu->base + ARM_SMMU_CR2);
+
+ /* Stream table */
+ writeq_relaxed(smmu->strtab_cfg.strtab_base,
+ smmu->base + ARM_SMMU_STRTAB_BASE);
+ writel_relaxed(smmu->strtab_cfg.strtab_base_cfg,
+ smmu->base + ARM_SMMU_STRTAB_BASE_CFG);
+
+ /* Command queue */
+ writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE);
+ writel_relaxed(smmu->cmdq.q.prod, smmu->base + ARM_SMMU_CMDQ_PROD);
+ writel_relaxed(smmu->cmdq.q.cons, smmu->base + ARM_SMMU_CMDQ_CONS);
+
+ enables = CR0_CMDQEN;
+ ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+ ARM_SMMU_CR0ACK);
+ if (ret) {
+ dev_err(smmu->dev, "failed to enable command queue\n");
+ return ret;
+ }
+
+ /* Invalidate any cached configuration */
+ cmd.opcode = CMDQ_OP_CFGI_ALL;
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+ cmd.opcode = CMDQ_OP_CMD_SYNC;
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+
+ /* Invalidate any stale TLB entries */
+ if (smmu->features & ARM_SMMU_FEAT_HYP) {
+ cmd.opcode = CMDQ_OP_TLBI_EL2_ALL;
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+ }
+
+ cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL;
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+ cmd.opcode = CMDQ_OP_CMD_SYNC;
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+
+ /* Event queue */
+ writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE);
+ writel_relaxed(smmu->evtq.q.prod, smmu->base + ARM_SMMU_EVTQ_PROD);
+ writel_relaxed(smmu->evtq.q.cons, smmu->base + ARM_SMMU_EVTQ_CONS);
+
+ enables |= CR0_EVTQEN;
+ ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+ ARM_SMMU_CR0ACK);
+ if (ret) {
+ dev_err(smmu->dev, "failed to enable event queue\n");
+ return ret;
+ }
+
+ /* PRI queue */
+ if (smmu->features & ARM_SMMU_FEAT_PRI) {
+ writeq_relaxed(smmu->priq.q.q_base,
+ smmu->base + ARM_SMMU_PRIQ_BASE);
+ writel_relaxed(smmu->priq.q.prod,
+ smmu->base + ARM_SMMU_PRIQ_PROD);
+ writel_relaxed(smmu->priq.q.cons,
+ smmu->base + ARM_SMMU_PRIQ_CONS);
+
+ enables |= CR0_PRIQEN;
+ ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+ ARM_SMMU_CR0ACK);
+ if (ret) {
+ dev_err(smmu->dev, "failed to enable PRI queue\n");
+ return ret;
+ }
+ }
+
+ ret = arm_smmu_setup_irqs(smmu);
+ if (ret) {
+ dev_err(smmu->dev, "failed to setup irqs\n");
+ return ret;
+ }
+
+ /* Enable the SMMU interface */
+ enables |= CR0_SMMUEN;
+ ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+ ARM_SMMU_CR0ACK);
+ if (ret) {
+ dev_err(smmu->dev, "failed to enable SMMU interface\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int arm_smmu_device_probe(struct arm_smmu_device *smmu)
+{
+ u32 reg;
+ bool coherent;
+ unsigned long pgsize_bitmap = 0;
+
+ /* IDR0 */
+ reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0);
+
+ /* 2-level structures */
+ if ((reg & IDR0_ST_LVL_MASK << IDR0_ST_LVL_SHIFT) == IDR0_ST_LVL_2LVL)
+ smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB;
+
+ if (reg & IDR0_CD2L)
+ smmu->features |= ARM_SMMU_FEAT_2_LVL_CDTAB;
+
+ /*
+ * Translation table endianness.
+ * We currently require the same endianness as the CPU, but this
+ * could be changed later by adding a new IO_PGTABLE_QUIRK.
+ */
+ switch (reg & IDR0_TTENDIAN_MASK << IDR0_TTENDIAN_SHIFT) {
+ case IDR0_TTENDIAN_MIXED:
+ smmu->features |= ARM_SMMU_FEAT_TT_LE | ARM_SMMU_FEAT_TT_BE;
+ break;
+#ifdef __BIG_ENDIAN
+ case IDR0_TTENDIAN_BE:
+ smmu->features |= ARM_SMMU_FEAT_TT_BE;
+ break;
+#else
+ case IDR0_TTENDIAN_LE:
+ smmu->features |= ARM_SMMU_FEAT_TT_LE;
+ break;
+#endif
+ default:
+ dev_err(smmu->dev, "unknown/unsupported TT endianness!\n");
+ return -ENXIO;
+ }
+
+ /* Boolean feature flags */
+ if (IS_ENABLED(CONFIG_PCI_PRI) && reg & IDR0_PRI)
+ smmu->features |= ARM_SMMU_FEAT_PRI;
+
+ if (IS_ENABLED(CONFIG_PCI_ATS) && reg & IDR0_ATS)
+ smmu->features |= ARM_SMMU_FEAT_ATS;
+
+ if (reg & IDR0_SEV)
+ smmu->features |= ARM_SMMU_FEAT_SEV;
+
+ if (reg & IDR0_MSI)
+ smmu->features |= ARM_SMMU_FEAT_MSI;
+
+ if (reg & IDR0_HYP)
+ smmu->features |= ARM_SMMU_FEAT_HYP;
+
+ /*
+ * The dma-coherent property is used in preference to the ID
+ * register, but warn on mismatch.
+ */
+ coherent = of_dma_is_coherent(smmu->dev->of_node);
+ if (coherent)
+ smmu->features |= ARM_SMMU_FEAT_COHERENCY;
+
+ if (!!(reg & IDR0_COHACC) != coherent)
+ dev_warn(smmu->dev, "IDR0.COHACC overridden by dma-coherent property (%s)\n",
+ coherent ? "true" : "false");
+
+ if (reg & IDR0_STALL_MODEL)
+ smmu->features |= ARM_SMMU_FEAT_STALLS;
+
+ if (reg & IDR0_S1P)
+ smmu->features |= ARM_SMMU_FEAT_TRANS_S1;
+
+ if (reg & IDR0_S2P)
+ smmu->features |= ARM_SMMU_FEAT_TRANS_S2;
+
+ if (!(reg & (IDR0_S1P | IDR0_S2P))) {
+ dev_err(smmu->dev, "no translation support!\n");
+ return -ENXIO;
+ }
+
+ /* We only support the AArch64 table format at present */
+ if ((reg & IDR0_TTF_MASK << IDR0_TTF_SHIFT) < IDR0_TTF_AARCH64) {
+ dev_err(smmu->dev, "AArch64 table format not supported!\n");
+ return -ENXIO;
+ }
+
+ /* ASID/VMID sizes */
+ smmu->asid_bits = reg & IDR0_ASID16 ? 16 : 8;
+ smmu->vmid_bits = reg & IDR0_VMID16 ? 16 : 8;
+
+ /* IDR1 */
+ reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
+ if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL)) {
+ dev_err(smmu->dev, "embedded implementation not supported\n");
+ return -ENXIO;
+ }
+
+ /* Queue sizes, capped at 4k */
+ smmu->cmdq.q.max_n_shift = min((u32)CMDQ_MAX_SZ_SHIFT,
+ reg >> IDR1_CMDQ_SHIFT & IDR1_CMDQ_MASK);
+ if (!smmu->cmdq.q.max_n_shift) {
+ /* Odd alignment restrictions on the base, so ignore for now */
+ dev_err(smmu->dev, "unit-length command queue not supported\n");
+ return -ENXIO;
+ }
+
+ smmu->evtq.q.max_n_shift = min((u32)EVTQ_MAX_SZ_SHIFT,
+ reg >> IDR1_EVTQ_SHIFT & IDR1_EVTQ_MASK);
+ smmu->priq.q.max_n_shift = min((u32)PRIQ_MAX_SZ_SHIFT,
+ reg >> IDR1_PRIQ_SHIFT & IDR1_PRIQ_MASK);
+
+ /* SID/SSID sizes */
+ smmu->ssid_bits = reg >> IDR1_SSID_SHIFT & IDR1_SSID_MASK;
+ smmu->sid_bits = reg >> IDR1_SID_SHIFT & IDR1_SID_MASK;
+
+ /* IDR5 */
+ reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
+
+ /* Maximum number of outstanding stalls */
+ smmu->evtq.max_stalls = reg >> IDR5_STALL_MAX_SHIFT
+ & IDR5_STALL_MAX_MASK;
+
+ /* Page sizes */
+ if (reg & IDR5_GRAN64K)
+ pgsize_bitmap |= SZ_64K | SZ_512M;
+ if (reg & IDR5_GRAN16K)
+ pgsize_bitmap |= SZ_16K | SZ_32M;
+ if (reg & IDR5_GRAN4K)
+ pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G;
+
+ arm_smmu_ops.pgsize_bitmap &= pgsize_bitmap;
+
+ /* Output address size */
+ switch (reg & IDR5_OAS_MASK << IDR5_OAS_SHIFT) {
+ case IDR5_OAS_32_BIT:
+ smmu->oas = 32;
+ break;
+ case IDR5_OAS_36_BIT:
+ smmu->oas = 36;
+ break;
+ case IDR5_OAS_40_BIT:
+ smmu->oas = 40;
+ break;
+ case IDR5_OAS_42_BIT:
+ smmu->oas = 42;
+ break;
+ case IDR5_OAS_44_BIT:
+ smmu->oas = 44;
+ break;
+ case IDR5_OAS_48_BIT:
+ smmu->oas = 48;
+ break;
+ default:
+ dev_err(smmu->dev, "unknown output address size!\n");
+ return -ENXIO;
+ }
+
+ /* Set the DMA mask for our table walker */
+ if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(smmu->oas)))
+ dev_warn(smmu->dev,
+ "failed to set DMA mask for table walker\n");
+
+ if (!smmu->ias)
+ smmu->ias = smmu->oas;
+
+ dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n",
+ smmu->ias, smmu->oas, smmu->features);
+ return 0;
+}
+
+static int arm_smmu_device_dt_probe(struct platform_device *pdev)
+{
+ int irq, ret;
+ struct resource *res;
+ struct arm_smmu_device *smmu;
+ struct device *dev = &pdev->dev;
+
+ smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
+ if (!smmu) {
+ dev_err(dev, "failed to allocate arm_smmu_device\n");
+ return -ENOMEM;
+ }
+ smmu->dev = dev;
+
+ /* Base address */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (resource_size(res) + 1 < SZ_128K) {
+ dev_err(dev, "MMIO region too small (%pr)\n", res);
+ return -EINVAL;
+ }
+
+ smmu->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(smmu->base))
+ return PTR_ERR(smmu->base);
+
+ /* Interrupt lines */
+ irq = platform_get_irq_byname(pdev, "eventq");
+ if (irq > 0)
+ smmu->evtq.q.irq = irq;
+
+ irq = platform_get_irq_byname(pdev, "priq");
+ if (irq > 0)
+ smmu->priq.q.irq = irq;
+
+ irq = platform_get_irq_byname(pdev, "cmdq-sync");
+ if (irq > 0)
+ smmu->cmdq.q.irq = irq;
+
+ irq = platform_get_irq_byname(pdev, "gerror");
+ if (irq > 0)
+ smmu->gerr_irq = irq;
+
+ /* Probe the h/w */
+ ret = arm_smmu_device_probe(smmu);
+ if (ret)
+ return ret;
+
+ /* Initialise in-memory data structures */
+ ret = arm_smmu_init_structures(smmu);
+ if (ret)
+ return ret;
+
+ /* Reset the device */
+ ret = arm_smmu_device_reset(smmu);
+ if (ret)
+ goto out_free_structures;
+
+ /* Record our private device structure */
+ INIT_LIST_HEAD(&smmu->list);
+ spin_lock(&arm_smmu_devices_lock);
+ list_add(&smmu->list, &arm_smmu_devices);
+ spin_unlock(&arm_smmu_devices_lock);
+ return 0;
+
+out_free_structures:
+ arm_smmu_free_structures(smmu);
+ return ret;
+}
+
+static int arm_smmu_device_remove(struct platform_device *pdev)
+{
+ struct arm_smmu_device *curr, *smmu = NULL;
+ struct device *dev = &pdev->dev;
+
+ spin_lock(&arm_smmu_devices_lock);
+ list_for_each_entry(curr, &arm_smmu_devices, list) {
+ if (curr->dev == dev) {
+ smmu = curr;
+ list_del(&smmu->list);
+ break;
+ }
+ }
+ spin_unlock(&arm_smmu_devices_lock);
+
+ if (!smmu)
+ return -ENODEV;
+
+ arm_smmu_device_disable(smmu);
+ arm_smmu_free_structures(smmu);
+ return 0;
+}
+
+static struct of_device_id arm_smmu_of_match[] = {
+ { .compatible = "arm,smmu-v3", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
+
+static struct platform_driver arm_smmu_driver = {
+ .driver = {
+ .name = "arm-smmu-v3",
+ .of_match_table = of_match_ptr(arm_smmu_of_match),
+ },
+ .probe = arm_smmu_device_dt_probe,
+ .remove = arm_smmu_device_remove,
+};
+
+static int __init arm_smmu_init(void)
+{
+ struct device_node *np;
+ int ret;
+
+ np = of_find_matching_node(NULL, arm_smmu_of_match);
+ if (!np)
+ return 0;
+
+ of_node_put(np);
+
+ ret = platform_driver_register(&arm_smmu_driver);
+ if (ret)
+ return ret;
+
+ return bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+}
+
+static void __exit arm_smmu_exit(void)
+{
+ return platform_driver_unregister(&arm_smmu_driver);
+}
+
+subsys_initcall(arm_smmu_init);
+module_exit(arm_smmu_exit);
+
+MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
+MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 9f7e1d34a32b..dce041b1c139 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -202,8 +202,7 @@
#define ARM_SMMU_CB_S1_TLBIVAL 0x620
#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630
#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638
-#define ARM_SMMU_CB_ATS1PR_LO 0x800
-#define ARM_SMMU_CB_ATS1PR_HI 0x804
+#define ARM_SMMU_CB_ATS1PR 0x800
#define ARM_SMMU_CB_ATSR 0x8f0
#define SCTLR_S1_ASIDPNE (1 << 12)
@@ -224,14 +223,7 @@
#define RESUME_TERMINATE (1 << 0)
#define TTBCR2_SEP_SHIFT 15
-#define TTBCR2_SEP_MASK 0x7
-
-#define TTBCR2_ADDR_32 0
-#define TTBCR2_ADDR_36 1
-#define TTBCR2_ADDR_40 2
-#define TTBCR2_ADDR_42 3
-#define TTBCR2_ADDR_44 4
-#define TTBCR2_ADDR_48 5
+#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
#define TTBRn_HI_ASID_SHIFT 16
@@ -254,7 +246,7 @@
#define FSYNR0_WNR (1 << 4)
static int force_stage;
-module_param_named(force_stage, force_stage, int, S_IRUGO | S_IWUSR);
+module_param_named(force_stage, force_stage, int, S_IRUGO);
MODULE_PARM_DESC(force_stage,
"Force SMMU mappings to be installed at a particular stage of translation. A value of '1' or '2' forces the corresponding stage. All other values are ignored (i.e. no stage is forced). Note that selecting a specific stage will disable support for nested translation.");
@@ -793,26 +785,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR);
if (smmu->version > ARM_SMMU_V1) {
reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
- switch (smmu->va_size) {
- case 32:
- reg |= (TTBCR2_ADDR_32 << TTBCR2_SEP_SHIFT);
- break;
- case 36:
- reg |= (TTBCR2_ADDR_36 << TTBCR2_SEP_SHIFT);
- break;
- case 40:
- reg |= (TTBCR2_ADDR_40 << TTBCR2_SEP_SHIFT);
- break;
- case 42:
- reg |= (TTBCR2_ADDR_42 << TTBCR2_SEP_SHIFT);
- break;
- case 44:
- reg |= (TTBCR2_ADDR_44 << TTBCR2_SEP_SHIFT);
- break;
- case 48:
- reg |= (TTBCR2_ADDR_48 << TTBCR2_SEP_SHIFT);
- break;
- }
+ reg |= TTBCR2_SEP_UPSTREAM;
writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2);
}
} else {
@@ -1255,18 +1228,18 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
void __iomem *cb_base;
u32 tmp;
u64 phys;
+ unsigned long va;
cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
- if (smmu->version == 1) {
- u32 reg = iova & ~0xfff;
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_LO);
- } else {
- u32 reg = iova & ~0xfff;
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_LO);
- reg = ((u64)iova & ~0xfff) >> 32;
- writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_HI);
- }
+ /* ATS1 registers can only be written atomically */
+ va = iova & ~0xfffUL;
+#ifdef CONFIG_64BIT
+ if (smmu->version == ARM_SMMU_V2)
+ writeq_relaxed(va, cb_base + ARM_SMMU_CB_ATS1PR);
+ else
+#endif
+ writel_relaxed(va, cb_base + ARM_SMMU_CB_ATS1PR);
if (readl_poll_timeout_atomic(cb_base + ARM_SMMU_CB_ATSR, tmp,
!(tmp & ATSR_ACTIVE), 5, 50)) {
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 9847613085e1..c9db04d4ef39 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -26,7 +26,7 @@
* These routines are used by both DMA-remapping and Interrupt-remapping
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* has to precede printk.h */
+#define pr_fmt(fmt) "DMAR: " fmt
#include <linux/pci.h>
#include <linux/dmar.h>
@@ -555,7 +555,7 @@ static int dmar_walk_remapping_entries(struct acpi_dmar_header *start,
break;
} else if (next > end) {
/* Avoid passing table end */
- pr_warn(FW_BUG "record passes table end\n");
+ pr_warn(FW_BUG "Record passes table end\n");
ret = -EINVAL;
break;
}
@@ -802,7 +802,7 @@ int __init dmar_table_init(void)
ret = parse_dmar_table();
if (ret < 0) {
if (ret != -ENODEV)
- pr_info("parse DMAR table failure.\n");
+ pr_info("Parse DMAR table failure.\n");
} else if (list_empty(&dmar_drhd_units)) {
pr_info("No DMAR devices found\n");
ret = -ENODEV;
@@ -847,7 +847,7 @@ dmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg)
else
addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
if (!addr) {
- pr_warn("IOMMU: can't validate: %llx\n", drhd->address);
+ pr_warn("Can't validate DRHD address: %llx\n", drhd->address);
return -EINVAL;
}
@@ -921,14 +921,14 @@ static int map_iommu(struct intel_iommu *iommu, u64 phys_addr)
iommu->reg_size = VTD_PAGE_SIZE;
if (!request_mem_region(iommu->reg_phys, iommu->reg_size, iommu->name)) {
- pr_err("IOMMU: can't reserve memory\n");
+ pr_err("Can't reserve memory\n");
err = -EBUSY;
goto out;
}
iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size);
if (!iommu->reg) {
- pr_err("IOMMU: can't map the region\n");
+ pr_err("Can't map the region\n");
err = -ENOMEM;
goto release;
}
@@ -952,13 +952,13 @@ static int map_iommu(struct intel_iommu *iommu, u64 phys_addr)
iommu->reg_size = map_size;
if (!request_mem_region(iommu->reg_phys, iommu->reg_size,
iommu->name)) {
- pr_err("IOMMU: can't reserve memory\n");
+ pr_err("Can't reserve memory\n");
err = -EBUSY;
goto out;
}
iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size);
if (!iommu->reg) {
- pr_err("IOMMU: can't map the region\n");
+ pr_err("Can't map the region\n");
err = -ENOMEM;
goto release;
}
@@ -1014,14 +1014,14 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
return -ENOMEM;
if (dmar_alloc_seq_id(iommu) < 0) {
- pr_err("IOMMU: failed to allocate seq_id\n");
+ pr_err("Failed to allocate seq_id\n");
err = -ENOSPC;
goto error;
}
err = map_iommu(iommu, drhd->reg_base_addr);
if (err) {
- pr_err("IOMMU: failed to map %s\n", iommu->name);
+ pr_err("Failed to map %s\n", iommu->name);
goto error_free_seq_id;
}
@@ -1045,8 +1045,8 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
iommu->node = -1;
ver = readl(iommu->reg + DMAR_VER_REG);
- pr_info("IOMMU %d: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n",
- iommu->seq_id,
+ pr_info("%s: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n",
+ iommu->name,
(unsigned long long)drhd->reg_base_addr,
DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
(unsigned long long)iommu->cap,
@@ -1087,8 +1087,8 @@ static void free_iommu(struct intel_iommu *iommu)
if (iommu->irq) {
free_irq(iommu->irq, iommu);
- irq_set_handler_data(iommu->irq, NULL);
dmar_free_hwirq(iommu->irq);
+ iommu->irq = 0;
}
if (iommu->qi) {
@@ -1642,26 +1642,17 @@ int dmar_set_interrupt(struct intel_iommu *iommu)
if (iommu->irq)
return 0;
- irq = dmar_alloc_hwirq();
- if (irq <= 0) {
- pr_err("IOMMU: no free vectors\n");
+ irq = dmar_alloc_hwirq(iommu->seq_id, iommu->node, iommu);
+ if (irq > 0) {
+ iommu->irq = irq;
+ } else {
+ pr_err("No free IRQ vectors\n");
return -EINVAL;
}
- irq_set_handler_data(irq, iommu);
- iommu->irq = irq;
-
- ret = arch_setup_dmar_msi(irq);
- if (ret) {
- irq_set_handler_data(irq, NULL);
- iommu->irq = 0;
- dmar_free_hwirq(irq);
- return ret;
- }
-
ret = request_irq(irq, dmar_fault, IRQF_NO_THREAD, iommu->name, iommu);
if (ret)
- pr_err("IOMMU: can't request irq\n");
+ pr_err("Can't request irq\n");
return ret;
}
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 3e898504a7c4..97c41b8ab5d9 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -12,21 +12,22 @@
#define DEBUG
#endif
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/pm_runtime.h>
#include <linux/clk.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
-#include <linux/mm.h>
+#include <linux/io.h>
#include <linux/iommu.h>
-#include <linux/errno.h>
+#include <linux/interrupt.h>
#include <linux/list.h>
-#include <linux/memblock.h>
-#include <linux/export.h>
+#include <linux/of.h>
+#include <linux/of_iommu.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
#include <asm/cacheflush.h>
+#include <asm/dma-iommu.h>
#include <asm/pgtable.h>
typedef u32 sysmmu_iova_t;
@@ -184,35 +185,50 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
"UNKNOWN FAULT"
};
-/* attached to dev.archdata.iommu of the master device */
+/*
+ * This structure is attached to dev.archdata.iommu of the master device
+ * on device add, contains a list of SYSMMU controllers defined by device tree,
+ * which are bound to given master device. It is usually referenced by 'owner'
+ * pointer.
+*/
struct exynos_iommu_owner {
- struct list_head client; /* entry of exynos_iommu_domain.clients */
- struct device *dev;
- struct device *sysmmu;
- struct iommu_domain *domain;
- void *vmm_data; /* IO virtual memory manager's data */
- spinlock_t lock; /* Lock to preserve consistency of System MMU */
+ struct list_head controllers; /* list of sysmmu_drvdata.owner_node */
};
+/*
+ * This structure exynos specific generalization of struct iommu_domain.
+ * It contains list of SYSMMU controllers from all master devices, which has
+ * been attached to this domain and page tables of IO address space defined by
+ * it. It is usually referenced by 'domain' pointer.
+ */
struct exynos_iommu_domain {
- struct list_head clients; /* list of sysmmu_drvdata.node */
- sysmmu_pte_t *pgtable; /* lv1 page table, 16KB */
- short *lv2entcnt; /* free lv2 entry counter for each section */
- spinlock_t lock; /* lock for this structure */
- spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */
+ struct list_head clients; /* list of sysmmu_drvdata.domain_node */
+ sysmmu_pte_t *pgtable; /* lv1 page table, 16KB */
+ short *lv2entcnt; /* free lv2 entry counter for each section */
+ spinlock_t lock; /* lock for modyfying list of clients */
+ spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */
struct iommu_domain domain; /* generic domain data structure */
};
+/*
+ * This structure hold all data of a single SYSMMU controller, this includes
+ * hw resources like registers and clocks, pointers and list nodes to connect
+ * it to all other structures, internal state and parameters read from device
+ * tree. It is usually referenced by 'data' pointer.
+ */
struct sysmmu_drvdata {
- struct device *sysmmu; /* System MMU's device descriptor */
- struct device *master; /* Owner of system MMU */
- void __iomem *sfrbase;
- struct clk *clk;
- struct clk *clk_master;
- int activations;
- spinlock_t lock;
- struct iommu_domain *domain;
- phys_addr_t pgtable;
+ struct device *sysmmu; /* SYSMMU controller device */
+ struct device *master; /* master device (owner) */
+ void __iomem *sfrbase; /* our registers */
+ struct clk *clk; /* SYSMMU's clock */
+ struct clk *clk_master; /* master's device clock */
+ int activations; /* number of calls to sysmmu_enable */
+ spinlock_t lock; /* lock for modyfying state */
+ struct exynos_iommu_domain *domain; /* domain we belong to */
+ struct list_head domain_node; /* node for domain clients list */
+ struct list_head owner_node; /* node for owner controllers list */
+ phys_addr_t pgtable; /* assigned page table structure */
+ unsigned int version; /* our version */
};
static struct exynos_iommu_domain *to_exynos_domain(struct iommu_domain *dom)
@@ -244,11 +260,6 @@ static void sysmmu_unblock(void __iomem *sfrbase)
__raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL);
}
-static unsigned int __raw_sysmmu_version(struct sysmmu_drvdata *data)
-{
- return MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION));
-}
-
static bool sysmmu_block(void __iomem *sfrbase)
{
int i = 120;
@@ -345,7 +356,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)
show_fault_information(dev_name(data->sysmmu),
itype, base, addr);
if (data->domain)
- ret = report_iommu_fault(data->domain,
+ ret = report_iommu_fault(&data->domain->domain,
data->master, addr, itype);
}
@@ -408,7 +419,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data)
unsigned int cfg = CFG_LRU | CFG_QOS(15);
unsigned int ver;
- ver = __raw_sysmmu_version(data);
+ ver = MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION));
if (MMU_MAJ_VER(ver) == 3) {
if (MMU_MIN_VER(ver) >= 2) {
cfg |= CFG_FLPDCACHE;
@@ -422,6 +433,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data)
}
__raw_writel(cfg, data->sfrbase + REG_MMU_CFG);
+ data->version = ver;
}
static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data)
@@ -442,8 +454,8 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data)
clk_disable(data->clk_master);
}
-static int __sysmmu_enable(struct sysmmu_drvdata *data,
- phys_addr_t pgtable, struct iommu_domain *domain)
+static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable,
+ struct exynos_iommu_domain *domain)
{
int ret = 0;
unsigned long flags;
@@ -470,77 +482,17 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data,
return ret;
}
-/* __exynos_sysmmu_enable: Enables System MMU
- *
- * returns -error if an error occurred and System MMU is not enabled,
- * 0 if the System MMU has been just enabled and 1 if System MMU was already
- * enabled before.
- */
-static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable,
- struct iommu_domain *domain)
-{
- int ret = 0;
- unsigned long flags;
- struct exynos_iommu_owner *owner = dev->archdata.iommu;
- struct sysmmu_drvdata *data;
-
- BUG_ON(!has_sysmmu(dev));
-
- spin_lock_irqsave(&owner->lock, flags);
-
- data = dev_get_drvdata(owner->sysmmu);
-
- ret = __sysmmu_enable(data, pgtable, domain);
- if (ret >= 0)
- data->master = dev;
-
- spin_unlock_irqrestore(&owner->lock, flags);
-
- return ret;
-}
-
-int exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable)
-{
- BUG_ON(!memblock_is_memory(pgtable));
-
- return __exynos_sysmmu_enable(dev, pgtable, NULL);
-}
-
-static bool exynos_sysmmu_disable(struct device *dev)
-{
- unsigned long flags;
- bool disabled = true;
- struct exynos_iommu_owner *owner = dev->archdata.iommu;
- struct sysmmu_drvdata *data;
-
- BUG_ON(!has_sysmmu(dev));
-
- spin_lock_irqsave(&owner->lock, flags);
-
- data = dev_get_drvdata(owner->sysmmu);
-
- disabled = __sysmmu_disable(data);
- if (disabled)
- data->master = NULL;
-
- spin_unlock_irqrestore(&owner->lock, flags);
-
- return disabled;
-}
-
static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data,
sysmmu_iova_t iova)
{
- if (__raw_sysmmu_version(data) == MAKE_MMU_VER(3, 3))
+ if (data->version == MAKE_MMU_VER(3, 3))
__raw_writel(iova | 0x1, data->sfrbase + REG_MMU_FLUSH_ENTRY);
}
-static void sysmmu_tlb_invalidate_flpdcache(struct device *dev,
+static void sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data,
sysmmu_iova_t iova)
{
unsigned long flags;
- struct exynos_iommu_owner *owner = dev->archdata.iommu;
- struct sysmmu_drvdata *data = dev_get_drvdata(owner->sysmmu);
if (!IS_ERR(data->clk_master))
clk_enable(data->clk_master);
@@ -554,14 +506,10 @@ static void sysmmu_tlb_invalidate_flpdcache(struct device *dev,
clk_disable(data->clk_master);
}
-static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova,
- size_t size)
+static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data,
+ sysmmu_iova_t iova, size_t size)
{
- struct exynos_iommu_owner *owner = dev->archdata.iommu;
unsigned long flags;
- struct sysmmu_drvdata *data;
-
- data = dev_get_drvdata(owner->sysmmu);
spin_lock_irqsave(&data->lock, flags);
if (is_sysmmu_active(data)) {
@@ -580,7 +528,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova,
* 1MB page can be cached in one of all sets.
* 64KB page can be one of 16 consecutive sets.
*/
- if (MMU_MAJ_VER(__raw_sysmmu_version(data)) == 2)
+ if (MMU_MAJ_VER(data->version) == 2)
num_inv = min_t(unsigned int, size / PAGE_SIZE, 64);
if (sysmmu_block(data->sfrbase)) {
@@ -591,32 +539,8 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova,
if (!IS_ERR(data->clk_master))
clk_disable(data->clk_master);
} else {
- dev_dbg(dev, "disabled. Skipping TLB invalidation @ %#x\n",
- iova);
- }
- spin_unlock_irqrestore(&data->lock, flags);
-}
-
-void exynos_sysmmu_tlb_invalidate(struct device *dev)
-{
- struct exynos_iommu_owner *owner = dev->archdata.iommu;
- unsigned long flags;
- struct sysmmu_drvdata *data;
-
- data = dev_get_drvdata(owner->sysmmu);
-
- spin_lock_irqsave(&data->lock, flags);
- if (is_sysmmu_active(data)) {
- if (!IS_ERR(data->clk_master))
- clk_enable(data->clk_master);
- if (sysmmu_block(data->sfrbase)) {
- __sysmmu_tlb_invalidate(data->sfrbase);
- sysmmu_unblock(data->sfrbase);
- }
- if (!IS_ERR(data->clk_master))
- clk_disable(data->clk_master);
- } else {
- dev_dbg(dev, "disabled. Skipping TLB invalidation\n");
+ dev_dbg(data->master,
+ "disabled. Skipping TLB invalidation @ %#x\n", iova);
}
spin_unlock_irqrestore(&data->lock, flags);
}
@@ -682,6 +606,36 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int exynos_sysmmu_suspend(struct device *dev)
+{
+ struct sysmmu_drvdata *data = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "suspend\n");
+ if (is_sysmmu_active(data)) {
+ __sysmmu_disable_nocount(data);
+ pm_runtime_put(dev);
+ }
+ return 0;
+}
+
+static int exynos_sysmmu_resume(struct device *dev)
+{
+ struct sysmmu_drvdata *data = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "resume\n");
+ if (is_sysmmu_active(data)) {
+ pm_runtime_get_sync(dev);
+ __sysmmu_enable_nocount(data);
+ }
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops sysmmu_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_sysmmu_suspend, exynos_sysmmu_resume)
+};
+
static const struct of_device_id sysmmu_of_match[] __initconst = {
{ .compatible = "samsung,exynos-sysmmu", },
{ },
@@ -692,6 +646,7 @@ static struct platform_driver exynos_sysmmu_driver __refdata = {
.driver = {
.name = "exynos-sysmmu",
.of_match_table = sysmmu_of_match,
+ .pm = &sysmmu_pm_ops,
}
};
@@ -704,104 +659,108 @@ static inline void pgtable_flush(void *vastart, void *vaend)
static struct iommu_domain *exynos_iommu_domain_alloc(unsigned type)
{
- struct exynos_iommu_domain *exynos_domain;
+ struct exynos_iommu_domain *domain;
int i;
if (type != IOMMU_DOMAIN_UNMANAGED)
return NULL;
- exynos_domain = kzalloc(sizeof(*exynos_domain), GFP_KERNEL);
- if (!exynos_domain)
+ domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+ if (!domain)
return NULL;
- exynos_domain->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2);
- if (!exynos_domain->pgtable)
+ domain->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2);
+ if (!domain->pgtable)
goto err_pgtable;
- exynos_domain->lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
- if (!exynos_domain->lv2entcnt)
+ domain->lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
+ if (!domain->lv2entcnt)
goto err_counter;
/* Workaround for System MMU v3.3 to prevent caching 1MiB mapping */
for (i = 0; i < NUM_LV1ENTRIES; i += 8) {
- exynos_domain->pgtable[i + 0] = ZERO_LV2LINK;
- exynos_domain->pgtable[i + 1] = ZERO_LV2LINK;
- exynos_domain->pgtable[i + 2] = ZERO_LV2LINK;
- exynos_domain->pgtable[i + 3] = ZERO_LV2LINK;
- exynos_domain->pgtable[i + 4] = ZERO_LV2LINK;
- exynos_domain->pgtable[i + 5] = ZERO_LV2LINK;
- exynos_domain->pgtable[i + 6] = ZERO_LV2LINK;
- exynos_domain->pgtable[i + 7] = ZERO_LV2LINK;
+ domain->pgtable[i + 0] = ZERO_LV2LINK;
+ domain->pgtable[i + 1] = ZERO_LV2LINK;
+ domain->pgtable[i + 2] = ZERO_LV2LINK;
+ domain->pgtable[i + 3] = ZERO_LV2LINK;
+ domain->pgtable[i + 4] = ZERO_LV2LINK;
+ domain->pgtable[i + 5] = ZERO_LV2LINK;
+ domain->pgtable[i + 6] = ZERO_LV2LINK;
+ domain->pgtable[i + 7] = ZERO_LV2LINK;
}
- pgtable_flush(exynos_domain->pgtable, exynos_domain->pgtable + NUM_LV1ENTRIES);
+ pgtable_flush(domain->pgtable, domain->pgtable + NUM_LV1ENTRIES);
- spin_lock_init(&exynos_domain->lock);
- spin_lock_init(&exynos_domain->pgtablelock);
- INIT_LIST_HEAD(&exynos_domain->clients);
+ spin_lock_init(&domain->lock);
+ spin_lock_init(&domain->pgtablelock);
+ INIT_LIST_HEAD(&domain->clients);
- exynos_domain->domain.geometry.aperture_start = 0;
- exynos_domain->domain.geometry.aperture_end = ~0UL;
- exynos_domain->domain.geometry.force_aperture = true;
+ domain->domain.geometry.aperture_start = 0;
+ domain->domain.geometry.aperture_end = ~0UL;
+ domain->domain.geometry.force_aperture = true;
- return &exynos_domain->domain;
+ return &domain->domain;
err_counter:
- free_pages((unsigned long)exynos_domain->pgtable, 2);
+ free_pages((unsigned long)domain->pgtable, 2);
err_pgtable:
- kfree(exynos_domain);
+ kfree(domain);
return NULL;
}
-static void exynos_iommu_domain_free(struct iommu_domain *domain)
+static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain)
{
- struct exynos_iommu_domain *priv = to_exynos_domain(domain);
- struct exynos_iommu_owner *owner;
+ struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
+ struct sysmmu_drvdata *data, *next;
unsigned long flags;
int i;
- WARN_ON(!list_empty(&priv->clients));
+ WARN_ON(!list_empty(&domain->clients));
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&domain->lock, flags);
- list_for_each_entry(owner, &priv->clients, client) {
- while (!exynos_sysmmu_disable(owner->dev))
- ; /* until System MMU is actually disabled */
+ list_for_each_entry_safe(data, next, &domain->clients, domain_node) {
+ if (__sysmmu_disable(data))
+ data->master = NULL;
+ list_del_init(&data->domain_node);
}
- while (!list_empty(&priv->clients))
- list_del_init(priv->clients.next);
-
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&domain->lock, flags);
for (i = 0; i < NUM_LV1ENTRIES; i++)
- if (lv1ent_page(priv->pgtable + i))
+ if (lv1ent_page(domain->pgtable + i))
kmem_cache_free(lv2table_kmem_cache,
- phys_to_virt(lv2table_base(priv->pgtable + i)));
+ phys_to_virt(lv2table_base(domain->pgtable + i)));
- free_pages((unsigned long)priv->pgtable, 2);
- free_pages((unsigned long)priv->lv2entcnt, 1);
- kfree(priv);
+ free_pages((unsigned long)domain->pgtable, 2);
+ free_pages((unsigned long)domain->lv2entcnt, 1);
+ kfree(domain);
}
-static int exynos_iommu_attach_device(struct iommu_domain *domain,
+static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
struct device *dev)
{
struct exynos_iommu_owner *owner = dev->archdata.iommu;
- struct exynos_iommu_domain *priv = to_exynos_domain(domain);
- phys_addr_t pagetable = virt_to_phys(priv->pgtable);
+ struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
+ struct sysmmu_drvdata *data;
+ phys_addr_t pagetable = virt_to_phys(domain->pgtable);
unsigned long flags;
- int ret;
+ int ret = -ENODEV;
- spin_lock_irqsave(&priv->lock, flags);
+ if (!has_sysmmu(dev))
+ return -ENODEV;
- ret = __exynos_sysmmu_enable(dev, pagetable, domain);
- if (ret == 0) {
- list_add_tail(&owner->client, &priv->clients);
- owner->domain = domain;
- }
+ list_for_each_entry(data, &owner->controllers, owner_node) {
+ pm_runtime_get_sync(data->sysmmu);
+ ret = __sysmmu_enable(data, pagetable, domain);
+ if (ret >= 0) {
+ data->master = dev;
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_lock_irqsave(&domain->lock, flags);
+ list_add_tail(&data->domain_node, &domain->clients);
+ spin_unlock_irqrestore(&domain->lock, flags);
+ }
+ }
if (ret < 0) {
dev_err(dev, "%s: Failed to attach IOMMU with pgtable %pa\n",
@@ -815,36 +774,39 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain,
return ret;
}
-static void exynos_iommu_detach_device(struct iommu_domain *domain,
+static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain,
struct device *dev)
{
- struct exynos_iommu_owner *owner;
- struct exynos_iommu_domain *priv = to_exynos_domain(domain);
- phys_addr_t pagetable = virt_to_phys(priv->pgtable);
+ struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
+ phys_addr_t pagetable = virt_to_phys(domain->pgtable);
+ struct sysmmu_drvdata *data, *next;
unsigned long flags;
+ bool found = false;
- spin_lock_irqsave(&priv->lock, flags);
+ if (!has_sysmmu(dev))
+ return;
- list_for_each_entry(owner, &priv->clients, client) {
- if (owner == dev->archdata.iommu) {
- if (exynos_sysmmu_disable(dev)) {
- list_del_init(&owner->client);
- owner->domain = NULL;
+ spin_lock_irqsave(&domain->lock, flags);
+ list_for_each_entry_safe(data, next, &domain->clients, domain_node) {
+ if (data->master == dev) {
+ if (__sysmmu_disable(data)) {
+ data->master = NULL;
+ list_del_init(&data->domain_node);
}
- break;
+ pm_runtime_put(data->sysmmu);
+ found = true;
}
}
+ spin_unlock_irqrestore(&domain->lock, flags);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- if (owner == dev->archdata.iommu)
+ if (found)
dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n",
__func__, &pagetable);
else
dev_err(dev, "%s: No IOMMU is attached\n", __func__);
}
-static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv,
+static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain,
sysmmu_pte_t *sent, sysmmu_iova_t iova, short *pgcounter)
{
if (lv1ent_section(sent)) {
@@ -862,6 +824,7 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv,
return ERR_PTR(-ENOMEM);
*sent = mk_lv1ent_page(virt_to_phys(pent));
+ kmemleak_ignore(pent);
*pgcounter = NUM_LV2ENTRIES;
pgtable_flush(pent, pent + NUM_LV2ENTRIES);
pgtable_flush(sent, sent + 1);
@@ -884,20 +847,19 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv,
* not currently mapped.
*/
if (need_flush_flpd_cache) {
- struct exynos_iommu_owner *owner;
+ struct sysmmu_drvdata *data;
- spin_lock(&priv->lock);
- list_for_each_entry(owner, &priv->clients, client)
- sysmmu_tlb_invalidate_flpdcache(
- owner->dev, iova);
- spin_unlock(&priv->lock);
+ spin_lock(&domain->lock);
+ list_for_each_entry(data, &domain->clients, domain_node)
+ sysmmu_tlb_invalidate_flpdcache(data, iova);
+ spin_unlock(&domain->lock);
}
}
return page_entry(sent, iova);
}
-static int lv1set_section(struct exynos_iommu_domain *priv,
+static int lv1set_section(struct exynos_iommu_domain *domain,
sysmmu_pte_t *sent, sysmmu_iova_t iova,
phys_addr_t paddr, short *pgcnt)
{
@@ -922,17 +884,17 @@ static int lv1set_section(struct exynos_iommu_domain *priv,
pgtable_flush(sent, sent + 1);
- spin_lock(&priv->lock);
+ spin_lock(&domain->lock);
if (lv1ent_page_zero(sent)) {
- struct exynos_iommu_owner *owner;
+ struct sysmmu_drvdata *data;
/*
* Flushing FLPD cache in System MMU v3.3 that may cache a FLPD
* entry by speculative prefetch of SLPD which has no mapping.
*/
- list_for_each_entry(owner, &priv->clients, client)
- sysmmu_tlb_invalidate_flpdcache(owner->dev, iova);
+ list_for_each_entry(data, &domain->clients, domain_node)
+ sysmmu_tlb_invalidate_flpdcache(data, iova);
}
- spin_unlock(&priv->lock);
+ spin_unlock(&domain->lock);
return 0;
}
@@ -992,74 +954,75 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size,
* than or equal to 128KiB.
* - Start address of an I/O virtual region must be aligned by 128KiB.
*/
-static int exynos_iommu_map(struct iommu_domain *domain, unsigned long l_iova,
- phys_addr_t paddr, size_t size, int prot)
+static int exynos_iommu_map(struct iommu_domain *iommu_domain,
+ unsigned long l_iova, phys_addr_t paddr, size_t size,
+ int prot)
{
- struct exynos_iommu_domain *priv = to_exynos_domain(domain);
+ struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
sysmmu_pte_t *entry;
sysmmu_iova_t iova = (sysmmu_iova_t)l_iova;
unsigned long flags;
int ret = -ENOMEM;
- BUG_ON(priv->pgtable == NULL);
+ BUG_ON(domain->pgtable == NULL);
- spin_lock_irqsave(&priv->pgtablelock, flags);
+ spin_lock_irqsave(&domain->pgtablelock, flags);
- entry = section_entry(priv->pgtable, iova);
+ entry = section_entry(domain->pgtable, iova);
if (size == SECT_SIZE) {
- ret = lv1set_section(priv, entry, iova, paddr,
- &priv->lv2entcnt[lv1ent_offset(iova)]);
+ ret = lv1set_section(domain, entry, iova, paddr,
+ &domain->lv2entcnt[lv1ent_offset(iova)]);
} else {
sysmmu_pte_t *pent;
- pent = alloc_lv2entry(priv, entry, iova,
- &priv->lv2entcnt[lv1ent_offset(iova)]);
+ pent = alloc_lv2entry(domain, entry, iova,
+ &domain->lv2entcnt[lv1ent_offset(iova)]);
if (IS_ERR(pent))
ret = PTR_ERR(pent);
else
ret = lv2set_page(pent, paddr, size,
- &priv->lv2entcnt[lv1ent_offset(iova)]);
+ &domain->lv2entcnt[lv1ent_offset(iova)]);
}
if (ret)
pr_err("%s: Failed(%d) to map %#zx bytes @ %#x\n",
__func__, ret, size, iova);
- spin_unlock_irqrestore(&priv->pgtablelock, flags);
+ spin_unlock_irqrestore(&domain->pgtablelock, flags);
return ret;
}
-static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *priv,
- sysmmu_iova_t iova, size_t size)
+static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *domain,
+ sysmmu_iova_t iova, size_t size)
{
- struct exynos_iommu_owner *owner;
+ struct sysmmu_drvdata *data;
unsigned long flags;
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&domain->lock, flags);
- list_for_each_entry(owner, &priv->clients, client)
- sysmmu_tlb_invalidate_entry(owner->dev, iova, size);
+ list_for_each_entry(data, &domain->clients, domain_node)
+ sysmmu_tlb_invalidate_entry(data, iova, size);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&domain->lock, flags);
}
-static size_t exynos_iommu_unmap(struct iommu_domain *domain,
- unsigned long l_iova, size_t size)
+static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain,
+ unsigned long l_iova, size_t size)
{
- struct exynos_iommu_domain *priv = to_exynos_domain(domain);
+ struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
sysmmu_iova_t iova = (sysmmu_iova_t)l_iova;
sysmmu_pte_t *ent;
size_t err_pgsize;
unsigned long flags;
- BUG_ON(priv->pgtable == NULL);
+ BUG_ON(domain->pgtable == NULL);
- spin_lock_irqsave(&priv->pgtablelock, flags);
+ spin_lock_irqsave(&domain->pgtablelock, flags);
- ent = section_entry(priv->pgtable, iova);
+ ent = section_entry(domain->pgtable, iova);
if (lv1ent_section(ent)) {
if (WARN_ON(size < SECT_SIZE)) {
@@ -1093,7 +1056,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain,
*ent = 0;
size = SPAGE_SIZE;
pgtable_flush(ent, ent + 1);
- priv->lv2entcnt[lv1ent_offset(iova)] += 1;
+ domain->lv2entcnt[lv1ent_offset(iova)] += 1;
goto done;
}
@@ -1107,15 +1070,15 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain,
pgtable_flush(ent, ent + SPAGES_PER_LPAGE);
size = LPAGE_SIZE;
- priv->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE;
+ domain->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE;
done:
- spin_unlock_irqrestore(&priv->pgtablelock, flags);
+ spin_unlock_irqrestore(&domain->pgtablelock, flags);
- exynos_iommu_tlb_invalidate_entry(priv, iova, size);
+ exynos_iommu_tlb_invalidate_entry(domain, iova, size);
return size;
err:
- spin_unlock_irqrestore(&priv->pgtablelock, flags);
+ spin_unlock_irqrestore(&domain->pgtablelock, flags);
pr_err("%s: Failed: size(%#zx) @ %#x is smaller than page size %#zx\n",
__func__, size, iova, err_pgsize);
@@ -1123,17 +1086,17 @@ err:
return 0;
}
-static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain,
+static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain,
dma_addr_t iova)
{
- struct exynos_iommu_domain *priv = to_exynos_domain(domain);
+ struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
sysmmu_pte_t *entry;
unsigned long flags;
phys_addr_t phys = 0;
- spin_lock_irqsave(&priv->pgtablelock, flags);
+ spin_lock_irqsave(&domain->pgtablelock, flags);
- entry = section_entry(priv->pgtable, iova);
+ entry = section_entry(domain->pgtable, iova);
if (lv1ent_section(entry)) {
phys = section_phys(entry) + section_offs(iova);
@@ -1146,7 +1109,7 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain,
phys = spage_phys(entry) + spage_offs(iova);
}
- spin_unlock_irqrestore(&priv->pgtablelock, flags);
+ spin_unlock_irqrestore(&domain->pgtablelock, flags);
return phys;
}
@@ -1156,6 +1119,9 @@ static int exynos_iommu_add_device(struct device *dev)
struct iommu_group *group;
int ret;
+ if (!has_sysmmu(dev))
+ return -ENODEV;
+
group = iommu_group_get(dev);
if (!group) {
@@ -1174,10 +1140,40 @@ static int exynos_iommu_add_device(struct device *dev)
static void exynos_iommu_remove_device(struct device *dev)
{
+ if (!has_sysmmu(dev))
+ return;
+
iommu_group_remove_device(dev);
}
-static const struct iommu_ops exynos_iommu_ops = {
+static int exynos_iommu_of_xlate(struct device *dev,
+ struct of_phandle_args *spec)
+{
+ struct exynos_iommu_owner *owner = dev->archdata.iommu;
+ struct platform_device *sysmmu = of_find_device_by_node(spec->np);
+ struct sysmmu_drvdata *data;
+
+ if (!sysmmu)
+ return -ENODEV;
+
+ data = platform_get_drvdata(sysmmu);
+ if (!data)
+ return -ENODEV;
+
+ if (!owner) {
+ owner = kzalloc(sizeof(*owner), GFP_KERNEL);
+ if (!owner)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&owner->controllers);
+ dev->archdata.iommu = owner;
+ }
+
+ list_add_tail(&data->owner_node, &owner->controllers);
+ return 0;
+}
+
+static struct iommu_ops exynos_iommu_ops = {
.domain_alloc = exynos_iommu_domain_alloc,
.domain_free = exynos_iommu_domain_free,
.attach_dev = exynos_iommu_attach_device,
@@ -1189,19 +1185,15 @@ static const struct iommu_ops exynos_iommu_ops = {
.add_device = exynos_iommu_add_device,
.remove_device = exynos_iommu_remove_device,
.pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE,
+ .of_xlate = exynos_iommu_of_xlate,
};
+static bool init_done;
+
static int __init exynos_iommu_init(void)
{
- struct device_node *np;
int ret;
- np = of_find_matching_node(NULL, sysmmu_of_match);
- if (!np)
- return 0;
-
- of_node_put(np);
-
lv2table_kmem_cache = kmem_cache_create("exynos-iommu-lv2table",
LV2TABLE_SIZE, LV2TABLE_SIZE, 0, NULL);
if (!lv2table_kmem_cache) {
@@ -1230,6 +1222,8 @@ static int __init exynos_iommu_init(void)
goto err_set_iommu;
}
+ init_done = true;
+
return 0;
err_set_iommu:
kmem_cache_free(lv2table_kmem_cache, zero_lv2_table);
@@ -1239,4 +1233,21 @@ err_reg_driver:
kmem_cache_destroy(lv2table_kmem_cache);
return ret;
}
-subsys_initcall(exynos_iommu_init);
+
+static int __init exynos_iommu_of_setup(struct device_node *np)
+{
+ struct platform_device *pdev;
+
+ if (!init_done)
+ exynos_iommu_init();
+
+ pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ of_iommu_set_ops(np, &exynos_iommu_ops);
+ return 0;
+}
+
+IOMMU_OF_DECLARE(exynos_iommu_of, "samsung,exynos-sysmmu",
+ exynos_iommu_of_setup);
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 68d43beccb7e..a98a7b27aca1 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -15,8 +15,11 @@
* Shaohua Li <shaohua.li@intel.com>,
* Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>,
* Fenghua Yu <fenghua.yu@intel.com>
+ * Joerg Roedel <jroedel@suse.de>
*/
+#define pr_fmt(fmt) "DMAR: " fmt
+
#include <linux/init.h>
#include <linux/bitmap.h>
#include <linux/debugfs.h>
@@ -40,6 +43,7 @@
#include <linux/pci-ats.h>
#include <linux/memblock.h>
#include <linux/dma-contiguous.h>
+#include <linux/crash_dump.h>
#include <asm/irq_remapping.h>
#include <asm/cacheflush.h>
#include <asm/iommu.h>
@@ -190,7 +194,29 @@ struct root_entry {
};
#define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry))
+/*
+ * Take a root_entry and return the Lower Context Table Pointer (LCTP)
+ * if marked present.
+ */
+static phys_addr_t root_entry_lctp(struct root_entry *re)
+{
+ if (!(re->lo & 1))
+ return 0;
+
+ return re->lo & VTD_PAGE_MASK;
+}
+
+/*
+ * Take a root_entry and return the Upper Context Table Pointer (UCTP)
+ * if marked present.
+ */
+static phys_addr_t root_entry_uctp(struct root_entry *re)
+{
+ if (!(re->hi & 1))
+ return 0;
+ return re->hi & VTD_PAGE_MASK;
+}
/*
* low 64 bits:
* 0: present
@@ -207,10 +233,38 @@ struct context_entry {
u64 hi;
};
-static inline bool context_present(struct context_entry *context)
+static inline void context_clear_pasid_enable(struct context_entry *context)
+{
+ context->lo &= ~(1ULL << 11);
+}
+
+static inline bool context_pasid_enabled(struct context_entry *context)
+{
+ return !!(context->lo & (1ULL << 11));
+}
+
+static inline void context_set_copied(struct context_entry *context)
+{
+ context->hi |= (1ull << 3);
+}
+
+static inline bool context_copied(struct context_entry *context)
+{
+ return !!(context->hi & (1ULL << 3));
+}
+
+static inline bool __context_present(struct context_entry *context)
{
return (context->lo & 1);
}
+
+static inline bool context_present(struct context_entry *context)
+{
+ return context_pasid_enabled(context) ?
+ __context_present(context) :
+ __context_present(context) && !context_copied(context);
+}
+
static inline void context_set_present(struct context_entry *context)
{
context->lo |= 1;
@@ -247,6 +301,11 @@ static inline void context_set_domain_id(struct context_entry *context,
context->hi |= (value & ((1 << 16) - 1)) << 8;
}
+static inline int context_domain_id(struct context_entry *c)
+{
+ return((c->hi >> 8) & 0xffff);
+}
+
static inline void context_clear_entry(struct context_entry *context)
{
context->lo = 0;
@@ -422,6 +481,14 @@ static int dmar_map_gfx = 1;
static int dmar_forcedac;
static int intel_iommu_strict;
static int intel_iommu_superpage = 1;
+static int intel_iommu_ecs = 1;
+
+/* We only actually use ECS when PASID support (on the new bit 40)
+ * is also advertised. Some early implementations — the ones with
+ * PASID support on bit 28 — have issues even when we *only* use
+ * extended root/context tables. */
+#define ecs_enabled(iommu) (intel_iommu_ecs && ecap_ecs(iommu->ecap) && \
+ ecap_pasid(iommu->ecap))
int intel_iommu_gfx_mapped;
EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped);
@@ -432,6 +499,25 @@ static LIST_HEAD(device_domain_list);
static const struct iommu_ops intel_iommu_ops;
+static bool translation_pre_enabled(struct intel_iommu *iommu)
+{
+ return (iommu->flags & VTD_FLAG_TRANS_PRE_ENABLED);
+}
+
+static void clear_translation_pre_enabled(struct intel_iommu *iommu)
+{
+ iommu->flags &= ~VTD_FLAG_TRANS_PRE_ENABLED;
+}
+
+static void init_translation_status(struct intel_iommu *iommu)
+{
+ u32 gsts;
+
+ gsts = readl(iommu->reg + DMAR_GSTS_REG);
+ if (gsts & DMA_GSTS_TES)
+ iommu->flags |= VTD_FLAG_TRANS_PRE_ENABLED;
+}
+
/* Convert generic 'struct iommu_domain to private struct dmar_domain */
static struct dmar_domain *to_dmar_domain(struct iommu_domain *dom)
{
@@ -445,26 +531,26 @@ static int __init intel_iommu_setup(char *str)
while (*str) {
if (!strncmp(str, "on", 2)) {
dmar_disabled = 0;
- printk(KERN_INFO "Intel-IOMMU: enabled\n");
+ pr_info("IOMMU enabled\n");
} else if (!strncmp(str, "off", 3)) {
dmar_disabled = 1;
- printk(KERN_INFO "Intel-IOMMU: disabled\n");
+ pr_info("IOMMU disabled\n");
} else if (!strncmp(str, "igfx_off", 8)) {
dmar_map_gfx = 0;
- printk(KERN_INFO
- "Intel-IOMMU: disable GFX device mapping\n");
+ pr_info("Disable GFX device mapping\n");
} else if (!strncmp(str, "forcedac", 8)) {
- printk(KERN_INFO
- "Intel-IOMMU: Forcing DAC for PCI devices\n");
+ pr_info("Forcing DAC for PCI devices\n");
dmar_forcedac = 1;
} else if (!strncmp(str, "strict", 6)) {
- printk(KERN_INFO
- "Intel-IOMMU: disable batched IOTLB flush\n");
+ pr_info("Disable batched IOTLB flush\n");
intel_iommu_strict = 1;
} else if (!strncmp(str, "sp_off", 6)) {
- printk(KERN_INFO
- "Intel-IOMMU: disable supported super page\n");
+ pr_info("Disable supported super page\n");
intel_iommu_superpage = 0;
+ } else if (!strncmp(str, "ecs_off", 7)) {
+ printk(KERN_INFO
+ "Intel-IOMMU: disable extended context table support\n");
+ intel_iommu_ecs = 0;
}
str += strcspn(str, ",");
@@ -669,7 +755,7 @@ static inline struct context_entry *iommu_context_addr(struct intel_iommu *iommu
struct context_entry *context;
u64 *entry;
- if (ecap_ecs(iommu->ecap)) {
+ if (ecs_enabled(iommu)) {
if (devfn >= 0x80) {
devfn -= 0x80;
entry = &root->hi;
@@ -696,6 +782,11 @@ static inline struct context_entry *iommu_context_addr(struct intel_iommu *iommu
return &context[devfn];
}
+static int iommu_dummy(struct device *dev)
+{
+ return dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
+}
+
static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
{
struct dmar_drhd_unit *drhd = NULL;
@@ -705,6 +796,9 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
u16 segment = 0;
int i;
+ if (iommu_dummy(dev))
+ return NULL;
+
if (dev_is_pci(dev)) {
pdev = to_pci_dev(dev);
segment = pci_domain_nr(pdev->bus);
@@ -798,7 +892,7 @@ static void free_context_table(struct intel_iommu *iommu)
if (context)
free_pgtable_page(context);
- if (!ecap_ecs(iommu->ecap))
+ if (!ecs_enabled(iommu))
continue;
context = iommu_context_addr(iommu, i, 0x80, 0);
@@ -1112,7 +1206,7 @@ static int iommu_alloc_root_entry(struct intel_iommu *iommu)
root = (struct root_entry *)alloc_pgtable_page(iommu->node);
if (!root) {
- pr_err("IOMMU: allocating root entry for %s failed\n",
+ pr_err("Allocating root entry for %s failed\n",
iommu->name);
return -ENOMEM;
}
@@ -1133,7 +1227,7 @@ static void iommu_set_root_entry(struct intel_iommu *iommu)
unsigned long flag;
addr = virt_to_phys(iommu->root_entry);
- if (ecap_ecs(iommu->ecap))
+ if (ecs_enabled(iommu))
addr |= DMA_RTADDR_RTT;
raw_spin_lock_irqsave(&iommu->register_lock, flag);
@@ -1250,9 +1344,9 @@ static void __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did,
/* check IOTLB invalidation granularity */
if (DMA_TLB_IAIG(val) == 0)
- printk(KERN_ERR"IOMMU: flush IOTLB failed\n");
+ pr_err("Flush IOTLB failed\n");
if (DMA_TLB_IAIG(val) != DMA_TLB_IIRG(type))
- pr_debug("IOMMU: tlb flush request %Lx, actual %Lx\n",
+ pr_debug("TLB flush request %Lx, actual %Lx\n",
(unsigned long long)DMA_TLB_IIRG(type),
(unsigned long long)DMA_TLB_IAIG(val));
}
@@ -1423,8 +1517,8 @@ static int iommu_init_domains(struct intel_iommu *iommu)
unsigned long nlongs;
ndomains = cap_ndoms(iommu->cap);
- pr_debug("IOMMU%d: Number of Domains supported <%ld>\n",
- iommu->seq_id, ndomains);
+ pr_debug("%s: Number of Domains supported <%ld>\n",
+ iommu->name, ndomains);
nlongs = BITS_TO_LONGS(ndomains);
spin_lock_init(&iommu->lock);
@@ -1434,15 +1528,15 @@ static int iommu_init_domains(struct intel_iommu *iommu)
*/
iommu->domain_ids = kcalloc(nlongs, sizeof(unsigned long), GFP_KERNEL);
if (!iommu->domain_ids) {
- pr_err("IOMMU%d: allocating domain id array failed\n",
- iommu->seq_id);
+ pr_err("%s: Allocating domain id array failed\n",
+ iommu->name);
return -ENOMEM;
}
iommu->domains = kcalloc(ndomains, sizeof(struct dmar_domain *),
GFP_KERNEL);
if (!iommu->domains) {
- pr_err("IOMMU%d: allocating domain array failed\n",
- iommu->seq_id);
+ pr_err("%s: Allocating domain array failed\n",
+ iommu->name);
kfree(iommu->domain_ids);
iommu->domain_ids = NULL;
return -ENOMEM;
@@ -1547,7 +1641,7 @@ static int iommu_attach_domain(struct dmar_domain *domain,
num = __iommu_attach_domain(domain, iommu);
spin_unlock_irqrestore(&iommu->lock, flags);
if (num < 0)
- pr_err("IOMMU: no free domain ids\n");
+ pr_err("%s: No free domain ids\n", iommu->name);
return num;
}
@@ -1639,7 +1733,7 @@ static int dmar_init_reserved_ranges(void)
iova = reserve_iova(&reserved_iova_list, IOVA_PFN(IOAPIC_RANGE_START),
IOVA_PFN(IOAPIC_RANGE_END));
if (!iova) {
- printk(KERN_ERR "Reserve IOAPIC range failed\n");
+ pr_err("Reserve IOAPIC range failed\n");
return -ENODEV;
}
@@ -1655,7 +1749,7 @@ static int dmar_init_reserved_ranges(void)
IOVA_PFN(r->start),
IOVA_PFN(r->end));
if (!iova) {
- printk(KERN_ERR "Reserve iova failed\n");
+ pr_err("Reserve iova failed\n");
return -ENODEV;
}
}
@@ -1702,7 +1796,7 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
sagaw = cap_sagaw(iommu->cap);
if (!test_bit(agaw, &sagaw)) {
/* hardware doesn't support it, choose a bigger one */
- pr_debug("IOMMU: hardware doesn't support agaw %d\n", agaw);
+ pr_debug("Hardware doesn't support agaw %d\n", agaw);
agaw = find_next_bit(&sagaw, 5, agaw);
if (agaw >= 5)
return -ENODEV;
@@ -1795,6 +1889,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
return 0;
}
+ context_clear_entry(context);
+
id = domain->id;
pgd = domain->pgd;
@@ -1803,7 +1899,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
id = iommu_attach_vm_domain(domain, iommu);
if (id < 0) {
spin_unlock_irqrestore(&iommu->lock, flags);
- pr_err("IOMMU: no free domain ids\n");
+ pr_err("%s: No free domain ids\n", iommu->name);
return -EFAULT;
}
}
@@ -2030,8 +2126,8 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
tmp = cmpxchg64_local(&pte->val, 0ULL, pteval);
if (tmp) {
static int dumps = 5;
- printk(KERN_CRIT "ERROR: DMA PTE for vPFN 0x%lx already set (to %llx not %llx)\n",
- iov_pfn, tmp, (unsigned long long)pteval);
+ pr_crit("ERROR: DMA PTE for vPFN 0x%lx already set (to %llx not %llx)\n",
+ iov_pfn, tmp, (unsigned long long)pteval);
if (dumps) {
dumps--;
debug_dma_dump_mappings(NULL);
@@ -2303,7 +2399,7 @@ static int iommu_domain_identity_map(struct dmar_domain *domain,
if (!reserve_iova(&domain->iovad, dma_to_mm_pfn(first_vpfn),
dma_to_mm_pfn(last_vpfn))) {
- printk(KERN_ERR "IOMMU: reserve iova failed\n");
+ pr_err("Reserving iova failed\n");
return -ENOMEM;
}
@@ -2336,15 +2432,14 @@ static int iommu_prepare_identity_map(struct device *dev,
range which is reserved in E820, so which didn't get set
up to start with in si_domain */
if (domain == si_domain && hw_pass_through) {
- printk("Ignoring identity map for HW passthrough device %s [0x%Lx - 0x%Lx]\n",
- dev_name(dev), start, end);
+ pr_warn("Ignoring identity map for HW passthrough device %s [0x%Lx - 0x%Lx]\n",
+ dev_name(dev), start, end);
return 0;
}
- printk(KERN_INFO
- "IOMMU: Setting identity map for device %s [0x%Lx - 0x%Lx]\n",
- dev_name(dev), start, end);
-
+ pr_info("Setting identity map for device %s [0x%Lx - 0x%Lx]\n",
+ dev_name(dev), start, end);
+
if (end < start) {
WARN(1, "Your BIOS is broken; RMRR ends before it starts!\n"
"BIOS vendor: %s; Ver: %s; Product Version: %s\n",
@@ -2401,12 +2496,11 @@ static inline void iommu_prepare_isa(void)
if (!pdev)
return;
- printk(KERN_INFO "IOMMU: Prepare 0-16MiB unity mapping for LPC\n");
+ pr_info("Prepare 0-16MiB unity mapping for LPC\n");
ret = iommu_prepare_identity_map(&pdev->dev, 0, 16*1024*1024 - 1);
if (ret)
- printk(KERN_ERR "IOMMU: Failed to create 0-16MiB identity map; "
- "floppy might not work\n");
+ pr_err("Failed to create 0-16MiB identity map - floppy might not work\n");
pci_dev_put(pdev);
}
@@ -2450,7 +2544,7 @@ static int __init si_domain_init(int hw)
return -EFAULT;
}
- pr_debug("IOMMU: identity mapping domain is domain %d\n",
+ pr_debug("Identity mapping domain is domain %d\n",
si_domain->id);
if (hw)
@@ -2650,8 +2744,8 @@ static int __init dev_prepare_static_identity_mapping(struct device *dev, int hw
hw ? CONTEXT_TT_PASS_THROUGH :
CONTEXT_TT_MULTI_LEVEL);
if (!ret)
- pr_info("IOMMU: %s identity mapping for device %s\n",
- hw ? "hardware" : "software", dev_name(dev));
+ pr_info("%s identity mapping for device %s\n",
+ hw ? "Hardware" : "Software", dev_name(dev));
else if (ret == -ENODEV)
/* device not associated with an iommu */
ret = 0;
@@ -2669,10 +2763,6 @@ static int __init iommu_prepare_static_identity_mapping(int hw)
int i;
int ret = 0;
- ret = si_domain_init(hw);
- if (ret)
- return -EFAULT;
-
for_each_pci_dev(pdev) {
ret = dev_prepare_static_identity_mapping(&pdev->dev, hw);
if (ret)
@@ -2686,7 +2776,7 @@ static int __init iommu_prepare_static_identity_mapping(int hw)
if (dev->bus != &acpi_bus_type)
continue;
-
+
adev= to_acpi_device(dev);
mutex_lock(&adev->physical_node_lock);
list_for_each_entry(pn, &adev->physical_node_list, node) {
@@ -2728,19 +2818,200 @@ static void intel_iommu_init_qi(struct intel_iommu *iommu)
*/
iommu->flush.flush_context = __iommu_flush_context;
iommu->flush.flush_iotlb = __iommu_flush_iotlb;
- pr_info("IOMMU: %s using Register based invalidation\n",
+ pr_info("%s: Using Register based invalidation\n",
iommu->name);
} else {
iommu->flush.flush_context = qi_flush_context;
iommu->flush.flush_iotlb = qi_flush_iotlb;
- pr_info("IOMMU: %s using Queued invalidation\n", iommu->name);
+ pr_info("%s: Using Queued invalidation\n", iommu->name);
}
}
+static int copy_context_table(struct intel_iommu *iommu,
+ struct root_entry *old_re,
+ struct context_entry **tbl,
+ int bus, bool ext)
+{
+ struct context_entry *old_ce = NULL, *new_ce = NULL, ce;
+ int tbl_idx, pos = 0, idx, devfn, ret = 0, did;
+ phys_addr_t old_ce_phys;
+
+ tbl_idx = ext ? bus * 2 : bus;
+
+ for (devfn = 0; devfn < 256; devfn++) {
+ /* First calculate the correct index */
+ idx = (ext ? devfn * 2 : devfn) % 256;
+
+ if (idx == 0) {
+ /* First save what we may have and clean up */
+ if (new_ce) {
+ tbl[tbl_idx] = new_ce;
+ __iommu_flush_cache(iommu, new_ce,
+ VTD_PAGE_SIZE);
+ pos = 1;
+ }
+
+ if (old_ce)
+ iounmap(old_ce);
+
+ ret = 0;
+ if (devfn < 0x80)
+ old_ce_phys = root_entry_lctp(old_re);
+ else
+ old_ce_phys = root_entry_uctp(old_re);
+
+ if (!old_ce_phys) {
+ if (ext && devfn == 0) {
+ /* No LCTP, try UCTP */
+ devfn = 0x7f;
+ continue;
+ } else {
+ goto out;
+ }
+ }
+
+ ret = -ENOMEM;
+ old_ce = ioremap_cache(old_ce_phys, PAGE_SIZE);
+ if (!old_ce)
+ goto out;
+
+ new_ce = alloc_pgtable_page(iommu->node);
+ if (!new_ce)
+ goto out_unmap;
+
+ ret = 0;
+ }
+
+ /* Now copy the context entry */
+ ce = old_ce[idx];
+
+ if (!__context_present(&ce))
+ continue;
+
+ did = context_domain_id(&ce);
+ if (did >= 0 && did < cap_ndoms(iommu->cap))
+ set_bit(did, iommu->domain_ids);
+
+ /*
+ * We need a marker for copied context entries. This
+ * marker needs to work for the old format as well as
+ * for extended context entries.
+ *
+ * Bit 67 of the context entry is used. In the old
+ * format this bit is available to software, in the
+ * extended format it is the PGE bit, but PGE is ignored
+ * by HW if PASIDs are disabled (and thus still
+ * available).
+ *
+ * So disable PASIDs first and then mark the entry
+ * copied. This means that we don't copy PASID
+ * translations from the old kernel, but this is fine as
+ * faults there are not fatal.
+ */
+ context_clear_pasid_enable(&ce);
+ context_set_copied(&ce);
+
+ new_ce[idx] = ce;
+ }
+
+ tbl[tbl_idx + pos] = new_ce;
+
+ __iommu_flush_cache(iommu, new_ce, VTD_PAGE_SIZE);
+
+out_unmap:
+ iounmap(old_ce);
+
+out:
+ return ret;
+}
+
+static int copy_translation_tables(struct intel_iommu *iommu)
+{
+ struct context_entry **ctxt_tbls;
+ struct root_entry *old_rt;
+ phys_addr_t old_rt_phys;
+ int ctxt_table_entries;
+ unsigned long flags;
+ u64 rtaddr_reg;
+ int bus, ret;
+ bool new_ext, ext;
+
+ rtaddr_reg = dmar_readq(iommu->reg + DMAR_RTADDR_REG);
+ ext = !!(rtaddr_reg & DMA_RTADDR_RTT);
+ new_ext = !!ecap_ecs(iommu->ecap);
+
+ /*
+ * The RTT bit can only be changed when translation is disabled,
+ * but disabling translation means to open a window for data
+ * corruption. So bail out and don't copy anything if we would
+ * have to change the bit.
+ */
+ if (new_ext != ext)
+ return -EINVAL;
+
+ old_rt_phys = rtaddr_reg & VTD_PAGE_MASK;
+ if (!old_rt_phys)
+ return -EINVAL;
+
+ old_rt = ioremap_cache(old_rt_phys, PAGE_SIZE);
+ if (!old_rt)
+ return -ENOMEM;
+
+ /* This is too big for the stack - allocate it from slab */
+ ctxt_table_entries = ext ? 512 : 256;
+ ret = -ENOMEM;
+ ctxt_tbls = kzalloc(ctxt_table_entries * sizeof(void *), GFP_KERNEL);
+ if (!ctxt_tbls)
+ goto out_unmap;
+
+ for (bus = 0; bus < 256; bus++) {
+ ret = copy_context_table(iommu, &old_rt[bus],
+ ctxt_tbls, bus, ext);
+ if (ret) {
+ pr_err("%s: Failed to copy context table for bus %d\n",
+ iommu->name, bus);
+ continue;
+ }
+ }
+
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ /* Context tables are copied, now write them to the root_entry table */
+ for (bus = 0; bus < 256; bus++) {
+ int idx = ext ? bus * 2 : bus;
+ u64 val;
+
+ if (ctxt_tbls[idx]) {
+ val = virt_to_phys(ctxt_tbls[idx]) | 1;
+ iommu->root_entry[bus].lo = val;
+ }
+
+ if (!ext || !ctxt_tbls[idx + 1])
+ continue;
+
+ val = virt_to_phys(ctxt_tbls[idx + 1]) | 1;
+ iommu->root_entry[bus].hi = val;
+ }
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+
+ kfree(ctxt_tbls);
+
+ __iommu_flush_cache(iommu, iommu->root_entry, PAGE_SIZE);
+
+ ret = 0;
+
+out_unmap:
+ iounmap(old_rt);
+
+ return ret;
+}
+
static int __init init_dmars(void)
{
struct dmar_drhd_unit *drhd;
struct dmar_rmrr_unit *rmrr;
+ bool copied_tables = false;
struct device *dev;
struct intel_iommu *iommu;
int i, ret;
@@ -2761,8 +3032,7 @@ static int __init init_dmars(void)
g_num_of_iommus++;
continue;
}
- printk_once(KERN_ERR "intel-iommu: exceeded %d IOMMUs\n",
- DMAR_UNITS_SUPPORTED);
+ pr_err_once("Exceeded %d IOMMUs\n", DMAR_UNITS_SUPPORTED);
}
/* Preallocate enough resources for IOMMU hot-addition */
@@ -2772,7 +3042,7 @@ static int __init init_dmars(void)
g_iommus = kcalloc(g_num_of_iommus, sizeof(struct intel_iommu *),
GFP_KERNEL);
if (!g_iommus) {
- printk(KERN_ERR "Allocating global iommu array failed\n");
+ pr_err("Allocating global iommu array failed\n");
ret = -ENOMEM;
goto error;
}
@@ -2787,10 +3057,21 @@ static int __init init_dmars(void)
for_each_active_iommu(iommu, drhd) {
g_iommus[iommu->seq_id] = iommu;
+ intel_iommu_init_qi(iommu);
+
ret = iommu_init_domains(iommu);
if (ret)
goto free_iommu;
+ init_translation_status(iommu);
+
+ if (translation_pre_enabled(iommu) && !is_kdump_kernel()) {
+ iommu_disable_translation(iommu);
+ clear_translation_pre_enabled(iommu);
+ pr_warn("Translation was enabled for %s but we are not in kdump mode\n",
+ iommu->name);
+ }
+
/*
* TBD:
* we could share the same root & context tables
@@ -2799,13 +3080,41 @@ static int __init init_dmars(void)
ret = iommu_alloc_root_entry(iommu);
if (ret)
goto free_iommu;
+
+ if (translation_pre_enabled(iommu)) {
+ pr_info("Translation already enabled - trying to copy translation structures\n");
+
+ ret = copy_translation_tables(iommu);
+ if (ret) {
+ /*
+ * We found the IOMMU with translation
+ * enabled - but failed to copy over the
+ * old root-entry table. Try to proceed
+ * by disabling translation now and
+ * allocating a clean root-entry table.
+ * This might cause DMAR faults, but
+ * probably the dump will still succeed.
+ */
+ pr_err("Failed to copy translation tables from previous kernel for %s\n",
+ iommu->name);
+ iommu_disable_translation(iommu);
+ clear_translation_pre_enabled(iommu);
+ } else {
+ pr_info("Copied translation tables from previous kernel for %s\n",
+ iommu->name);
+ copied_tables = true;
+ }
+ }
+
+ iommu_flush_write_buffer(iommu);
+ iommu_set_root_entry(iommu);
+ iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
+ iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
+
if (!ecap_pass_through(iommu->ecap))
hw_pass_through = 0;
}
- for_each_active_iommu(iommu, drhd)
- intel_iommu_init_qi(iommu);
-
if (iommu_pass_through)
iommu_identity_mapping |= IDENTMAP_ALL;
@@ -2813,9 +3122,24 @@ static int __init init_dmars(void)
iommu_identity_mapping |= IDENTMAP_GFX;
#endif
+ if (iommu_identity_mapping) {
+ ret = si_domain_init(hw_pass_through);
+ if (ret)
+ goto free_iommu;
+ }
+
check_tylersburg_isoch();
/*
+ * If we copied translations from a previous kernel in the kdump
+ * case, we can not assign the devices to domains now, as that
+ * would eliminate the old mappings. So skip this part and defer
+ * the assignment to device driver initialization time.
+ */
+ if (copied_tables)
+ goto domains_done;
+
+ /*
* If pass through is not set or not enabled, setup context entries for
* identity mappings for rmrr, gfx, and isa and may fall back to static
* identity mapping if iommu_identity_mapping is set.
@@ -2823,7 +3147,7 @@ static int __init init_dmars(void)
if (iommu_identity_mapping) {
ret = iommu_prepare_static_identity_mapping(hw_pass_through);
if (ret) {
- printk(KERN_CRIT "Failed to setup IOMMU pass-through\n");
+ pr_crit("Failed to setup IOMMU pass-through\n");
goto free_iommu;
}
}
@@ -2841,20 +3165,21 @@ static int __init init_dmars(void)
* endfor
* endfor
*/
- printk(KERN_INFO "IOMMU: Setting RMRR:\n");
+ pr_info("Setting RMRR:\n");
for_each_rmrr_units(rmrr) {
/* some BIOS lists non-exist devices in DMAR table. */
for_each_active_dev_scope(rmrr->devices, rmrr->devices_cnt,
i, dev) {
ret = iommu_prepare_rmrr_dev(rmrr, dev);
if (ret)
- printk(KERN_ERR
- "IOMMU: mapping reserved region failed\n");
+ pr_err("Mapping reserved region failed\n");
}
}
iommu_prepare_isa();
+domains_done:
+
/*
* for each drhd
* enable fault log
@@ -2879,11 +3204,9 @@ static int __init init_dmars(void)
if (ret)
goto free_iommu;
- iommu_set_root_entry(iommu);
+ if (!translation_pre_enabled(iommu))
+ iommu_enable_translation(iommu);
- iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
- iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
- iommu_enable_translation(iommu);
iommu_disable_protect_mem_regions(iommu);
}
@@ -2924,7 +3247,7 @@ static struct iova *intel_alloc_iova(struct device *dev,
}
iova = alloc_iova(&domain->iovad, nrpages, IOVA_PFN(dma_mask), 1);
if (unlikely(!iova)) {
- printk(KERN_ERR "Allocating %ld-page iova for %s failed",
+ pr_err("Allocating %ld-page iova for %s failed",
nrpages, dev_name(dev));
return NULL;
}
@@ -2939,7 +3262,7 @@ static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev)
domain = get_domain_for_dev(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
if (!domain) {
- printk(KERN_ERR "Allocating domain for %s failed",
+ pr_err("Allocating domain for %s failed\n",
dev_name(dev));
return NULL;
}
@@ -2948,7 +3271,7 @@ static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev)
if (unlikely(!domain_context_mapped(dev))) {
ret = domain_context_mapping(domain, dev, CONTEXT_TT_MULTI_LEVEL);
if (ret) {
- printk(KERN_ERR "Domain context map for %s failed",
+ pr_err("Domain context map for %s failed\n",
dev_name(dev));
return NULL;
}
@@ -2969,11 +3292,6 @@ static inline struct dmar_domain *get_valid_domain_for_dev(struct device *dev)
return __get_valid_domain_for_dev(dev);
}
-static int iommu_dummy(struct device *dev)
-{
- return dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
-}
-
/* Check if the dev needs to go through non-identity map and unmap process.*/
static int iommu_no_mapping(struct device *dev)
{
@@ -2995,8 +3313,8 @@ static int iommu_no_mapping(struct device *dev)
* to non-identity mapping.
*/
domain_remove_one_dev_info(si_domain, dev);
- printk(KERN_INFO "32bit %s uses non-identity mapping\n",
- dev_name(dev));
+ pr_info("32bit %s uses non-identity mapping\n",
+ dev_name(dev));
return 0;
}
} else {
@@ -3011,8 +3329,8 @@ static int iommu_no_mapping(struct device *dev)
CONTEXT_TT_PASS_THROUGH :
CONTEXT_TT_MULTI_LEVEL);
if (!ret) {
- printk(KERN_INFO "64bit %s uses identity mapping\n",
- dev_name(dev));
+ pr_info("64bit %s uses identity mapping\n",
+ dev_name(dev));
return 1;
}
}
@@ -3081,7 +3399,7 @@ static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr,
error:
if (iova)
__free_iova(&domain->iovad, iova);
- printk(KERN_ERR"Device %s request: %zx@%llx dir %d --- failed\n",
+ pr_err("Device %s request: %zx@%llx dir %d --- failed\n",
dev_name(dev), size, (unsigned long long)paddr, dir);
return 0;
}
@@ -3396,7 +3714,7 @@ static inline int iommu_domain_cache_init(void)
NULL);
if (!iommu_domain_cache) {
- printk(KERN_ERR "Couldn't create iommu_domain cache\n");
+ pr_err("Couldn't create iommu_domain cache\n");
ret = -ENOMEM;
}
@@ -3413,7 +3731,7 @@ static inline int iommu_devinfo_cache_init(void)
SLAB_HWCACHE_ALIGN,
NULL);
if (!iommu_devinfo_cache) {
- printk(KERN_ERR "Couldn't create devinfo cache\n");
+ pr_err("Couldn't create devinfo cache\n");
ret = -ENOMEM;
}
@@ -3790,19 +4108,19 @@ static int intel_iommu_add(struct dmar_drhd_unit *dmaru)
return 0;
if (hw_pass_through && !ecap_pass_through(iommu->ecap)) {
- pr_warn("IOMMU: %s doesn't support hardware pass through.\n",
+ pr_warn("%s: Doesn't support hardware pass through.\n",
iommu->name);
return -ENXIO;
}
if (!ecap_sc_support(iommu->ecap) &&
domain_update_iommu_snooping(iommu)) {
- pr_warn("IOMMU: %s doesn't support snooping.\n",
+ pr_warn("%s: Doesn't support snooping.\n",
iommu->name);
return -ENXIO;
}
sp = domain_update_iommu_superpage(iommu) - 1;
if (sp >= 0 && !(cap_super_page_val(iommu->cap) & (1 << sp))) {
- pr_warn("IOMMU: %s doesn't support large page.\n",
+ pr_warn("%s: Doesn't support large page.\n",
iommu->name);
return -ENXIO;
}
@@ -4033,7 +4351,7 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb,
start = mhp->start_pfn << PAGE_SHIFT;
end = ((mhp->start_pfn + mhp->nr_pages) << PAGE_SHIFT) - 1;
if (iommu_domain_identity_map(si_domain, start, end)) {
- pr_warn("dmar: failed to build identity map for [%llx-%llx]\n",
+ pr_warn("Failed to build identity map for [%llx-%llx]\n",
start, end);
return NOTIFY_BAD;
}
@@ -4051,7 +4369,7 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb,
iova = find_iova(&si_domain->iovad, start_vpfn);
if (iova == NULL) {
- pr_debug("dmar: failed get IOVA for PFN %lx\n",
+ pr_debug("Failed get IOVA for PFN %lx\n",
start_vpfn);
break;
}
@@ -4059,7 +4377,7 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb,
iova = split_and_remove_iova(&si_domain->iovad, iova,
start_vpfn, last_vpfn);
if (iova == NULL) {
- pr_warn("dmar: failed to split IOVA PFN [%lx-%lx]\n",
+ pr_warn("Failed to split IOVA PFN [%lx-%lx]\n",
start_vpfn, last_vpfn);
return NOTIFY_BAD;
}
@@ -4168,13 +4486,6 @@ int __init intel_iommu_init(void)
goto out_free_dmar;
}
- /*
- * Disable translation if already enabled prior to OS handover.
- */
- for_each_active_iommu(iommu, drhd)
- if (iommu->gcmd & DMA_GCMD_TE)
- iommu_disable_translation(iommu);
-
if (dmar_dev_scope_init() < 0) {
if (force_on)
panic("tboot: Failed to initialize DMAR device scope\n");
@@ -4185,10 +4496,10 @@ int __init intel_iommu_init(void)
goto out_free_dmar;
if (list_empty(&dmar_rmrr_units))
- printk(KERN_INFO "DMAR: No RMRR found\n");
+ pr_info("No RMRR found\n");
if (list_empty(&dmar_atsr_units))
- printk(KERN_INFO "DMAR: No ATSR found\n");
+ pr_info("No ATSR found\n");
if (dmar_init_reserved_ranges()) {
if (force_on)
@@ -4202,12 +4513,11 @@ int __init intel_iommu_init(void)
if (ret) {
if (force_on)
panic("tboot: Failed to initialize DMARs\n");
- printk(KERN_ERR "IOMMU: dmar init failed\n");
+ pr_err("Initialization failed\n");
goto out_free_reserved_range;
}
up_write(&dmar_global_lock);
- printk(KERN_INFO
- "PCI-DMA: Intel(R) Virtualization Technology for Directed I/O\n");
+ pr_info("Intel(R) Virtualization Technology for Directed I/O\n");
init_timer(&unmap_timer);
#ifdef CONFIG_SWIOTLB
@@ -4349,13 +4659,11 @@ static struct iommu_domain *intel_iommu_domain_alloc(unsigned type)
dmar_domain = alloc_domain(DOMAIN_FLAG_VIRTUAL_MACHINE);
if (!dmar_domain) {
- printk(KERN_ERR
- "intel_iommu_domain_init: dmar_domain == NULL\n");
+ pr_err("Can't allocate dmar_domain\n");
return NULL;
}
if (md_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) {
- printk(KERN_ERR
- "intel_iommu_domain_init() failed\n");
+ pr_err("Domain initialization failed\n");
domain_exit(dmar_domain);
return NULL;
}
@@ -4414,7 +4722,7 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
addr_width = cap_mgaw(iommu->cap);
if (dmar_domain->max_addr > (1LL << addr_width)) {
- printk(KERN_ERR "%s: iommu width (%d) is not "
+ pr_err("%s: iommu width (%d) is not "
"sufficient for the mapped address (%llx)\n",
__func__, addr_width, dmar_domain->max_addr);
return -EFAULT;
@@ -4468,7 +4776,7 @@ static int intel_iommu_map(struct iommu_domain *domain,
/* check if minimum agaw is sufficient for mapped address */
end = __DOMAIN_MAX_ADDR(dmar_domain->gaw) + 1;
if (end < max_addr) {
- printk(KERN_ERR "%s: iommu width (%d) is not "
+ pr_err("%s: iommu width (%d) is not "
"sufficient for the mapped address (%llx)\n",
__func__, dmar_domain->gaw, max_addr);
return -EFAULT;
@@ -4609,7 +4917,7 @@ static const struct iommu_ops intel_iommu_ops = {
static void quirk_iommu_g4x_gfx(struct pci_dev *dev)
{
/* G4x/GM45 integrated gfx dmar support is totally busted. */
- printk(KERN_INFO "DMAR: Disabling IOMMU for graphics on this chipset\n");
+ pr_info("Disabling IOMMU for graphics on this chipset\n");
dmar_map_gfx = 0;
}
@@ -4627,7 +4935,7 @@ static void quirk_iommu_rwbf(struct pci_dev *dev)
* Mobile 4 Series Chipset neglects to set RWBF capability,
* but needs it. Same seems to hold for the desktop versions.
*/
- printk(KERN_INFO "DMAR: Forcing write-buffer flush capability\n");
+ pr_info("Forcing write-buffer flush capability\n");
rwbf_quirk = 1;
}
@@ -4657,11 +4965,11 @@ static void quirk_calpella_no_shadow_gtt(struct pci_dev *dev)
return;
if (!(ggc & GGC_MEMORY_VT_ENABLED)) {
- printk(KERN_INFO "DMAR: BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n");
+ pr_info("BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n");
dmar_map_gfx = 0;
} else if (dmar_map_gfx) {
/* we have to ensure the gfx device is idle before we flush */
- printk(KERN_INFO "DMAR: Disabling batched IOTLB flush on Ironlake\n");
+ pr_info("Disabling batched IOTLB flush on Ironlake\n");
intel_iommu_strict = 1;
}
}
@@ -4723,7 +5031,7 @@ static void __init check_tylersburg_isoch(void)
iommu_identity_mapping |= IDENTMAP_AZALIA;
return;
}
-
- printk(KERN_WARNING "DMAR: Recommended TLB entries for ISOCH unit is 16; your BIOS set %d\n",
+
+ pr_warn("Recommended TLB entries for ISOCH unit is 16; your BIOS set %d\n",
vtisochctrl);
}
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index 5709ae9c3e77..f15692a410c7 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -1,3 +1,6 @@
+
+#define pr_fmt(fmt) "DMAR-IR: " fmt
+
#include <linux/interrupt.h>
#include <linux/dmar.h>
#include <linux/spinlock.h>
@@ -8,6 +11,8 @@
#include <linux/irq.h>
#include <linux/intel-iommu.h>
#include <linux/acpi.h>
+#include <linux/irqdomain.h>
+#include <linux/crash_dump.h>
#include <asm/io_apic.h>
#include <asm/smp.h>
#include <asm/cpu.h>
@@ -17,6 +22,11 @@
#include "irq_remapping.h"
+enum irq_mode {
+ IRQ_REMAPPING,
+ IRQ_POSTING,
+};
+
struct ioapic_scope {
struct intel_iommu *iommu;
unsigned int id;
@@ -31,6 +41,22 @@ struct hpet_scope {
unsigned int devfn;
};
+struct irq_2_iommu {
+ struct intel_iommu *iommu;
+ u16 irte_index;
+ u16 sub_handle;
+ u8 irte_mask;
+ enum irq_mode mode;
+};
+
+struct intel_ir_data {
+ struct irq_2_iommu irq_2_iommu;
+ struct irte irte_entry;
+ union {
+ struct msi_msg msi_entry;
+ };
+};
+
#define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
#define IRTE_DEST(dest) ((eim_mode) ? dest : dest << 8)
@@ -50,43 +76,34 @@ static struct hpet_scope ir_hpet[MAX_HPET_TBS];
* the dmar_global_lock.
*/
static DEFINE_RAW_SPINLOCK(irq_2_ir_lock);
+static struct irq_domain_ops intel_ir_domain_ops;
+static void iommu_disable_irq_remapping(struct intel_iommu *iommu);
static int __init parse_ioapics_under_ir(void);
-static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
+static bool ir_pre_enabled(struct intel_iommu *iommu)
{
- struct irq_cfg *cfg = irq_cfg(irq);
- return cfg ? &cfg->irq_2_iommu : NULL;
+ return (iommu->flags & VTD_FLAG_IRQ_REMAP_PRE_ENABLED);
}
-static int get_irte(int irq, struct irte *entry)
+static void clear_ir_pre_enabled(struct intel_iommu *iommu)
{
- struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
- unsigned long flags;
- int index;
-
- if (!entry || !irq_iommu)
- return -1;
-
- raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
-
- if (unlikely(!irq_iommu->iommu)) {
- raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
- return -1;
- }
+ iommu->flags &= ~VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
+}
- index = irq_iommu->irte_index + irq_iommu->sub_handle;
- *entry = *(irq_iommu->iommu->ir_table->base + index);
+static void init_ir_status(struct intel_iommu *iommu)
+{
+ u32 gsts;
- raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
- return 0;
+ gsts = readl(iommu->reg + DMAR_GSTS_REG);
+ if (gsts & DMA_GSTS_IRES)
+ iommu->flags |= VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
}
-static int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
+static int alloc_irte(struct intel_iommu *iommu, int irq,
+ struct irq_2_iommu *irq_iommu, u16 count)
{
struct ir_table *table = iommu->ir_table;
- struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
- struct irq_cfg *cfg = irq_cfg(irq);
unsigned int mask = 0;
unsigned long flags;
int index;
@@ -100,8 +117,7 @@ static int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
}
if (mask > ecap_max_handle_mask(iommu->ecap)) {
- printk(KERN_ERR
- "Requested mask %x exceeds the max invalidation handle"
+ pr_err("Requested mask %x exceeds the max invalidation handle"
" mask value %Lx\n", mask,
ecap_max_handle_mask(iommu->ecap));
return -1;
@@ -113,11 +129,11 @@ static int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
if (index < 0) {
pr_warn("IR%d: can't allocate an IRTE\n", iommu->seq_id);
} else {
- cfg->remapped = 1;
irq_iommu->iommu = iommu;
irq_iommu->irte_index = index;
irq_iommu->sub_handle = 0;
irq_iommu->irte_mask = mask;
+ irq_iommu->mode = IRQ_REMAPPING;
}
raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
@@ -135,47 +151,9 @@ static int qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
return qi_submit_sync(&desc, iommu);
}
-static int map_irq_to_irte_handle(int irq, u16 *sub_handle)
-{
- struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
- unsigned long flags;
- int index;
-
- if (!irq_iommu)
- return -1;
-
- raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
- *sub_handle = irq_iommu->sub_handle;
- index = irq_iommu->irte_index;
- raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
- return index;
-}
-
-static int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle)
-{
- struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
- struct irq_cfg *cfg = irq_cfg(irq);
- unsigned long flags;
-
- if (!irq_iommu)
- return -1;
-
- raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
-
- cfg->remapped = 1;
- irq_iommu->iommu = iommu;
- irq_iommu->irte_index = index;
- irq_iommu->sub_handle = subhandle;
- irq_iommu->irte_mask = 0;
-
- raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
-
- return 0;
-}
-
-static int modify_irte(int irq, struct irte *irte_modified)
+static int modify_irte(struct irq_2_iommu *irq_iommu,
+ struct irte *irte_modified)
{
- struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
struct intel_iommu *iommu;
unsigned long flags;
struct irte *irte;
@@ -196,6 +174,9 @@ static int modify_irte(int irq, struct irte *irte_modified)
__iommu_flush_cache(iommu, irte, sizeof(*irte));
rc = qi_flush_iec(iommu, index, 0);
+
+ /* Update iommu mode according to the IRTE mode */
+ irq_iommu->mode = irte->pst ? IRQ_POSTING : IRQ_REMAPPING;
raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
return rc;
@@ -242,7 +223,7 @@ static int clear_entries(struct irq_2_iommu *irq_iommu)
return 0;
iommu = irq_iommu->iommu;
- index = irq_iommu->irte_index + irq_iommu->sub_handle;
+ index = irq_iommu->irte_index;
start = iommu->ir_table->base + index;
end = start + (1 << irq_iommu->irte_mask);
@@ -257,29 +238,6 @@ static int clear_entries(struct irq_2_iommu *irq_iommu)
return qi_flush_iec(iommu, index, irq_iommu->irte_mask);
}
-static int free_irte(int irq)
-{
- struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
- unsigned long flags;
- int rc;
-
- if (!irq_iommu)
- return -1;
-
- raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
-
- rc = clear_entries(irq_iommu);
-
- irq_iommu->iommu = NULL;
- irq_iommu->irte_index = 0;
- irq_iommu->sub_handle = 0;
- irq_iommu->irte_mask = 0;
-
- raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
-
- return rc;
-}
-
/*
* source validation type
*/
@@ -333,7 +291,7 @@ static int set_ioapic_sid(struct irte *irte, int apic)
up_read(&dmar_global_lock);
if (sid == 0) {
- pr_warning("Failed to set source-id of IOAPIC (%d)\n", apic);
+ pr_warn("Failed to set source-id of IOAPIC (%d)\n", apic);
return -1;
}
@@ -360,7 +318,7 @@ static int set_hpet_sid(struct irte *irte, u8 id)
up_read(&dmar_global_lock);
if (sid == 0) {
- pr_warning("Failed to set source-id of HPET block (%d)\n", id);
+ pr_warn("Failed to set source-id of HPET block (%d)\n", id);
return -1;
}
@@ -424,11 +382,59 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
return 0;
}
+static int iommu_load_old_irte(struct intel_iommu *iommu)
+{
+ struct irte *old_ir_table;
+ phys_addr_t irt_phys;
+ unsigned int i;
+ size_t size;
+ u64 irta;
+
+ if (!is_kdump_kernel()) {
+ pr_warn("IRQ remapping was enabled on %s but we are not in kdump mode\n",
+ iommu->name);
+ clear_ir_pre_enabled(iommu);
+ iommu_disable_irq_remapping(iommu);
+ return -EINVAL;
+ }
+
+ /* Check whether the old ir-table has the same size as ours */
+ irta = dmar_readq(iommu->reg + DMAR_IRTA_REG);
+ if ((irta & INTR_REMAP_TABLE_REG_SIZE_MASK)
+ != INTR_REMAP_TABLE_REG_SIZE)
+ return -EINVAL;
+
+ irt_phys = irta & VTD_PAGE_MASK;
+ size = INTR_REMAP_TABLE_ENTRIES*sizeof(struct irte);
+
+ /* Map the old IR table */
+ old_ir_table = ioremap_cache(irt_phys, size);
+ if (!old_ir_table)
+ return -ENOMEM;
+
+ /* Copy data over */
+ memcpy(iommu->ir_table->base, old_ir_table, size);
+
+ __iommu_flush_cache(iommu, iommu->ir_table->base, size);
+
+ /*
+ * Now check the table for used entries and mark those as
+ * allocated in the bitmap
+ */
+ for (i = 0; i < INTR_REMAP_TABLE_ENTRIES; i++) {
+ if (iommu->ir_table->base[i].present)
+ bitmap_set(iommu->ir_table->bitmap, i, 1);
+ }
+
+ return 0;
+}
+
+
static void iommu_set_irq_remapping(struct intel_iommu *iommu, int mode)
{
+ unsigned long flags;
u64 addr;
u32 sts;
- unsigned long flags;
addr = virt_to_phys((void *)iommu->ir_table->base);
@@ -445,10 +451,16 @@ static void iommu_set_irq_remapping(struct intel_iommu *iommu, int mode)
raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
/*
- * global invalidation of interrupt entry cache before enabling
- * interrupt-remapping.
+ * Global invalidation of interrupt entry cache to make sure the
+ * hardware uses the new irq remapping table.
*/
qi_global_iec(iommu);
+}
+
+static void iommu_enable_irq_remapping(struct intel_iommu *iommu)
+{
+ unsigned long flags;
+ u32 sts;
raw_spin_lock_irqsave(&iommu->register_lock, flags);
@@ -488,7 +500,6 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu)
pages = alloc_pages_node(iommu->node, GFP_KERNEL | __GFP_ZERO,
INTR_REMAP_PAGE_ORDER);
-
if (!pages) {
pr_err("IR%d: failed to allocate pages of order %d\n",
iommu->seq_id, INTR_REMAP_PAGE_ORDER);
@@ -502,21 +513,75 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu)
goto out_free_pages;
}
+ iommu->ir_domain = irq_domain_add_hierarchy(arch_get_ir_parent_domain(),
+ 0, INTR_REMAP_TABLE_ENTRIES,
+ NULL, &intel_ir_domain_ops,
+ iommu);
+ if (!iommu->ir_domain) {
+ pr_err("IR%d: failed to allocate irqdomain\n", iommu->seq_id);
+ goto out_free_bitmap;
+ }
+ iommu->ir_msi_domain = arch_create_msi_irq_domain(iommu->ir_domain);
+
ir_table->base = page_address(pages);
ir_table->bitmap = bitmap;
iommu->ir_table = ir_table;
+
+ /*
+ * If the queued invalidation is already initialized,
+ * shouldn't disable it.
+ */
+ if (!iommu->qi) {
+ /*
+ * Clear previous faults.
+ */
+ dmar_fault(-1, iommu);
+ dmar_disable_qi(iommu);
+
+ if (dmar_enable_qi(iommu)) {
+ pr_err("Failed to enable queued invalidation\n");
+ goto out_free_bitmap;
+ }
+ }
+
+ init_ir_status(iommu);
+
+ if (ir_pre_enabled(iommu)) {
+ if (iommu_load_old_irte(iommu))
+ pr_err("Failed to copy IR table for %s from previous kernel\n",
+ iommu->name);
+ else
+ pr_info("Copied IR table for %s from previous kernel\n",
+ iommu->name);
+ }
+
+ iommu_set_irq_remapping(iommu, eim_mode);
+
return 0;
+out_free_bitmap:
+ kfree(bitmap);
out_free_pages:
__free_pages(pages, INTR_REMAP_PAGE_ORDER);
out_free_table:
kfree(ir_table);
+
+ iommu->ir_table = NULL;
+
return -ENOMEM;
}
static void intel_teardown_irq_remapping(struct intel_iommu *iommu)
{
if (iommu && iommu->ir_table) {
+ if (iommu->ir_msi_domain) {
+ irq_domain_remove(iommu->ir_msi_domain);
+ iommu->ir_msi_domain = NULL;
+ }
+ if (iommu->ir_domain) {
+ irq_domain_remove(iommu->ir_domain);
+ iommu->ir_domain = NULL;
+ }
free_pages((unsigned long)iommu->ir_table->base,
INTR_REMAP_PAGE_ORDER);
kfree(iommu->ir_table->bitmap);
@@ -580,17 +645,17 @@ static void __init intel_cleanup_irq_remapping(void)
}
if (x2apic_supported())
- pr_warn("Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n");
+ pr_warn("Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n");
}
static int __init intel_prepare_irq_remapping(void)
{
struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu;
+ int eim = 0;
if (irq_remap_broken) {
- printk(KERN_WARNING
- "This system BIOS has enabled interrupt remapping\n"
+ pr_warn("This system BIOS has enabled interrupt remapping\n"
"on a chipset that contains an erratum making that\n"
"feature unstable. To maintain system stability\n"
"interrupt remapping is being disabled. Please\n"
@@ -606,7 +671,7 @@ static int __init intel_prepare_irq_remapping(void)
return -ENODEV;
if (parse_ioapics_under_ir() != 1) {
- printk(KERN_INFO "Not enabling interrupt remapping\n");
+ pr_info("Not enabling interrupt remapping\n");
goto error;
}
@@ -615,85 +680,74 @@ static int __init intel_prepare_irq_remapping(void)
if (!ecap_ir_support(iommu->ecap))
goto error;
- /* Do the allocations early */
- for_each_iommu(iommu, drhd)
- if (intel_setup_irq_remapping(iommu))
- goto error;
-
- return 0;
-
-error:
- intel_cleanup_irq_remapping();
- return -ENODEV;
-}
-
-static int __init intel_enable_irq_remapping(void)
-{
- struct dmar_drhd_unit *drhd;
- struct intel_iommu *iommu;
- bool setup = false;
- int eim = 0;
-
+ /* Detect remapping mode: lapic or x2apic */
if (x2apic_supported()) {
eim = !dmar_x2apic_optout();
- if (!eim)
- pr_info("x2apic is disabled because BIOS sets x2apic opt out bit. You can use 'intremap=no_x2apic_optout' to override the BIOS setting.\n");
+ if (!eim) {
+ pr_info("x2apic is disabled because BIOS sets x2apic opt out bit.");
+ pr_info("Use 'intremap=no_x2apic_optout' to override the BIOS setting.\n");
+ }
}
for_each_iommu(iommu, drhd) {
- /*
- * If the queued invalidation is already initialized,
- * shouldn't disable it.
- */
- if (iommu->qi)
- continue;
-
- /*
- * Clear previous faults.
- */
- dmar_fault(-1, iommu);
-
- /*
- * Disable intr remapping and queued invalidation, if already
- * enabled prior to OS handover.
- */
- iommu_disable_irq_remapping(iommu);
-
- dmar_disable_qi(iommu);
- }
-
- /*
- * check for the Interrupt-remapping support
- */
- for_each_iommu(iommu, drhd)
if (eim && !ecap_eim_support(iommu->ecap)) {
- printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, "
- " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap);
+ pr_info("%s does not support EIM\n", iommu->name);
eim = 0;
}
+ }
+
eim_mode = eim;
if (eim)
pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");
- /*
- * Enable queued invalidation for all the DRHD's.
- */
+ /* Do the initializations early */
for_each_iommu(iommu, drhd) {
- int ret = dmar_enable_qi(iommu);
-
- if (ret) {
- printk(KERN_ERR "DRHD %Lx: failed to enable queued, "
- " invalidation, ecap %Lx, ret %d\n",
- drhd->reg_base_addr, iommu->ecap, ret);
+ if (intel_setup_irq_remapping(iommu)) {
+ pr_err("Failed to setup irq remapping for %s\n",
+ iommu->name);
goto error;
}
}
+ return 0;
+
+error:
+ intel_cleanup_irq_remapping();
+ return -ENODEV;
+}
+
+/*
+ * Set Posted-Interrupts capability.
+ */
+static inline void set_irq_posting_cap(void)
+{
+ struct dmar_drhd_unit *drhd;
+ struct intel_iommu *iommu;
+
+ if (!disable_irq_post) {
+ intel_irq_remap_ops.capability |= 1 << IRQ_POSTING_CAP;
+
+ for_each_iommu(iommu, drhd)
+ if (!cap_pi_support(iommu->cap)) {
+ intel_irq_remap_ops.capability &=
+ ~(1 << IRQ_POSTING_CAP);
+ break;
+ }
+ }
+}
+
+static int __init intel_enable_irq_remapping(void)
+{
+ struct dmar_drhd_unit *drhd;
+ struct intel_iommu *iommu;
+ bool setup = false;
+
/*
* Setup Interrupt-remapping for all the DRHD's now.
*/
for_each_iommu(iommu, drhd) {
- iommu_set_irq_remapping(iommu, eim);
+ if (!ir_pre_enabled(iommu))
+ iommu_enable_irq_remapping(iommu);
setup = true;
}
@@ -702,16 +756,11 @@ static int __init intel_enable_irq_remapping(void)
irq_remapping_enabled = 1;
- /*
- * VT-d has a different layout for IO-APIC entries when
- * interrupt remapping is enabled. So it needs a special routine
- * to print IO-APIC entries for debugging purposes too.
- */
- x86_io_apic_ops.print_entries = intel_ir_io_apic_print_entries;
+ set_irq_posting_cap();
- pr_info("Enabled IRQ remapping in %s mode\n", eim ? "x2apic" : "xapic");
+ pr_info("Enabled IRQ remapping in %s mode\n", eim_mode ? "x2apic" : "xapic");
- return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
+ return eim_mode ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
error:
intel_cleanup_irq_remapping();
@@ -909,6 +958,12 @@ static void disable_irq_remapping(void)
iommu_disable_irq_remapping(iommu);
}
+
+ /*
+ * Clear Posted-Interrupts capability.
+ */
+ if (!disable_irq_post)
+ intel_irq_remap_ops.capability &= ~(1 << IRQ_POSTING_CAP);
}
static int reenable_irq_remapping(int eim)
@@ -930,12 +985,15 @@ static int reenable_irq_remapping(int eim)
/* Set up interrupt remapping for iommu.*/
iommu_set_irq_remapping(iommu, eim);
+ iommu_enable_irq_remapping(iommu);
setup = true;
}
if (!setup)
goto error;
+ set_irq_posting_cap();
+
return 0;
error:
@@ -945,8 +1003,7 @@ error:
return -1;
}
-static void prepare_irte(struct irte *irte, int vector,
- unsigned int dest)
+static void prepare_irte(struct irte *irte, int vector, unsigned int dest)
{
memset(irte, 0, sizeof(*irte));
@@ -966,76 +1023,63 @@ static void prepare_irte(struct irte *irte, int vector,
irte->redir_hint = 1;
}
-static int intel_setup_ioapic_entry(int irq,
- struct IO_APIC_route_entry *route_entry,
- unsigned int destination, int vector,
- struct io_apic_irq_attr *attr)
+static struct irq_domain *intel_get_ir_irq_domain(struct irq_alloc_info *info)
{
- int ioapic_id = mpc_ioapic_id(attr->ioapic);
- struct intel_iommu *iommu;
- struct IR_IO_APIC_route_entry *entry;
- struct irte irte;
- int index;
-
- down_read(&dmar_global_lock);
- iommu = map_ioapic_to_ir(ioapic_id);
- if (!iommu) {
- pr_warn("No mapping iommu for ioapic %d\n", ioapic_id);
- index = -ENODEV;
- } else {
- index = alloc_irte(iommu, irq, 1);
- if (index < 0) {
- pr_warn("Failed to allocate IRTE for ioapic %d\n",
- ioapic_id);
- index = -ENOMEM;
- }
- }
- up_read(&dmar_global_lock);
- if (index < 0)
- return index;
-
- prepare_irte(&irte, vector, destination);
+ struct intel_iommu *iommu = NULL;
- /* Set source-id of interrupt request */
- set_ioapic_sid(&irte, ioapic_id);
+ if (!info)
+ return NULL;
- modify_irte(irq, &irte);
+ switch (info->type) {
+ case X86_IRQ_ALLOC_TYPE_IOAPIC:
+ iommu = map_ioapic_to_ir(info->ioapic_id);
+ break;
+ case X86_IRQ_ALLOC_TYPE_HPET:
+ iommu = map_hpet_to_ir(info->hpet_id);
+ break;
+ case X86_IRQ_ALLOC_TYPE_MSI:
+ case X86_IRQ_ALLOC_TYPE_MSIX:
+ iommu = map_dev_to_ir(info->msi_dev);
+ break;
+ default:
+ BUG_ON(1);
+ break;
+ }
- apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: "
- "Set IRTE entry (P:%d FPD:%d Dst_Mode:%d "
- "Redir_hint:%d Trig_Mode:%d Dlvry_Mode:%X "
- "Avail:%X Vector:%02X Dest:%08X "
- "SID:%04X SQ:%X SVT:%X)\n",
- attr->ioapic, irte.present, irte.fpd, irte.dst_mode,
- irte.redir_hint, irte.trigger_mode, irte.dlvry_mode,
- irte.avail, irte.vector, irte.dest_id,
- irte.sid, irte.sq, irte.svt);
+ return iommu ? iommu->ir_domain : NULL;
+}
- entry = (struct IR_IO_APIC_route_entry *)route_entry;
- memset(entry, 0, sizeof(*entry));
+static struct irq_domain *intel_get_irq_domain(struct irq_alloc_info *info)
+{
+ struct intel_iommu *iommu;
- entry->index2 = (index >> 15) & 0x1;
- entry->zero = 0;
- entry->format = 1;
- entry->index = (index & 0x7fff);
- /*
- * IO-APIC RTE will be configured with virtual vector.
- * irq handler will do the explicit EOI to the io-apic.
- */
- entry->vector = attr->ioapic_pin;
- entry->mask = 0; /* enable IRQ */
- entry->trigger = attr->trigger;
- entry->polarity = attr->polarity;
+ if (!info)
+ return NULL;
- /* Mask level triggered irqs.
- * Use IRQ_DELAYED_DISABLE for edge triggered irqs.
- */
- if (attr->trigger)
- entry->mask = 1;
+ switch (info->type) {
+ case X86_IRQ_ALLOC_TYPE_MSI:
+ case X86_IRQ_ALLOC_TYPE_MSIX:
+ iommu = map_dev_to_ir(info->msi_dev);
+ if (iommu)
+ return iommu->ir_msi_domain;
+ break;
+ default:
+ break;
+ }
- return 0;
+ return NULL;
}
+struct irq_remap_ops intel_irq_remap_ops = {
+ .prepare = intel_prepare_irq_remapping,
+ .enable = intel_enable_irq_remapping,
+ .disable = disable_irq_remapping,
+ .reenable = reenable_irq_remapping,
+ .enable_faulting = enable_drhd_fault_handling,
+ .get_ir_irq_domain = intel_get_ir_irq_domain,
+ .get_irq_domain = intel_get_irq_domain,
+};
+
/*
* Migrate the IO-APIC irq in the presence of intr-remapping.
*
@@ -1051,170 +1095,281 @@ static int intel_setup_ioapic_entry(int irq,
* is used to migrate MSI irq's in the presence of interrupt-remapping.
*/
static int
-intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask,
- bool force)
+intel_ir_set_affinity(struct irq_data *data, const struct cpumask *mask,
+ bool force)
{
+ struct intel_ir_data *ir_data = data->chip_data;
+ struct irte *irte = &ir_data->irte_entry;
struct irq_cfg *cfg = irqd_cfg(data);
- unsigned int dest, irq = data->irq;
- struct irte irte;
- int err;
-
- if (!config_enabled(CONFIG_SMP))
- return -EINVAL;
-
- if (!cpumask_intersects(mask, cpu_online_mask))
- return -EINVAL;
-
- if (get_irte(irq, &irte))
- return -EBUSY;
-
- err = assign_irq_vector(irq, cfg, mask);
- if (err)
- return err;
-
- err = apic->cpu_mask_to_apicid_and(cfg->domain, mask, &dest);
- if (err) {
- if (assign_irq_vector(irq, cfg, data->affinity))
- pr_err("Failed to recover vector for irq %d\n", irq);
- return err;
- }
+ struct irq_data *parent = data->parent_data;
+ int ret;
- irte.vector = cfg->vector;
- irte.dest_id = IRTE_DEST(dest);
+ ret = parent->chip->irq_set_affinity(parent, mask, force);
+ if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
+ return ret;
/*
* Atomically updates the IRTE with the new destination, vector
* and flushes the interrupt entry cache.
*/
- modify_irte(irq, &irte);
+ irte->vector = cfg->vector;
+ irte->dest_id = IRTE_DEST(cfg->dest_apicid);
+
+ /* Update the hardware only if the interrupt is in remapped mode. */
+ if (ir_data->irq_2_iommu.mode == IRQ_REMAPPING)
+ modify_irte(&ir_data->irq_2_iommu, irte);
/*
* After this point, all the interrupts will start arriving
* at the new destination. So, time to cleanup the previous
* vector allocation.
*/
- if (cfg->move_in_progress)
- send_cleanup_vector(cfg);
+ send_cleanup_vector(cfg);
- cpumask_copy(data->affinity, mask);
- return 0;
+ return IRQ_SET_MASK_OK_DONE;
}
-static void intel_compose_msi_msg(struct pci_dev *pdev,
- unsigned int irq, unsigned int dest,
- struct msi_msg *msg, u8 hpet_id)
+static void intel_ir_compose_msi_msg(struct irq_data *irq_data,
+ struct msi_msg *msg)
{
- struct irq_cfg *cfg;
- struct irte irte;
- u16 sub_handle = 0;
- int ir_index;
-
- cfg = irq_cfg(irq);
+ struct intel_ir_data *ir_data = irq_data->chip_data;
- ir_index = map_irq_to_irte_handle(irq, &sub_handle);
- BUG_ON(ir_index == -1);
+ *msg = ir_data->msi_entry;
+}
- prepare_irte(&irte, cfg->vector, dest);
+static int intel_ir_set_vcpu_affinity(struct irq_data *data, void *info)
+{
+ struct intel_ir_data *ir_data = data->chip_data;
+ struct vcpu_data *vcpu_pi_info = info;
- /* Set source-id of interrupt request */
- if (pdev)
- set_msi_sid(&irte, pdev);
- else
- set_hpet_sid(&irte, hpet_id);
+ /* stop posting interrupts, back to remapping mode */
+ if (!vcpu_pi_info) {
+ modify_irte(&ir_data->irq_2_iommu, &ir_data->irte_entry);
+ } else {
+ struct irte irte_pi;
- modify_irte(irq, &irte);
+ /*
+ * We are not caching the posted interrupt entry. We
+ * copy the data from the remapped entry and modify
+ * the fields which are relevant for posted mode. The
+ * cached remapped entry is used for switching back to
+ * remapped mode.
+ */
+ memset(&irte_pi, 0, sizeof(irte_pi));
+ dmar_copy_shared_irte(&irte_pi, &ir_data->irte_entry);
+
+ /* Update the posted mode fields */
+ irte_pi.p_pst = 1;
+ irte_pi.p_urgent = 0;
+ irte_pi.p_vector = vcpu_pi_info->vector;
+ irte_pi.pda_l = (vcpu_pi_info->pi_desc_addr >>
+ (32 - PDA_LOW_BIT)) & ~(-1UL << PDA_LOW_BIT);
+ irte_pi.pda_h = (vcpu_pi_info->pi_desc_addr >> 32) &
+ ~(-1UL << PDA_HIGH_BIT);
+
+ modify_irte(&ir_data->irq_2_iommu, &irte_pi);
+ }
- msg->address_hi = MSI_ADDR_BASE_HI;
- msg->data = sub_handle;
- msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
- MSI_ADDR_IR_SHV |
- MSI_ADDR_IR_INDEX1(ir_index) |
- MSI_ADDR_IR_INDEX2(ir_index);
+ return 0;
}
-/*
- * Map the PCI dev to the corresponding remapping hardware unit
- * and allocate 'nvec' consecutive interrupt-remapping table entries
- * in it.
- */
-static int intel_msi_alloc_irq(struct pci_dev *dev, int irq, int nvec)
+static struct irq_chip intel_ir_chip = {
+ .irq_ack = ir_ack_apic_edge,
+ .irq_set_affinity = intel_ir_set_affinity,
+ .irq_compose_msi_msg = intel_ir_compose_msi_msg,
+ .irq_set_vcpu_affinity = intel_ir_set_vcpu_affinity,
+};
+
+static void intel_irq_remapping_prepare_irte(struct intel_ir_data *data,
+ struct irq_cfg *irq_cfg,
+ struct irq_alloc_info *info,
+ int index, int sub_handle)
{
- struct intel_iommu *iommu;
- int index;
+ struct IR_IO_APIC_route_entry *entry;
+ struct irte *irte = &data->irte_entry;
+ struct msi_msg *msg = &data->msi_entry;
+
+ prepare_irte(irte, irq_cfg->vector, irq_cfg->dest_apicid);
+ switch (info->type) {
+ case X86_IRQ_ALLOC_TYPE_IOAPIC:
+ /* Set source-id of interrupt request */
+ set_ioapic_sid(irte, info->ioapic_id);
+ apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: Set IRTE entry (P:%d FPD:%d Dst_Mode:%d Redir_hint:%d Trig_Mode:%d Dlvry_Mode:%X Avail:%X Vector:%02X Dest:%08X SID:%04X SQ:%X SVT:%X)\n",
+ info->ioapic_id, irte->present, irte->fpd,
+ irte->dst_mode, irte->redir_hint,
+ irte->trigger_mode, irte->dlvry_mode,
+ irte->avail, irte->vector, irte->dest_id,
+ irte->sid, irte->sq, irte->svt);
+
+ entry = (struct IR_IO_APIC_route_entry *)info->ioapic_entry;
+ info->ioapic_entry = NULL;
+ memset(entry, 0, sizeof(*entry));
+ entry->index2 = (index >> 15) & 0x1;
+ entry->zero = 0;
+ entry->format = 1;
+ entry->index = (index & 0x7fff);
+ /*
+ * IO-APIC RTE will be configured with virtual vector.
+ * irq handler will do the explicit EOI to the io-apic.
+ */
+ entry->vector = info->ioapic_pin;
+ entry->mask = 0; /* enable IRQ */
+ entry->trigger = info->ioapic_trigger;
+ entry->polarity = info->ioapic_polarity;
+ if (info->ioapic_trigger)
+ entry->mask = 1; /* Mask level triggered irqs. */
+ break;
+
+ case X86_IRQ_ALLOC_TYPE_HPET:
+ case X86_IRQ_ALLOC_TYPE_MSI:
+ case X86_IRQ_ALLOC_TYPE_MSIX:
+ if (info->type == X86_IRQ_ALLOC_TYPE_HPET)
+ set_hpet_sid(irte, info->hpet_id);
+ else
+ set_msi_sid(irte, info->msi_dev);
+
+ msg->address_hi = MSI_ADDR_BASE_HI;
+ msg->data = sub_handle;
+ msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
+ MSI_ADDR_IR_SHV |
+ MSI_ADDR_IR_INDEX1(index) |
+ MSI_ADDR_IR_INDEX2(index);
+ break;
+
+ default:
+ BUG_ON(1);
+ break;
+ }
+}
- down_read(&dmar_global_lock);
- iommu = map_dev_to_ir(dev);
- if (!iommu) {
- printk(KERN_ERR
- "Unable to map PCI %s to iommu\n", pci_name(dev));
- index = -ENOENT;
- } else {
- index = alloc_irte(iommu, irq, nvec);
- if (index < 0) {
- printk(KERN_ERR
- "Unable to allocate %d IRTE for PCI %s\n",
- nvec, pci_name(dev));
- index = -ENOSPC;
+static void intel_free_irq_resources(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ struct irq_data *irq_data;
+ struct intel_ir_data *data;
+ struct irq_2_iommu *irq_iommu;
+ unsigned long flags;
+ int i;
+ for (i = 0; i < nr_irqs; i++) {
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+ if (irq_data && irq_data->chip_data) {
+ data = irq_data->chip_data;
+ irq_iommu = &data->irq_2_iommu;
+ raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
+ clear_entries(irq_iommu);
+ raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+ irq_domain_reset_irq_data(irq_data);
+ kfree(data);
}
}
- up_read(&dmar_global_lock);
-
- return index;
}
-static int intel_msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
- int index, int sub_handle)
+static int intel_irq_remapping_alloc(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs,
+ void *arg)
{
- struct intel_iommu *iommu;
- int ret = -ENOENT;
+ struct intel_iommu *iommu = domain->host_data;
+ struct irq_alloc_info *info = arg;
+ struct intel_ir_data *data, *ird;
+ struct irq_data *irq_data;
+ struct irq_cfg *irq_cfg;
+ int i, ret, index;
+
+ if (!info || !iommu)
+ return -EINVAL;
+ if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_MSI &&
+ info->type != X86_IRQ_ALLOC_TYPE_MSIX)
+ return -EINVAL;
+
+ /*
+ * With IRQ remapping enabled, don't need contiguous CPU vectors
+ * to support multiple MSI interrupts.
+ */
+ if (info->type == X86_IRQ_ALLOC_TYPE_MSI)
+ info->flags &= ~X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
+
+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+ if (ret < 0)
+ return ret;
+
+ ret = -ENOMEM;
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ goto out_free_parent;
down_read(&dmar_global_lock);
- iommu = map_dev_to_ir(pdev);
- if (iommu) {
- /*
- * setup the mapping between the irq and the IRTE
- * base index, the sub_handle pointing to the
- * appropriate interrupt remap table entry.
- */
- set_irte_irq(irq, iommu, index, sub_handle);
- ret = 0;
- }
+ index = alloc_irte(iommu, virq, &data->irq_2_iommu, nr_irqs);
up_read(&dmar_global_lock);
+ if (index < 0) {
+ pr_warn("Failed to allocate IRTE\n");
+ kfree(data);
+ goto out_free_parent;
+ }
+
+ for (i = 0; i < nr_irqs; i++) {
+ irq_data = irq_domain_get_irq_data(domain, virq + i);
+ irq_cfg = irqd_cfg(irq_data);
+ if (!irq_data || !irq_cfg) {
+ ret = -EINVAL;
+ goto out_free_data;
+ }
+
+ if (i > 0) {
+ ird = kzalloc(sizeof(*ird), GFP_KERNEL);
+ if (!ird)
+ goto out_free_data;
+ /* Initialize the common data */
+ ird->irq_2_iommu = data->irq_2_iommu;
+ ird->irq_2_iommu.sub_handle = i;
+ } else {
+ ird = data;
+ }
+ irq_data->hwirq = (index << 16) + i;
+ irq_data->chip_data = ird;
+ irq_data->chip = &intel_ir_chip;
+ intel_irq_remapping_prepare_irte(ird, irq_cfg, info, index, i);
+ irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT);
+ }
+ return 0;
+
+out_free_data:
+ intel_free_irq_resources(domain, virq, i);
+out_free_parent:
+ irq_domain_free_irqs_common(domain, virq, nr_irqs);
return ret;
}
-static int intel_alloc_hpet_msi(unsigned int irq, unsigned int id)
+static void intel_irq_remapping_free(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
{
- int ret = -1;
- struct intel_iommu *iommu;
- int index;
+ intel_free_irq_resources(domain, virq, nr_irqs);
+ irq_domain_free_irqs_common(domain, virq, nr_irqs);
+}
- down_read(&dmar_global_lock);
- iommu = map_hpet_to_ir(id);
- if (iommu) {
- index = alloc_irte(iommu, irq, 1);
- if (index >= 0)
- ret = 0;
- }
- up_read(&dmar_global_lock);
+static void intel_irq_remapping_activate(struct irq_domain *domain,
+ struct irq_data *irq_data)
+{
+ struct intel_ir_data *data = irq_data->chip_data;
- return ret;
+ modify_irte(&data->irq_2_iommu, &data->irte_entry);
}
-struct irq_remap_ops intel_irq_remap_ops = {
- .prepare = intel_prepare_irq_remapping,
- .enable = intel_enable_irq_remapping,
- .disable = disable_irq_remapping,
- .reenable = reenable_irq_remapping,
- .enable_faulting = enable_drhd_fault_handling,
- .setup_ioapic_entry = intel_setup_ioapic_entry,
- .set_affinity = intel_ioapic_set_affinity,
- .free_irq = free_irte,
- .compose_msi_msg = intel_compose_msi_msg,
- .msi_alloc_irq = intel_msi_alloc_irq,
- .msi_setup_irq = intel_msi_setup_irq,
- .alloc_hpet_msi = intel_alloc_hpet_msi,
+static void intel_irq_remapping_deactivate(struct irq_domain *domain,
+ struct irq_data *irq_data)
+{
+ struct intel_ir_data *data = irq_data->chip_data;
+ struct irte entry;
+
+ memset(&entry, 0, sizeof(entry));
+ modify_irte(&data->irq_2_iommu, &entry);
+}
+
+static struct irq_domain_ops intel_ir_domain_ops = {
+ .alloc = intel_irq_remapping_alloc,
+ .free = intel_irq_remapping_free,
+ .activate = intel_irq_remapping_activate,
+ .deactivate = intel_irq_remapping_deactivate,
};
/*
@@ -1242,28 +1397,12 @@ static int dmar_ir_add(struct dmar_drhd_unit *dmaru, struct intel_iommu *iommu)
/* Setup Interrupt-remapping now. */
ret = intel_setup_irq_remapping(iommu);
if (ret) {
- pr_err("DRHD %Lx: failed to allocate resource\n",
- iommu->reg_phys);
- ir_remove_ioapic_hpet_scope(iommu);
- return ret;
- }
-
- if (!iommu->qi) {
- /* Clear previous faults. */
- dmar_fault(-1, iommu);
- iommu_disable_irq_remapping(iommu);
- dmar_disable_qi(iommu);
- }
-
- /* Enable queued invalidation */
- ret = dmar_enable_qi(iommu);
- if (!ret) {
- iommu_set_irq_remapping(iommu, eim);
- } else {
- pr_err("DRHD %Lx: failed to enable queued invalidation, ecap %Lx, ret %d\n",
- iommu->reg_phys, iommu->ecap, ret);
+ pr_err("Failed to setup irq remapping for %s\n",
+ iommu->name);
intel_teardown_irq_remapping(iommu);
ir_remove_ioapic_hpet_scope(iommu);
+ } else {
+ iommu_enable_irq_remapping(iommu);
}
return ret;
@@ -1280,6 +1419,9 @@ int dmar_ir_hotplug(struct dmar_drhd_unit *dmaru, bool insert)
return -EINVAL;
if (!ecap_ir_support(iommu->ecap))
return 0;
+ if (irq_remapping_cap(IRQ_POSTING_CAP) &&
+ !cap_pi_support(iommu->cap))
+ return -EBUSY;
if (insert) {
if (!iommu->ir_table)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index d4f527e56679..49e7542510d1 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -16,7 +16,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#define pr_fmt(fmt) "%s: " fmt, __func__
+#define pr_fmt(fmt) "iommu: " fmt
#include <linux/device.h>
#include <linux/kernel.h>
@@ -51,6 +51,8 @@ struct iommu_group {
void (*iommu_data_release)(void *iommu_data);
char *name;
int id;
+ struct iommu_domain *default_domain;
+ struct iommu_domain *domain;
};
struct iommu_device {
@@ -75,6 +77,15 @@ struct iommu_group_attribute iommu_group_attr_##_name = \
#define to_iommu_group(_kobj) \
container_of(_kobj, struct iommu_group, kobj)
+static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
+ unsigned type);
+static int __iommu_attach_device(struct iommu_domain *domain,
+ struct device *dev);
+static int __iommu_attach_group(struct iommu_domain *domain,
+ struct iommu_group *group);
+static void __iommu_detach_group(struct iommu_domain *domain,
+ struct iommu_group *group);
+
static ssize_t iommu_group_attr_show(struct kobject *kobj,
struct attribute *__attr, char *buf)
{
@@ -128,6 +139,8 @@ static void iommu_group_release(struct kobject *kobj)
{
struct iommu_group *group = to_iommu_group(kobj);
+ pr_debug("Releasing group %d\n", group->id);
+
if (group->iommu_data_release)
group->iommu_data_release(group->iommu_data);
@@ -135,6 +148,9 @@ static void iommu_group_release(struct kobject *kobj)
ida_remove(&iommu_group_ida, group->id);
mutex_unlock(&iommu_group_mutex);
+ if (group->default_domain)
+ iommu_domain_free(group->default_domain);
+
kfree(group->name);
kfree(group);
}
@@ -207,6 +223,8 @@ again:
*/
kobject_put(&group->kobj);
+ pr_debug("Allocated group %d\n", group->id);
+
return group;
}
EXPORT_SYMBOL_GPL(iommu_group_alloc);
@@ -307,6 +325,52 @@ int iommu_group_set_name(struct iommu_group *group, const char *name)
}
EXPORT_SYMBOL_GPL(iommu_group_set_name);
+static int iommu_group_create_direct_mappings(struct iommu_group *group,
+ struct device *dev)
+{
+ struct iommu_domain *domain = group->default_domain;
+ struct iommu_dm_region *entry;
+ struct list_head mappings;
+ unsigned long pg_size;
+ int ret = 0;
+
+ if (!domain || domain->type != IOMMU_DOMAIN_DMA)
+ return 0;
+
+ BUG_ON(!domain->ops->pgsize_bitmap);
+
+ pg_size = 1UL << __ffs(domain->ops->pgsize_bitmap);
+ INIT_LIST_HEAD(&mappings);
+
+ iommu_get_dm_regions(dev, &mappings);
+
+ /* We need to consider overlapping regions for different devices */
+ list_for_each_entry(entry, &mappings, list) {
+ dma_addr_t start, end, addr;
+
+ start = ALIGN(entry->start, pg_size);
+ end = ALIGN(entry->start + entry->length, pg_size);
+
+ for (addr = start; addr < end; addr += pg_size) {
+ phys_addr_t phys_addr;
+
+ phys_addr = iommu_iova_to_phys(domain, addr);
+ if (phys_addr)
+ continue;
+
+ ret = iommu_map(domain, addr, addr, pg_size, entry->prot);
+ if (ret)
+ goto out;
+ }
+
+ }
+
+out:
+ iommu_put_dm_regions(dev, &mappings);
+
+ return ret;
+}
+
/**
* iommu_group_add_device - add a device to an iommu group
* @group: the group into which to add the device (reference should be held)
@@ -363,8 +427,12 @@ rename:
dev->iommu_group = group;
+ iommu_group_create_direct_mappings(group, dev);
+
mutex_lock(&group->mutex);
list_add_tail(&device->list, &group->devices);
+ if (group->domain)
+ __iommu_attach_device(group->domain, dev);
mutex_unlock(&group->mutex);
/* Notify any listeners about change to group. */
@@ -372,6 +440,9 @@ rename:
IOMMU_GROUP_NOTIFY_ADD_DEVICE, dev);
trace_add_device_to_group(group->id, dev);
+
+ pr_info("Adding device %s to group %d\n", dev_name(dev), group->id);
+
return 0;
}
EXPORT_SYMBOL_GPL(iommu_group_add_device);
@@ -388,6 +459,8 @@ void iommu_group_remove_device(struct device *dev)
struct iommu_group *group = dev->iommu_group;
struct iommu_device *tmp_device, *device = NULL;
+ pr_info("Removing device %s from group %d\n", dev_name(dev), group->id);
+
/* Pre-notify listeners that a device is being removed. */
blocking_notifier_call_chain(&group->notifier,
IOMMU_GROUP_NOTIFY_DEL_DEVICE, dev);
@@ -417,6 +490,17 @@ void iommu_group_remove_device(struct device *dev)
}
EXPORT_SYMBOL_GPL(iommu_group_remove_device);
+static int iommu_group_device_count(struct iommu_group *group)
+{
+ struct iommu_device *entry;
+ int ret = 0;
+
+ list_for_each_entry(entry, &group->devices, list)
+ ret++;
+
+ return ret;
+}
+
/**
* iommu_group_for_each_dev - iterate over each device in the group
* @group: the group
@@ -428,19 +512,30 @@ EXPORT_SYMBOL_GPL(iommu_group_remove_device);
* The group->mutex is held across callbacks, which will block calls to
* iommu_group_add/remove_device.
*/
-int iommu_group_for_each_dev(struct iommu_group *group, void *data,
- int (*fn)(struct device *, void *))
+static int __iommu_group_for_each_dev(struct iommu_group *group, void *data,
+ int (*fn)(struct device *, void *))
{
struct iommu_device *device;
int ret = 0;
- mutex_lock(&group->mutex);
list_for_each_entry(device, &group->devices, list) {
ret = fn(device->dev, data);
if (ret)
break;
}
+ return ret;
+}
+
+
+int iommu_group_for_each_dev(struct iommu_group *group, void *data,
+ int (*fn)(struct device *, void *))
+{
+ int ret;
+
+ mutex_lock(&group->mutex);
+ ret = __iommu_group_for_each_dev(group, data, fn);
mutex_unlock(&group->mutex);
+
return ret;
}
EXPORT_SYMBOL_GPL(iommu_group_for_each_dev);
@@ -692,7 +787,19 @@ static struct iommu_group *iommu_group_get_for_pci_dev(struct pci_dev *pdev)
return group;
/* No shared group found, allocate new */
- return iommu_group_alloc();
+ group = iommu_group_alloc();
+ if (IS_ERR(group))
+ return NULL;
+
+ /*
+ * Try to allocate a default domain - needs support from the
+ * IOMMU driver.
+ */
+ group->default_domain = __iommu_domain_alloc(pdev->dev.bus,
+ IOMMU_DOMAIN_DMA);
+ group->domain = group->default_domain;
+
+ return group;
}
/**
@@ -731,6 +838,11 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev)
return group;
}
+struct iommu_domain *iommu_group_default_domain(struct iommu_group *group)
+{
+ return group->default_domain;
+}
+
static int add_iommu_group(struct device *dev, void *data)
{
struct iommu_callback_data *cb = data;
@@ -741,7 +853,16 @@ static int add_iommu_group(struct device *dev, void *data)
WARN_ON(dev->iommu_group);
- ops->add_device(dev);
+ return ops->add_device(dev);
+}
+
+static int remove_iommu_group(struct device *dev, void *data)
+{
+ struct iommu_callback_data *cb = data;
+ const struct iommu_ops *ops = cb->ops;
+
+ if (ops->remove_device && dev->iommu_group)
+ ops->remove_device(dev);
return 0;
}
@@ -761,7 +882,7 @@ static int iommu_bus_notifier(struct notifier_block *nb,
if (action == BUS_NOTIFY_ADD_DEVICE) {
if (ops->add_device)
return ops->add_device(dev);
- } else if (action == BUS_NOTIFY_DEL_DEVICE) {
+ } else if (action == BUS_NOTIFY_REMOVED_DEVICE) {
if (ops->remove_device && dev->iommu_group) {
ops->remove_device(dev);
return 0;
@@ -814,19 +935,25 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops)
nb->notifier_call = iommu_bus_notifier;
err = bus_register_notifier(bus, nb);
- if (err) {
- kfree(nb);
- return err;
- }
+ if (err)
+ goto out_free;
err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
- if (err) {
- bus_unregister_notifier(bus, nb);
- kfree(nb);
- return err;
- }
+ if (err)
+ goto out_err;
+
return 0;
+
+out_err:
+ /* Clean up */
+ bus_for_each_dev(bus, NULL, &cb, remove_iommu_group);
+ bus_unregister_notifier(bus, nb);
+
+out_free:
+ kfree(nb);
+
+ return err;
}
/**
@@ -898,22 +1025,28 @@ void iommu_set_fault_handler(struct iommu_domain *domain,
}
EXPORT_SYMBOL_GPL(iommu_set_fault_handler);
-struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
+static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
+ unsigned type)
{
struct iommu_domain *domain;
if (bus == NULL || bus->iommu_ops == NULL)
return NULL;
- domain = bus->iommu_ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED);
+ domain = bus->iommu_ops->domain_alloc(type);
if (!domain)
return NULL;
domain->ops = bus->iommu_ops;
- domain->type = IOMMU_DOMAIN_UNMANAGED;
+ domain->type = type;
return domain;
}
+
+struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
+{
+ return __iommu_domain_alloc(bus, IOMMU_DOMAIN_UNMANAGED);
+}
EXPORT_SYMBOL_GPL(iommu_domain_alloc);
void iommu_domain_free(struct iommu_domain *domain)
@@ -922,7 +1055,8 @@ void iommu_domain_free(struct iommu_domain *domain)
}
EXPORT_SYMBOL_GPL(iommu_domain_free);
-int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
+static int __iommu_attach_device(struct iommu_domain *domain,
+ struct device *dev)
{
int ret;
if (unlikely(domain->ops->attach_dev == NULL))
@@ -933,9 +1067,38 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
trace_attach_device_to_domain(dev);
return ret;
}
+
+int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
+{
+ struct iommu_group *group;
+ int ret;
+
+ group = iommu_group_get(dev);
+ /* FIXME: Remove this when groups a mandatory for iommu drivers */
+ if (group == NULL)
+ return __iommu_attach_device(domain, dev);
+
+ /*
+ * We have a group - lock it to make sure the device-count doesn't
+ * change while we are attaching
+ */
+ mutex_lock(&group->mutex);
+ ret = -EINVAL;
+ if (iommu_group_device_count(group) != 1)
+ goto out_unlock;
+
+ ret = __iommu_attach_group(domain, group);
+
+out_unlock:
+ mutex_unlock(&group->mutex);
+ iommu_group_put(group);
+
+ return ret;
+}
EXPORT_SYMBOL_GPL(iommu_attach_device);
-void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
+static void __iommu_detach_device(struct iommu_domain *domain,
+ struct device *dev)
{
if (unlikely(domain->ops->detach_dev == NULL))
return;
@@ -943,8 +1106,48 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
domain->ops->detach_dev(domain, dev);
trace_detach_device_from_domain(dev);
}
+
+void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
+{
+ struct iommu_group *group;
+
+ group = iommu_group_get(dev);
+ /* FIXME: Remove this when groups a mandatory for iommu drivers */
+ if (group == NULL)
+ return __iommu_detach_device(domain, dev);
+
+ mutex_lock(&group->mutex);
+ if (iommu_group_device_count(group) != 1) {
+ WARN_ON(1);
+ goto out_unlock;
+ }
+
+ __iommu_detach_group(domain, group);
+
+out_unlock:
+ mutex_unlock(&group->mutex);
+ iommu_group_put(group);
+}
EXPORT_SYMBOL_GPL(iommu_detach_device);
+struct iommu_domain *iommu_get_domain_for_dev(struct device *dev)
+{
+ struct iommu_domain *domain;
+ struct iommu_group *group;
+
+ group = iommu_group_get(dev);
+ /* FIXME: Remove this when groups a mandatory for iommu drivers */
+ if (group == NULL)
+ return NULL;
+
+ domain = group->domain;
+
+ iommu_group_put(group);
+
+ return domain;
+}
+EXPORT_SYMBOL_GPL(iommu_get_domain_for_dev);
+
/*
* IOMMU groups are really the natrual working unit of the IOMMU, but
* the IOMMU API works on domains and devices. Bridge that gap by
@@ -959,13 +1162,34 @@ static int iommu_group_do_attach_device(struct device *dev, void *data)
{
struct iommu_domain *domain = data;
- return iommu_attach_device(domain, dev);
+ return __iommu_attach_device(domain, dev);
+}
+
+static int __iommu_attach_group(struct iommu_domain *domain,
+ struct iommu_group *group)
+{
+ int ret;
+
+ if (group->default_domain && group->domain != group->default_domain)
+ return -EBUSY;
+
+ ret = __iommu_group_for_each_dev(group, domain,
+ iommu_group_do_attach_device);
+ if (ret == 0)
+ group->domain = domain;
+
+ return ret;
}
int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group)
{
- return iommu_group_for_each_dev(group, domain,
- iommu_group_do_attach_device);
+ int ret;
+
+ mutex_lock(&group->mutex);
+ ret = __iommu_attach_group(domain, group);
+ mutex_unlock(&group->mutex);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(iommu_attach_group);
@@ -973,14 +1197,40 @@ static int iommu_group_do_detach_device(struct device *dev, void *data)
{
struct iommu_domain *domain = data;
- iommu_detach_device(domain, dev);
+ __iommu_detach_device(domain, dev);
return 0;
}
+static void __iommu_detach_group(struct iommu_domain *domain,
+ struct iommu_group *group)
+{
+ int ret;
+
+ if (!group->default_domain) {
+ __iommu_group_for_each_dev(group, domain,
+ iommu_group_do_detach_device);
+ group->domain = NULL;
+ return;
+ }
+
+ if (group->domain == group->default_domain)
+ return;
+
+ /* Detach by re-attaching to the default domain */
+ ret = __iommu_group_for_each_dev(group, group->default_domain,
+ iommu_group_do_attach_device);
+ if (ret != 0)
+ WARN_ON(1);
+ else
+ group->domain = group->default_domain;
+}
+
void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group)
{
- iommu_group_for_each_dev(group, domain, iommu_group_do_detach_device);
+ mutex_lock(&group->mutex);
+ __iommu_detach_group(domain, group);
+ mutex_unlock(&group->mutex);
}
EXPORT_SYMBOL_GPL(iommu_detach_group);
@@ -1207,7 +1457,7 @@ static int __init iommu_init(void)
return 0;
}
-arch_initcall(iommu_init);
+core_initcall(iommu_init);
int iommu_domain_get_attr(struct iommu_domain *domain,
enum iommu_attr attr, void *data)
@@ -1273,3 +1523,72 @@ int iommu_domain_set_attr(struct iommu_domain *domain,
return ret;
}
EXPORT_SYMBOL_GPL(iommu_domain_set_attr);
+
+void iommu_get_dm_regions(struct device *dev, struct list_head *list)
+{
+ const struct iommu_ops *ops = dev->bus->iommu_ops;
+
+ if (ops && ops->get_dm_regions)
+ ops->get_dm_regions(dev, list);
+}
+
+void iommu_put_dm_regions(struct device *dev, struct list_head *list)
+{
+ const struct iommu_ops *ops = dev->bus->iommu_ops;
+
+ if (ops && ops->put_dm_regions)
+ ops->put_dm_regions(dev, list);
+}
+
+/* Request that a device is direct mapped by the IOMMU */
+int iommu_request_dm_for_dev(struct device *dev)
+{
+ struct iommu_domain *dm_domain;
+ struct iommu_group *group;
+ int ret;
+
+ /* Device must already be in a group before calling this function */
+ group = iommu_group_get_for_dev(dev);
+ if (IS_ERR(group))
+ return PTR_ERR(group);
+
+ mutex_lock(&group->mutex);
+
+ /* Check if the default domain is already direct mapped */
+ ret = 0;
+ if (group->default_domain &&
+ group->default_domain->type == IOMMU_DOMAIN_IDENTITY)
+ goto out;
+
+ /* Don't change mappings of existing devices */
+ ret = -EBUSY;
+ if (iommu_group_device_count(group) != 1)
+ goto out;
+
+ /* Allocate a direct mapped domain */
+ ret = -ENOMEM;
+ dm_domain = __iommu_domain_alloc(dev->bus, IOMMU_DOMAIN_IDENTITY);
+ if (!dm_domain)
+ goto out;
+
+ /* Attach the device to the domain */
+ ret = __iommu_attach_group(dm_domain, group);
+ if (ret) {
+ iommu_domain_free(dm_domain);
+ goto out;
+ }
+
+ /* Make the direct mapped domain the default for this group */
+ if (group->default_domain)
+ iommu_domain_free(group->default_domain);
+ group->default_domain = dm_domain;
+
+ pr_info("Using direct mapping for device %s\n", dev_name(dev));
+
+ ret = 0;
+out:
+ mutex_unlock(&group->mutex);
+ iommu_group_put(group);
+
+ return ret;
+}
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index 9dd8208312c2..b7c3d923f3e1 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -227,6 +227,7 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova)
/* Figure out where to put new node */
while (*new) {
struct iova *this = container_of(*new, struct iova, node);
+
parent = *new;
if (iova->pfn_lo < this->pfn_lo)
@@ -350,6 +351,7 @@ void
free_iova(struct iova_domain *iovad, unsigned long pfn)
{
struct iova *iova = find_iova(iovad, pfn);
+
if (iova)
__free_iova(iovad, iova);
@@ -369,6 +371,7 @@ void put_iova_domain(struct iova_domain *iovad)
node = rb_first(&iovad->rbroot);
while (node) {
struct iova *iova = container_of(node, struct iova, node);
+
rb_erase(node, &iovad->rbroot);
free_iova_mem(iova);
node = rb_first(&iovad->rbroot);
@@ -482,6 +485,7 @@ copy_reserved_iova(struct iova_domain *from, struct iova_domain *to)
for (node = rb_first(&from->rbroot); node; node = rb_next(node)) {
struct iova *iova = container_of(node, struct iova, node);
struct iova *new_iova;
+
new_iova = reserve_iova(to, iova->pfn_lo, iova->pfn_hi);
if (!new_iova)
printk(KERN_ERR "Reserve iova range %lx@%lx failed\n",
diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c
index 390079ee1350..2d9993062ded 100644
--- a/drivers/iommu/irq_remapping.c
+++ b/drivers/iommu/irq_remapping.c
@@ -6,6 +6,7 @@
#include <linux/msi.h>
#include <linux/irq.h>
#include <linux/pci.h>
+#include <linux/irqdomain.h>
#include <asm/hw_irq.h>
#include <asm/irq_remapping.h>
@@ -21,21 +22,11 @@ int irq_remap_broken;
int disable_sourceid_checking;
int no_x2apic_optout;
+int disable_irq_post = 1;
+
static int disable_irq_remap;
static struct irq_remap_ops *remap_ops;
-static int msi_alloc_remapped_irq(struct pci_dev *pdev, int irq, int nvec);
-static int msi_setup_remapped_irq(struct pci_dev *pdev, unsigned int irq,
- int index, int sub_handle);
-static int set_remapped_irq_affinity(struct irq_data *data,
- const struct cpumask *mask,
- bool force);
-
-static bool irq_remapped(struct irq_cfg *cfg)
-{
- return (cfg->remapped == 1);
-}
-
static void irq_remapping_disable_io_apic(void)
{
/*
@@ -49,117 +40,9 @@ static void irq_remapping_disable_io_apic(void)
disconnect_bsp_APIC(0);
}
-static int do_setup_msi_irqs(struct pci_dev *dev, int nvec)
-{
- int ret, sub_handle, nvec_pow2, index = 0;
- unsigned int irq;
- struct msi_desc *msidesc;
-
- msidesc = list_entry(dev->msi_list.next, struct msi_desc, list);
-
- irq = irq_alloc_hwirqs(nvec, dev_to_node(&dev->dev));
- if (irq == 0)
- return -ENOSPC;
-
- nvec_pow2 = __roundup_pow_of_two(nvec);
- for (sub_handle = 0; sub_handle < nvec; sub_handle++) {
- if (!sub_handle) {
- index = msi_alloc_remapped_irq(dev, irq, nvec_pow2);
- if (index < 0) {
- ret = index;
- goto error;
- }
- } else {
- ret = msi_setup_remapped_irq(dev, irq + sub_handle,
- index, sub_handle);
- if (ret < 0)
- goto error;
- }
- ret = setup_msi_irq(dev, msidesc, irq, sub_handle);
- if (ret < 0)
- goto error;
- }
- return 0;
-
-error:
- irq_free_hwirqs(irq, nvec);
-
- /*
- * Restore altered MSI descriptor fields and prevent just destroyed
- * IRQs from tearing down again in default_teardown_msi_irqs()
- */
- msidesc->irq = 0;
-
- return ret;
-}
-
-static int do_setup_msix_irqs(struct pci_dev *dev, int nvec)
-{
- int node, ret, sub_handle, index = 0;
- struct msi_desc *msidesc;
- unsigned int irq;
-
- node = dev_to_node(&dev->dev);
- sub_handle = 0;
-
- list_for_each_entry(msidesc, &dev->msi_list, list) {
-
- irq = irq_alloc_hwirq(node);
- if (irq == 0)
- return -1;
-
- if (sub_handle == 0)
- ret = index = msi_alloc_remapped_irq(dev, irq, nvec);
- else
- ret = msi_setup_remapped_irq(dev, irq, index, sub_handle);
-
- if (ret < 0)
- goto error;
-
- ret = setup_msi_irq(dev, msidesc, irq, 0);
- if (ret < 0)
- goto error;
-
- sub_handle += 1;
- irq += 1;
- }
-
- return 0;
-
-error:
- irq_free_hwirq(irq);
- return ret;
-}
-
-static int irq_remapping_setup_msi_irqs(struct pci_dev *dev,
- int nvec, int type)
-{
- if (type == PCI_CAP_ID_MSI)
- return do_setup_msi_irqs(dev, nvec);
- else
- return do_setup_msix_irqs(dev, nvec);
-}
-
-static void eoi_ioapic_pin_remapped(int apic, int pin, int vector)
-{
- /*
- * Intr-remapping uses pin number as the virtual vector
- * in the RTE. Actual vector is programmed in
- * intr-remapping table entry. Hence for the io-apic
- * EOI we use the pin number.
- */
- io_apic_eoi(apic, pin);
-}
-
static void __init irq_remapping_modify_x86_ops(void)
{
x86_io_apic_ops.disable = irq_remapping_disable_io_apic;
- x86_io_apic_ops.set_affinity = set_remapped_irq_affinity;
- x86_io_apic_ops.setup_entry = setup_ioapic_remapped_entry;
- x86_io_apic_ops.eoi_ioapic_pin = eoi_ioapic_pin_remapped;
- x86_msi.setup_msi_irqs = irq_remapping_setup_msi_irqs;
- x86_msi.setup_hpet_msi = setup_hpet_msi_remapped;
- x86_msi.compose_msi_msg = compose_remapped_msi_msg;
}
static __init int setup_nointremap(char *str)
@@ -198,6 +81,15 @@ void set_irq_remapping_broken(void)
irq_remap_broken = 1;
}
+bool irq_remapping_cap(enum irq_remap_cap cap)
+{
+ if (!remap_ops || disable_irq_post)
+ return 0;
+
+ return (remap_ops->capability & (1 << cap));
+}
+EXPORT_SYMBOL_GPL(irq_remapping_cap);
+
int __init irq_remapping_prepare(void)
{
if (disable_irq_remap)
@@ -254,113 +146,48 @@ int __init irq_remap_enable_fault_handling(void)
return remap_ops->enable_faulting();
}
-int setup_ioapic_remapped_entry(int irq,
- struct IO_APIC_route_entry *entry,
- unsigned int destination, int vector,
- struct io_apic_irq_attr *attr)
-{
- if (!remap_ops->setup_ioapic_entry)
- return -ENODEV;
-
- return remap_ops->setup_ioapic_entry(irq, entry, destination,
- vector, attr);
-}
-
-static int set_remapped_irq_affinity(struct irq_data *data,
- const struct cpumask *mask, bool force)
-{
- if (!config_enabled(CONFIG_SMP) || !remap_ops->set_affinity)
- return 0;
-
- return remap_ops->set_affinity(data, mask, force);
-}
-
-void free_remapped_irq(int irq)
-{
- struct irq_cfg *cfg = irq_cfg(irq);
-
- if (irq_remapped(cfg) && remap_ops->free_irq)
- remap_ops->free_irq(irq);
-}
-
-void compose_remapped_msi_msg(struct pci_dev *pdev,
- unsigned int irq, unsigned int dest,
- struct msi_msg *msg, u8 hpet_id)
-{
- struct irq_cfg *cfg = irq_cfg(irq);
-
- if (!irq_remapped(cfg))
- native_compose_msi_msg(pdev, irq, dest, msg, hpet_id);
- else if (remap_ops->compose_msi_msg)
- remap_ops->compose_msi_msg(pdev, irq, dest, msg, hpet_id);
-}
-
-static int msi_alloc_remapped_irq(struct pci_dev *pdev, int irq, int nvec)
-{
- if (!remap_ops->msi_alloc_irq)
- return -ENODEV;
-
- return remap_ops->msi_alloc_irq(pdev, irq, nvec);
-}
-
-static int msi_setup_remapped_irq(struct pci_dev *pdev, unsigned int irq,
- int index, int sub_handle)
-{
- if (!remap_ops->msi_setup_irq)
- return -ENODEV;
-
- return remap_ops->msi_setup_irq(pdev, irq, index, sub_handle);
-}
-
-int setup_hpet_msi_remapped(unsigned int irq, unsigned int id)
-{
- int ret;
-
- if (!remap_ops->alloc_hpet_msi)
- return -ENODEV;
-
- ret = remap_ops->alloc_hpet_msi(irq, id);
- if (ret)
- return -EINVAL;
-
- return default_setup_hpet_msi(irq, id);
-}
-
void panic_if_irq_remap(const char *msg)
{
if (irq_remapping_enabled)
panic(msg);
}
-static void ir_ack_apic_edge(struct irq_data *data)
+void ir_ack_apic_edge(struct irq_data *data)
{
ack_APIC_irq();
}
-static void ir_ack_apic_level(struct irq_data *data)
+/**
+ * irq_remapping_get_ir_irq_domain - Get the irqdomain associated with the IOMMU
+ * device serving request @info
+ * @info: interrupt allocation information, used to identify the IOMMU device
+ *
+ * It's used to get parent irqdomain for HPET and IOAPIC irqdomains.
+ * Returns pointer to IRQ domain, or NULL on failure.
+ */
+struct irq_domain *
+irq_remapping_get_ir_irq_domain(struct irq_alloc_info *info)
{
- ack_APIC_irq();
- eoi_ioapic_irq(data->irq, irqd_cfg(data));
-}
+ if (!remap_ops || !remap_ops->get_ir_irq_domain)
+ return NULL;
-static void ir_print_prefix(struct irq_data *data, struct seq_file *p)
-{
- seq_printf(p, " IR-%s", data->chip->name);
+ return remap_ops->get_ir_irq_domain(info);
}
-void irq_remap_modify_chip_defaults(struct irq_chip *chip)
+/**
+ * irq_remapping_get_irq_domain - Get the irqdomain serving the request @info
+ * @info: interrupt allocation information, used to identify the IOMMU device
+ *
+ * There will be one PCI MSI/MSIX irqdomain associated with each interrupt
+ * remapping device, so this interface is used to retrieve the PCI MSI/MSIX
+ * irqdomain serving request @info.
+ * Returns pointer to IRQ domain, or NULL on failure.
+ */
+struct irq_domain *
+irq_remapping_get_irq_domain(struct irq_alloc_info *info)
{
- chip->irq_print_chip = ir_print_prefix;
- chip->irq_ack = ir_ack_apic_edge;
- chip->irq_eoi = ir_ack_apic_level;
- chip->irq_set_affinity = x86_io_apic_ops.set_affinity;
-}
+ if (!remap_ops || !remap_ops->get_irq_domain)
+ return NULL;
-bool setup_remapped_irq(int irq, struct irq_cfg *cfg, struct irq_chip *chip)
-{
- if (!irq_remapped(cfg))
- return false;
- irq_set_status_flags(irq, IRQ_MOVE_PCNTXT);
- irq_remap_modify_chip_defaults(chip);
- return true;
+ return remap_ops->get_irq_domain(info);
}
diff --git a/drivers/iommu/irq_remapping.h b/drivers/iommu/irq_remapping.h
index 7c70cc29ffe6..039c7af7b190 100644
--- a/drivers/iommu/irq_remapping.h
+++ b/drivers/iommu/irq_remapping.h
@@ -24,19 +24,22 @@
#ifdef CONFIG_IRQ_REMAP
-struct IO_APIC_route_entry;
-struct io_apic_irq_attr;
struct irq_data;
-struct cpumask;
-struct pci_dev;
struct msi_msg;
+struct irq_domain;
+struct irq_alloc_info;
extern int irq_remap_broken;
extern int disable_sourceid_checking;
extern int no_x2apic_optout;
extern int irq_remapping_enabled;
+extern int disable_irq_post;
+
struct irq_remap_ops {
+ /* The supported capabilities */
+ int capability;
+
/* Initializes hardware and makes it ready for remapping interrupts */
int (*prepare)(void);
@@ -52,40 +55,23 @@ struct irq_remap_ops {
/* Enable fault handling */
int (*enable_faulting)(void);
- /* IO-APIC setup routine */
- int (*setup_ioapic_entry)(int irq, struct IO_APIC_route_entry *,
- unsigned int, int,
- struct io_apic_irq_attr *);
-
- /* Set the CPU affinity of a remapped interrupt */
- int (*set_affinity)(struct irq_data *data, const struct cpumask *mask,
- bool force);
-
- /* Free an IRQ */
- int (*free_irq)(int);
+ /* Get the irqdomain associated the IOMMU device */
+ struct irq_domain *(*get_ir_irq_domain)(struct irq_alloc_info *);
- /* Create MSI msg to use for interrupt remapping */
- void (*compose_msi_msg)(struct pci_dev *,
- unsigned int, unsigned int,
- struct msi_msg *, u8);
-
- /* Allocate remapping resources for MSI */
- int (*msi_alloc_irq)(struct pci_dev *, int, int);
-
- /* Setup the remapped MSI irq */
- int (*msi_setup_irq)(struct pci_dev *, unsigned int, int, int);
-
- /* Setup interrupt remapping for an HPET MSI */
- int (*alloc_hpet_msi)(unsigned int, unsigned int);
+ /* Get the MSI irqdomain associated with the IOMMU device */
+ struct irq_domain *(*get_irq_domain)(struct irq_alloc_info *);
};
extern struct irq_remap_ops intel_irq_remap_ops;
extern struct irq_remap_ops amd_iommu_irq_ops;
+extern void ir_ack_apic_edge(struct irq_data *data);
+
#else /* CONFIG_IRQ_REMAP */
#define irq_remapping_enabled 0
#define irq_remap_broken 0
+#define disable_irq_post 1
#endif /* CONFIG_IRQ_REMAP */
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 4015560bf486..ebf0adb8e7ea 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -551,6 +551,15 @@ static void rk_iommu_zap_iova(struct rk_iommu_domain *rk_domain,
spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
}
+static void rk_iommu_zap_iova_first_last(struct rk_iommu_domain *rk_domain,
+ dma_addr_t iova, size_t size)
+{
+ rk_iommu_zap_iova(rk_domain, iova, SPAGE_SIZE);
+ if (size > SPAGE_SIZE)
+ rk_iommu_zap_iova(rk_domain, iova + size - SPAGE_SIZE,
+ SPAGE_SIZE);
+}
+
static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
dma_addr_t iova)
{
@@ -575,12 +584,6 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
rk_table_flush(page_table, NUM_PT_ENTRIES);
rk_table_flush(dte_addr, 1);
- /*
- * Zap the first iova of newly allocated page table so iommu evicts
- * old cached value of new dte from the iotlb.
- */
- rk_iommu_zap_iova(rk_domain, iova, SPAGE_SIZE);
-
done:
pt_phys = rk_dte_pt_address(dte);
return (u32 *)phys_to_virt(pt_phys);
@@ -630,6 +633,14 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
rk_table_flush(pte_addr, pte_count);
+ /*
+ * Zap the first and last iova to evict from iotlb any previously
+ * mapped cachelines holding stale values for its dte and pte.
+ * We only zap the first and last iova, since only they could have
+ * dte or pte shared with an existing mapping.
+ */
+ rk_iommu_zap_iova_first_last(rk_domain, iova, size);
+
return 0;
unwind:
/* Unmap the range of iovas that we just mapped */
@@ -774,7 +785,7 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
list_add_tail(&iommu->node, &rk_domain->iommus);
spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
- dev_info(dev, "Attached to iommu domain\n");
+ dev_dbg(dev, "Attached to iommu domain\n");
rk_iommu_disable_stall(iommu);
@@ -808,7 +819,7 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
iommu->domain = NULL;
- dev_info(dev, "Detached from iommu domain\n");
+ dev_dbg(dev, "Detached from iommu domain\n");
}
static struct iommu_domain *rk_iommu_domain_alloc(unsigned type)
@@ -1004,20 +1015,18 @@ static int rk_iommu_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_OF
static const struct of_device_id rk_iommu_dt_ids[] = {
{ .compatible = "rockchip,iommu" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids);
-#endif
static struct platform_driver rk_iommu_driver = {
.probe = rk_iommu_probe,
.remove = rk_iommu_remove,
.driver = {
.name = "rk_iommu",
- .of_match_table = of_match_ptr(rk_iommu_dt_ids),
+ .of_match_table = rk_iommu_dt_ids,
},
};
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 6de62a96e79c..99b9a9792975 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -30,6 +30,7 @@ config ARM_GIC_V3_ITS
config ARM_NVIC
bool
select IRQ_DOMAIN
+ select IRQ_DOMAIN_HIERARCHY
select GENERIC_IRQ_CHIP
config ARM_VIC
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index 5945223b73fa..5c82e3bdafdf 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/syscore_ops.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/interrupt.h>
@@ -34,9 +35,14 @@ struct combiner_chip_data {
unsigned int irq_mask;
void __iomem *base;
unsigned int parent_irq;
+#ifdef CONFIG_PM
+ u32 pm_save;
+#endif
};
+static struct combiner_chip_data *combiner_data;
static struct irq_domain *combiner_irq_domain;
+static unsigned int max_nr = 20;
static inline void __iomem *combiner_base(struct irq_data *data)
{
@@ -164,18 +170,16 @@ static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
return 0;
}
-static struct irq_domain_ops combiner_irq_domain_ops = {
+static const struct irq_domain_ops combiner_irq_domain_ops = {
.xlate = combiner_irq_domain_xlate,
.map = combiner_irq_domain_map,
};
static void __init combiner_init(void __iomem *combiner_base,
- struct device_node *np,
- unsigned int max_nr)
+ struct device_node *np)
{
int i, irq;
unsigned int nr_irq;
- struct combiner_chip_data *combiner_data;
nr_irq = max_nr * IRQ_IN_COMBINER;
@@ -201,11 +205,59 @@ static void __init combiner_init(void __iomem *combiner_base,
}
}
+#ifdef CONFIG_PM
+
+/**
+ * combiner_suspend - save interrupt combiner state before suspend
+ *
+ * Save the interrupt enable set register for all combiner groups since
+ * the state is lost when the system enters into a sleep state.
+ *
+ */
+static int combiner_suspend(void)
+{
+ int i;
+
+ for (i = 0; i < max_nr; i++)
+ combiner_data[i].pm_save =
+ __raw_readl(combiner_data[i].base + COMBINER_ENABLE_SET);
+
+ return 0;
+}
+
+/**
+ * combiner_resume - restore interrupt combiner state after resume
+ *
+ * Restore the interrupt enable set register for all combiner groups since
+ * the state is lost when the system enters into a sleep state on suspend.
+ *
+ */
+static void combiner_resume(void)
+{
+ int i;
+
+ for (i = 0; i < max_nr; i++) {
+ __raw_writel(combiner_data[i].irq_mask,
+ combiner_data[i].base + COMBINER_ENABLE_CLEAR);
+ __raw_writel(combiner_data[i].pm_save,
+ combiner_data[i].base + COMBINER_ENABLE_SET);
+ }
+}
+
+#else
+#define combiner_suspend NULL
+#define combiner_resume NULL
+#endif
+
+static struct syscore_ops combiner_syscore_ops = {
+ .suspend = combiner_suspend,
+ .resume = combiner_resume,
+};
+
static int __init combiner_of_init(struct device_node *np,
struct device_node *parent)
{
void __iomem *combiner_base;
- unsigned int max_nr = 20;
combiner_base = of_iomap(np, 0);
if (!combiner_base) {
@@ -219,7 +271,9 @@ static int __init combiner_of_init(struct device_node *np,
__func__, max_nr);
}
- combiner_init(combiner_base, np, max_nr);
+ combiner_init(combiner_base, np);
+
+ register_syscore_ops(&combiner_syscore_ops);
return 0;
}
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index daccc8bdbb42..0d3b0fe2f175 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -409,7 +409,7 @@ static struct notifier_block mpic_cascaded_cpu_notifier = {
};
#endif /* CONFIG_SMP */
-static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
+static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
.map = armada_370_xp_mpic_irq_map,
.xlate = irq_domain_xlate_onecell,
};
diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c
index a2e8c3f876cb..459bf4429d36 100644
--- a/drivers/irqchip/irq-atmel-aic5.c
+++ b/drivers/irqchip/irq-atmel-aic5.c
@@ -339,6 +339,15 @@ static int __init aic5_of_init(struct device_node *node,
return 0;
}
+#define NR_SAMA5D2_IRQS 77
+
+static int __init sama5d2_aic5_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return aic5_of_init(node, parent, NR_SAMA5D2_IRQS);
+}
+IRQCHIP_DECLARE(sama5d2_aic5, "atmel,sama5d2-aic", sama5d2_aic5_of_init);
+
#define NR_SAMA5D3_IRQS 48
static int __init sama5d3_aic5_of_init(struct device_node *node,
diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c
index 5916d6cdafa1..e68c3b60a681 100644
--- a/drivers/irqchip/irq-bcm2835.c
+++ b/drivers/irqchip/irq-bcm2835.c
@@ -135,7 +135,7 @@ static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr,
return 0;
}
-static struct irq_domain_ops armctrl_ops = {
+static const struct irq_domain_ops armctrl_ops = {
.xlate = armctrl_xlate
};
diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c
index ad96ebb0c7ab..9448e391cb71 100644
--- a/drivers/irqchip/irq-gic-common.c
+++ b/drivers/irqchip/irq-gic-common.c
@@ -24,11 +24,8 @@
int gic_configure_irq(unsigned int irq, unsigned int type,
void __iomem *base, void (*sync_access)(void))
{
- u32 enablemask = 1 << (irq % 32);
- u32 enableoff = (irq / 32) * 4;
u32 confmask = 0x2 << ((irq % 16) * 2);
u32 confoff = (irq / 16) * 4;
- bool enabled = false;
u32 val, oldval;
int ret = 0;
@@ -43,17 +40,6 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
val |= confmask;
/*
- * As recommended by the spec, disable the interrupt before changing
- * the configuration
- */
- if (readl_relaxed(base + GIC_DIST_ENABLE_SET + enableoff) & enablemask) {
- writel_relaxed(enablemask, base + GIC_DIST_ENABLE_CLEAR + enableoff);
- if (sync_access)
- sync_access();
- enabled = true;
- }
-
- /*
* Write back the new configuration, and possibly re-enable
* the interrupt. If we tried to write a new configuration and failed,
* return an error.
@@ -62,9 +48,6 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval)
ret = -EINVAL;
- if (enabled)
- writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
-
if (sync_access)
sync_access();
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 9687f8afebff..1b7e155869f6 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -828,7 +828,14 @@ static int its_alloc_tables(struct its_node *its)
u64 typer = readq_relaxed(its->base + GITS_TYPER);
u32 ids = GITS_TYPER_DEVBITS(typer);
- order = get_order((1UL << ids) * entry_size);
+ /*
+ * 'order' was initialized earlier to the default page
+ * granule of the the ITS. We can't have an allocation
+ * smaller than that. If the requested allocation
+ * is smaller, round up to the default page granule.
+ */
+ order = max(get_order((1UL << ids) * entry_size),
+ order);
if (order >= MAX_ORDER) {
order = MAX_ORDER - 1;
pr_warn("%s: Device Table too large, reduce its page order to %u\n",
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 49875adb6b44..c52f7ba205b4 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -658,6 +658,7 @@ static struct irq_chip gic_chip = {
.irq_set_affinity = gic_set_affinity,
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
+ .flags = IRQCHIP_SET_TYPE_MASKED,
};
#define GIC_ID_NR (1U << gic_data.rdists.id_bits)
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 7b315e385ba3..8d7e1c8b6d56 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -82,19 +82,6 @@ static DEFINE_RAW_SPINLOCK(irq_controller_lock);
#define NR_GIC_CPU_IF 8
static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly;
-/*
- * Supported arch specific GIC irq extension.
- * Default make them NULL.
- */
-struct irq_chip gic_arch_extn = {
- .irq_eoi = NULL,
- .irq_mask = NULL,
- .irq_unmask = NULL,
- .irq_retrigger = NULL,
- .irq_set_type = NULL,
- .irq_set_wake = NULL,
-};
-
#ifndef MAX_GIC_NR
#define MAX_GIC_NR 1
#endif
@@ -167,34 +154,16 @@ static int gic_peek_irq(struct irq_data *d, u32 offset)
static void gic_mask_irq(struct irq_data *d)
{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&irq_controller_lock, flags);
gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR);
- if (gic_arch_extn.irq_mask)
- gic_arch_extn.irq_mask(d);
- raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
static void gic_unmask_irq(struct irq_data *d)
{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&irq_controller_lock, flags);
- if (gic_arch_extn.irq_unmask)
- gic_arch_extn.irq_unmask(d);
gic_poke_irq(d, GIC_DIST_ENABLE_SET);
- raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
static void gic_eoi_irq(struct irq_data *d)
{
- if (gic_arch_extn.irq_eoi) {
- raw_spin_lock(&irq_controller_lock);
- gic_arch_extn.irq_eoi(d);
- raw_spin_unlock(&irq_controller_lock);
- }
-
writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI);
}
@@ -251,8 +220,6 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
{
void __iomem *base = gic_dist_base(d);
unsigned int gicirq = gic_irq(d);
- unsigned long flags;
- int ret;
/* Interrupt configuration for SGIs can't be changed */
if (gicirq < 16)
@@ -263,25 +230,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;
- raw_spin_lock_irqsave(&irq_controller_lock, flags);
-
- if (gic_arch_extn.irq_set_type)
- gic_arch_extn.irq_set_type(d, type);
-
- ret = gic_configure_irq(gicirq, type, base, NULL);
-
- raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
-
- return ret;
-}
-
-static int gic_retrigger(struct irq_data *d)
-{
- if (gic_arch_extn.irq_retrigger)
- return gic_arch_extn.irq_retrigger(d);
-
- /* the genirq layer expects 0 if we can't retrigger in hardware */
- return 0;
+ return gic_configure_irq(gicirq, type, base, NULL);
}
#ifdef CONFIG_SMP
@@ -312,21 +261,6 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
}
#endif
-#ifdef CONFIG_PM
-static int gic_set_wake(struct irq_data *d, unsigned int on)
-{
- int ret = -ENXIO;
-
- if (gic_arch_extn.irq_set_wake)
- ret = gic_arch_extn.irq_set_wake(d, on);
-
- return ret;
-}
-
-#else
-#define gic_set_wake NULL
-#endif
-
static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
u32 irqstat, irqnr;
@@ -385,13 +319,12 @@ static struct irq_chip gic_chip = {
.irq_unmask = gic_unmask_irq,
.irq_eoi = gic_eoi_irq,
.irq_set_type = gic_set_type,
- .irq_retrigger = gic_retrigger,
#ifdef CONFIG_SMP
.irq_set_affinity = gic_set_affinity,
#endif
- .irq_set_wake = gic_set_wake,
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
+ .flags = IRQCHIP_SET_TYPE_MASKED,
};
void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
@@ -1055,7 +988,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
set_handle_irq(gic_handle_irq);
}
- gic_chip.flags |= gic_arch_extn.flags;
gic_dist_init(gic);
gic_cpu_init(gic);
gic_pm_init(gic);
diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c
index 7d6ffb5de84f..0cae45d10695 100644
--- a/drivers/irqchip/irq-hip04.c
+++ b/drivers/irqchip/irq-hip04.c
@@ -202,6 +202,7 @@ static struct irq_chip hip04_irq_chip = {
#ifdef CONFIG_SMP
.irq_set_affinity = hip04_irq_set_affinity,
#endif
+ .flags = IRQCHIP_SET_TYPE_MASKED,
};
static u16 hip04_get_cpumask(struct hip04_irq_data *intc)
diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c
index 78e8b3ce5252..81e3cf5b9a1f 100644
--- a/drivers/irqchip/irq-keystone.c
+++ b/drivers/irqchip/irq-keystone.c
@@ -131,7 +131,7 @@ static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
return 0;
}
-static struct irq_domain_ops keystone_irq_ops = {
+static const struct irq_domain_ops keystone_irq_ops = {
.map = keystone_irq_map,
.xlate = irq_domain_xlate_onecell,
};
@@ -184,8 +184,7 @@ static int keystone_irq_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, kirq);
- irq_set_chained_handler(kirq->irq, keystone_irq_handler);
- irq_set_handler_data(kirq->irq, kirq);
+ irq_set_chained_handler_and_data(kirq->irq, keystone_irq_handler, kirq);
/* clear all source bits */
keystone_irq_writel(kirq, ~0x0);
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index 57f09cb54464..4400edd1a6c7 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -271,7 +271,7 @@ int gic_get_c0_fdc_int(void)
GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_FDC));
}
-static void gic_handle_shared_int(void)
+static void gic_handle_shared_int(bool chained)
{
unsigned int i, intr, virq;
unsigned long *pcpu_mask;
@@ -299,7 +299,10 @@ static void gic_handle_shared_int(void)
while (intr != gic_shared_intrs) {
virq = irq_linear_revmap(gic_irq_domain,
GIC_SHARED_TO_HWIRQ(intr));
- do_IRQ(virq);
+ if (chained)
+ generic_handle_irq(virq);
+ else
+ do_IRQ(virq);
/* go to next pending bit */
bitmap_clear(pending, intr, 1);
@@ -431,7 +434,7 @@ static struct irq_chip gic_edge_irq_controller = {
#endif
};
-static void gic_handle_local_int(void)
+static void gic_handle_local_int(bool chained)
{
unsigned long pending, masked;
unsigned int intr, virq;
@@ -445,7 +448,10 @@ static void gic_handle_local_int(void)
while (intr != GIC_NUM_LOCAL_INTRS) {
virq = irq_linear_revmap(gic_irq_domain,
GIC_LOCAL_TO_HWIRQ(intr));
- do_IRQ(virq);
+ if (chained)
+ generic_handle_irq(virq);
+ else
+ do_IRQ(virq);
/* go to next pending bit */
bitmap_clear(&pending, intr, 1);
@@ -509,13 +515,14 @@ static struct irq_chip gic_all_vpes_local_irq_controller = {
static void __gic_irq_dispatch(void)
{
- gic_handle_local_int();
- gic_handle_shared_int();
+ gic_handle_local_int(false);
+ gic_handle_shared_int(false);
}
static void gic_irq_dispatch(unsigned int irq, struct irq_desc *desc)
{
- __gic_irq_dispatch();
+ gic_handle_local_int(true);
+ gic_handle_shared_int(true);
}
#ifdef CONFIG_MIPS_GIC_IPI
@@ -739,7 +746,7 @@ static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
return 0;
}
-static struct irq_domain_ops gic_irq_domain_ops = {
+static const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map,
.xlate = gic_irq_domain_xlate,
};
diff --git a/drivers/irqchip/irq-mtk-sysirq.c b/drivers/irqchip/irq-mtk-sysirq.c
index eaf0a710e98a..15c13039bba2 100644
--- a/drivers/irqchip/irq-mtk-sysirq.c
+++ b/drivers/irqchip/irq-mtk-sysirq.c
@@ -111,7 +111,7 @@ static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
}
-static struct irq_domain_ops sysirq_domain_ops = {
+static const struct irq_domain_ops sysirq_domain_ops = {
.xlate = mtk_sysirq_domain_xlate,
.alloc = mtk_sysirq_domain_alloc,
.free = irq_domain_free_irqs_common,
@@ -144,7 +144,7 @@ static int __init mtk_sysirq_of_init(struct device_node *node,
chip_data->intpol_base = ioremap(res.start, size);
if (!chip_data->intpol_base) {
pr_err("mtk_sysirq: unable to map sysirq register\n");
- ret = PTR_ERR(chip_data->intpol_base);
+ ret = -ENXIO;
goto out_free;
}
diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c
index e4acf1e3f8e3..04bf97b289cf 100644
--- a/drivers/irqchip/irq-mxs.c
+++ b/drivers/irqchip/irq-mxs.c
@@ -90,7 +90,7 @@ static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
return 0;
}
-static struct irq_domain_ops icoll_irq_domain_ops = {
+static const struct irq_domain_ops icoll_irq_domain_ops = {
.map = icoll_irq_domain_map,
.xlate = irq_domain_xlate_onecell,
};
diff --git a/drivers/irqchip/irq-nvic.c b/drivers/irqchip/irq-nvic.c
index 4ff0805fca01..5fac9100f6cb 100644
--- a/drivers/irqchip/irq-nvic.c
+++ b/drivers/irqchip/irq-nvic.c
@@ -49,6 +49,31 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs)
handle_IRQ(irq, regs);
}
+static int nvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
+{
+ int i, ret;
+ irq_hw_number_t hwirq;
+ unsigned int type = IRQ_TYPE_NONE;
+ struct of_phandle_args *irq_data = arg;
+
+ ret = irq_domain_xlate_onecell(domain, irq_data->np, irq_data->args,
+ irq_data->args_count, &hwirq, &type);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_map_generic_chip(domain, virq + i, hwirq + i);
+
+ return 0;
+}
+
+static const struct irq_domain_ops nvic_irq_domain_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ .alloc = nvic_irq_domain_alloc,
+ .free = irq_domain_free_irqs_top,
+};
+
static int __init nvic_of_init(struct device_node *node,
struct device_node *parent)
{
@@ -70,7 +95,8 @@ static int __init nvic_of_init(struct device_node *node,
irqs = NVIC_MAX_IRQ;
nvic_irq_domain =
- irq_domain_add_linear(node, irqs, &irq_generic_chip_ops, NULL);
+ irq_domain_add_linear(node, irqs, &nvic_irq_domain_ops, NULL);
+
if (!nvic_irq_domain) {
pr_warn("Failed to allocate irq domain\n");
return -ENOMEM;
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c
index 9a0767b9c89d..0670ab4e3897 100644
--- a/drivers/irqchip/irq-renesas-intc-irqpin.c
+++ b/drivers/irqchip/irq-renesas-intc-irqpin.c
@@ -347,7 +347,7 @@ static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq,
return 0;
}
-static struct irq_domain_ops intc_irqpin_irq_domain_ops = {
+static const struct irq_domain_ops intc_irqpin_irq_domain_ops = {
.map = intc_irqpin_irq_domain_map,
.xlate = irq_domain_xlate_twocell,
};
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c
index cdf80b7794cd..778bd076aeea 100644
--- a/drivers/irqchip/irq-renesas-irqc.c
+++ b/drivers/irqchip/irq-renesas-irqc.c
@@ -29,7 +29,6 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/platform_data/irq-renesas-irqc.h>
#include <linux/pm_runtime.h>
#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */
@@ -62,7 +61,6 @@ struct irqc_priv {
void __iomem *iomem;
void __iomem *cpu_int_base;
struct irqc_irq irq[IRQC_IRQ_MAX];
- struct renesas_irqc_config config;
unsigned int number_of_irqs;
struct platform_device *pdev;
struct irq_chip irq_chip;
@@ -168,14 +166,13 @@ static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
return 0;
}
-static struct irq_domain_ops irqc_irq_domain_ops = {
+static const struct irq_domain_ops irqc_irq_domain_ops = {
.map = irqc_irq_domain_map,
.xlate = irq_domain_xlate_twocell,
};
static int irqc_probe(struct platform_device *pdev)
{
- struct renesas_irqc_config *pdata = pdev->dev.platform_data;
struct irqc_priv *p;
struct resource *io;
struct resource *irq;
@@ -191,10 +188,6 @@ static int irqc_probe(struct platform_device *pdev)
goto err0;
}
- /* deal with driver instance configuration */
- if (pdata)
- memcpy(&p->config, pdata, sizeof(*pdata));
-
p->pdev = pdev;
platform_set_drvdata(pdev, p);
@@ -251,8 +244,7 @@ static int irqc_probe(struct platform_device *pdev)
irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND;
p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
- p->number_of_irqs,
- p->config.irq_base,
+ p->number_of_irqs, 0,
&irqc_irq_domain_ops, p);
if (!p->irq_domain) {
ret = -ENXIO;
@@ -272,13 +264,6 @@ static int irqc_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
- /* warn in case of mismatch if irq base is specified */
- if (p->config.irq_base) {
- if (p->config.irq_base != p->irq[0].domain_irq)
- dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n",
- p->config.irq_base, p->irq[0].domain_irq);
- }
-
return 0;
err3:
while (--k >= 0)
diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c
index c8d373fcd823..e96717f45ea1 100644
--- a/drivers/irqchip/irq-s3c24xx.c
+++ b/drivers/irqchip/irq-s3c24xx.c
@@ -502,7 +502,7 @@ err:
return -EINVAL;
}
-static struct irq_domain_ops s3c24xx_irq_ops = {
+static const struct irq_domain_ops s3c24xx_irq_ops = {
.map = s3c24xx_irq_map,
.xlate = irq_domain_xlate_twocell,
};
@@ -1228,7 +1228,7 @@ static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,
return 0;
}
-static struct irq_domain_ops s3c24xx_irq_ops_of = {
+static const struct irq_domain_ops s3c24xx_irq_ops_of = {
.map = s3c24xx_irq_map_of,
.xlate = s3c24xx_irq_xlate_of,
};
diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c
index 64155b686081..83d6aa6464ee 100644
--- a/drivers/irqchip/irq-sun4i.c
+++ b/drivers/irqchip/irq-sun4i.c
@@ -89,7 +89,7 @@ static int sun4i_irq_map(struct irq_domain *d, unsigned int virq,
return 0;
}
-static struct irq_domain_ops sun4i_irq_ops = {
+static const struct irq_domain_ops sun4i_irq_ops = {
.map = sun4i_irq_map,
.xlate = irq_domain_xlate_onecell,
};
diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c
index 4a9ce5b50c5b..6b2b582433bd 100644
--- a/drivers/irqchip/irq-sunxi-nmi.c
+++ b/drivers/irqchip/irq-sunxi-nmi.c
@@ -104,7 +104,7 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type)
irqd_set_trigger_type(data, flow_type);
irq_setup_alt_chip(data, flow_type);
- for (i = 0; i <= gc->num_ct; i++, ct++)
+ for (i = 0; i < gc->num_ct; i++, ct++)
if (ct->type & flow_type)
ctrl_off = ct->regs.type;
diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c
index 51c485d9a877..f67bbd80433e 100644
--- a/drivers/irqchip/irq-tegra.c
+++ b/drivers/irqchip/irq-tegra.c
@@ -264,7 +264,7 @@ static int tegra_ictlr_domain_alloc(struct irq_domain *domain,
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
&tegra_ictlr_chip,
- &info->base[ictlr]);
+ info->base[ictlr]);
}
parent_args = *args;
diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c
index 1ab451729a5c..888111b76ea0 100644
--- a/drivers/irqchip/irq-versatile-fpga.c
+++ b/drivers/irqchip/irq-versatile-fpga.c
@@ -132,7 +132,7 @@ static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq,
return 0;
}
-static struct irq_domain_ops fpga_irqdomain_ops = {
+static const struct irq_domain_ops fpga_irqdomain_ops = {
.map = fpga_irqdomain_map,
.xlate = irq_domain_xlate_onetwocell,
};
diff --git a/drivers/irqchip/irq-vf610-mscm-ir.c b/drivers/irqchip/irq-vf610-mscm-ir.c
index 9521057d4744..f5c01cbcc73a 100644
--- a/drivers/irqchip/irq-vf610-mscm-ir.c
+++ b/drivers/irqchip/irq-vf610-mscm-ir.c
@@ -47,6 +47,7 @@ struct vf610_mscm_ir_chip_data {
void __iomem *mscm_ir_base;
u16 cpu_mask;
u16 saved_irsprc[MSCM_IRSPRC_NUM];
+ bool is_nvic;
};
static struct vf610_mscm_ir_chip_data *mscm_ir_data;
@@ -101,7 +102,7 @@ static void vf610_mscm_ir_enable(struct irq_data *data)
writew_relaxed(chip_data->cpu_mask,
chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
- irq_chip_unmask_parent(data);
+ irq_chip_enable_parent(data);
}
static void vf610_mscm_ir_disable(struct irq_data *data)
@@ -111,7 +112,7 @@ static void vf610_mscm_ir_disable(struct irq_data *data)
writew_relaxed(0x0, chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
- irq_chip_mask_parent(data);
+ irq_chip_disable_parent(data);
}
static struct irq_chip vf610_mscm_ir_irq_chip = {
@@ -143,10 +144,17 @@ static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int vi
domain->host_data);
gic_data.np = domain->parent->of_node;
- gic_data.args_count = 3;
- gic_data.args[0] = GIC_SPI;
- gic_data.args[1] = irq_data->args[0];
- gic_data.args[2] = irq_data->args[1];
+
+ if (mscm_ir_data->is_nvic) {
+ gic_data.args_count = 1;
+ gic_data.args[0] = irq_data->args[0];
+ } else {
+ gic_data.args_count = 3;
+ gic_data.args[0] = GIC_SPI;
+ gic_data.args[1] = irq_data->args[0];
+ gic_data.args[2] = irq_data->args[1];
+ }
+
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
}
@@ -174,10 +182,9 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
return -ENOMEM;
mscm_ir_data->mscm_ir_base = of_io_request_and_map(node, 0, "mscm-ir");
-
- if (!mscm_ir_data->mscm_ir_base) {
+ if (IS_ERR(mscm_ir_data->mscm_ir_base)) {
pr_err("vf610_mscm_ir: unable to map mscm register\n");
- ret = -ENOMEM;
+ ret = PTR_ERR(mscm_ir_data->mscm_ir_base);
goto out_free;
}
@@ -199,6 +206,9 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
goto out_unmap;
}
+ if (of_device_is_compatible(domain->parent->of_node, "arm,armv7m-nvic"))
+ mscm_ir_data->is_nvic = true;
+
cpu_pm_register_notifier(&mscm_ir_notifier_block);
return 0;
diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c
index 54089debf2dc..d4ce331ea4a0 100644
--- a/drivers/irqchip/irq-vic.c
+++ b/drivers/irqchip/irq-vic.c
@@ -256,7 +256,7 @@ static void __exception_irq_entry vic_handle_irq(struct pt_regs *regs)
} while (handled);
}
-static struct irq_domain_ops vic_irqdomain_ops = {
+static const struct irq_domain_ops vic_irqdomain_ops = {
.map = vic_irqdomain_map,
.xlate = irq_domain_xlate_onetwocell,
};
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index b7af816f2769..0b297009b856 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -173,7 +173,7 @@ static int vt8500_irq_map(struct irq_domain *h, unsigned int virq,
return 0;
}
-static struct irq_domain_ops vt8500_irq_domain_ops = {
+static const struct irq_domain_ops vt8500_irq_domain_ops = {
.map = vt8500_irq_map,
.xlate = irq_domain_xlate_onecell,
};
diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c
index 9c145a7cb056..a45121546caf 100644
--- a/drivers/irqchip/spear-shirq.c
+++ b/drivers/irqchip/spear-shirq.c
@@ -207,8 +207,7 @@ static void __init spear_shirq_register(struct spear_shirq *shirq,
if (!shirq->irq_chip)
return;
- irq_set_chained_handler(parent_irq, shirq_handler);
- irq_set_handler_data(parent_irq, shirq);
+ irq_set_chained_handler_and_data(parent_irq, shirq_handler, shirq);
for (i = 0; i < shirq->nr_irqs; i++) {
irq_set_chip_and_handler(shirq->virq_base + i,
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 546b7e81161d..aa5dd5668528 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -58,7 +58,7 @@
* About SOFTNET:
* Most of the changes were pretty obvious and basically done by HE already.
*
- * One problem of the isdn net device code is that is uses struct net_device
+ * One problem of the isdn net device code is that it uses struct net_device
* for masters and slaves. However, only master interface are registered to
* the network layer, and therefore, it only makes sense to call netif_*
* functions on them.
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 728681debdbe..7fb2a19ac649 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -187,6 +187,7 @@ void led_classdev_resume(struct led_classdev *led_cdev)
}
EXPORT_SYMBOL_GPL(led_classdev_resume);
+#ifdef CONFIG_PM_SLEEP
static int led_suspend(struct device *dev)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
@@ -206,11 +207,9 @@ static int led_resume(struct device *dev)
return 0;
}
+#endif
-static const struct dev_pm_ops leds_class_dev_pm_ops = {
- .suspend = led_suspend,
- .resume = led_resume,
-};
+static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
static int match_name(struct device *dev, const void *data)
{
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c
index 7dc93aa004c8..312ffd3d0017 100644
--- a/drivers/lguest/core.c
+++ b/drivers/lguest/core.c
@@ -173,7 +173,7 @@ static void unmap_switcher(void)
bool lguest_address_ok(const struct lguest *lg,
unsigned long addr, unsigned long len)
{
- return (addr+len) / PAGE_SIZE < lg->pfn_limit && (addr+len >= addr);
+ return addr+len <= lg->pfn_limit * PAGE_SIZE && (addr+len >= addr);
}
/*
diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c
index 5e7559be222a..eb934b0242e0 100644
--- a/drivers/lguest/interrupts_and_traps.c
+++ b/drivers/lguest/interrupts_and_traps.c
@@ -20,7 +20,7 @@
#include "lg.h"
/* Allow Guests to use a non-128 (ie. non-Linux) syscall trap. */
-static unsigned int syscall_vector = SYSCALL_VECTOR;
+static unsigned int syscall_vector = IA32_SYSCALL_VECTOR;
module_param(syscall_vector, uint, 0444);
/* The address of the interrupt handler is split into two bits: */
@@ -333,8 +333,8 @@ void set_interrupt(struct lg_cpu *cpu, unsigned int irq)
*/
static bool could_be_syscall(unsigned int num)
{
- /* Normal Linux SYSCALL_VECTOR or reserved vector? */
- return num == SYSCALL_VECTOR || num == syscall_vector;
+ /* Normal Linux IA32_SYSCALL_VECTOR or reserved vector? */
+ return num == IA32_SYSCALL_VECTOR || num == syscall_vector;
}
/* The syscall vector it wants must be unused by Host. */
@@ -351,7 +351,7 @@ bool check_syscall_vector(struct lguest *lg)
int init_interrupts(void)
{
/* If they want some strange system call vector, reserve it now */
- if (syscall_vector != SYSCALL_VECTOR) {
+ if (syscall_vector != IA32_SYSCALL_VECTOR) {
if (test_bit(syscall_vector, used_vectors) ||
vector_used_by_percpu_irq(syscall_vector)) {
printk(KERN_ERR "lg: couldn't reserve syscall %u\n",
@@ -366,7 +366,7 @@ int init_interrupts(void)
void free_interrupts(void)
{
- if (syscall_vector != SYSCALL_VECTOR)
+ if (syscall_vector != IA32_SYSCALL_VECTOR)
clear_bit(syscall_vector, used_vectors);
}
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c
index 30f2aef69d78..6a4cd771a2be 100644
--- a/drivers/lguest/x86/core.c
+++ b/drivers/lguest/x86/core.c
@@ -46,7 +46,7 @@
#include <asm/setup.h>
#include <asm/lguest.h>
#include <asm/uaccess.h>
-#include <asm/i387.h>
+#include <asm/fpu/internal.h>
#include <asm/tlbflush.h>
#include "../lg.h"
@@ -251,7 +251,7 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
* we set it now, so we can trap and pass that trap to the Guest if it
* uses the FPU.
*/
- if (cpu->ts && user_has_fpu())
+ if (cpu->ts && fpregs_active())
stts();
/*
@@ -283,7 +283,7 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0);
/* Clear the host TS bit if it was set above. */
- if (cpu->ts && user_has_fpu())
+ if (cpu->ts && fpregs_active())
clts();
/*
@@ -297,12 +297,12 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
/*
* Similarly, if we took a trap because the Guest used the FPU,
* we have to restore the FPU it expects to see.
- * math_state_restore() may sleep and we may even move off to
+ * fpu__restore() may sleep and we may even move off to
* a different CPU. So all the critical stuff should be done
* before this.
*/
- else if (cpu->regs->trapnum == 7 && !user_has_fpu())
- math_state_restore();
+ else if (cpu->regs->trapnum == 7 && !fpregs_active())
+ fpu__restore(&current->thread.fpu);
}
/*H:130
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 2bc56e2a3526..135a0907e9de 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -177,11 +177,16 @@ static struct md_rdev *next_active_rdev(struct md_rdev *rdev, struct mddev *mdde
* nr_pending is 0 and In_sync is clear, the entries we return will
* still be in the same position on the list when we re-enter
* list_for_each_entry_continue_rcu.
+ *
+ * Note that if entered with 'rdev == NULL' to start at the
+ * beginning, we temporarily assign 'rdev' to an address which
+ * isn't really an rdev, but which can be used by
+ * list_for_each_entry_continue_rcu() to find the first entry.
*/
rcu_read_lock();
if (rdev == NULL)
/* start at the beginning */
- rdev = list_entry_rcu(&mddev->disks, struct md_rdev, same_set);
+ rdev = list_entry(&mddev->disks, struct md_rdev, same_set);
else {
/* release the previous rdev and start from there. */
rdev_dec_pending(rdev, mddev);
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 9eeea196328a..5503e43e5f28 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -925,10 +925,11 @@ static int crypt_convert(struct crypt_config *cc,
switch (r) {
/* async */
- case -EINPROGRESS:
case -EBUSY:
wait_for_completion(&ctx->restart);
reinit_completion(&ctx->restart);
+ /* fall through*/
+ case -EINPROGRESS:
ctx->req = NULL;
ctx->cc_sector++;
continue;
@@ -1345,8 +1346,10 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
struct dm_crypt_io *io = container_of(ctx, struct dm_crypt_io, ctx);
struct crypt_config *cc = io->cc;
- if (error == -EINPROGRESS)
+ if (error == -EINPROGRESS) {
+ complete(&ctx->restart);
return;
+ }
if (!error && cc->iv_gen_ops && cc->iv_gen_ops->post)
error = cc->iv_gen_ops->post(cc, iv_of_dmreq(cc, dmreq), dmreq);
@@ -1357,15 +1360,12 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
crypt_free_req(cc, req_of_dmreq(cc, dmreq), io->base_bio);
if (!atomic_dec_and_test(&ctx->cc_pending))
- goto done;
+ return;
if (bio_data_dir(io->base_bio) == READ)
kcryptd_crypt_read_done(io);
else
kcryptd_crypt_write_io_submit(io, 1);
-done:
- if (!completion_done(&ctx->restart))
- complete(&ctx->restart);
}
static void kcryptd_crypt(struct work_struct *work)
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index c8a18e4ee9dc..720ceeb7fa9b 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1298,21 +1298,22 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
goto err_unlock_md_type;
}
- if (dm_get_md_type(md) == DM_TYPE_NONE)
+ if (dm_get_md_type(md) == DM_TYPE_NONE) {
/* Initial table load: acquire type of table. */
dm_set_md_type(md, dm_table_get_type(t));
- else if (dm_get_md_type(md) != dm_table_get_type(t)) {
+
+ /* setup md->queue to reflect md's type (may block) */
+ r = dm_setup_md_queue(md);
+ if (r) {
+ DMWARN("unable to set up device queue for new table.");
+ goto err_unlock_md_type;
+ }
+ } else if (dm_get_md_type(md) != dm_table_get_type(t)) {
DMWARN("can't change device type after initial table load.");
r = -EINVAL;
goto err_unlock_md_type;
}
- /* setup md->queue to reflect md's type (may block) */
- r = dm_setup_md_queue(md);
- if (r) {
- DMWARN("unable to set up device queue for new table.");
- goto err_unlock_md_type;
- }
dm_unlock_md_type(md);
/* stage inactive table */
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 63953477a07c..eff7bdd7731d 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -429,9 +429,11 @@ static int __multipath_map(struct dm_target *ti, struct request *clone,
/* blk-mq request-based interface */
*__clone = blk_get_request(bdev_get_queue(bdev),
rq_data_dir(rq), GFP_ATOMIC);
- if (IS_ERR(*__clone))
+ if (IS_ERR(*__clone)) {
/* ENOMEM, requeue */
+ clear_mapinfo(m, map_context);
return r;
+ }
(*__clone)->bio = (*__clone)->biotail = NULL;
(*__clone)->rq_disk = bdev->bd_disk;
(*__clone)->cmd_flags |= REQ_FAILFAST_TRANSPORT;
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index d9b00b8565c6..16ba55ad7089 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -820,6 +820,12 @@ void dm_consume_args(struct dm_arg_set *as, unsigned num_args)
}
EXPORT_SYMBOL(dm_consume_args);
+static bool __table_type_request_based(unsigned table_type)
+{
+ return (table_type == DM_TYPE_REQUEST_BASED ||
+ table_type == DM_TYPE_MQ_REQUEST_BASED);
+}
+
static int dm_table_set_type(struct dm_table *t)
{
unsigned i;
@@ -852,8 +858,7 @@ static int dm_table_set_type(struct dm_table *t)
* Determine the type from the live device.
* Default to bio-based if device is new.
*/
- if (live_md_type == DM_TYPE_REQUEST_BASED ||
- live_md_type == DM_TYPE_MQ_REQUEST_BASED)
+ if (__table_type_request_based(live_md_type))
request_based = 1;
else
bio_based = 1;
@@ -903,7 +908,7 @@ static int dm_table_set_type(struct dm_table *t)
}
t->type = DM_TYPE_MQ_REQUEST_BASED;
- } else if (hybrid && list_empty(devices) && live_md_type != DM_TYPE_NONE) {
+ } else if (list_empty(devices) && __table_type_request_based(live_md_type)) {
/* inherit live MD type */
t->type = live_md_type;
@@ -925,10 +930,7 @@ struct target_type *dm_table_get_immutable_target_type(struct dm_table *t)
bool dm_table_request_based(struct dm_table *t)
{
- unsigned table_type = dm_table_get_type(t);
-
- return (table_type == DM_TYPE_REQUEST_BASED ||
- table_type == DM_TYPE_MQ_REQUEST_BASED);
+ return __table_type_request_based(dm_table_get_type(t));
}
bool dm_table_mq_request_based(struct dm_table *t)
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index f8c7ca3e8947..2caf492890d6 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1089,11 +1089,17 @@ static void free_rq_clone(struct request *clone)
blk_rq_unprep_clone(clone);
- if (clone->q->mq_ops)
+ if (md->type == DM_TYPE_MQ_REQUEST_BASED)
+ /* stacked on blk-mq queue(s) */
tio->ti->type->release_clone_rq(clone);
else if (!md->queue->mq_ops)
/* request_fn queue stacked on request_fn queue(s) */
free_clone_request(md, clone);
+ /*
+ * NOTE: for the blk-mq queue stacked on request_fn queue(s) case:
+ * no need to call free_clone_request() because we leverage blk-mq by
+ * allocating the clone at the end of the blk-mq pdu (see: clone_rq)
+ */
if (!md->queue->mq_ops)
free_rq_tio(tio);
@@ -1156,6 +1162,7 @@ static void old_requeue_request(struct request *rq)
spin_lock_irqsave(q->queue_lock, flags);
blk_requeue_request(q, rq);
+ blk_run_queue_async(q);
spin_unlock_irqrestore(q->queue_lock, flags);
}
@@ -1716,8 +1723,7 @@ static int dm_merge_bvec(struct request_queue *q,
struct mapped_device *md = q->queuedata;
struct dm_table *map = dm_get_live_table_fast(md);
struct dm_target *ti;
- sector_t max_sectors;
- int max_size = 0;
+ sector_t max_sectors, max_size = 0;
if (unlikely(!map))
goto out;
@@ -1732,8 +1738,16 @@ static int dm_merge_bvec(struct request_queue *q,
max_sectors = min(max_io_len(bvm->bi_sector, ti),
(sector_t) queue_max_sectors(q));
max_size = (max_sectors << SECTOR_SHIFT) - bvm->bi_size;
- if (unlikely(max_size < 0)) /* this shouldn't _ever_ happen */
- max_size = 0;
+
+ /*
+ * FIXME: this stop-gap fix _must_ be cleaned up (by passing a sector_t
+ * to the targets' merge function since it holds sectors not bytes).
+ * Just doing this as an interim fix for stable@ because the more
+ * comprehensive cleanup of switching to sector_t will impact every
+ * DM target that implements a ->merge hook.
+ */
+ if (max_size > INT_MAX)
+ max_size = INT_MAX;
/*
* merge_bvec_fn() returns number of bytes
@@ -1741,7 +1755,7 @@ static int dm_merge_bvec(struct request_queue *q,
* max is precomputed maximal io size
*/
if (max_size && ti->type->merge)
- max_size = ti->type->merge(ti, bvm, biovec, max_size);
+ max_size = ti->type->merge(ti, bvm, biovec, (int) max_size);
/*
* If the target doesn't support merge method and some of the devices
* provided their merge_bvec method (we know this by looking for the
@@ -1963,8 +1977,8 @@ static int map_request(struct dm_rq_target_io *tio, struct request *rq,
dm_kill_unmapped_request(rq, r);
return r;
}
- if (IS_ERR(clone))
- return DM_MAPIO_REQUEUE;
+ if (r != DM_MAPIO_REMAPPED)
+ return r;
if (setup_clone(clone, rq, tio, GFP_ATOMIC)) {
/* -ENOMEM */
ti->type->release_clone_rq(clone);
@@ -2662,9 +2676,6 @@ static int dm_init_request_based_queue(struct mapped_device *md)
{
struct request_queue *q = NULL;
- if (md->queue->elevator)
- return 0;
-
/* Fully initialize the queue */
q = blk_init_allocated_queue(md->queue, dm_request_fn, NULL);
if (!q)
@@ -2748,13 +2759,15 @@ static int dm_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
if (dm_table_get_type(map) == DM_TYPE_REQUEST_BASED) {
/* clone request is allocated at the end of the pdu */
tio->clone = (void *)blk_mq_rq_to_pdu(rq) + sizeof(struct dm_rq_target_io);
- if (!clone_rq(rq, md, tio, GFP_ATOMIC))
- return BLK_MQ_RQ_QUEUE_BUSY;
+ (void) clone_rq(rq, md, tio, GFP_ATOMIC);
queue_kthread_work(&md->kworker, &tio->work);
} else {
/* Direct call is fine since .queue_rq allows allocations */
- if (map_request(tio, rq, md) == DM_MAPIO_REQUEUE)
- dm_requeue_unmapped_original_request(md, rq);
+ if (map_request(tio, rq, md) == DM_MAPIO_REQUEUE) {
+ /* Undo dm_start_request() before requeuing */
+ rq_completed(md, rq_data_dir(rq), false);
+ return BLK_MQ_RQ_QUEUE_BUSY;
+ }
}
return BLK_MQ_RQ_QUEUE_OK;
diff --git a/drivers/md/md.c b/drivers/md/md.c
index d4f31e195e26..4dbed4a67aaf 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -3834,7 +3834,7 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
err = -EBUSY;
}
spin_unlock(&mddev->lock);
- return err;
+ return err ?: len;
}
err = mddev_lock(mddev);
if (err)
@@ -4211,34 +4211,36 @@ action_store(struct mddev *mddev, const char *page, size_t len)
if (!mddev->pers || !mddev->pers->sync_request)
return -EINVAL;
- if (cmd_match(page, "frozen"))
- set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
- else
- clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
if (cmd_match(page, "idle") || cmd_match(page, "frozen")) {
- flush_workqueue(md_misc_wq);
- if (mddev->sync_thread) {
- set_bit(MD_RECOVERY_INTR, &mddev->recovery);
- if (mddev_lock(mddev) == 0) {
+ if (cmd_match(page, "frozen"))
+ set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ else
+ clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) &&
+ mddev_lock(mddev) == 0) {
+ flush_workqueue(md_misc_wq);
+ if (mddev->sync_thread) {
+ set_bit(MD_RECOVERY_INTR, &mddev->recovery);
md_reap_sync_thread(mddev);
- mddev_unlock(mddev);
}
+ mddev_unlock(mddev);
}
} else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
return -EBUSY;
else if (cmd_match(page, "resync"))
- set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+ clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
else if (cmd_match(page, "recover")) {
+ clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
- set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
} else if (cmd_match(page, "reshape")) {
int err;
if (mddev->pers->start_reshape == NULL)
return -EINVAL;
err = mddev_lock(mddev);
if (!err) {
+ clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
err = mddev->pers->start_reshape(mddev);
mddev_unlock(mddev);
}
@@ -4250,6 +4252,7 @@ action_store(struct mddev *mddev, const char *page, size_t len)
set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
else if (!cmd_match(page, "repair"))
return -EINVAL;
+ clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
}
@@ -4818,12 +4821,12 @@ static void md_free(struct kobject *ko)
if (mddev->sysfs_state)
sysfs_put(mddev->sysfs_state);
+ if (mddev->queue)
+ blk_cleanup_queue(mddev->queue);
if (mddev->gendisk) {
del_gendisk(mddev->gendisk);
put_disk(mddev->gendisk);
}
- if (mddev->queue)
- blk_cleanup_queue(mddev->queue);
kfree(mddev);
}
@@ -8259,6 +8262,7 @@ void md_reap_sync_thread(struct mddev *mddev)
if (mddev_is_clustered(mddev))
md_cluster_ops->metadata_update_finish(mddev);
clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+ clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
clear_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 2cb59a641cd2..efb654eb5399 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -188,8 +188,9 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
}
dev[j] = rdev1;
- disk_stack_limits(mddev->gendisk, rdev1->bdev,
- rdev1->data_offset << 9);
+ if (mddev->queue)
+ disk_stack_limits(mddev->gendisk, rdev1->bdev,
+ rdev1->data_offset << 9);
if (rdev1->bdev->bd_disk->queue->merge_bvec_fn)
conf->has_merge_bvec = 1;
@@ -523,6 +524,9 @@ static void raid0_make_request(struct mddev *mddev, struct bio *bio)
? (sector & (chunk_sects-1))
: sector_div(sector, chunk_sects));
+ /* Restore due to sector_div */
+ sector = bio->bi_iter.bi_sector;
+
if (sectors < bio_sectors(bio)) {
split = bio_split(bio, sectors, GFP_NOIO, fs_bio_set);
bio_chain(split, bio);
@@ -530,7 +534,6 @@ static void raid0_make_request(struct mddev *mddev, struct bio *bio)
split = bio;
}
- sector = bio->bi_iter.bi_sector;
zone = find_zone(mddev->private, &sector);
tmp_dev = map_sector(mddev, zone, sector, &sector);
split->bi_bdev = tmp_dev->bdev;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index e793ab6b3570..f55c3f35b746 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -4156,6 +4156,7 @@ static int raid10_start_reshape(struct mddev *mddev)
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+ clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 77dfd720aaa0..b6793d2e051f 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -749,6 +749,7 @@ static void unlock_two_stripes(struct stripe_head *sh1, struct stripe_head *sh2)
static bool stripe_can_batch(struct stripe_head *sh)
{
return test_bit(STRIPE_BATCH_READY, &sh->state) &&
+ !test_bit(STRIPE_BITMAP_PENDING, &sh->state) &&
is_full_stripe_write(sh);
}
@@ -837,6 +838,15 @@ static void stripe_add_to_batch_list(struct r5conf *conf, struct stripe_head *sh
< IO_THRESHOLD)
md_wakeup_thread(conf->mddev->thread);
+ if (test_and_clear_bit(STRIPE_BIT_DELAY, &sh->state)) {
+ int seq = sh->bm_seq;
+ if (test_bit(STRIPE_BIT_DELAY, &sh->batch_head->state) &&
+ sh->batch_head->bm_seq > seq)
+ seq = sh->batch_head->bm_seq;
+ set_bit(STRIPE_BIT_DELAY, &sh->batch_head->state);
+ sh->batch_head->bm_seq = seq;
+ }
+
atomic_inc(&sh->count);
unlock_out:
unlock_two_stripes(head, sh);
@@ -1078,9 +1088,6 @@ again:
pr_debug("skip op %ld on disc %d for sector %llu\n",
bi->bi_rw, i, (unsigned long long)sh->sector);
clear_bit(R5_LOCKED, &sh->dev[i].flags);
- if (sh->batch_head)
- set_bit(STRIPE_BATCH_ERR,
- &sh->batch_head->state);
set_bit(STRIPE_HANDLE, &sh->state);
}
@@ -1825,7 +1832,7 @@ again:
} else
init_async_submit(&submit, 0, tx, NULL, NULL,
to_addr_conv(sh, percpu, j));
- async_gen_syndrome(blocks, 0, count+2, STRIPE_SIZE, &submit);
+ tx = async_gen_syndrome(blocks, 0, count+2, STRIPE_SIZE, &submit);
if (!last_stripe) {
j++;
sh = list_first_entry(&sh->batch_list, struct stripe_head,
@@ -1971,17 +1978,30 @@ static void raid_run_ops(struct stripe_head *sh, unsigned long ops_request)
put_cpu();
}
+static struct stripe_head *alloc_stripe(struct kmem_cache *sc, gfp_t gfp)
+{
+ struct stripe_head *sh;
+
+ sh = kmem_cache_zalloc(sc, gfp);
+ if (sh) {
+ spin_lock_init(&sh->stripe_lock);
+ spin_lock_init(&sh->batch_lock);
+ INIT_LIST_HEAD(&sh->batch_list);
+ INIT_LIST_HEAD(&sh->lru);
+ atomic_set(&sh->count, 1);
+ }
+ return sh;
+}
static int grow_one_stripe(struct r5conf *conf, gfp_t gfp)
{
struct stripe_head *sh;
- sh = kmem_cache_zalloc(conf->slab_cache, gfp);
+
+ sh = alloc_stripe(conf->slab_cache, gfp);
if (!sh)
return 0;
sh->raid_conf = conf;
- spin_lock_init(&sh->stripe_lock);
-
if (grow_buffers(sh, gfp)) {
shrink_buffers(sh);
kmem_cache_free(conf->slab_cache, sh);
@@ -1990,13 +2010,8 @@ static int grow_one_stripe(struct r5conf *conf, gfp_t gfp)
sh->hash_lock_index =
conf->max_nr_stripes % NR_STRIPE_HASH_LOCKS;
/* we just created an active stripe so... */
- atomic_set(&sh->count, 1);
atomic_inc(&conf->active_stripes);
- INIT_LIST_HEAD(&sh->lru);
- spin_lock_init(&sh->batch_lock);
- INIT_LIST_HEAD(&sh->batch_list);
- sh->batch_head = NULL;
release_stripe(sh);
conf->max_nr_stripes++;
return 1;
@@ -2060,6 +2075,35 @@ static struct flex_array *scribble_alloc(int num, int cnt, gfp_t flags)
return ret;
}
+static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors)
+{
+ unsigned long cpu;
+ int err = 0;
+
+ mddev_suspend(conf->mddev);
+ get_online_cpus();
+ for_each_present_cpu(cpu) {
+ struct raid5_percpu *percpu;
+ struct flex_array *scribble;
+
+ percpu = per_cpu_ptr(conf->percpu, cpu);
+ scribble = scribble_alloc(new_disks,
+ new_sectors / STRIPE_SECTORS,
+ GFP_NOIO);
+
+ if (scribble) {
+ flex_array_free(percpu->scribble);
+ percpu->scribble = scribble;
+ } else {
+ err = -ENOMEM;
+ break;
+ }
+ }
+ put_online_cpus();
+ mddev_resume(conf->mddev);
+ return err;
+}
+
static int resize_stripes(struct r5conf *conf, int newsize)
{
/* Make all the stripes able to hold 'newsize' devices.
@@ -2088,7 +2132,6 @@ static int resize_stripes(struct r5conf *conf, int newsize)
struct stripe_head *osh, *nsh;
LIST_HEAD(newstripes);
struct disk_info *ndisks;
- unsigned long cpu;
int err;
struct kmem_cache *sc;
int i;
@@ -2109,13 +2152,11 @@ static int resize_stripes(struct r5conf *conf, int newsize)
return -ENOMEM;
for (i = conf->max_nr_stripes; i; i--) {
- nsh = kmem_cache_zalloc(sc, GFP_KERNEL);
+ nsh = alloc_stripe(sc, GFP_KERNEL);
if (!nsh)
break;
nsh->raid_conf = conf;
- spin_lock_init(&nsh->stripe_lock);
-
list_add(&nsh->lru, &newstripes);
}
if (i) {
@@ -2142,13 +2183,11 @@ static int resize_stripes(struct r5conf *conf, int newsize)
lock_device_hash_lock(conf, hash));
osh = get_free_stripe(conf, hash);
unlock_device_hash_lock(conf, hash);
- atomic_set(&nsh->count, 1);
+
for(i=0; i<conf->pool_size; i++) {
nsh->dev[i].page = osh->dev[i].page;
nsh->dev[i].orig_page = osh->dev[i].page;
}
- for( ; i<newsize; i++)
- nsh->dev[i].page = NULL;
nsh->hash_lock_index = hash;
kmem_cache_free(conf->slab_cache, osh);
cnt++;
@@ -2174,25 +2213,6 @@ static int resize_stripes(struct r5conf *conf, int newsize)
} else
err = -ENOMEM;
- get_online_cpus();
- for_each_present_cpu(cpu) {
- struct raid5_percpu *percpu;
- struct flex_array *scribble;
-
- percpu = per_cpu_ptr(conf->percpu, cpu);
- scribble = scribble_alloc(newsize, conf->chunk_sectors /
- STRIPE_SECTORS, GFP_NOIO);
-
- if (scribble) {
- flex_array_free(percpu->scribble);
- percpu->scribble = scribble;
- } else {
- err = -ENOMEM;
- break;
- }
- }
- put_online_cpus();
-
/* Step 4, return new stripes to service */
while(!list_empty(&newstripes)) {
nsh = list_entry(newstripes.next, struct stripe_head, lru);
@@ -2212,7 +2232,8 @@ static int resize_stripes(struct r5conf *conf, int newsize)
conf->slab_cache = sc;
conf->active_name = 1-conf->active_name;
- conf->pool_size = newsize;
+ if (!err)
+ conf->pool_size = newsize;
return err;
}
@@ -2434,7 +2455,7 @@ static void raid5_end_write_request(struct bio *bi, int error)
}
rdev_dec_pending(rdev, conf->mddev);
- if (sh->batch_head && !uptodate)
+ if (sh->batch_head && !uptodate && !replacement)
set_bit(STRIPE_BATCH_ERR, &sh->batch_head->state);
if (!test_and_clear_bit(R5_DOUBLE_LOCKED, &sh->dev[i].flags))
@@ -2976,14 +2997,32 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx,
pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n",
(unsigned long long)(*bip)->bi_iter.bi_sector,
(unsigned long long)sh->sector, dd_idx);
- spin_unlock_irq(&sh->stripe_lock);
if (conf->mddev->bitmap && firstwrite) {
+ /* Cannot hold spinlock over bitmap_startwrite,
+ * but must ensure this isn't added to a batch until
+ * we have added to the bitmap and set bm_seq.
+ * So set STRIPE_BITMAP_PENDING to prevent
+ * batching.
+ * If multiple add_stripe_bio() calls race here they
+ * much all set STRIPE_BITMAP_PENDING. So only the first one
+ * to complete "bitmap_startwrite" gets to set
+ * STRIPE_BIT_DELAY. This is important as once a stripe
+ * is added to a batch, STRIPE_BIT_DELAY cannot be changed
+ * any more.
+ */
+ set_bit(STRIPE_BITMAP_PENDING, &sh->state);
+ spin_unlock_irq(&sh->stripe_lock);
bitmap_startwrite(conf->mddev->bitmap, sh->sector,
STRIPE_SECTORS, 0);
- sh->bm_seq = conf->seq_flush+1;
- set_bit(STRIPE_BIT_DELAY, &sh->state);
+ spin_lock_irq(&sh->stripe_lock);
+ clear_bit(STRIPE_BITMAP_PENDING, &sh->state);
+ if (!sh->batch_head) {
+ sh->bm_seq = conf->seq_flush+1;
+ set_bit(STRIPE_BIT_DELAY, &sh->state);
+ }
}
+ spin_unlock_irq(&sh->stripe_lock);
if (stripe_can_batch(sh))
stripe_add_to_batch_list(conf, sh);
@@ -3278,7 +3317,9 @@ static int need_this_block(struct stripe_head *sh, struct stripe_head_state *s,
/* reconstruct-write isn't being forced */
return 0;
for (i = 0; i < s->failed; i++) {
- if (!test_bit(R5_UPTODATE, &fdev[i]->flags) &&
+ if (s->failed_num[i] != sh->pd_idx &&
+ s->failed_num[i] != sh->qd_idx &&
+ !test_bit(R5_UPTODATE, &fdev[i]->flags) &&
!test_bit(R5_OVERWRITE, &fdev[i]->flags))
return 1;
}
@@ -3298,6 +3339,7 @@ static int fetch_block(struct stripe_head *sh, struct stripe_head_state *s,
*/
BUG_ON(test_bit(R5_Wantcompute, &dev->flags));
BUG_ON(test_bit(R5_Wantread, &dev->flags));
+ BUG_ON(sh->batch_head);
if ((s->uptodate == disks - 1) &&
(s->failed && (disk_idx == s->failed_num[0] ||
disk_idx == s->failed_num[1]))) {
@@ -3366,7 +3408,6 @@ static void handle_stripe_fill(struct stripe_head *sh,
{
int i;
- BUG_ON(sh->batch_head);
/* look for blocks to read/compute, skip this if a compute
* is already in flight, or if the stripe contents are in the
* midst of changing due to a write
@@ -3379,6 +3420,8 @@ static void handle_stripe_fill(struct stripe_head *sh,
set_bit(STRIPE_HANDLE, &sh->state);
}
+static void break_stripe_batch_list(struct stripe_head *head_sh,
+ unsigned long handle_flags);
/* handle_stripe_clean_event
* any written block on an uptodate or failed drive can be returned.
* Note that if we 'wrote' to a failed drive, it will be UPTODATE, but
@@ -3392,7 +3435,6 @@ static void handle_stripe_clean_event(struct r5conf *conf,
int discard_pending = 0;
struct stripe_head *head_sh = sh;
bool do_endio = false;
- int wakeup_nr = 0;
for (i = disks; i--; )
if (sh->dev[i].written) {
@@ -3481,44 +3523,8 @@ unhash:
if (atomic_dec_and_test(&conf->pending_full_writes))
md_wakeup_thread(conf->mddev->thread);
- if (!head_sh->batch_head || !do_endio)
- return;
- for (i = 0; i < head_sh->disks; i++) {
- if (test_and_clear_bit(R5_Overlap, &head_sh->dev[i].flags))
- wakeup_nr++;
- }
- while (!list_empty(&head_sh->batch_list)) {
- int i;
- sh = list_first_entry(&head_sh->batch_list,
- struct stripe_head, batch_list);
- list_del_init(&sh->batch_list);
-
- set_mask_bits(&sh->state, ~STRIPE_EXPAND_SYNC_FLAG,
- head_sh->state & ~((1 << STRIPE_ACTIVE) |
- (1 << STRIPE_PREREAD_ACTIVE) |
- STRIPE_EXPAND_SYNC_FLAG));
- sh->check_state = head_sh->check_state;
- sh->reconstruct_state = head_sh->reconstruct_state;
- for (i = 0; i < sh->disks; i++) {
- if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
- wakeup_nr++;
- sh->dev[i].flags = head_sh->dev[i].flags;
- }
-
- spin_lock_irq(&sh->stripe_lock);
- sh->batch_head = NULL;
- spin_unlock_irq(&sh->stripe_lock);
- if (sh->state & STRIPE_EXPAND_SYNC_FLAG)
- set_bit(STRIPE_HANDLE, &sh->state);
- release_stripe(sh);
- }
-
- spin_lock_irq(&head_sh->stripe_lock);
- head_sh->batch_head = NULL;
- spin_unlock_irq(&head_sh->stripe_lock);
- wake_up_nr(&conf->wait_for_overlap, wakeup_nr);
- if (head_sh->state & STRIPE_EXPAND_SYNC_FLAG)
- set_bit(STRIPE_HANDLE, &head_sh->state);
+ if (head_sh->batch_head && do_endio)
+ break_stripe_batch_list(head_sh, STRIPE_EXPAND_SYNC_FLAGS);
}
static void handle_stripe_dirtying(struct r5conf *conf,
@@ -4159,9 +4165,13 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
static int clear_batch_ready(struct stripe_head *sh)
{
+ /* Return '1' if this is a member of batch, or
+ * '0' if it is a lone stripe or a head which can now be
+ * handled.
+ */
struct stripe_head *tmp;
if (!test_and_clear_bit(STRIPE_BATCH_READY, &sh->state))
- return 0;
+ return (sh->batch_head && sh->batch_head != sh);
spin_lock(&sh->stripe_lock);
if (!sh->batch_head) {
spin_unlock(&sh->stripe_lock);
@@ -4189,46 +4199,65 @@ static int clear_batch_ready(struct stripe_head *sh)
return 0;
}
-static void check_break_stripe_batch_list(struct stripe_head *sh)
+static void break_stripe_batch_list(struct stripe_head *head_sh,
+ unsigned long handle_flags)
{
- struct stripe_head *head_sh, *next;
+ struct stripe_head *sh, *next;
int i;
+ int do_wakeup = 0;
- if (!test_and_clear_bit(STRIPE_BATCH_ERR, &sh->state))
- return;
+ list_for_each_entry_safe(sh, next, &head_sh->batch_list, batch_list) {
- head_sh = sh;
- do {
- sh = list_first_entry(&sh->batch_list,
- struct stripe_head, batch_list);
- BUG_ON(sh == head_sh);
- } while (!test_bit(STRIPE_DEGRADED, &sh->state));
-
- while (sh != head_sh) {
- next = list_first_entry(&sh->batch_list,
- struct stripe_head, batch_list);
list_del_init(&sh->batch_list);
- set_mask_bits(&sh->state, ~STRIPE_EXPAND_SYNC_FLAG,
- head_sh->state & ~((1 << STRIPE_ACTIVE) |
- (1 << STRIPE_PREREAD_ACTIVE) |
- (1 << STRIPE_DEGRADED) |
- STRIPE_EXPAND_SYNC_FLAG));
+ WARN_ON_ONCE(sh->state & ((1 << STRIPE_ACTIVE) |
+ (1 << STRIPE_SYNCING) |
+ (1 << STRIPE_REPLACED) |
+ (1 << STRIPE_PREREAD_ACTIVE) |
+ (1 << STRIPE_DELAYED) |
+ (1 << STRIPE_BIT_DELAY) |
+ (1 << STRIPE_FULL_WRITE) |
+ (1 << STRIPE_BIOFILL_RUN) |
+ (1 << STRIPE_COMPUTE_RUN) |
+ (1 << STRIPE_OPS_REQ_PENDING) |
+ (1 << STRIPE_DISCARD) |
+ (1 << STRIPE_BATCH_READY) |
+ (1 << STRIPE_BATCH_ERR) |
+ (1 << STRIPE_BITMAP_PENDING)));
+ WARN_ON_ONCE(head_sh->state & ((1 << STRIPE_DISCARD) |
+ (1 << STRIPE_REPLACED)));
+
+ set_mask_bits(&sh->state, ~(STRIPE_EXPAND_SYNC_FLAGS |
+ (1 << STRIPE_DEGRADED)),
+ head_sh->state & (1 << STRIPE_INSYNC));
+
sh->check_state = head_sh->check_state;
sh->reconstruct_state = head_sh->reconstruct_state;
- for (i = 0; i < sh->disks; i++)
+ for (i = 0; i < sh->disks; i++) {
+ if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
+ do_wakeup = 1;
sh->dev[i].flags = head_sh->dev[i].flags &
(~((1 << R5_WriteError) | (1 << R5_Overlap)));
-
+ }
spin_lock_irq(&sh->stripe_lock);
sh->batch_head = NULL;
spin_unlock_irq(&sh->stripe_lock);
-
- set_bit(STRIPE_HANDLE, &sh->state);
+ if (handle_flags == 0 ||
+ sh->state & handle_flags)
+ set_bit(STRIPE_HANDLE, &sh->state);
release_stripe(sh);
-
- sh = next;
}
+ spin_lock_irq(&head_sh->stripe_lock);
+ head_sh->batch_head = NULL;
+ spin_unlock_irq(&head_sh->stripe_lock);
+ for (i = 0; i < head_sh->disks; i++)
+ if (test_and_clear_bit(R5_Overlap, &head_sh->dev[i].flags))
+ do_wakeup = 1;
+ if (head_sh->state & handle_flags)
+ set_bit(STRIPE_HANDLE, &head_sh->state);
+
+ if (do_wakeup)
+ wake_up(&head_sh->raid_conf->wait_for_overlap);
}
static void handle_stripe(struct stripe_head *sh)
@@ -4253,7 +4282,8 @@ static void handle_stripe(struct stripe_head *sh)
return;
}
- check_break_stripe_batch_list(sh);
+ if (test_and_clear_bit(STRIPE_BATCH_ERR, &sh->state))
+ break_stripe_batch_list(sh, 0);
if (test_bit(STRIPE_SYNC_REQUESTED, &sh->state) && !sh->batch_head) {
spin_lock(&sh->stripe_lock);
@@ -4307,6 +4337,7 @@ static void handle_stripe(struct stripe_head *sh)
if (s.failed > conf->max_degraded) {
sh->check_state = 0;
sh->reconstruct_state = 0;
+ break_stripe_batch_list(sh, 0);
if (s.to_read+s.to_write+s.written)
handle_failed_stripe(conf, sh, &s, disks, &s.return_bi);
if (s.syncing + s.replacing)
@@ -6221,8 +6252,11 @@ static int alloc_scratch_buffer(struct r5conf *conf, struct raid5_percpu *percpu
percpu->spare_page = alloc_page(GFP_KERNEL);
if (!percpu->scribble)
percpu->scribble = scribble_alloc(max(conf->raid_disks,
- conf->previous_raid_disks), conf->chunk_sectors /
- STRIPE_SECTORS, GFP_KERNEL);
+ conf->previous_raid_disks),
+ max(conf->chunk_sectors,
+ conf->prev_chunk_sectors)
+ / STRIPE_SECTORS,
+ GFP_KERNEL);
if (!percpu->scribble || (conf->level == 6 && !percpu->spare_page)) {
free_scratch_buffer(conf, percpu);
@@ -7198,6 +7232,15 @@ static int check_reshape(struct mddev *mddev)
if (!check_stripe_cache(mddev))
return -ENOSPC;
+ if (mddev->new_chunk_sectors > mddev->chunk_sectors ||
+ mddev->delta_disks > 0)
+ if (resize_chunks(conf,
+ conf->previous_raid_disks
+ + max(0, mddev->delta_disks),
+ max(mddev->new_chunk_sectors,
+ mddev->chunk_sectors)
+ ) < 0)
+ return -ENOMEM;
return resize_stripes(conf, (conf->previous_raid_disks
+ mddev->delta_disks));
}
@@ -7311,6 +7354,7 @@ static int raid5_start_reshape(struct mddev *mddev)
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+ clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
mddev->sync_thread = md_register_thread(md_do_sync, mddev,
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 7dc0dd86074b..896d603ad0da 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -337,9 +337,12 @@ enum {
STRIPE_ON_RELEASE_LIST,
STRIPE_BATCH_READY,
STRIPE_BATCH_ERR,
+ STRIPE_BITMAP_PENDING, /* Being added to bitmap, don't add
+ * to batch yet.
+ */
};
-#define STRIPE_EXPAND_SYNC_FLAG \
+#define STRIPE_EXPAND_SYNC_FLAGS \
((1 << STRIPE_EXPAND_SOURCE) |\
(1 << STRIPE_EXPAND_READY) |\
(1 << STRIPE_EXPANDING) |\
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 3ef0f90b128f..157099243d61 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -97,6 +97,7 @@ config MEDIA_CONTROLLER
config MEDIA_CONTROLLER_DVB
bool "Enable Media controller for DVB"
depends on MEDIA_CONTROLLER
+ depends on BROKEN
---help---
Enable the media controller API support for DVB.
diff --git a/drivers/media/pci/cx25821/cx25821-medusa-reg.h b/drivers/media/pci/cx25821/cx25821-medusa-reg.h
index c98ac946b277..2e10643a86b7 100644
--- a/drivers/media/pci/cx25821/cx25821-medusa-reg.h
+++ b/drivers/media/pci/cx25821/cx25821-medusa-reg.h
@@ -84,9 +84,9 @@
#define ABIST_BIN4_VGA3 0x01D4
#define ABIST_BIN5_VGA4 0x01D8
#define ABIST_BIN6_VGA5 0x01DC
-#define ABIST_BIN7_VGA6 0x0x1E0
-#define ABIST_CLAMP_A 0x0x1E4
-#define ABIST_CLAMP_B 0x0x1E8
+#define ABIST_BIN7_VGA6 0x01E0
+#define ABIST_CLAMP_A 0x01E4
+#define ABIST_CLAMP_B 0x01E8
#define ABIST_CLAMP_C 0x01EC
#define ABIST_CLAMP_D 0x01F0
#define ABIST_CLAMP_E 0x01F4
diff --git a/drivers/media/pci/ivtv/Kconfig b/drivers/media/pci/ivtv/Kconfig
index dd6ee57e3a4c..6e5867c57305 100644
--- a/drivers/media/pci/ivtv/Kconfig
+++ b/drivers/media/pci/ivtv/Kconfig
@@ -57,5 +57,8 @@ config VIDEO_FB_IVTV
This is used in the Hauppauge PVR-350 card. There is a driver
homepage at <http://www.ivtvdriver.org>.
+ In order to use this module, you will need to boot with PAT disabled
+ on x86 systems, using the nopat kernel parameter.
+
To compile this driver as a module, choose M here: the
module will be called ivtvfb.
diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c
index 9ff1230192e8..4cb365d4ffdc 100644
--- a/drivers/media/pci/ivtv/ivtvfb.c
+++ b/drivers/media/pci/ivtv/ivtvfb.c
@@ -44,8 +44,8 @@
#include <linux/ivtvfb.h>
#include <linux/slab.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
+#ifdef CONFIG_X86_64
+#include <asm/pat.h>
#endif
#include "ivtv-driver.h"
@@ -155,12 +155,11 @@ struct osd_info {
/* Buffer size */
u32 video_buffer_size;
-#ifdef CONFIG_MTRR
/* video_base rounded down as required by hardware MTRRs */
unsigned long fb_start_aligned_physaddr;
/* video_base rounded up as required by hardware MTRRs */
unsigned long fb_end_aligned_physaddr;
-#endif
+ int wc_cookie;
/* Store the buffer offset */
int set_osd_coords_x;
@@ -1099,6 +1098,8 @@ static int ivtvfb_init_vidmode(struct ivtv *itv)
static int ivtvfb_init_io(struct ivtv *itv)
{
struct osd_info *oi = itv->osd_info;
+ /* Find the largest power of two that maps the whole buffer */
+ int size_shift = 31;
mutex_lock(&itv->serialize_lock);
if (ivtv_init_on_first_open(itv)) {
@@ -1132,29 +1133,16 @@ static int ivtvfb_init_io(struct ivtv *itv)
oi->video_pbase, oi->video_vbase,
oi->video_buffer_size / 1024);
-#ifdef CONFIG_MTRR
- {
- /* Find the largest power of two that maps the whole buffer */
- int size_shift = 31;
-
- while (!(oi->video_buffer_size & (1 << size_shift))) {
- size_shift--;
- }
- size_shift++;
- oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
- oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
- oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
- oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
- if (mtrr_add(oi->fb_start_aligned_physaddr,
- oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
- MTRR_TYPE_WRCOMB, 1) < 0) {
- IVTVFB_INFO("disabled mttr\n");
- oi->fb_start_aligned_physaddr = 0;
- oi->fb_end_aligned_physaddr = 0;
- }
- }
-#endif
-
+ while (!(oi->video_buffer_size & (1 << size_shift)))
+ size_shift--;
+ size_shift++;
+ oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
+ oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
+ oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
+ oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
+ oi->wc_cookie = arch_phys_wc_add(oi->fb_start_aligned_physaddr,
+ oi->fb_end_aligned_physaddr -
+ oi->fb_start_aligned_physaddr);
/* Blank the entire osd. */
memset_io(oi->video_vbase, 0, oi->video_buffer_size);
@@ -1172,14 +1160,7 @@ static void ivtvfb_release_buffers (struct ivtv *itv)
/* Release pseudo palette */
kfree(oi->ivtvfb_info.pseudo_palette);
-
-#ifdef CONFIG_MTRR
- if (oi->fb_end_aligned_physaddr) {
- mtrr_del(-1, oi->fb_start_aligned_physaddr,
- oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
- }
-#endif
-
+ arch_phys_wc_del(oi->wc_cookie);
kfree(oi);
itv->osd_info = NULL;
}
@@ -1284,6 +1265,13 @@ static int __init ivtvfb_init(void)
int registered = 0;
int err;
+#ifdef CONFIG_X86_64
+ if (WARN(pat_enabled(),
+ "ivtvfb needs PAT disabled, boot with nopat kernel parameter\n")) {
+ return -ENODEV;
+ }
+#endif
+
if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
IVTV_MAX_CARDS - 1);
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 9c64b5d01c6a..110fd70c7326 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -116,8 +116,8 @@ static struct mcam_format_struct {
.planar = false,
},
{
- .desc = "UYVY 4:2:2",
- .pixelformat = V4L2_PIX_FMT_UYVY,
+ .desc = "YVYU 4:2:2",
+ .pixelformat = V4L2_PIX_FMT_YVYU,
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = false,
@@ -748,7 +748,7 @@ static void mcam_ctlr_image(struct mcam_camera *cam)
switch (fmt->pixelformat) {
case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
widthy = fmt->width * 2;
widthuv = 0;
break;
@@ -784,15 +784,15 @@ static void mcam_ctlr_image(struct mcam_camera *cam)
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
mcam_reg_write_mask(cam, REG_CTRL0,
- C0_DF_YUV | C0_YUV_420PL | C0_YUVE_YVYU, C0_DF_MASK);
+ C0_DF_YUV | C0_YUV_420PL | C0_YUVE_VYUY, C0_DF_MASK);
break;
case V4L2_PIX_FMT_YUYV:
mcam_reg_write_mask(cam, REG_CTRL0,
- C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_UYVY, C0_DF_MASK);
+ C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_NOSWAP, C0_DF_MASK);
break;
- case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
mcam_reg_write_mask(cam, REG_CTRL0,
- C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV, C0_DF_MASK);
+ C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_SWAP24, C0_DF_MASK);
break;
case V4L2_PIX_FMT_JPEG:
mcam_reg_write_mask(cam, REG_CTRL0,
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
index aa0c6eac254a..7ffdf4dbaf8c 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell-ccic/mcam-core.h
@@ -330,10 +330,10 @@ int mccic_resume(struct mcam_camera *cam);
#define C0_YUVE_YVYU 0x00010000 /* Y1CrY0Cb */
#define C0_YUVE_VYUY 0x00020000 /* CrY1CbY0 */
#define C0_YUVE_UYVY 0x00030000 /* CbY1CrY0 */
-#define C0_YUVE_XYUV 0x00000000 /* 420: .YUV */
-#define C0_YUVE_XYVU 0x00010000 /* 420: .YVU */
-#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */
-#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */
+#define C0_YUVE_NOSWAP 0x00000000 /* no bytes swapping */
+#define C0_YUVE_SWAP13 0x00010000 /* swap byte 1 and 3 */
+#define C0_YUVE_SWAP24 0x00020000 /* swap byte 2 and 4 */
+#define C0_YUVE_SWAP1324 0x00030000 /* swap bytes 1&3 and 2&4 */
/* Bayer bits 18,19 if needed */
#define C0_EOF_VSYNC 0x00400000 /* Generate EOF by VSYNC */
#define C0_VEDGE_CTRL 0x00800000 /* Detect falling edge of VSYNC */
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
index 9351f64dee7b..6460f8e1b07f 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -135,6 +135,8 @@
#define VIN_MAX_WIDTH 2048
#define VIN_MAX_HEIGHT 2048
+#define TIMEOUT_MS 100
+
enum chip_id {
RCAR_GEN2,
RCAR_H1,
@@ -820,7 +822,10 @@ static void rcar_vin_wait_stop_streaming(struct rcar_vin_priv *priv)
if (priv->state == STOPPING) {
priv->request_to_stop = true;
spin_unlock_irq(&priv->lock);
- wait_for_completion(&priv->capture_stop);
+ if (!wait_for_completion_timeout(
+ &priv->capture_stop,
+ msecs_to_jiffies(TIMEOUT_MS)))
+ priv->state = STOPPED;
spin_lock_irq(&priv->lock);
}
}
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index 187f83629f7e..5dcc0313c38a 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -59,10 +59,6 @@
#include <linux/delay.h>
#include <linux/interrupt.h> /* needed for in_interrupt() proto */
#include <linux/dma-mapping.h>
-#include <asm/io.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
#include <linux/kthread.h>
#include <scsi/scsi_host.h>
@@ -2820,13 +2816,6 @@ mpt_adapter_dispose(MPT_ADAPTER *ioc)
pci_disable_device(ioc->pcidev);
pci_release_selected_regions(ioc->pcidev, ioc->bars);
-#if defined(CONFIG_MTRR) && 0
- if (ioc->mtrr_reg > 0) {
- mtrr_del(ioc->mtrr_reg, 0, 0);
- dprintk(ioc, printk(MYIOC_s_INFO_FMT "MTRR region de-registered\n", ioc->name));
- }
-#endif
-
/* Zap the adapter lookup ptr! */
list_del(&ioc->list);
@@ -4512,19 +4501,6 @@ PrimeIocFifos(MPT_ADAPTER *ioc)
ioc->req_frames_low_dma = (u32) (alloc_dma & 0xFFFFFFFF);
-#if defined(CONFIG_MTRR) && 0
- /*
- * Enable Write Combining MTRR for IOC's memory region.
- * (at least as much as we can; "size and base must be
- * multiples of 4 kiB"
- */
- ioc->mtrr_reg = mtrr_add(ioc->req_frames_dma,
- sz,
- MTRR_TYPE_WRCOMB, 1);
- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "MTRR region registered (base:size=%08x:%x)\n",
- ioc->name, ioc->req_frames_dma, sz));
-#endif
-
for (i = 0; i < ioc->req_depth; i++) {
alloc_dma += ioc->req_sz;
mem += ioc->req_sz;
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index 8f14090b8b71..813d46311f6a 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -671,7 +671,6 @@ typedef struct _MPT_ADAPTER
u8 *HostPageBuffer; /* SAS - host page buffer support */
u32 HostPageBuffer_sz;
dma_addr_t HostPageBuffer_dma;
- int mtrr_reg;
struct pci_dev *pcidev; /* struct pci_dev pointer */
int bars; /* bitmask of BAR's that must be configured */
int msi_enable;
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 5bdaae15a742..005a88b9f440 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -4090,7 +4090,7 @@ mptsas_handle_queue_full_event(struct fw_event_work *fw_event)
continue;
}
depth = scsi_track_queue_full(sdev,
- current_depth - 1);
+ sdev->queue_depth - 1);
if (depth > 0)
sdev_printk(KERN_INFO, sdev,
"Queue depth reduced to (%d)\n",
@@ -4100,7 +4100,7 @@ mptsas_handle_queue_full_event(struct fw_event_work *fw_event)
"Tagged Command Queueing is being "
"disabled\n");
else if (depth == 0)
- sdev_printk(KERN_INFO, sdev,
+ sdev_printk(KERN_DEBUG, sdev,
"Queue depth not changed yet\n");
}
}
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index d2a85cde68da..e03b7f45b8f7 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -566,7 +566,7 @@ static int pm860x_irq_domain_map(struct irq_domain *d, unsigned int virq,
return 0;
}
-static struct irq_domain_ops pm860x_irq_domain_ops = {
+static const struct irq_domain_ops pm860x_irq_domain_ops = {
.map = pm860x_irq_domain_map,
.xlate = irq_domain_xlate_onetwocell,
};
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d5ad04dad081..653815950aa2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -52,7 +52,8 @@ config PMIC_ADP5520
config MFD_AAT2870_CORE
bool "AnalogicTech AAT2870"
select MFD_CORE
- depends on I2C=y && GPIOLIB
+ depends on I2C=y
+ depends on GPIOLIB || COMPILE_TEST
help
If you say yes here you get support for the AAT2870.
This driver provides common support for accessing the device,
@@ -94,6 +95,8 @@ config MFD_AXP20X
config MFD_CROS_EC
tristate "ChromeOS Embedded Controller"
select MFD_CORE
+ select CHROME_PLATFORMS
+ select CROS_EC_PROTO
help
If you say Y here you get support for the ChromeOS Embedded
Controller (EC) providing keyboard, battery and power services.
@@ -102,7 +105,7 @@ config MFD_CROS_EC
config MFD_CROS_EC_I2C
tristate "ChromeOS Embedded Controller (I2C)"
- depends on MFD_CROS_EC && I2C
+ depends on MFD_CROS_EC && CROS_EC_PROTO && I2C
help
If you say Y here, you get support for talking to the ChromeOS
@@ -112,7 +115,7 @@ config MFD_CROS_EC_I2C
config MFD_CROS_EC_SPI
tristate "ChromeOS Embedded Controller (SPI)"
- depends on MFD_CROS_EC && SPI && OF
+ depends on MFD_CROS_EC && CROS_EC_PROTO && SPI && OF
---help---
If you say Y here, you get support for talking to the ChromeOS EC
@@ -1115,7 +1118,8 @@ config MFD_TPS6586X
config MFD_TPS65910
bool "TI TPS65910 Power Management chip"
- depends on I2C=y && GPIOLIB
+ depends on I2C=y
+ depends on GPIOLIB || COMPILE_TEST
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 0e5cfeba107c..ea40e076cb61 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -39,13 +39,13 @@ obj-$(CONFIG_MFD_ARIZONA) += arizona-core.o
obj-$(CONFIG_MFD_ARIZONA) += arizona-irq.o
obj-$(CONFIG_MFD_ARIZONA_I2C) += arizona-i2c.o
obj-$(CONFIG_MFD_ARIZONA_SPI) += arizona-spi.o
-ifneq ($(CONFIG_MFD_WM5102),n)
+ifeq ($(CONFIG_MFD_WM5102),y)
obj-$(CONFIG_MFD_ARIZONA) += wm5102-tables.o
endif
-ifneq ($(CONFIG_MFD_WM5110),n)
+ifeq ($(CONFIG_MFD_WM5110),y)
obj-$(CONFIG_MFD_ARIZONA) += wm5110-tables.o
endif
-ifneq ($(CONFIG_MFD_WM8997),n)
+ifeq ($(CONFIG_MFD_WM8997),y)
obj-$(CONFIG_MFD_ARIZONA) += wm8997-tables.o
endif
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index c80a2925f8e5..000da72a0ae9 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -574,7 +574,7 @@ static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
return 0;
}
-static struct irq_domain_ops ab8500_irq_ops = {
+static const struct irq_domain_ops ab8500_irq_ops = {
.map = ab8500_irq_map,
.xlate = irq_domain_xlate_twocell,
};
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index cdd6f3d63314..0236cd7cdce4 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -2885,7 +2885,7 @@ static ssize_t ab8500_subscribe_write(struct file *file,
}
err = request_threaded_irq(user_val, NULL, ab8500_debug_handler,
- IRQF_SHARED | IRQF_NO_SUSPEND,
+ IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
"ab8500-debug", &dev->kobj);
if (err < 0) {
pr_info("request_threaded_irq failed %d, %lu\n",
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index dabbc93abdd7..c51c1b188d64 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -948,7 +948,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
if (gpadc->irq_sw >= 0) {
ret = request_threaded_irq(gpadc->irq_sw, NULL,
ab8500_bm_gpadcconvend_handler,
- IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw",
+ IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
+ "ab8500-gpadc-sw",
gpadc);
if (ret < 0) {
dev_err(gpadc->dev,
@@ -961,7 +962,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
if (gpadc->irq_hw >= 0) {
ret = request_threaded_irq(gpadc->irq_hw, NULL,
ab8500_bm_gpadcconvend_handler,
- IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw",
+ IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
+ "ab8500-gpadc-hw",
gpadc);
if (ret < 0) {
dev_err(gpadc->dev,
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index 6ca6dfab50eb..bebf58a06a6b 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -250,20 +250,50 @@ static int arizona_wait_for_boot(struct arizona *arizona)
return ret;
}
-static int arizona_apply_hardware_patch(struct arizona* arizona)
+static inline void arizona_enable_reset(struct arizona *arizona)
+{
+ if (arizona->pdata.reset)
+ gpio_set_value_cansleep(arizona->pdata.reset, 0);
+}
+
+static void arizona_disable_reset(struct arizona *arizona)
+{
+ if (arizona->pdata.reset) {
+ switch (arizona->type) {
+ case WM5110:
+ case WM8280:
+ /* Meet requirements for minimum reset duration */
+ msleep(5);
+ break;
+ default:
+ break;
+ }
+
+ gpio_set_value_cansleep(arizona->pdata.reset, 1);
+ msleep(1);
+ }
+}
+
+struct arizona_sysclk_state {
+ unsigned int fll;
+ unsigned int sysclk;
+};
+
+static int arizona_enable_freerun_sysclk(struct arizona *arizona,
+ struct arizona_sysclk_state *state)
{
- unsigned int fll, sysclk;
int ret, err;
/* Cache existing FLL and SYSCLK settings */
- ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll);
- if (ret != 0) {
+ ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &state->fll);
+ if (ret) {
dev_err(arizona->dev, "Failed to cache FLL settings: %d\n",
ret);
return ret;
}
- ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk);
- if (ret != 0) {
+ ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1,
+ &state->sysclk);
+ if (ret) {
dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n",
ret);
return ret;
@@ -272,7 +302,7 @@ static int arizona_apply_hardware_patch(struct arizona* arizona)
/* Start up SYSCLK using the FLL in free running mode */
ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1,
ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN);
- if (ret != 0) {
+ if (ret) {
dev_err(arizona->dev,
"Failed to start FLL in freerunning mode: %d\n",
ret);
@@ -281,53 +311,137 @@ static int arizona_apply_hardware_patch(struct arizona* arizona)
ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5,
ARIZONA_FLL1_CLOCK_OK_STS,
ARIZONA_FLL1_CLOCK_OK_STS);
- if (ret != 0) {
+ if (ret) {
ret = -ETIMEDOUT;
goto err_fll;
}
ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144);
- if (ret != 0) {
+ if (ret) {
dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret);
goto err_fll;
}
+ return 0;
+
+err_fll:
+ err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, state->fll);
+ if (err)
+ dev_err(arizona->dev,
+ "Failed to re-apply old FLL settings: %d\n", err);
+
+ return ret;
+}
+
+static int arizona_disable_freerun_sysclk(struct arizona *arizona,
+ struct arizona_sysclk_state *state)
+{
+ int ret;
+
+ ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1,
+ state->sysclk);
+ if (ret) {
+ dev_err(arizona->dev,
+ "Failed to re-apply old SYSCLK settings: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, state->fll);
+ if (ret) {
+ dev_err(arizona->dev,
+ "Failed to re-apply old FLL settings: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm5102_apply_hardware_patch(struct arizona *arizona)
+{
+ struct arizona_sysclk_state state;
+ int err, ret;
+
+ ret = arizona_enable_freerun_sysclk(arizona, &state);
+ if (ret)
+ return ret;
+
/* Start the write sequencer and wait for it to finish */
ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
- ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
- if (ret != 0) {
+ ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
+ if (ret) {
dev_err(arizona->dev, "Failed to start write sequencer: %d\n",
ret);
- goto err_sysclk;
+ goto err;
}
+
ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1,
ARIZONA_WSEQ_BUSY, 0);
- if (ret != 0) {
+ if (ret) {
regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
- ARIZONA_WSEQ_ABORT);
+ ARIZONA_WSEQ_ABORT);
ret = -ETIMEDOUT;
}
-err_sysclk:
- err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk);
- if (err != 0) {
- dev_err(arizona->dev,
- "Failed to re-apply old SYSCLK settings: %d\n",
- err);
- }
+err:
+ err = arizona_disable_freerun_sysclk(arizona, &state);
-err_fll:
- err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll);
- if (err != 0) {
+ return ret ?: err;
+}
+
+/*
+ * Register patch to some of the CODECs internal write sequences
+ * to ensure a clean exit from the low power sleep state.
+ */
+static const struct reg_default wm5110_sleep_patch[] = {
+ { 0x337A, 0xC100 },
+ { 0x337B, 0x0041 },
+ { 0x3300, 0xA210 },
+ { 0x3301, 0x050C },
+};
+
+static int wm5110_apply_sleep_patch(struct arizona *arizona)
+{
+ struct arizona_sysclk_state state;
+ int err, ret;
+
+ ret = arizona_enable_freerun_sysclk(arizona, &state);
+ if (ret)
+ return ret;
+
+ ret = regmap_multi_reg_write_bypassed(arizona->regmap,
+ wm5110_sleep_patch,
+ ARRAY_SIZE(wm5110_sleep_patch));
+
+ err = arizona_disable_freerun_sysclk(arizona, &state);
+
+ return ret ?: err;
+}
+
+static int wm5102_clear_write_sequencer(struct arizona *arizona)
+{
+ int ret;
+
+ ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_3,
+ 0x0);
+ if (ret) {
dev_err(arizona->dev,
- "Failed to re-apply old FLL settings: %d\n",
- err);
+ "Failed to clear write sequencer state: %d\n", ret);
+ return ret;
}
- if (ret != 0)
+ arizona_enable_reset(arizona);
+ regulator_disable(arizona->dcvdd);
+
+ msleep(20);
+
+ ret = regulator_enable(arizona->dcvdd);
+ if (ret) {
+ dev_err(arizona->dev, "Failed to re-enable DCVDD: %d\n", ret);
return ret;
- else
- return err;
+ }
+ arizona_disable_reset(arizona);
+
+ return 0;
}
#ifdef CONFIG_PM
@@ -338,12 +452,33 @@ static int arizona_runtime_resume(struct device *dev)
dev_dbg(arizona->dev, "Leaving AoD mode\n");
+ if (arizona->has_fully_powered_off) {
+ dev_dbg(arizona->dev, "Re-enabling core supplies\n");
+
+ ret = regulator_bulk_enable(arizona->num_core_supplies,
+ arizona->core_supplies);
+ if (ret) {
+ dev_err(dev, "Failed to enable core supplies: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
ret = regulator_enable(arizona->dcvdd);
if (ret != 0) {
dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret);
+ if (arizona->has_fully_powered_off)
+ regulator_bulk_disable(arizona->num_core_supplies,
+ arizona->core_supplies);
return ret;
}
+ if (arizona->has_fully_powered_off) {
+ arizona_disable_reset(arizona);
+ enable_irq(arizona->irq);
+ arizona->has_fully_powered_off = false;
+ }
+
regcache_cache_only(arizona->regmap, false);
switch (arizona->type) {
@@ -366,14 +501,53 @@ static int arizona_runtime_resume(struct device *dev)
goto err;
}
- ret = arizona_apply_hardware_patch(arizona);
- if (ret != 0) {
+ ret = wm5102_apply_hardware_patch(arizona);
+ if (ret) {
dev_err(arizona->dev,
"Failed to apply hardware patch: %d\n",
ret);
goto err;
}
break;
+ case WM5110:
+ case WM8280:
+ ret = arizona_wait_for_boot(arizona);
+ if (ret)
+ goto err;
+
+ if (arizona->external_dcvdd) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_ISOLATION_CONTROL,
+ ARIZONA_ISOLATE_DCVDD1, 0);
+ if (ret) {
+ dev_err(arizona->dev,
+ "Failed to connect DCVDD: %d\n", ret);
+ goto err;
+ }
+ } else {
+ /*
+ * As this is only called for the internal regulator
+ * (where we know voltage ranges available) it is ok
+ * to request an exact range.
+ */
+ ret = regulator_set_voltage(arizona->dcvdd,
+ 1200000, 1200000);
+ if (ret < 0) {
+ dev_err(arizona->dev,
+ "Failed to set resume voltage: %d\n",
+ ret);
+ goto err;
+ }
+ }
+
+ ret = wm5110_apply_sleep_patch(arizona);
+ if (ret) {
+ dev_err(arizona->dev,
+ "Failed to re-apply sleep patch: %d\n",
+ ret);
+ goto err;
+ }
+ break;
default:
ret = arizona_wait_for_boot(arizona);
if (ret != 0) {
@@ -410,10 +584,17 @@ err:
static int arizona_runtime_suspend(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
+ unsigned int val;
int ret;
dev_dbg(arizona->dev, "Entering AoD mode\n");
+ ret = regmap_read(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, &val);
+ if (ret) {
+ dev_err(dev, "Failed to check jack det status: %d\n", ret);
+ return ret;
+ }
+
if (arizona->external_dcvdd) {
ret = regmap_update_bits(arizona->regmap,
ARIZONA_ISOLATION_CONTROL,
@@ -426,10 +607,56 @@ static int arizona_runtime_suspend(struct device *dev)
}
}
+ switch (arizona->type) {
+ case WM5110:
+ case WM8280:
+ if (arizona->external_dcvdd)
+ break;
+
+ /*
+ * As this is only called for the internal regulator
+ * (where we know voltage ranges available) it is ok
+ * to request an exact range.
+ */
+ ret = regulator_set_voltage(arizona->dcvdd, 1175000, 1175000);
+ if (ret < 0) {
+ dev_err(arizona->dev,
+ "Failed to set suspend voltage: %d\n", ret);
+ return ret;
+ }
+ break;
+ case WM5102:
+ if (!(val & ARIZONA_JD1_ENA)) {
+ ret = regmap_write(arizona->regmap,
+ ARIZONA_WRITE_SEQUENCER_CTRL_3, 0x0);
+ if (ret) {
+ dev_err(arizona->dev,
+ "Failed to clear write sequencer: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
regcache_cache_only(arizona->regmap, true);
regcache_mark_dirty(arizona->regmap);
regulator_disable(arizona->dcvdd);
+ /* Allow us to completely power down if no jack detection */
+ if (!(val & ARIZONA_JD1_ENA)) {
+ dev_dbg(arizona->dev, "Fully powering off\n");
+
+ arizona->has_fully_powered_off = true;
+
+ disable_irq(arizona->irq);
+ arizona_enable_reset(arizona);
+ regulator_bulk_disable(arizona->num_core_supplies,
+ arizona->core_supplies);
+ }
+
return 0;
}
#endif
@@ -728,9 +955,9 @@ int arizona_dev_init(struct arizona *arizona)
if (arizona->pdata.reset) {
/* Start out with /RESET low to put the chip into reset */
- ret = gpio_request_one(arizona->pdata.reset,
- GPIOF_DIR_OUT | GPIOF_INIT_LOW,
- "arizona /RESET");
+ ret = devm_gpio_request_one(arizona->dev, arizona->pdata.reset,
+ GPIOF_DIR_OUT | GPIOF_INIT_LOW,
+ "arizona /RESET");
if (ret != 0) {
dev_err(dev, "Failed to request /RESET: %d\n", ret);
goto err_dcvdd;
@@ -751,10 +978,7 @@ int arizona_dev_init(struct arizona *arizona)
goto err_enable;
}
- if (arizona->pdata.reset) {
- gpio_set_value_cansleep(arizona->pdata.reset, 1);
- msleep(1);
- }
+ arizona_disable_reset(arizona);
regcache_cache_only(arizona->regmap, false);
@@ -777,8 +1001,6 @@ int arizona_dev_init(struct arizona *arizona)
/* If we have a /RESET GPIO we'll already be reset */
if (!arizona->pdata.reset) {
- regcache_mark_dirty(arizona->regmap);
-
ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
if (ret != 0) {
dev_err(dev, "Failed to reset device: %d\n", ret);
@@ -786,12 +1008,6 @@ int arizona_dev_init(struct arizona *arizona)
}
msleep(1);
-
- ret = regcache_sync(arizona->regmap);
- if (ret != 0) {
- dev_err(dev, "Failed to sync device: %d\n", ret);
- goto err_reset;
- }
}
/* Ensure device startup is complete */
@@ -799,21 +1015,24 @@ int arizona_dev_init(struct arizona *arizona)
case WM5102:
ret = regmap_read(arizona->regmap,
ARIZONA_WRITE_SEQUENCER_CTRL_3, &val);
- if (ret != 0)
+ if (ret) {
dev_err(dev,
"Failed to check write sequencer state: %d\n",
ret);
- else if (val & 0x01)
- break;
- /* Fall through */
- default:
- ret = arizona_wait_for_boot(arizona);
- if (ret != 0) {
- dev_err(arizona->dev,
- "Device failed initial boot: %d\n", ret);
- goto err_reset;
+ } else if (val & 0x01) {
+ ret = wm5102_clear_write_sequencer(arizona);
+ if (ret)
+ return ret;
}
break;
+ default:
+ break;
+ }
+
+ ret = arizona_wait_for_boot(arizona);
+ if (ret) {
+ dev_err(arizona->dev, "Device failed initial boot: %d\n", ret);
+ goto err_reset;
}
/* Read the device ID information & do device specific stuff */
@@ -891,14 +1110,24 @@ int arizona_dev_init(struct arizona *arizona)
switch (arizona->type) {
case WM5102:
- ret = arizona_apply_hardware_patch(arizona);
- if (ret != 0) {
+ ret = wm5102_apply_hardware_patch(arizona);
+ if (ret) {
dev_err(arizona->dev,
"Failed to apply hardware patch: %d\n",
ret);
goto err_reset;
}
break;
+ case WM5110:
+ case WM8280:
+ ret = wm5110_apply_sleep_patch(arizona);
+ if (ret) {
+ dev_err(arizona->dev,
+ "Failed to apply sleep patch: %d\n",
+ ret);
+ goto err_reset;
+ }
+ break;
default:
break;
}
@@ -977,12 +1206,16 @@ int arizona_dev_init(struct arizona *arizona)
/* Default for both is 0 so noop with defaults */
val = arizona->pdata.dmic_ref[i]
<< ARIZONA_IN1_DMIC_SUP_SHIFT;
- val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
+ if (arizona->pdata.inmode[i] & ARIZONA_INMODE_DMIC)
+ val |= 1 << ARIZONA_IN1_MODE_SHIFT;
+ if (arizona->pdata.inmode[i] & ARIZONA_INMODE_SE)
+ val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT;
regmap_update_bits(arizona->regmap,
ARIZONA_IN1L_CONTROL + (i * 8),
ARIZONA_IN1_DMIC_SUP_MASK |
- ARIZONA_IN1_MODE_MASK, val);
+ ARIZONA_IN1_MODE_MASK |
+ ARIZONA_IN1_SINGLE_ENDED_MASK, val);
}
for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
@@ -1054,10 +1287,7 @@ int arizona_dev_init(struct arizona *arizona)
err_irq:
arizona_irq_exit(arizona);
err_reset:
- if (arizona->pdata.reset) {
- gpio_set_value_cansleep(arizona->pdata.reset, 0);
- gpio_free(arizona->pdata.reset);
- }
+ arizona_enable_reset(arizona);
regulator_disable(arizona->dcvdd);
err_enable:
regulator_bulk_disable(arizona->num_core_supplies,
@@ -1082,8 +1312,7 @@ int arizona_dev_exit(struct arizona *arizona)
arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
arizona_irq_exit(arizona);
- if (arizona->pdata.reset)
- gpio_set_value_cansleep(arizona->pdata.reset, 0);
+ arizona_enable_reset(arizona);
regulator_bulk_disable(arizona->num_core_supplies,
arizona->core_supplies);
diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c
index d063b94b94b5..2b9965d53e4e 100644
--- a/drivers/mfd/arizona-irq.c
+++ b/drivers/mfd/arizona-irq.c
@@ -186,7 +186,7 @@ static int arizona_irq_map(struct irq_domain *h, unsigned int virq,
return 0;
}
-static struct irq_domain_ops arizona_domain_ops = {
+static const struct irq_domain_ops arizona_domain_ops = {
.map = arizona_irq_map,
.xlate = irq_domain_xlate_twocell,
};
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index d18029be6a78..6df91556faf3 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -32,6 +32,7 @@
static const char * const axp20x_model_names[] = {
"AXP202",
"AXP209",
+ "AXP221",
"AXP288",
};
@@ -54,6 +55,25 @@ static const struct regmap_access_table axp20x_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges),
};
+static const struct regmap_range axp22x_writeable_ranges[] = {
+ regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
+ regmap_reg_range(AXP20X_DCDC_MODE, AXP22X_BATLOW_THRES1),
+};
+
+static const struct regmap_range axp22x_volatile_ranges[] = {
+ regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
+};
+
+static const struct regmap_access_table axp22x_writeable_table = {
+ .yes_ranges = axp22x_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp22x_writeable_ranges),
+};
+
+static const struct regmap_access_table axp22x_volatile_table = {
+ .yes_ranges = axp22x_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp22x_volatile_ranges),
+};
+
static const struct regmap_range axp288_writeable_ranges[] = {
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE),
regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5),
@@ -87,6 +107,20 @@ static struct resource axp20x_pek_resources[] = {
},
};
+static struct resource axp22x_pek_resources[] = {
+ {
+ .name = "PEK_DBR",
+ .start = AXP22X_IRQ_PEK_RIS_EDGE,
+ .end = AXP22X_IRQ_PEK_RIS_EDGE,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .name = "PEK_DBF",
+ .start = AXP22X_IRQ_PEK_FAL_EDGE,
+ .end = AXP22X_IRQ_PEK_FAL_EDGE,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static struct resource axp288_fuel_gauge_resources[] = {
{
.start = AXP288_IRQ_QWBTU,
@@ -129,6 +163,15 @@ static const struct regmap_config axp20x_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+static const struct regmap_config axp22x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp22x_writeable_table,
+ .volatile_table = &axp22x_volatile_table,
+ .max_register = AXP22X_BATLOW_THRES1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
static const struct regmap_config axp288_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -181,6 +224,34 @@ static const struct regmap_irq axp20x_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT, 4, 0),
};
+static const struct regmap_irq axp22x_regmap_irqs[] = {
+ INIT_REGMAP_IRQ(AXP22X, ACIN_OVER_V, 0, 7),
+ INIT_REGMAP_IRQ(AXP22X, ACIN_PLUGIN, 0, 6),
+ INIT_REGMAP_IRQ(AXP22X, ACIN_REMOVAL, 0, 5),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_OVER_V, 0, 4),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_PLUGIN, 0, 3),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_REMOVAL, 0, 2),
+ INIT_REGMAP_IRQ(AXP22X, VBUS_V_LOW, 0, 1),
+ INIT_REGMAP_IRQ(AXP22X, BATT_PLUGIN, 1, 7),
+ INIT_REGMAP_IRQ(AXP22X, BATT_REMOVAL, 1, 6),
+ INIT_REGMAP_IRQ(AXP22X, BATT_ENT_ACT_MODE, 1, 5),
+ INIT_REGMAP_IRQ(AXP22X, BATT_EXIT_ACT_MODE, 1, 4),
+ INIT_REGMAP_IRQ(AXP22X, CHARG, 1, 3),
+ INIT_REGMAP_IRQ(AXP22X, CHARG_DONE, 1, 2),
+ INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_HIGH, 1, 1),
+ INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_LOW, 1, 0),
+ INIT_REGMAP_IRQ(AXP22X, DIE_TEMP_HIGH, 2, 7),
+ INIT_REGMAP_IRQ(AXP22X, PEK_SHORT, 2, 1),
+ INIT_REGMAP_IRQ(AXP22X, PEK_LONG, 2, 0),
+ INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL1, 3, 1),
+ INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL2, 3, 0),
+ INIT_REGMAP_IRQ(AXP22X, TIMER, 4, 7),
+ INIT_REGMAP_IRQ(AXP22X, PEK_RIS_EDGE, 4, 6),
+ INIT_REGMAP_IRQ(AXP22X, PEK_FAL_EDGE, 4, 5),
+ INIT_REGMAP_IRQ(AXP22X, GPIO1_INPUT, 4, 1),
+ INIT_REGMAP_IRQ(AXP22X, GPIO0_INPUT, 4, 0),
+};
+
/* some IRQs are compatible with axp20x models */
static const struct regmap_irq axp288_regmap_irqs[] = {
INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2),
@@ -224,6 +295,7 @@ static const struct regmap_irq axp288_regmap_irqs[] = {
static const struct of_device_id axp20x_of_match[] = {
{ .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
{ .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
+ { .compatible = "x-powers,axp221", .data = (void *) AXP221_ID },
{ },
};
MODULE_DEVICE_TABLE(of, axp20x_of_match);
@@ -258,6 +330,18 @@ static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
};
+static const struct regmap_irq_chip axp22x_regmap_irq_chip = {
+ .name = "axp22x_irq_chip",
+ .status_base = AXP20X_IRQ1_STATE,
+ .ack_base = AXP20X_IRQ1_STATE,
+ .mask_base = AXP20X_IRQ1_EN,
+ .mask_invert = true,
+ .init_ack_masked = true,
+ .irqs = axp22x_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp22x_regmap_irqs),
+ .num_regs = 5,
+};
+
static const struct regmap_irq_chip axp288_regmap_irq_chip = {
.name = "axp288_irq_chip",
.status_base = AXP20X_IRQ1_STATE,
@@ -281,6 +365,16 @@ static struct mfd_cell axp20x_cells[] = {
},
};
+static struct mfd_cell axp22x_cells[] = {
+ {
+ .name = "axp20x-pek",
+ .num_resources = ARRAY_SIZE(axp22x_pek_resources),
+ .resources = axp22x_pek_resources,
+ }, {
+ .name = "axp20x-regulator",
+ },
+};
+
static struct resource axp288_adc_resources[] = {
{
.name = "GPADC",
@@ -426,6 +520,12 @@ static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev)
axp20x->regmap_cfg = &axp20x_regmap_config;
axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip;
break;
+ case AXP221_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp22x_cells);
+ axp20x->cells = axp22x_cells;
+ axp20x->regmap_cfg = &axp22x_regmap_config;
+ axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip;
+ break;
case AXP288_ID:
axp20x->cells = axp288_cells;
axp20x->nr_cells = ARRAY_SIZE(axp288_cells);
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
index c4aecc6f8373..0eee63542038 100644
--- a/drivers/mfd/cros_ec.c
+++ b/drivers/mfd/cros_ec.c
@@ -17,111 +17,36 @@
* battery charging and regulator control, firmware update.
*/
+#include <linux/of_platform.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mfd/core.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
-#include <linux/delay.h>
-#define EC_COMMAND_RETRIES 50
+#define CROS_EC_DEV_EC_INDEX 0
+#define CROS_EC_DEV_PD_INDEX 1
-int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
- struct cros_ec_command *msg)
-{
- uint8_t *out;
- int csum, i;
-
- BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
- out = ec_dev->dout;
- out[0] = EC_CMD_VERSION0 + msg->version;
- out[1] = msg->command;
- out[2] = msg->outsize;
- csum = out[0] + out[1] + out[2];
- for (i = 0; i < msg->outsize; i++)
- csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i];
- out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff);
-
- return EC_MSG_TX_PROTO_BYTES + msg->outsize;
-}
-EXPORT_SYMBOL(cros_ec_prepare_tx);
-
-int cros_ec_check_result(struct cros_ec_device *ec_dev,
- struct cros_ec_command *msg)
-{
- switch (msg->result) {
- case EC_RES_SUCCESS:
- return 0;
- case EC_RES_IN_PROGRESS:
- dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
- msg->command);
- return -EAGAIN;
- default:
- dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
- msg->command, msg->result);
- return 0;
- }
-}
-EXPORT_SYMBOL(cros_ec_check_result);
-
-int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
- struct cros_ec_command *msg)
-{
- int ret;
-
- mutex_lock(&ec_dev->lock);
- ret = ec_dev->cmd_xfer(ec_dev, msg);
- if (msg->result == EC_RES_IN_PROGRESS) {
- int i;
- struct cros_ec_command status_msg = { };
- struct ec_response_get_comms_status *status;
-
- status_msg.command = EC_CMD_GET_COMMS_STATUS;
- status_msg.insize = sizeof(*status);
-
- /*
- * Query the EC's status until it's no longer busy or
- * we encounter an error.
- */
- for (i = 0; i < EC_COMMAND_RETRIES; i++) {
- usleep_range(10000, 11000);
-
- ret = ec_dev->cmd_xfer(ec_dev, &status_msg);
- if (ret < 0)
- break;
+static struct cros_ec_platform ec_p = {
+ .ec_name = CROS_EC_DEV_NAME,
+ .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX),
+};
- msg->result = status_msg.result;
- if (status_msg.result != EC_RES_SUCCESS)
- break;
+static struct cros_ec_platform pd_p = {
+ .ec_name = CROS_EC_DEV_PD_NAME,
+ .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
+};
- status = (struct ec_response_get_comms_status *)
- status_msg.indata;
- if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
- break;
- }
- }
- mutex_unlock(&ec_dev->lock);
+static const struct mfd_cell ec_cell = {
+ .name = "cros-ec-ctl",
+ .platform_data = &ec_p,
+ .pdata_size = sizeof(ec_p),
+};
- return ret;
-}
-EXPORT_SYMBOL(cros_ec_cmd_xfer);
-
-static const struct mfd_cell cros_devs[] = {
- {
- .name = "cros-ec-keyb",
- .id = 1,
- .of_compatible = "google,cros-ec-keyb",
- },
- {
- .name = "cros-ec-i2c-tunnel",
- .id = 2,
- .of_compatible = "google,cros-ec-i2c-tunnel",
- },
- {
- .name = "cros-ec-ctl",
- .id = 3,
- },
+static const struct mfd_cell ec_pd_cell = {
+ .name = "cros-ec-ctl",
+ .platform_data = &pd_p,
+ .pdata_size = sizeof(pd_p),
};
int cros_ec_register(struct cros_ec_device *ec_dev)
@@ -129,27 +54,59 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
struct device *dev = ec_dev->dev;
int err = 0;
- if (ec_dev->din_size) {
- ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
- if (!ec_dev->din)
- return -ENOMEM;
- }
- if (ec_dev->dout_size) {
- ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
- if (!ec_dev->dout)
- return -ENOMEM;
- }
+ ec_dev->max_request = sizeof(struct ec_params_hello);
+ ec_dev->max_response = sizeof(struct ec_response_get_protocol_info);
+ ec_dev->max_passthru = 0;
+
+ ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
+ if (!ec_dev->din)
+ return -ENOMEM;
+
+ ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
+ if (!ec_dev->dout)
+ return -ENOMEM;
mutex_init(&ec_dev->lock);
- err = mfd_add_devices(dev, 0, cros_devs,
- ARRAY_SIZE(cros_devs),
+ cros_ec_query_all(ec_dev);
+
+ err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1,
NULL, ec_dev->irq, NULL);
if (err) {
- dev_err(dev, "failed to add mfd devices\n");
+ dev_err(dev,
+ "Failed to register Embedded Controller subdevice %d\n",
+ err);
return err;
}
+ if (ec_dev->max_passthru) {
+ /*
+ * Register a PD device as well on top of this device.
+ * We make the following assumptions:
+ * - behind an EC, we have a pd
+ * - only one device added.
+ * - the EC is responsive at init time (it is not true for a
+ * sensor hub.
+ */
+ err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO,
+ &ec_pd_cell, 1, NULL, ec_dev->irq, NULL);
+ if (err) {
+ dev_err(dev,
+ "Failed to register Power Delivery subdevice %d\n",
+ err);
+ return err;
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
+ err = of_platform_populate(dev->of_node, NULL, NULL, dev);
+ if (err) {
+ mfd_remove_devices(dev);
+ dev_err(dev, "Failed to register sub-devices\n");
+ return err;
+ }
+ }
+
dev_info(dev, "Chrome EC device registered\n");
return 0;
diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c
index c0c30f4f946f..b9a0963ca5c3 100644
--- a/drivers/mfd/cros_ec_i2c.c
+++ b/drivers/mfd/cros_ec_i2c.c
@@ -13,6 +13,7 @@
* GNU General Public License for more details.
*/
+#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
@@ -22,6 +23,32 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
+/**
+ * Request format for protocol v3
+ * byte 0 0xda (EC_COMMAND_PROTOCOL_3)
+ * byte 1-8 struct ec_host_request
+ * byte 10- response data
+ */
+struct ec_host_request_i2c {
+ /* Always 0xda to backward compatible with v2 struct */
+ uint8_t command_protocol;
+ struct ec_host_request ec_request;
+} __packed;
+
+
+/*
+ * Response format for protocol v3
+ * byte 0 result code
+ * byte 1 packet_length
+ * byte 2-9 struct ec_host_response
+ * byte 10- response data
+ */
+struct ec_host_response_i2c {
+ uint8_t result;
+ uint8_t packet_length;
+ struct ec_host_response ec_response;
+} __packed;
+
static inline struct cros_ec_device *to_ec_dev(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -29,6 +56,134 @@ static inline struct cros_ec_device *to_ec_dev(struct device *dev)
return i2c_get_clientdata(client);
}
+static int cros_ec_pkt_xfer_i2c(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ struct i2c_client *client = ec_dev->priv;
+ int ret = -ENOMEM;
+ int i;
+ int packet_len;
+ u8 *out_buf = NULL;
+ u8 *in_buf = NULL;
+ u8 sum;
+ struct i2c_msg i2c_msg[2];
+ struct ec_host_response *ec_response;
+ struct ec_host_request_i2c *ec_request_i2c;
+ struct ec_host_response_i2c *ec_response_i2c;
+ int request_header_size = sizeof(struct ec_host_request_i2c);
+ int response_header_size = sizeof(struct ec_host_response_i2c);
+
+ i2c_msg[0].addr = client->addr;
+ i2c_msg[0].flags = 0;
+ i2c_msg[1].addr = client->addr;
+ i2c_msg[1].flags = I2C_M_RD;
+
+ packet_len = msg->insize + response_header_size;
+ BUG_ON(packet_len > ec_dev->din_size);
+ in_buf = ec_dev->din;
+ i2c_msg[1].len = packet_len;
+ i2c_msg[1].buf = (char *) in_buf;
+
+ packet_len = msg->outsize + request_header_size;
+ BUG_ON(packet_len > ec_dev->dout_size);
+ out_buf = ec_dev->dout;
+ i2c_msg[0].len = packet_len;
+ i2c_msg[0].buf = (char *) out_buf;
+
+ /* create request data */
+ ec_request_i2c = (struct ec_host_request_i2c *) out_buf;
+ ec_request_i2c->command_protocol = EC_COMMAND_PROTOCOL_3;
+
+ ec_dev->dout++;
+ ret = cros_ec_prepare_tx(ec_dev, msg);
+ ec_dev->dout--;
+
+ /* send command to EC and read answer */
+ ret = i2c_transfer(client->adapter, i2c_msg, 2);
+ if (ret < 0) {
+ dev_dbg(ec_dev->dev, "i2c transfer failed: %d\n", ret);
+ goto done;
+ } else if (ret != 2) {
+ dev_err(ec_dev->dev, "failed to get response: %d\n", ret);
+ ret = -EIO;
+ goto done;
+ }
+
+ ec_response_i2c = (struct ec_host_response_i2c *) in_buf;
+ msg->result = ec_response_i2c->result;
+ ec_response = &ec_response_i2c->ec_response;
+
+ switch (msg->result) {
+ case EC_RES_SUCCESS:
+ break;
+ case EC_RES_IN_PROGRESS:
+ ret = -EAGAIN;
+ dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
+ msg->command);
+ goto done;
+
+ default:
+ dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
+ msg->command, msg->result);
+ /*
+ * When we send v3 request to v2 ec, ec won't recognize the
+ * 0xda (EC_COMMAND_PROTOCOL_3) and will return with status
+ * EC_RES_INVALID_COMMAND with zero data length.
+ *
+ * In case of invalid command for v3 protocol the data length
+ * will be at least sizeof(struct ec_host_response)
+ */
+ if (ec_response_i2c->result == EC_RES_INVALID_COMMAND &&
+ ec_response_i2c->packet_length == 0) {
+ ret = -EPROTONOSUPPORT;
+ goto done;
+ }
+ }
+
+ if (ec_response_i2c->packet_length < sizeof(struct ec_host_response)) {
+ dev_err(ec_dev->dev,
+ "response of %u bytes too short; not a full header\n",
+ ec_response_i2c->packet_length);
+ ret = -EBADMSG;
+ goto done;
+ }
+
+ if (msg->insize < ec_response->data_len) {
+ dev_err(ec_dev->dev,
+ "response data size is too large: expected %u, got %u\n",
+ msg->insize,
+ ec_response->data_len);
+ ret = -EMSGSIZE;
+ goto done;
+ }
+
+ /* copy response packet payload and compute checksum */
+ sum = 0;
+ for (i = 0; i < sizeof(struct ec_host_response); i++)
+ sum += ((u8 *)ec_response)[i];
+
+ memcpy(msg->data,
+ in_buf + response_header_size,
+ ec_response->data_len);
+ for (i = 0; i < ec_response->data_len; i++)
+ sum += msg->data[i];
+
+ /* All bytes should sum to zero */
+ if (sum) {
+ dev_err(ec_dev->dev, "bad packet checksum\n");
+ ret = -EBADMSG;
+ goto done;
+ }
+
+ ret = ec_response->data_len;
+
+done:
+ if (msg->command == EC_CMD_REBOOT_EC)
+ msleep(EC_REBOOT_DELAY_MS);
+
+ return ret;
+}
+
static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
struct cros_ec_command *msg)
{
@@ -76,7 +231,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
/* copy message payload and compute checksum */
sum = out_buf[0] + out_buf[1] + out_buf[2];
for (i = 0; i < msg->outsize; i++) {
- out_buf[3 + i] = msg->outdata[i];
+ out_buf[3 + i] = msg->data[i];
sum += out_buf[3 + i];
}
out_buf[3 + msg->outsize] = sum;
@@ -109,7 +264,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
/* copy response packet payload and compute checksum */
sum = in_buf[0] + in_buf[1];
for (i = 0; i < len; i++) {
- msg->indata[i] = in_buf[2 + i];
+ msg->data[i] = in_buf[2 + i];
sum += in_buf[2 + i];
}
dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n",
@@ -121,9 +276,12 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
}
ret = len;
- done:
+done:
kfree(in_buf);
kfree(out_buf);
+ if (msg->command == EC_CMD_REBOOT_EC)
+ msleep(EC_REBOOT_DELAY_MS);
+
return ret;
}
@@ -143,9 +301,11 @@ static int cros_ec_i2c_probe(struct i2c_client *client,
ec_dev->priv = client;
ec_dev->irq = client->irq;
ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c;
- ec_dev->ec_name = client->name;
+ ec_dev->pkt_xfer = cros_ec_pkt_xfer_i2c;
ec_dev->phys_name = client->adapter->name;
- ec_dev->parent = &client->dev;
+ ec_dev->din_size = sizeof(struct ec_host_response_i2c) +
+ sizeof(struct ec_response_get_protocol_info);
+ ec_dev->dout_size = sizeof(struct ec_host_request_i2c);
err = cros_ec_register(ec_dev);
if (err) {
diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c
index bf6e08e8013e..16f228dc243f 100644
--- a/drivers/mfd/cros_ec_spi.c
+++ b/drivers/mfd/cros_ec_spi.c
@@ -65,29 +65,26 @@
*/
#define EC_SPI_RECOVERY_TIME_NS (200 * 1000)
-/*
- * The EC is unresponsive for a time after a reboot command. Add a
- * simple delay to make sure that the bus stays locked.
- */
-#define EC_REBOOT_DELAY_MS 50
-
/**
* struct cros_ec_spi - information about a SPI-connected EC
*
* @spi: SPI device we are connected to
* @last_transfer_ns: time that we last finished a transfer, or 0 if there
* if no record
+ * @start_of_msg_delay: used to set the delay_usecs on the spi_transfer that
+ * is sent when we want to turn on CS at the start of a transaction.
* @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that
* is sent when we want to turn off CS at the end of a transaction.
*/
struct cros_ec_spi {
struct spi_device *spi;
s64 last_transfer_ns;
+ unsigned int start_of_msg_delay;
unsigned int end_of_msg_delay;
};
static void debug_packet(struct device *dev, const char *name, u8 *ptr,
- int len)
+ int len)
{
#ifdef DEBUG
int i;
@@ -100,6 +97,172 @@ static void debug_packet(struct device *dev, const char *name, u8 *ptr,
#endif
}
+static int terminate_request(struct cros_ec_device *ec_dev)
+{
+ struct cros_ec_spi *ec_spi = ec_dev->priv;
+ struct spi_message msg;
+ struct spi_transfer trans;
+ int ret;
+
+ /*
+ * Turn off CS, possibly adding a delay to ensure the rising edge
+ * doesn't come too soon after the end of the data.
+ */
+ spi_message_init(&msg);
+ memset(&trans, 0, sizeof(trans));
+ trans.delay_usecs = ec_spi->end_of_msg_delay;
+ spi_message_add_tail(&trans, &msg);
+
+ ret = spi_sync(ec_spi->spi, &msg);
+
+ /* Reset end-of-response timer */
+ ec_spi->last_transfer_ns = ktime_get_ns();
+ if (ret < 0) {
+ dev_err(ec_dev->dev,
+ "cs-deassert spi transfer failed: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
+/**
+ * receive_n_bytes - receive n bytes from the EC.
+ *
+ * Assumes buf is a pointer into the ec_dev->din buffer
+ */
+static int receive_n_bytes(struct cros_ec_device *ec_dev, u8 *buf, int n)
+{
+ struct cros_ec_spi *ec_spi = ec_dev->priv;
+ struct spi_transfer trans;
+ struct spi_message msg;
+ int ret;
+
+ BUG_ON(buf - ec_dev->din + n > ec_dev->din_size);
+
+ memset(&trans, 0, sizeof(trans));
+ trans.cs_change = 1;
+ trans.rx_buf = buf;
+ trans.len = n;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&trans, &msg);
+ ret = spi_sync(ec_spi->spi, &msg);
+ if (ret < 0)
+ dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * cros_ec_spi_receive_packet - Receive a packet from the EC.
+ *
+ * This function has two phases: reading the preamble bytes (since if we read
+ * data from the EC before it is ready to send, we just get preamble) and
+ * reading the actual message.
+ *
+ * The received data is placed into ec_dev->din.
+ *
+ * @ec_dev: ChromeOS EC device
+ * @need_len: Number of message bytes we need to read
+ */
+static int cros_ec_spi_receive_packet(struct cros_ec_device *ec_dev,
+ int need_len)
+{
+ struct ec_host_response *response;
+ u8 *ptr, *end;
+ int ret;
+ unsigned long deadline;
+ int todo;
+
+ BUG_ON(EC_MSG_PREAMBLE_COUNT > ec_dev->din_size);
+
+ /* Receive data until we see the header byte */
+ deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
+ while (true) {
+ unsigned long start_jiffies = jiffies;
+
+ ret = receive_n_bytes(ec_dev,
+ ec_dev->din,
+ EC_MSG_PREAMBLE_COUNT);
+ if (ret < 0)
+ return ret;
+
+ ptr = ec_dev->din;
+ for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) {
+ if (*ptr == EC_SPI_FRAME_START) {
+ dev_dbg(ec_dev->dev, "msg found at %zd\n",
+ ptr - ec_dev->din);
+ break;
+ }
+ }
+ if (ptr != end)
+ break;
+
+ /*
+ * Use the time at the start of the loop as a timeout. This
+ * gives us one last shot at getting the transfer and is useful
+ * in case we got context switched out for a while.
+ */
+ if (time_after(start_jiffies, deadline)) {
+ dev_warn(ec_dev->dev, "EC failed to respond in time\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ /*
+ * ptr now points to the header byte. Copy any valid data to the
+ * start of our buffer
+ */
+ todo = end - ++ptr;
+ BUG_ON(todo < 0 || todo > ec_dev->din_size);
+ todo = min(todo, need_len);
+ memmove(ec_dev->din, ptr, todo);
+ ptr = ec_dev->din + todo;
+ dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n",
+ need_len, todo);
+ need_len -= todo;
+
+ /* If the entire response struct wasn't read, get the rest of it. */
+ if (todo < sizeof(*response)) {
+ ret = receive_n_bytes(ec_dev, ptr, sizeof(*response) - todo);
+ if (ret < 0)
+ return -EBADMSG;
+ ptr += (sizeof(*response) - todo);
+ todo = sizeof(*response);
+ }
+
+ response = (struct ec_host_response *)ec_dev->din;
+
+ /* Abort if data_len is too large. */
+ if (response->data_len > ec_dev->din_size)
+ return -EMSGSIZE;
+
+ /* Receive data until we have it all */
+ while (need_len > 0) {
+ /*
+ * We can't support transfers larger than the SPI FIFO size
+ * unless we have DMA. We don't have DMA on the ISP SPI ports
+ * for Exynos. We need a way of asking SPI driver for
+ * maximum-supported transfer size.
+ */
+ todo = min(need_len, 256);
+ dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
+ todo, need_len, ptr - ec_dev->din);
+
+ ret = receive_n_bytes(ec_dev, ptr, todo);
+ if (ret < 0)
+ return ret;
+
+ ptr += todo;
+ need_len -= todo;
+ }
+
+ dev_dbg(ec_dev->dev, "loop done, ptr=%zd\n", ptr - ec_dev->din);
+
+ return 0;
+}
+
/**
* cros_ec_spi_receive_response - Receive a response from the EC.
*
@@ -115,34 +278,27 @@ static void debug_packet(struct device *dev, const char *name, u8 *ptr,
static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
int need_len)
{
- struct cros_ec_spi *ec_spi = ec_dev->priv;
- struct spi_transfer trans;
- struct spi_message msg;
u8 *ptr, *end;
int ret;
unsigned long deadline;
int todo;
+ BUG_ON(EC_MSG_PREAMBLE_COUNT > ec_dev->din_size);
+
/* Receive data until we see the header byte */
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
while (true) {
unsigned long start_jiffies = jiffies;
- memset(&trans, 0, sizeof(trans));
- trans.cs_change = 1;
- trans.rx_buf = ptr = ec_dev->din;
- trans.len = EC_MSG_PREAMBLE_COUNT;
-
- spi_message_init(&msg);
- spi_message_add_tail(&trans, &msg);
- ret = spi_sync(ec_spi->spi, &msg);
- if (ret < 0) {
- dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ ret = receive_n_bytes(ec_dev,
+ ec_dev->din,
+ EC_MSG_PREAMBLE_COUNT);
+ if (ret < 0)
return ret;
- }
+ ptr = ec_dev->din;
for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) {
- if (*ptr == EC_MSG_HEADER) {
+ if (*ptr == EC_SPI_FRAME_START) {
dev_dbg(ec_dev->dev, "msg found at %zd\n",
ptr - ec_dev->din);
break;
@@ -187,21 +343,9 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
todo, need_len, ptr - ec_dev->din);
- memset(&trans, 0, sizeof(trans));
- trans.cs_change = 1;
- trans.rx_buf = ptr;
- trans.len = todo;
- spi_message_init(&msg);
- spi_message_add_tail(&trans, &msg);
-
- /* send command to EC and read answer */
- BUG_ON((u8 *)trans.rx_buf - ec_dev->din + todo >
- ec_dev->din_size);
- ret = spi_sync(ec_spi->spi, &msg);
- if (ret < 0) {
- dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ ret = receive_n_bytes(ec_dev, ptr, todo);
+ if (ret < 0)
return ret;
- }
debug_packet(ec_dev->dev, "interim", ptr, todo);
ptr += todo;
@@ -214,6 +358,138 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
}
/**
+ * cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply
+ *
+ * @ec_dev: ChromeOS EC device
+ * @ec_msg: Message to transfer
+ */
+static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *ec_msg)
+{
+ struct ec_host_request *request;
+ struct ec_host_response *response;
+ struct cros_ec_spi *ec_spi = ec_dev->priv;
+ struct spi_transfer trans, trans_delay;
+ struct spi_message msg;
+ int i, len;
+ u8 *ptr;
+ u8 *rx_buf;
+ u8 sum;
+ int ret = 0, final_ret;
+
+ len = cros_ec_prepare_tx(ec_dev, ec_msg);
+ request = (struct ec_host_request *)ec_dev->dout;
+ dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
+
+ /* If it's too soon to do another transaction, wait */
+ if (ec_spi->last_transfer_ns) {
+ unsigned long delay; /* The delay completed so far */
+
+ delay = ktime_get_ns() - ec_spi->last_transfer_ns;
+ if (delay < EC_SPI_RECOVERY_TIME_NS)
+ ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
+ }
+
+ rx_buf = kzalloc(len, GFP_KERNEL);
+ if (!rx_buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ /*
+ * Leave a gap between CS assertion and clocking of data to allow the
+ * EC time to wakeup.
+ */
+ spi_message_init(&msg);
+ if (ec_spi->start_of_msg_delay) {
+ memset(&trans_delay, 0, sizeof(trans_delay));
+ trans_delay.delay_usecs = ec_spi->start_of_msg_delay;
+ spi_message_add_tail(&trans_delay, &msg);
+ }
+
+ /* Transmit phase - send our message */
+ memset(&trans, 0, sizeof(trans));
+ trans.tx_buf = ec_dev->dout;
+ trans.rx_buf = rx_buf;
+ trans.len = len;
+ trans.cs_change = 1;
+ spi_message_add_tail(&trans, &msg);
+ ret = spi_sync(ec_spi->spi, &msg);
+
+ /* Get the response */
+ if (!ret) {
+ /* Verify that EC can process command */
+ for (i = 0; i < len; i++) {
+ switch (rx_buf[i]) {
+ case EC_SPI_PAST_END:
+ case EC_SPI_RX_BAD_DATA:
+ case EC_SPI_NOT_READY:
+ ret = -EAGAIN;
+ ec_msg->result = EC_RES_IN_PROGRESS;
+ default:
+ break;
+ }
+ if (ret)
+ break;
+ }
+ if (!ret)
+ ret = cros_ec_spi_receive_packet(ec_dev,
+ ec_msg->insize + sizeof(*response));
+ } else {
+ dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ }
+
+ final_ret = terminate_request(ec_dev);
+ if (!ret)
+ ret = final_ret;
+ if (ret < 0)
+ goto exit;
+
+ ptr = ec_dev->din;
+
+ /* check response error code */
+ response = (struct ec_host_response *)ptr;
+ ec_msg->result = response->result;
+
+ ret = cros_ec_check_result(ec_dev, ec_msg);
+ if (ret)
+ goto exit;
+
+ len = response->data_len;
+ sum = 0;
+ if (len > ec_msg->insize) {
+ dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
+ len, ec_msg->insize);
+ ret = -EMSGSIZE;
+ goto exit;
+ }
+
+ for (i = 0; i < sizeof(*response); i++)
+ sum += ptr[i];
+
+ /* copy response packet payload and compute checksum */
+ memcpy(ec_msg->data, ptr + sizeof(*response), len);
+ for (i = 0; i < len; i++)
+ sum += ec_msg->data[i];
+
+ if (sum) {
+ dev_err(ec_dev->dev,
+ "bad packet checksum, calculated %x\n",
+ sum);
+ ret = -EBADMSG;
+ goto exit;
+ }
+
+ ret = len;
+exit:
+ kfree(rx_buf);
+ if (ec_msg->command == EC_CMD_REBOOT_EC)
+ msleep(EC_REBOOT_DELAY_MS);
+
+ return ret;
+}
+
+/**
* cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply
*
* @ec_dev: ChromeOS EC device
@@ -227,6 +503,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
struct spi_message msg;
int i, len;
u8 *ptr;
+ u8 *rx_buf;
int sum;
int ret = 0, final_ret;
@@ -242,10 +519,17 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
}
+ rx_buf = kzalloc(len, GFP_KERNEL);
+ if (!rx_buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
/* Transmit phase - send our message */
debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
memset(&trans, 0, sizeof(trans));
trans.tx_buf = ec_dev->dout;
+ trans.rx_buf = rx_buf;
trans.len = len;
trans.cs_change = 1;
spi_message_init(&msg);
@@ -254,29 +538,32 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
/* Get the response */
if (!ret) {
- ret = cros_ec_spi_receive_response(ec_dev,
- ec_msg->insize + EC_MSG_TX_PROTO_BYTES);
+ /* Verify that EC can process command */
+ for (i = 0; i < len; i++) {
+ switch (rx_buf[i]) {
+ case EC_SPI_PAST_END:
+ case EC_SPI_RX_BAD_DATA:
+ case EC_SPI_NOT_READY:
+ ret = -EAGAIN;
+ ec_msg->result = EC_RES_IN_PROGRESS;
+ default:
+ break;
+ }
+ if (ret)
+ break;
+ }
+ if (!ret)
+ ret = cros_ec_spi_receive_response(ec_dev,
+ ec_msg->insize + EC_MSG_TX_PROTO_BYTES);
} else {
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
}
- /*
- * Turn off CS, possibly adding a delay to ensure the rising edge
- * doesn't come too soon after the end of the data.
- */
- spi_message_init(&msg);
- memset(&trans, 0, sizeof(trans));
- trans.delay_usecs = ec_spi->end_of_msg_delay;
- spi_message_add_tail(&trans, &msg);
-
- final_ret = spi_sync(ec_spi->spi, &msg);
- ec_spi->last_transfer_ns = ktime_get_ns();
+ final_ret = terminate_request(ec_dev);
if (!ret)
ret = final_ret;
- if (ret < 0) {
- dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+ if (ret < 0)
goto exit;
- }
ptr = ec_dev->din;
@@ -299,7 +586,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
for (i = 0; i < len; i++) {
sum += ptr[i + 2];
if (ec_msg->insize)
- ec_msg->indata[i] = ptr[i + 2];
+ ec_msg->data[i] = ptr[i + 2];
}
sum &= 0xff;
@@ -315,6 +602,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
ret = len;
exit:
+ kfree(rx_buf);
if (ec_msg->command == EC_CMD_REBOOT_EC)
msleep(EC_REBOOT_DELAY_MS);
@@ -327,6 +615,10 @@ static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev)
u32 val;
int ret;
+ ret = of_property_read_u32(np, "google,cros-ec-spi-pre-delay", &val);
+ if (!ret)
+ ec_spi->start_of_msg_delay = val;
+
ret = of_property_read_u32(np, "google,cros-ec-spi-msg-delay", &val);
if (!ret)
ec_spi->end_of_msg_delay = val;
@@ -361,11 +653,13 @@ static int cros_ec_spi_probe(struct spi_device *spi)
ec_dev->priv = ec_spi;
ec_dev->irq = spi->irq;
ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi;
- ec_dev->ec_name = ec_spi->spi->modalias;
+ ec_dev->pkt_xfer = cros_ec_pkt_xfer_spi;
ec_dev->phys_name = dev_name(&ec_spi->spi->dev);
- ec_dev->parent = &ec_spi->spi->dev;
- ec_dev->din_size = EC_MSG_BYTES + EC_MSG_PREAMBLE_COUNT;
- ec_dev->dout_size = EC_MSG_BYTES;
+ ec_dev->din_size = EC_MSG_PREAMBLE_COUNT +
+ sizeof(struct ec_host_response) +
+ sizeof(struct ec_response_get_protocol_info);
+ ec_dev->dout_size = sizeof(struct ec_host_request);
+
err = cros_ec_register(ec_dev);
if (err) {
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
index ae498b53ee40..46e3840c7a37 100644
--- a/drivers/mfd/da9052-core.c
+++ b/drivers/mfd/da9052-core.c
@@ -433,6 +433,10 @@ EXPORT_SYMBOL_GPL(da9052_adc_read_temp);
static const struct mfd_cell da9052_subdev_info[] = {
{
.name = "da9052-regulator",
+ .id = 0,
+ },
+ {
+ .name = "da9052-regulator",
.id = 1,
},
{
@@ -484,10 +488,6 @@ static const struct mfd_cell da9052_subdev_info[] = {
.id = 13,
},
{
- .name = "da9052-regulator",
- .id = 14,
- },
- {
.name = "da9052-onkey",
},
{
diff --git a/drivers/mfd/da9052-irq.c b/drivers/mfd/da9052-irq.c
index e65ca194fa98..f4cb4613140b 100644
--- a/drivers/mfd/da9052-irq.c
+++ b/drivers/mfd/da9052-irq.c
@@ -35,7 +35,7 @@
#define DA9052_IRQ_MASK_POS_7 0x40
#define DA9052_IRQ_MASK_POS_8 0x80
-static struct regmap_irq da9052_irqs[] = {
+static const struct regmap_irq da9052_irqs[] = {
[DA9052_IRQ_DCIN] = {
.reg_offset = 0,
.mask = DA9052_IRQ_MASK_POS_1,
@@ -166,7 +166,7 @@ static struct regmap_irq da9052_irqs[] = {
},
};
-static struct regmap_irq_chip da9052_regmap_irq_chip = {
+static const struct regmap_irq_chip da9052_regmap_irq_chip = {
.name = "da9052_irq",
.status_base = DA9052_EVENT_A_REG,
.mask_base = DA9052_IRQ_MASK_A_REG,
diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c
index b4d920c1ead1..177e65a12c12 100644
--- a/drivers/mfd/da9055-core.c
+++ b/drivers/mfd/da9055-core.c
@@ -222,7 +222,7 @@ static bool da9055_register_volatile(struct device *dev, unsigned int reg)
}
}
-static struct regmap_irq da9055_irqs[] = {
+static const struct regmap_irq da9055_irqs[] = {
[DA9055_IRQ_NONKEY] = {
.reg_offset = 0,
.mask = DA9055_IRQ_NONKEY_MASK,
@@ -245,7 +245,7 @@ static struct regmap_irq da9055_irqs[] = {
},
};
-struct regmap_config da9055_regmap_config = {
+const struct regmap_config da9055_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -367,7 +367,7 @@ static const struct mfd_cell da9055_devs[] = {
},
};
-static struct regmap_irq_chip da9055_regmap_irq_chip = {
+static const struct regmap_irq_chip da9055_regmap_irq_chip = {
.name = "da9055_irq",
.status_base = DA9055_REG_EVENT_A,
.mask_base = DA9055_REG_IRQ_MASK_A,
diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c
index facd3610ac77..af841c165787 100644
--- a/drivers/mfd/da9063-core.c
+++ b/drivers/mfd/da9063-core.c
@@ -60,6 +60,7 @@ static struct resource da9063_rtc_resources[] = {
static struct resource da9063_onkey_resources[] = {
{
+ .name = "ONKEY",
.start = DA9063_IRQ_ONKEY,
.end = DA9063_IRQ_ONKEY,
.flags = IORESOURCE_IRQ,
@@ -97,6 +98,7 @@ static const struct mfd_cell da9063_devs[] = {
.name = DA9063_DRVNAME_ONKEY,
.num_resources = ARRAY_SIZE(da9063_onkey_resources),
.resources = da9063_onkey_resources,
+ .of_compatible = "dlg,da9063-onkey",
},
{
.name = DA9063_DRVNAME_RTC,
@@ -109,12 +111,64 @@ static const struct mfd_cell da9063_devs[] = {
},
};
+static int da9063_clear_fault_log(struct da9063 *da9063)
+{
+ int ret = 0;
+ int fault_log = 0;
+
+ ret = regmap_read(da9063->regmap, DA9063_REG_FAULT_LOG, &fault_log);
+ if (ret < 0) {
+ dev_err(da9063->dev, "Cannot read FAULT_LOG.\n");
+ return -EIO;
+ }
+
+ if (fault_log) {
+ if (fault_log & DA9063_TWD_ERROR)
+ dev_dbg(da9063->dev,
+ "Fault log entry detected: DA9063_TWD_ERROR\n");
+ if (fault_log & DA9063_POR)
+ dev_dbg(da9063->dev,
+ "Fault log entry detected: DA9063_POR\n");
+ if (fault_log & DA9063_VDD_FAULT)
+ dev_dbg(da9063->dev,
+ "Fault log entry detected: DA9063_VDD_FAULT\n");
+ if (fault_log & DA9063_VDD_START)
+ dev_dbg(da9063->dev,
+ "Fault log entry detected: DA9063_VDD_START\n");
+ if (fault_log & DA9063_TEMP_CRIT)
+ dev_dbg(da9063->dev,
+ "Fault log entry detected: DA9063_TEMP_CRIT\n");
+ if (fault_log & DA9063_KEY_RESET)
+ dev_dbg(da9063->dev,
+ "Fault log entry detected: DA9063_KEY_RESET\n");
+ if (fault_log & DA9063_NSHUTDOWN)
+ dev_dbg(da9063->dev,
+ "Fault log entry detected: DA9063_NSHUTDOWN\n");
+ if (fault_log & DA9063_WAIT_SHUT)
+ dev_dbg(da9063->dev,
+ "Fault log entry detected: DA9063_WAIT_SHUT\n");
+ }
+
+ ret = regmap_write(da9063->regmap,
+ DA9063_REG_FAULT_LOG,
+ fault_log);
+ if (ret < 0)
+ dev_err(da9063->dev,
+ "Cannot reset FAULT_LOG values %d\n", ret);
+
+ return ret;
+}
+
int da9063_device_init(struct da9063 *da9063, unsigned int irq)
{
struct da9063_pdata *pdata = da9063->dev->platform_data;
int model, variant_id, variant_code;
int ret;
+ ret = da9063_clear_fault_log(da9063);
+ if (ret < 0)
+ dev_err(da9063->dev, "Cannot clear fault log\n");
+
if (pdata) {
da9063->flags = pdata->flags;
da9063->irq_base = pdata->irq_base;
diff --git a/drivers/mfd/da9063-irq.c b/drivers/mfd/da9063-irq.c
index 822922602ce9..eaf1ec9208b2 100644
--- a/drivers/mfd/da9063-irq.c
+++ b/drivers/mfd/da9063-irq.c
@@ -34,7 +34,7 @@ struct da9063_irq_data {
u8 mask;
};
-static struct regmap_irq da9063_irqs[] = {
+static const struct regmap_irq da9063_irqs[] = {
/* DA9063 event A register */
[DA9063_IRQ_ONKEY] = {
.reg_offset = DA9063_REG_EVENT_A_OFFSET,
@@ -153,7 +153,7 @@ static struct regmap_irq da9063_irqs[] = {
},
};
-static struct regmap_irq_chip da9063_irq_chip = {
+static const struct regmap_irq_chip da9063_irq_chip = {
.name = "da9063-irq",
.irqs = da9063_irqs,
.num_irqs = DA9063_NUM_IRQ,
diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c
index 5549817df32e..94b9bbd1a69b 100644
--- a/drivers/mfd/da9150-core.c
+++ b/drivers/mfd/da9150-core.c
@@ -164,7 +164,7 @@ void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf)
}
EXPORT_SYMBOL_GPL(da9150_bulk_write);
-static struct regmap_irq da9150_irqs[] = {
+static const struct regmap_irq da9150_irqs[] = {
[DA9150_IRQ_VBUS] = {
.reg_offset = 0,
.mask = DA9150_E_VBUS_MASK,
@@ -251,7 +251,7 @@ static struct regmap_irq da9150_irqs[] = {
},
};
-static struct regmap_irq_chip da9150_regmap_irq_chip = {
+static const struct regmap_irq_chip da9150_regmap_irq_chip = {
.name = "da9150_irq",
.status_base = DA9150_EVENT_E,
.mask_base = DA9150_IRQ_MASK_E,
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index cc1a404328c2..8b14740f9fca 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -2659,7 +2659,7 @@ static int db8500_irq_map(struct irq_domain *d, unsigned int virq,
return 0;
}
-static struct irq_domain_ops db8500_irq_ops = {
+static const struct irq_domain_ops db8500_irq_ops = {
.map = db8500_irq_map,
.xlate = irq_domain_xlate_twocell,
};
diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c
index ebb9cf19e347..b54baad30164 100644
--- a/drivers/mfd/htc-i2cpld.c
+++ b/drivers/mfd/htc-i2cpld.c
@@ -564,7 +564,8 @@ static int htcpld_core_probe(struct platform_device *pdev)
htcpld->chained_irq = res->start;
/* Setup the chained interrupt handler */
- flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
+ flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT;
ret = request_threaded_irq(htcpld->chained_irq,
NULL, htcpld_handler,
flags, pdev->name, htcpld);
diff --git a/drivers/mfd/intel_soc_pmic_core.h b/drivers/mfd/intel_soc_pmic_core.h
index 9498d6719847..ff2464bc172f 100644
--- a/drivers/mfd/intel_soc_pmic_core.h
+++ b/drivers/mfd/intel_soc_pmic_core.h
@@ -24,7 +24,7 @@ struct intel_soc_pmic_config {
struct mfd_cell *cell_dev;
int n_cell_devs;
const struct regmap_config *regmap_config;
- struct regmap_irq_chip *irq_chip;
+ const struct regmap_irq_chip *irq_chip;
};
extern struct intel_soc_pmic_config intel_soc_pmic_config_crc;
diff --git a/drivers/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c
index 4cc1b324e971..7436075e8983 100644
--- a/drivers/mfd/intel_soc_pmic_crc.c
+++ b/drivers/mfd/intel_soc_pmic_crc.c
@@ -143,7 +143,7 @@ static const struct regmap_irq crystal_cove_irqs[] = {
},
};
-static struct regmap_irq_chip crystal_cove_irq_chip = {
+static const struct regmap_irq_chip crystal_cove_irq_chip = {
.name = "Crystal Cove",
.irqs = crystal_cove_irqs,
.num_irqs = ARRAY_SIZE(crystal_cove_irqs),
diff --git a/drivers/mfd/lp8788-irq.c b/drivers/mfd/lp8788-irq.c
index 23982dbf014d..a87f2b548f71 100644
--- a/drivers/mfd/lp8788-irq.c
+++ b/drivers/mfd/lp8788-irq.c
@@ -151,7 +151,7 @@ static int lp8788_irq_map(struct irq_domain *d, unsigned int virq,
return 0;
}
-static struct irq_domain_ops lp8788_domain_ops = {
+static const struct irq_domain_ops lp8788_domain_ops = {
.map = lp8788_irq_map,
};
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index 12d960a60ec4..8de34398abc0 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -934,8 +934,8 @@ gpe0_done:
lpc_ich_enable_gpio_space(dev);
lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]);
- ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
- 1, NULL, 0, NULL);
+ ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
+ &lpc_ich_cells[LPC_GPIO], 1, NULL, 0, NULL);
gpio_done:
if (acpi_conflict)
@@ -1008,8 +1008,8 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
}
lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
- ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
- 1, NULL, 0, NULL);
+ ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
+ &lpc_ich_cells[LPC_WDT], 1, NULL, 0, NULL);
wdt_done:
return ret;
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index 97a787ab3d51..8520bd68c1ff 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -658,7 +658,7 @@ static int max8925_irq_domain_map(struct irq_domain *d, unsigned int virq,
return 0;
}
-static struct irq_domain_ops max8925_irq_domain_ops = {
+static const struct irq_domain_ops max8925_irq_domain_ops = {
.map = max8925_irq_domain_map,
.xlate = irq_domain_xlate_onetwocell,
};
diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c
index 43fa61413e93..d3025be57f39 100644
--- a/drivers/mfd/max8997-irq.c
+++ b/drivers/mfd/max8997-irq.c
@@ -303,7 +303,7 @@ static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq,
return 0;
}
-static struct irq_domain_ops max8997_irq_domain_ops = {
+static const struct irq_domain_ops max8997_irq_domain_ops = {
.map = max8997_irq_domain_map,
};
diff --git a/drivers/mfd/max8998-irq.c b/drivers/mfd/max8998-irq.c
index c469477eb778..3702056628a8 100644
--- a/drivers/mfd/max8998-irq.c
+++ b/drivers/mfd/max8998-irq.c
@@ -214,7 +214,7 @@ static int max8998_irq_domain_map(struct irq_domain *d, unsigned int irq,
return 0;
}
-static struct irq_domain_ops max8998_irq_domain_ops = {
+static const struct irq_domain_ops max8998_irq_domain_ops = {
.map = max8998_irq_domain_map,
};
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 25fd7116493a..3f9f4c874d2a 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -163,7 +163,7 @@ int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq,
int virq = regmap_irq_get_virq(mc13xxx->irq_data, irq);
return devm_request_threaded_irq(mc13xxx->dev, virq, NULL, handler,
- 0, name, dev);
+ IRQF_ONESHOT, name, dev);
}
EXPORT_SYMBOL(mc13xxx_irq_request);
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 1aed3b7b8d9b..14fd5cbcf0f2 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -207,9 +207,11 @@ static int mfd_add_device(struct device *parent, int id,
}
if (!cell->ignore_resource_conflicts) {
- ret = acpi_check_resource_conflict(&res[r]);
- if (ret)
- goto fail_alias;
+ if (has_acpi_companion(&pdev->dev)) {
+ ret = acpi_check_resource_conflict(&res[r]);
+ if (ret)
+ goto fail_alias;
+ }
}
}
diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
index 09bc7804952a..38a0458f7834 100644
--- a/drivers/mfd/mt6397-core.c
+++ b/drivers/mfd/mt6397-core.c
@@ -34,6 +34,9 @@ static const struct mfd_cell mt6397_devs[] = {
}, {
.name = "mt6397-clk",
.of_compatible = "mediatek,mt6397-clk",
+ }, {
+ .name = "mt6397-pinctrl",
+ .of_compatible = "mediatek,mt6397-pinctrl",
},
};
@@ -130,7 +133,7 @@ static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq,
return 0;
}
-static struct irq_domain_ops mt6397_irq_domain_ops = {
+static const struct irq_domain_ops mt6397_irq_domain_ops = {
.map = mt6397_irq_domain_map,
};
diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c
index 7f87c62d91b3..e3deb466628b 100644
--- a/drivers/mfd/si476x-i2c.c
+++ b/drivers/mfd/si476x-i2c.c
@@ -777,7 +777,8 @@ static int si476x_core_probe(struct i2c_client *client,
rval = devm_request_threaded_irq(&client->dev,
client->irq, NULL,
si476x_core_interrupt,
- IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
client->name, core);
if (rval < 0) {
dev_err(&client->dev, "Could not request IRQ %d\n",
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 2d7fae94c861..18c4d72d1d2a 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -989,7 +989,7 @@ static void stmpe_irq_unmap(struct irq_domain *d, unsigned int virq)
irq_set_chip_data(virq, NULL);
}
-static struct irq_domain_ops stmpe_irq_ops = {
+static const struct irq_domain_ops stmpe_irq_ops = {
.map = stmpe_irq_map,
.unmap = stmpe_irq_unmap,
.xlate = irq_domain_xlate_twocell,
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index cf356395c9e9..96d420dfc15d 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -233,7 +233,7 @@ static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq)
irq_set_chip_data(virq, NULL);
}
-static struct irq_domain_ops tc3589x_irq_ops = {
+static const struct irq_domain_ops tc3589x_irq_ops = {
.map = tc3589x_irq_map,
.unmap = tc3589x_irq_unmap,
.xlate = irq_domain_xlate_onecell,
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index 8e1dbc469580..e0a2583916ce 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -311,7 +311,7 @@ static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq,
return 0;
}
-static struct irq_domain_ops tps6586x_domain_ops = {
+static const struct irq_domain_ops tps6586x_domain_ops = {
.map = tps6586x_irq_map,
.xlate = irq_domain_xlate_twocell,
};
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index 1b772ef761cb..a3fa7f4f1fb4 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -674,7 +674,7 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base)
irq_set_handler_data(irq, agent);
agent->irq_name = kasprintf(GFP_KERNEL, "twl4030_%s", sih->name);
status = request_threaded_irq(irq, NULL, handle_twl4030_sih,
- IRQF_EARLY_RESUME,
+ IRQF_EARLY_RESUME | IRQF_ONESHOT,
agent->irq_name ?: sih->name, NULL);
dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name,
diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c
index f440aed61305..04b539850e72 100644
--- a/drivers/mfd/twl4030-power.c
+++ b/drivers/mfd/twl4030-power.c
@@ -264,7 +264,9 @@ out:
return err;
}
-static int twl4030_config_wakeup12_sequence(u8 address)
+static int
+twl4030_config_wakeup12_sequence(const struct twl4030_power_data *pdata,
+ u8 address)
{
int err = 0;
u8 data;
@@ -293,13 +295,14 @@ static int twl4030_config_wakeup12_sequence(u8 address)
if (err)
goto out;
- if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
+ if (pdata->ac_charger_quirk || machine_is_omap_3430sdp() ||
+ machine_is_omap_ldp()) {
/* Disabling AC charger effect on sleep-active transitions */
err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data,
R_CFG_P1_TRANSITION);
if (err)
goto out;
- data &= ~(1<<1);
+ data &= ~STARTON_CHG;
err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data,
R_CFG_P1_TRANSITION);
if (err)
@@ -459,8 +462,9 @@ static int twl4030_configure_resource(struct twl4030_resconfig *rconfig)
return 0;
}
-static int load_twl4030_script(struct twl4030_script *tscript,
- u8 address)
+static int load_twl4030_script(const struct twl4030_power_data *pdata,
+ struct twl4030_script *tscript,
+ u8 address)
{
int err;
static int order;
@@ -487,7 +491,7 @@ static int load_twl4030_script(struct twl4030_script *tscript,
if (err)
goto out;
- err = twl4030_config_wakeup12_sequence(address);
+ err = twl4030_config_wakeup12_sequence(pdata, address);
if (err)
goto out;
order = 1;
@@ -567,7 +571,7 @@ twl4030_power_configure_scripts(const struct twl4030_power_data *pdata)
u8 address = twl4030_start_script_address;
for (i = 0; i < pdata->num; i++) {
- err = load_twl4030_script(pdata->scripts[i], address);
+ err = load_twl4030_script(pdata, pdata->scripts[i], address);
if (err)
return err;
address += pdata->scripts[i]->size;
@@ -829,6 +833,21 @@ static struct twl4030_power_data osc_off_idle = {
.board_config = osc_off_rconfig,
};
+static struct twl4030_power_data omap3_idle_ac_quirk = {
+ .scripts = omap3_idle_scripts,
+ .num = ARRAY_SIZE(omap3_idle_scripts),
+ .resource_config = omap3_idle_rconfig,
+ .ac_charger_quirk = true,
+};
+
+static struct twl4030_power_data omap3_idle_ac_quirk_osc_off = {
+ .scripts = omap3_idle_scripts,
+ .num = ARRAY_SIZE(omap3_idle_scripts),
+ .resource_config = omap3_idle_rconfig,
+ .board_config = osc_off_rconfig,
+ .ac_charger_quirk = true,
+};
+
static const struct of_device_id twl4030_power_of_match[] = {
{
.compatible = "ti,twl4030-power",
@@ -845,6 +864,18 @@ static const struct of_device_id twl4030_power_of_match[] = {
.compatible = "ti,twl4030-power-idle-osc-off",
.data = &osc_off_idle,
},
+ {
+ .compatible = "ti,twl4030-power-omap3-sdp",
+ .data = &omap3_idle_ac_quirk,
+ },
+ {
+ .compatible = "ti,twl4030-power-omap3-ldp",
+ .data = &omap3_idle_ac_quirk_osc_off,
+ },
+ {
+ .compatible = "ti,twl4030-power-omap3-evm",
+ .data = &omap3_idle_ac_quirk,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, twl4030_power_of_match);
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index 2807e1a95663..20fb58179ada 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -376,7 +376,7 @@ static void twl6030_irq_unmap(struct irq_domain *d, unsigned int virq)
irq_set_chip_data(virq, NULL);
}
-static struct irq_domain_ops twl6030_irq_domain_ops = {
+static const struct irq_domain_ops twl6030_irq_domain_ops = {
.map = twl6030_irq_map,
.unmap = twl6030_irq_unmap,
.xlate = irq_domain_xlate_onetwocell,
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index 58ea9fdd3a15..3591550598ad 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -566,8 +566,7 @@ static int ucb1x00_probe(struct mcp *mcp)
}
irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING);
- irq_set_handler_data(ucb->irq, ucb);
- irq_set_chained_handler(ucb->irq, ucb1x00_irq);
+ irq_set_chained_handler_and_data(ucb->irq, ucb1x00_irq, ucb);
if (pdata && pdata->gpio_base) {
ucb->gpio.label = dev_name(&ucb->dev);
diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c
index 6ee3018d8653..fd789d2eb0f5 100644
--- a/drivers/mfd/wm831x-auxadc.c
+++ b/drivers/mfd/wm831x-auxadc.c
@@ -285,7 +285,8 @@ void wm831x_auxadc_init(struct wm831x *wm831x)
ret = request_threaded_irq(wm831x_irq(wm831x,
WM831X_IRQ_AUXADC_DATA),
- NULL, wm831x_auxadc_irq, 0,
+ NULL, wm831x_auxadc_irq,
+ IRQF_ONESHOT,
"auxadc", wm831x);
if (ret < 0) {
dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
index 64e512eadf17..3da81263c764 100644
--- a/drivers/mfd/wm831x-irq.c
+++ b/drivers/mfd/wm831x-irq.c
@@ -564,7 +564,7 @@ static int wm831x_irq_map(struct irq_domain *h, unsigned int virq,
return 0;
}
-static struct irq_domain_ops wm831x_irq_domain_ops = {
+static const struct irq_domain_ops wm831x_irq_domain_ops = {
.map = wm831x_irq_map,
.xlate = irq_domain_xlate_twocell,
};
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index f5124a8acad8..8a07c5634aee 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -404,7 +404,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
if (wm8350->irq_base) {
ret = request_threaded_irq(wm8350->irq_base +
WM8350_IRQ_AUXADC_DATARDY,
- NULL, wm8350_auxadc_irq, 0,
+ NULL, wm8350_auxadc_irq,
+ IRQF_ONESHOT,
"auxadc", wm8350);
if (ret < 0)
dev_warn(wm8350->dev,
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index a14407edbd89..55c380a67686 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -28,7 +28,7 @@
#include <linux/delay.h>
-static struct regmap_irq wm8994_irqs[] = {
+static const struct regmap_irq wm8994_irqs[] = {
[WM8994_IRQ_TEMP_SHUT] = {
.reg_offset = 1,
.mask = WM8994_TEMP_SHUT_EINT,
@@ -128,7 +128,7 @@ static struct regmap_irq wm8994_irqs[] = {
},
};
-static struct regmap_irq_chip wm8994_irq_chip = {
+static const struct regmap_irq_chip wm8994_irq_chip = {
.name = "wm8994",
.irqs = wm8994_irqs,
.num_irqs = ARRAY_SIZE(wm8994_irqs),
@@ -184,7 +184,7 @@ static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq,
return 0;
}
-static struct irq_domain_ops wm8994_edge_irq_ops = {
+static const struct irq_domain_ops wm8994_edge_irq_ops = {
.map = wm8994_edge_irq_map,
.xlate = irq_domain_xlate_twocell,
};
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 2c25271f8c41..c9c3d20b784b 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -913,6 +913,9 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
if (!err)
break;
+ /* Re-tune if needed */
+ mmc_retune_recheck(card->host);
+
prev_cmd_status_valid = false;
pr_err("%s: error %d sending status command, %sing\n",
req->rq_disk->disk_name, err, retry ? "retry" : "abort");
@@ -1029,6 +1032,18 @@ static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type)
md->reset_done &= ~type;
}
+int mmc_access_rpmb(struct mmc_queue *mq)
+{
+ struct mmc_blk_data *md = mq->data;
+ /*
+ * If this is a RPMB partition access, return ture
+ */
+ if (md && md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB)
+ return true;
+
+ return false;
+}
+
static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
@@ -1192,6 +1207,7 @@ static int mmc_blk_err_check(struct mmc_card *card,
mmc_active);
struct mmc_blk_request *brq = &mq_mrq->brq;
struct request *req = mq_mrq->req;
+ int need_retune = card->host->need_retune;
int ecc_err = 0, gen_err = 0;
/*
@@ -1259,6 +1275,12 @@ static int mmc_blk_err_check(struct mmc_card *card,
}
if (brq->data.error) {
+ if (need_retune && !brq->retune_retry_done) {
+ pr_info("%s: retrying because a re-tune was needed\n",
+ req->rq_disk->disk_name);
+ brq->retune_retry_done = 1;
+ return MMC_BLK_RETRY;
+ }
pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n",
req->rq_disk->disk_name, brq->data.error,
(unsigned)blk_rq_pos(req),
@@ -1818,7 +1840,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
- int ret = 1, disable_multi = 0, retry = 0, type;
+ int ret = 1, disable_multi = 0, retry = 0, type, retune_retry_done = 0;
enum mmc_blk_status status;
struct mmc_queue_req *mq_rq;
struct request *req = rqc;
@@ -1898,10 +1920,13 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
break;
case MMC_BLK_CMD_ERR:
ret = mmc_blk_cmd_err(md, card, brq, req, ret);
- if (!mmc_blk_reset(md, card->host, type))
- break;
- goto cmd_abort;
+ if (mmc_blk_reset(md, card->host, type))
+ goto cmd_abort;
+ if (!ret)
+ goto start_new_req;
+ break;
case MMC_BLK_RETRY:
+ retune_retry_done = brq->retune_retry_done;
if (retry++ < 5)
break;
/* Fall through */
@@ -1964,6 +1989,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
mmc_start_req(card->host,
&mq_rq->mmc_active, NULL);
}
+ mq_rq->brq.retune_retry_done = retune_retry_done;
}
} while (ret);
@@ -2205,7 +2231,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
* The CSD capacity field is in units of read_blkbits.
* set_capacity takes units of 512 bytes.
*/
- size = card->csd.capacity << (card->csd.read_blkbits - 9);
+ size = (typeof(sector_t))card->csd.capacity
+ << (card->csd.read_blkbits - 9);
}
return mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 53b741398b93..b78cf5d403a3 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -268,8 +268,6 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
static int mmc_test_buffer_transfer(struct mmc_test_card *test,
u8 *buffer, unsigned addr, unsigned blksz, int write)
{
- int ret;
-
struct mmc_request mrq = {0};
struct mmc_command cmd = {0};
struct mmc_command stop = {0};
@@ -292,11 +290,7 @@ static int mmc_test_buffer_transfer(struct mmc_test_card *test,
if (data.error)
return data.error;
- ret = mmc_test_wait_busy(test);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_wait_busy(test);
}
static void mmc_test_free_mem(struct mmc_test_mem *mem)
@@ -826,9 +820,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
mmc_test_nonblock_reset(&mrq1, &cmd1,
&stop1, &data1);
}
- done_areq = cur_areq;
- cur_areq = other_areq;
- other_areq = done_areq;
+ swap(cur_areq, other_areq);
dev_addr += blocks;
}
@@ -994,11 +986,7 @@ static int mmc_test_basic_write(struct mmc_test_card *test)
sg_init_one(&sg, test->buffer, 512);
- ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
}
static int mmc_test_basic_read(struct mmc_test_card *test)
@@ -1012,44 +1000,29 @@ static int mmc_test_basic_read(struct mmc_test_card *test)
sg_init_one(&sg, test->buffer, 512);
- ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0);
}
static int mmc_test_verify_write(struct mmc_test_card *test)
{
- int ret;
struct scatterlist sg;
sg_init_one(&sg, test->buffer, 512);
- ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
}
static int mmc_test_verify_read(struct mmc_test_card *test)
{
- int ret;
struct scatterlist sg;
sg_init_one(&sg, test->buffer, 512);
- ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
}
static int mmc_test_multi_write(struct mmc_test_card *test)
{
- int ret;
unsigned int size;
struct scatterlist sg;
@@ -1066,16 +1039,11 @@ static int mmc_test_multi_write(struct mmc_test_card *test)
sg_init_one(&sg, test->buffer, size);
- ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
}
static int mmc_test_multi_read(struct mmc_test_card *test)
{
- int ret;
unsigned int size;
struct scatterlist sg;
@@ -1092,11 +1060,7 @@ static int mmc_test_multi_read(struct mmc_test_card *test)
sg_init_one(&sg, test->buffer, size);
- ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
}
static int mmc_test_pow2_write(struct mmc_test_card *test)
@@ -1263,11 +1227,7 @@ static int mmc_test_xfersize_write(struct mmc_test_card *test)
if (ret)
return ret;
- ret = mmc_test_broken_transfer(test, 1, 512, 1);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_broken_transfer(test, 1, 512, 1);
}
static int mmc_test_xfersize_read(struct mmc_test_card *test)
@@ -1278,11 +1238,7 @@ static int mmc_test_xfersize_read(struct mmc_test_card *test)
if (ret)
return ret;
- ret = mmc_test_broken_transfer(test, 1, 512, 0);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_broken_transfer(test, 1, 512, 0);
}
static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
@@ -1296,11 +1252,7 @@ static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
if (ret)
return ret;
- ret = mmc_test_broken_transfer(test, 2, 512, 1);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_broken_transfer(test, 2, 512, 1);
}
static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
@@ -1314,48 +1266,33 @@ static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
if (ret)
return ret;
- ret = mmc_test_broken_transfer(test, 2, 512, 0);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_broken_transfer(test, 2, 512, 0);
}
#ifdef CONFIG_HIGHMEM
static int mmc_test_write_high(struct mmc_test_card *test)
{
- int ret;
struct scatterlist sg;
sg_init_table(&sg, 1);
sg_set_page(&sg, test->highmem, 512, 0);
- ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
}
static int mmc_test_read_high(struct mmc_test_card *test)
{
- int ret;
struct scatterlist sg;
sg_init_table(&sg, 1);
sg_set_page(&sg, test->highmem, 512, 0);
- ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
}
static int mmc_test_multi_write_high(struct mmc_test_card *test)
{
- int ret;
unsigned int size;
struct scatterlist sg;
@@ -1373,16 +1310,11 @@ static int mmc_test_multi_write_high(struct mmc_test_card *test)
sg_init_table(&sg, 1);
sg_set_page(&sg, test->highmem, size, 0);
- ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
}
static int mmc_test_multi_read_high(struct mmc_test_card *test)
{
- int ret;
unsigned int size;
struct scatterlist sg;
@@ -1400,11 +1332,7 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test)
sg_init_table(&sg, 1);
sg_set_page(&sg, test->highmem, size, 0);
- ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
- if (ret)
- return ret;
-
- return 0;
+ return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
}
#else
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 236d194c2883..b5a2b145d89f 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -38,7 +38,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req)
return BLKPREP_KILL;
}
- if (mq && mmc_card_removed(mq->card))
+ if (mq && (mmc_card_removed(mq->card) || mmc_access_rpmb(mq)))
return BLKPREP_KILL;
req->cmd_flags |= REQ_DONTPREP;
@@ -56,7 +56,6 @@ static int mmc_queue_thread(void *d)
down(&mq->thread_sem);
do {
struct request *req = NULL;
- struct mmc_queue_req *tmp;
unsigned int cmd_flags = 0;
spin_lock_irq(q->queue_lock);
@@ -69,6 +68,7 @@ static int mmc_queue_thread(void *d)
set_current_state(TASK_RUNNING);
cmd_flags = req ? req->cmd_flags : 0;
mq->issue_fn(mq, req);
+ cond_resched();
if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
continue; /* fetch again */
@@ -86,9 +86,7 @@ static int mmc_queue_thread(void *d)
mq->mqrq_prev->brq.mrq.data = NULL;
mq->mqrq_prev->req = NULL;
- tmp = mq->mqrq_prev;
- mq->mqrq_prev = mq->mqrq_cur;
- mq->mqrq_cur = tmp;
+ swap(mq->mqrq_prev, mq->mqrq_cur);
} else {
if (kthread_should_stop()) {
set_current_state(TASK_RUNNING);
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 5752d50049a3..36cddab57d77 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -12,6 +12,7 @@ struct mmc_blk_request {
struct mmc_command cmd;
struct mmc_command stop;
struct mmc_data data;
+ int retune_retry_done;
};
enum mmc_packed_type {
@@ -73,4 +74,6 @@ extern void mmc_queue_bounce_post(struct mmc_queue_req *);
extern int mmc_packed_init(struct mmc_queue *, struct mmc_card *);
extern void mmc_packed_clean(struct mmc_queue *);
+extern int mmc_access_rpmb(struct mmc_queue *);
+
#endif
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c296bc098fe2..9ad73f30f744 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -133,6 +133,12 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
struct mmc_command *cmd = mrq->cmd;
int err = cmd->error;
+ /* Flag re-tuning needed on CRC errors */
+ if (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) ||
+ (mrq->data && mrq->data->error == -EILSEQ) ||
+ (mrq->stop && mrq->stop->error == -EILSEQ))
+ mmc_retune_needed(host);
+
if (err && cmd->retries && mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
cmd->retries = 0;
@@ -186,12 +192,29 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
EXPORT_SYMBOL(mmc_request_done);
+static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
+{
+ int err;
+
+ /* Assumes host controller has been runtime resumed by mmc_claim_host */
+ err = mmc_retune(host);
+ if (err) {
+ mrq->cmd->error = err;
+ mmc_request_done(host, mrq);
+ return;
+ }
+
+ host->ops->request(host, mrq);
+}
+
static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
#ifdef CONFIG_MMC_DEBUG
unsigned int i, sz;
struct scatterlist *sg;
#endif
+ mmc_retune_hold(host);
+
if (mmc_card_removed(host->card))
return -ENOMEDIUM;
@@ -252,7 +275,7 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
}
mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);
- host->ops->request(host, mrq);
+ __mmc_start_request(host, mrq);
return 0;
}
@@ -301,12 +324,15 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
use_busy_signal = false;
}
+ mmc_retune_hold(card->host);
+
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BKOPS_START, 1, timeout,
use_busy_signal, true, false);
if (err) {
pr_warn("%s: Error %d starting bkops\n",
mmc_hostname(card->host), err);
+ mmc_retune_release(card->host);
goto out;
}
@@ -317,6 +343,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
*/
if (!use_busy_signal)
mmc_card_set_doing_bkops(card);
+ else
+ mmc_retune_release(card->host);
out:
mmc_release_host(card->host);
}
@@ -417,22 +445,22 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
host->areq);
break; /* return err */
} else {
+ mmc_retune_recheck(host);
pr_info("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host),
cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
- host->ops->request(host, mrq);
+ __mmc_start_request(host, mrq);
continue; /* wait for done/new event again */
}
} else if (context_info->is_new_req) {
context_info->is_new_req = false;
- if (!next_req) {
- err = MMC_BLK_NEW_REQUEST;
- break; /* return err */
- }
+ if (!next_req)
+ return MMC_BLK_NEW_REQUEST;
}
}
+ mmc_retune_release(host);
return err;
}
@@ -467,12 +495,16 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
mmc_card_removed(host->card))
break;
+ mmc_retune_recheck(host);
+
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host), cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
- host->ops->request(host, mrq);
+ __mmc_start_request(host, mrq);
}
+
+ mmc_retune_release(host);
}
/**
@@ -728,6 +760,7 @@ int mmc_stop_bkops(struct mmc_card *card)
*/
if (!err || (err == -EINVAL)) {
mmc_card_clr_doing_bkops(card);
+ mmc_retune_release(card->host);
err = 0;
}
@@ -1109,6 +1142,8 @@ int mmc_execute_tuning(struct mmc_card *card)
if (err)
pr_err("%s: tuning execution failed\n", mmc_hostname(host));
+ else
+ mmc_retune_enable(host);
return err;
}
@@ -1140,6 +1175,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
*/
void mmc_set_initial_state(struct mmc_host *host)
{
+ mmc_retune_disable(host);
+
if (mmc_host_is_spi(host))
host->ios.chip_select = MMC_CS_HIGH;
else
@@ -1147,6 +1184,7 @@ void mmc_set_initial_state(struct mmc_host *host)
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
+ host->ios.drv_type = 0;
mmc_set_ios(host);
}
@@ -1551,8 +1589,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
goto power_cycle;
}
- /* Keep clock gated for at least 5 ms */
- mmc_delay(5);
+ /* Keep clock gated for at least 10 ms, though spec only says 5 ms */
+ mmc_delay(10);
host->ios.clock = clock;
mmc_set_ios(host);
@@ -1601,6 +1639,44 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
mmc_host_clk_release(host);
}
+int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
+ int card_drv_type, int *drv_type)
+{
+ struct mmc_host *host = card->host;
+ int host_drv_type = SD_DRIVER_TYPE_B;
+ int drive_strength;
+
+ *drv_type = 0;
+
+ if (!host->ops->select_drive_strength)
+ return 0;
+
+ /* Use SD definition of driver strength for hosts */
+ if (host->caps & MMC_CAP_DRIVER_TYPE_A)
+ host_drv_type |= SD_DRIVER_TYPE_A;
+
+ if (host->caps & MMC_CAP_DRIVER_TYPE_C)
+ host_drv_type |= SD_DRIVER_TYPE_C;
+
+ if (host->caps & MMC_CAP_DRIVER_TYPE_D)
+ host_drv_type |= SD_DRIVER_TYPE_D;
+
+ /*
+ * The drive strength that the hardware can support
+ * depends on the board design. Pass the appropriate
+ * information and let the hardware specific code
+ * return what is possible given the options
+ */
+ mmc_host_clk_hold(host);
+ drive_strength = host->ops->select_drive_strength(card, max_dtr,
+ host_drv_type,
+ card_drv_type,
+ drv_type);
+ mmc_host_clk_release(host);
+
+ return drive_strength;
+}
+
/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
@@ -1970,6 +2046,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
unsigned long timeout;
int err;
+ mmc_retune_hold(card->host);
+
/*
* qty is used to calculate the erase timeout which depends on how many
* erase groups (or allocation units in SD terminology) are affected.
@@ -2073,6 +2151,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
(R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
out:
+ mmc_retune_release(card->host);
return err;
}
@@ -2331,7 +2410,8 @@ int mmc_hw_reset(struct mmc_host *host)
ret = host->bus_ops->reset(host);
mmc_bus_put(host);
- pr_warn("%s: tried to reset card\n", mmc_hostname(host));
+ if (ret != -EOPNOTSUPP)
+ pr_warn("%s: tried to reset card\n", mmc_hostname(host));
return ret;
}
@@ -2651,6 +2731,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
+ case PM_RESTORE_PREPARE:
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 1;
spin_unlock_irqrestore(&host->lock, flags);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index cfba3c05aab1..1a22a82209b2 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -50,6 +50,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
+int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
+ int card_drv_type, int *drv_type);
void mmc_power_up(struct mmc_host *host, u32 ocr);
void mmc_power_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
@@ -88,6 +90,8 @@ void mmc_remove_card_debugfs(struct mmc_card *card);
void mmc_init_context_info(struct mmc_host *host);
int mmc_execute_tuning(struct mmc_card *card);
+int mmc_hs200_to_hs400(struct mmc_card *card);
+int mmc_hs400_to_hs200(struct mmc_card *card);
#endif
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 8be0df758e68..99a9c9011c50 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -301,6 +301,90 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
#endif
+void mmc_retune_enable(struct mmc_host *host)
+{
+ host->can_retune = 1;
+ if (host->retune_period)
+ mod_timer(&host->retune_timer,
+ jiffies + host->retune_period * HZ);
+}
+
+void mmc_retune_disable(struct mmc_host *host)
+{
+ host->can_retune = 0;
+ del_timer_sync(&host->retune_timer);
+ host->retune_now = 0;
+ host->need_retune = 0;
+}
+
+void mmc_retune_timer_stop(struct mmc_host *host)
+{
+ del_timer_sync(&host->retune_timer);
+}
+EXPORT_SYMBOL(mmc_retune_timer_stop);
+
+void mmc_retune_hold(struct mmc_host *host)
+{
+ if (!host->hold_retune)
+ host->retune_now = 1;
+ host->hold_retune += 1;
+}
+
+void mmc_retune_release(struct mmc_host *host)
+{
+ if (host->hold_retune)
+ host->hold_retune -= 1;
+ else
+ WARN_ON(1);
+}
+
+int mmc_retune(struct mmc_host *host)
+{
+ bool return_to_hs400 = false;
+ int err;
+
+ if (host->retune_now)
+ host->retune_now = 0;
+ else
+ return 0;
+
+ if (!host->need_retune || host->doing_retune || !host->card)
+ return 0;
+
+ host->need_retune = 0;
+
+ host->doing_retune = 1;
+
+ if (host->ios.timing == MMC_TIMING_MMC_HS400) {
+ err = mmc_hs400_to_hs200(host->card);
+ if (err)
+ goto out;
+
+ return_to_hs400 = true;
+
+ if (host->ops->prepare_hs400_tuning)
+ host->ops->prepare_hs400_tuning(host, &host->ios);
+ }
+
+ err = mmc_execute_tuning(host->card);
+ if (err)
+ goto out;
+
+ if (return_to_hs400)
+ err = mmc_hs200_to_hs400(host->card);
+out:
+ host->doing_retune = 0;
+
+ return err;
+}
+
+static void mmc_retune_timer(unsigned long data)
+{
+ struct mmc_host *host = (struct mmc_host *)data;
+
+ mmc_retune_needed(host);
+}
+
/**
* mmc_of_parse() - parse host's device-tree node
* @host: host whose node should be parsed.
@@ -400,6 +484,9 @@ int mmc_of_parse(struct mmc_host *host)
else if (ret != -ENOENT)
return ret;
+ if (of_property_read_bool(np, "disable-wp"))
+ host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
+
/* See the comment on CD inversion above */
if (ro_cap_invert ^ ro_gpio_invert)
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
@@ -504,6 +591,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
#ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify;
#endif
+ setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);
/*
* By default, hosts do not support SGIO or large requests.
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index f2ab9e578126..992bf5397633 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -15,5 +15,11 @@
int mmc_register_host_class(void);
void mmc_unregister_host_class(void);
+void mmc_retune_enable(struct mmc_host *host);
+void mmc_retune_disable(struct mmc_host *host);
+void mmc_retune_hold(struct mmc_host *host);
+void mmc_retune_release(struct mmc_host *host);
+int mmc_retune(struct mmc_host *host);
+
#endif
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index f36c76f8b232..e726903170a8 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -21,6 +21,7 @@
#include <linux/mmc/mmc.h>
#include "core.h"
+#include "host.h"
#include "bus.h"
#include "mmc_ops.h"
#include "sd_ops.h"
@@ -266,8 +267,10 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd)
* calculate the enhanced data area offset, in bytes
*/
card->ext_csd.enhanced_area_offset =
- (ext_csd[139] << 24) + (ext_csd[138] << 16) +
- (ext_csd[137] << 8) + ext_csd[136];
+ (((unsigned long long)ext_csd[139]) << 24) +
+ (((unsigned long long)ext_csd[138]) << 16) +
+ (((unsigned long long)ext_csd[137]) << 8) +
+ (((unsigned long long)ext_csd[136]));
if (mmc_card_blockaddr(card))
card->ext_csd.enhanced_area_offset <<= 9;
/*
@@ -434,6 +437,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.raw_trim_mult =
ext_csd[EXT_CSD_TRIM_MULT];
card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
+ card->ext_csd.raw_driver_strength = ext_csd[EXT_CSD_DRIVER_STRENGTH];
if (card->ext_csd.rev >= 4) {
if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] &
EXT_CSD_PART_SETTING_COMPLETED)
@@ -1040,6 +1044,7 @@ static int mmc_select_hs400(struct mmc_card *card)
{
struct mmc_host *host = card->host;
int err = 0;
+ u8 val;
/*
* HS400 mode requires 8-bit bus width
@@ -1055,8 +1060,10 @@ static int mmc_select_hs400(struct mmc_card *card)
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
mmc_set_bus_speed(card);
+ val = EXT_CSD_TIMING_HS |
+ card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
+ EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time,
true, true, true);
if (err) {
@@ -1075,8 +1082,10 @@ static int mmc_select_hs400(struct mmc_card *card)
return err;
}
+ val = EXT_CSD_TIMING_HS400 |
+ card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400,
+ EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time,
true, true, true);
if (err) {
@@ -1091,6 +1100,115 @@ static int mmc_select_hs400(struct mmc_card *card)
return 0;
}
+int mmc_hs200_to_hs400(struct mmc_card *card)
+{
+ return mmc_select_hs400(card);
+}
+
+/* Caller must hold re-tuning */
+static int mmc_switch_status(struct mmc_card *card)
+{
+ u32 status;
+ int err;
+
+ err = mmc_send_status(card, &status);
+ if (err)
+ return err;
+
+ return mmc_switch_status_error(card->host, status);
+}
+
+int mmc_hs400_to_hs200(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ bool send_status = true;
+ unsigned int max_dtr;
+ int err;
+ u8 val;
+
+ if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ send_status = false;
+
+ /* Reduce frequency to HS */
+ max_dtr = card->ext_csd.hs_max_dtr;
+ mmc_set_clock(host, max_dtr);
+
+ /* Switch HS400 to HS DDR */
+ val = EXT_CSD_TIMING_HS |
+ card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+ val, card->ext_csd.generic_cmd6_time,
+ true, send_status, true);
+ if (err)
+ goto out_err;
+
+ mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
+
+ if (!send_status) {
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+ }
+
+ /* Switch HS DDR to HS */
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
+ true, send_status, true);
+ if (err)
+ goto out_err;
+
+ mmc_set_timing(host, MMC_TIMING_MMC_HS);
+
+ if (!send_status) {
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+ }
+
+ /* Switch HS to HS200 */
+ val = EXT_CSD_TIMING_HS200 |
+ card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+ val, card->ext_csd.generic_cmd6_time, true,
+ send_status, true);
+ if (err)
+ goto out_err;
+
+ mmc_set_timing(host, MMC_TIMING_MMC_HS200);
+
+ if (!send_status) {
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+ }
+
+ mmc_set_bus_speed(card);
+
+ return 0;
+
+out_err:
+ pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
+ __func__, err);
+ return err;
+}
+
+static void mmc_select_driver_type(struct mmc_card *card)
+{
+ int card_drv_type, drive_strength, drv_type;
+
+ card_drv_type = card->ext_csd.raw_driver_strength |
+ mmc_driver_type_mask(0);
+
+ drive_strength = mmc_select_drive_strength(card,
+ card->ext_csd.hs200_max_dtr,
+ card_drv_type, &drv_type);
+
+ card->drive_strength = drive_strength;
+
+ if (drv_type)
+ mmc_set_driver_type(card->host, drv_type);
+}
+
/*
* For device supporting HS200 mode, the following sequence
* should be done before executing the tuning process.
@@ -1102,6 +1220,7 @@ static int mmc_select_hs200(struct mmc_card *card)
{
struct mmc_host *host = card->host;
int err = -EINVAL;
+ u8 val;
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
@@ -1113,14 +1232,18 @@ static int mmc_select_hs200(struct mmc_card *card)
if (err)
goto err;
+ mmc_select_driver_type(card);
+
/*
* Set the bus width(4 or 8) with host's support and
* switch to HS200 mode if bus width is set successfully.
*/
err = mmc_select_bus_width(card);
if (!IS_ERR_VALUE(err)) {
+ val = EXT_CSD_TIMING_HS200 |
+ card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200,
+ EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time,
true, true, true);
if (!err)
@@ -1511,9 +1634,12 @@ static int mmc_sleep(struct mmc_host *host)
unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
int err;
+ /* Re-tuning can't be done once the card is deselected */
+ mmc_retune_hold(host);
+
err = mmc_deselect_cards(host);
if (err)
- return err;
+ goto out_release;
cmd.opcode = MMC_SLEEP_AWAKE;
cmd.arg = card->rca << 16;
@@ -1534,7 +1660,7 @@ static int mmc_sleep(struct mmc_host *host)
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
- return err;
+ goto out_release;
/*
* If the host does not wait while the card signals busy, then we will
@@ -1545,6 +1671,8 @@ static int mmc_sleep(struct mmc_host *host)
if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
mmc_delay(timeout_ms);
+out_release:
+ mmc_retune_release(host);
return err;
}
@@ -1782,17 +1910,6 @@ static int mmc_runtime_resume(struct mmc_host *host)
return 0;
}
-static int mmc_power_restore(struct mmc_host *host)
-{
- int ret;
-
- mmc_claim_host(host);
- ret = mmc_init_card(host, host->card->ocr, host->card);
- mmc_release_host(host);
-
- return ret;
-}
-
int mmc_can_reset(struct mmc_card *card)
{
u8 rst_n_function;
@@ -1830,7 +1947,7 @@ static int mmc_reset(struct mmc_host *host)
mmc_set_initial_state(host);
mmc_host_clk_release(host);
- return mmc_power_restore(host);
+ return mmc_init_card(host, card->ocr, card);
}
static const struct mmc_bus_ops mmc_ops = {
@@ -1840,7 +1957,6 @@ static const struct mmc_bus_ops mmc_ops = {
.resume = mmc_resume,
.runtime_suspend = mmc_runtime_suspend,
.runtime_resume = mmc_runtime_resume,
- .power_restore = mmc_power_restore,
.alive = mmc_alive,
.shutdown = mmc_shutdown,
.reset = mmc_reset,
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 0ea042dc7443..0e9ae1c276c8 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -19,6 +19,7 @@
#include <linux/mmc/mmc.h>
#include "core.h"
+#include "host.h"
#include "mmc_ops.h"
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
@@ -449,6 +450,21 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
return err;
}
+int mmc_switch_status_error(struct mmc_host *host, u32 status)
+{
+ if (mmc_host_is_spi(host)) {
+ if (status & R1_SPI_ILLEGAL_COMMAND)
+ return -EBADMSG;
+ } else {
+ if (status & 0xFDFFA000)
+ pr_warn("%s: unexpected status %#x after switch\n",
+ mmc_hostname(host), status);
+ if (status & R1_SWITCH_ERROR)
+ return -EBADMSG;
+ }
+ return 0;
+}
+
/**
* __mmc_switch - modify EXT_CSD register
* @card: the MMC card associated with the data transfer
@@ -474,6 +490,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
u32 status = 0;
bool use_r1b_resp = use_busy_signal;
+ mmc_retune_hold(host);
+
/*
* If the cmd timeout and the max_busy_timeout of the host are both
* specified, let's validate them. A failure means we need to prevent
@@ -506,11 +524,11 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err)
- return err;
+ goto out;
/* No need to check card status in case of unblocking command */
if (!use_busy_signal)
- return 0;
+ goto out;
/*
* CRC errors shall only be ignored in cases were CMD13 is used to poll
@@ -529,7 +547,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
if (send_status) {
err = __mmc_send_status(card, &status, ignore_crc);
if (err)
- return err;
+ goto out;
}
if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
break;
@@ -543,29 +561,23 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
*/
if (!send_status) {
mmc_delay(timeout_ms);
- return 0;
+ goto out;
}
/* Timeout if the device never leaves the program state. */
if (time_after(jiffies, timeout)) {
pr_err("%s: Card stuck in programming state! %s\n",
mmc_hostname(host), __func__);
- return -ETIMEDOUT;
+ err = -ETIMEDOUT;
+ goto out;
}
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
- if (mmc_host_is_spi(host)) {
- if (status & R1_SPI_ILLEGAL_COMMAND)
- return -EBADMSG;
- } else {
- if (status & 0xFDFFA000)
- pr_warn("%s: unexpected status %#x after switch\n",
- mmc_hostname(host), status);
- if (status & R1_SWITCH_ERROR)
- return -EBADMSG;
- }
+ err = mmc_switch_status_error(host, status);
+out:
+ mmc_retune_release(host);
- return 0;
+ return err;
}
EXPORT_SYMBOL_GPL(__mmc_switch);
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 6f4b00ed93de..f498f9ae21f0 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -27,6 +27,7 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
int mmc_can_ext_csd(struct mmc_card *card);
+int mmc_switch_status_error(struct mmc_host *host, u32 status);
#endif
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 31a9ef256d06..4e7366ab187f 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -386,64 +386,31 @@ out:
static int sd_select_driver_type(struct mmc_card *card, u8 *status)
{
- int host_drv_type = SD_DRIVER_TYPE_B;
- int card_drv_type = SD_DRIVER_TYPE_B;
- int drive_strength;
+ int card_drv_type, drive_strength, drv_type;
int err;
- /*
- * If the host doesn't support any of the Driver Types A,C or D,
- * or there is no board specific handler then default Driver
- * Type B is used.
- */
- if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C
- | MMC_CAP_DRIVER_TYPE_D)))
- return 0;
-
- if (!card->host->ops->select_drive_strength)
- return 0;
-
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
- host_drv_type |= SD_DRIVER_TYPE_A;
-
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
- host_drv_type |= SD_DRIVER_TYPE_C;
-
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
- host_drv_type |= SD_DRIVER_TYPE_D;
-
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
- card_drv_type |= SD_DRIVER_TYPE_A;
-
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
- card_drv_type |= SD_DRIVER_TYPE_C;
+ card->drive_strength = 0;
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
- card_drv_type |= SD_DRIVER_TYPE_D;
+ card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
- /*
- * The drive strength that the hardware can support
- * depends on the board design. Pass the appropriate
- * information and let the hardware specific code
- * return what is possible given the options
- */
- mmc_host_clk_hold(card->host);
- drive_strength = card->host->ops->select_drive_strength(
- card->sw_caps.uhs_max_dtr,
- host_drv_type, card_drv_type);
- mmc_host_clk_release(card->host);
-
- err = mmc_sd_switch(card, 1, 2, drive_strength, status);
- if (err)
- return err;
+ drive_strength = mmc_select_drive_strength(card,
+ card->sw_caps.uhs_max_dtr,
+ card_drv_type, &drv_type);
- if ((status[15] & 0xF) != drive_strength) {
- pr_warn("%s: Problem setting drive strength!\n",
- mmc_hostname(card->host));
- return 0;
+ if (drive_strength) {
+ err = mmc_sd_switch(card, 1, 2, drive_strength, status);
+ if (err)
+ return err;
+ if ((status[15] & 0xF) != drive_strength) {
+ pr_warn("%s: Problem setting drive strength!\n",
+ mmc_hostname(card->host));
+ return 0;
+ }
+ card->drive_strength = drive_strength;
}
- mmc_set_driver_type(card->host, drive_strength);
+ if (drv_type)
+ mmc_set_driver_type(card->host, drv_type);
return 0;
}
@@ -804,6 +771,28 @@ int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card)
return 0;
}
+static int mmc_sd_get_ro(struct mmc_host *host)
+{
+ int ro;
+
+ /*
+ * Some systems don't feature a write-protect pin and don't need one.
+ * E.g. because they only have micro-SD card slot. For those systems
+ * assume that the SD card is always read-write.
+ */
+ if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
+ return 0;
+
+ if (!host->ops->get_ro)
+ return -1;
+
+ mmc_host_clk_hold(host);
+ ro = host->ops->get_ro(host);
+ mmc_host_clk_release(host);
+
+ return ro;
+}
+
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
bool reinit)
{
@@ -855,13 +844,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
* Check if read-only switch is active.
*/
if (!reinit) {
- int ro = -1;
-
- if (host->ops->get_ro) {
- mmc_host_clk_hold(card->host);
- ro = host->ops->get_ro(host);
- mmc_host_clk_release(card->host);
- }
+ int ro = mmc_sd_get_ro(host);
if (ro < 0) {
pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n",
@@ -1181,21 +1164,10 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
return 0;
}
-static int mmc_sd_power_restore(struct mmc_host *host)
-{
- int ret;
-
- mmc_claim_host(host);
- ret = mmc_sd_init_card(host, host->card->ocr, host->card);
- mmc_release_host(host);
-
- return ret;
-}
-
static int mmc_sd_reset(struct mmc_host *host)
{
mmc_power_cycle(host, host->card->ocr);
- return mmc_sd_power_restore(host);
+ return mmc_sd_init_card(host, host->card->ocr, host->card);
}
static const struct mmc_bus_ops mmc_sd_ops = {
@@ -1205,7 +1177,6 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.runtime_resume = mmc_sd_runtime_resume,
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
- .power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive,
.shutdown = mmc_sd_suspend,
.reset = mmc_sd_reset,
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 5bc6c7dbbd60..b91abedcfdca 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -402,69 +402,38 @@ static unsigned char host_drive_to_sdio_drive(int host_strength)
static void sdio_select_driver_type(struct mmc_card *card)
{
- int host_drv_type = SD_DRIVER_TYPE_B;
- int card_drv_type = SD_DRIVER_TYPE_B;
- int drive_strength;
+ int card_drv_type, drive_strength, drv_type;
unsigned char card_strength;
int err;
- /*
- * If the host doesn't support any of the Driver Types A,C or D,
- * or there is no board specific handler then default Driver
- * Type B is used.
- */
- if (!(card->host->caps &
- (MMC_CAP_DRIVER_TYPE_A |
- MMC_CAP_DRIVER_TYPE_C |
- MMC_CAP_DRIVER_TYPE_D)))
- return;
-
- if (!card->host->ops->select_drive_strength)
- return;
-
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
- host_drv_type |= SD_DRIVER_TYPE_A;
-
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
- host_drv_type |= SD_DRIVER_TYPE_C;
+ card->drive_strength = 0;
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
- host_drv_type |= SD_DRIVER_TYPE_D;
+ card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
- card_drv_type |= SD_DRIVER_TYPE_A;
-
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
- card_drv_type |= SD_DRIVER_TYPE_C;
-
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
- card_drv_type |= SD_DRIVER_TYPE_D;
-
- /*
- * The drive strength that the hardware can support
- * depends on the board design. Pass the appropriate
- * information and let the hardware specific code
- * return what is possible given the options
- */
- drive_strength = card->host->ops->select_drive_strength(
- card->sw_caps.uhs_max_dtr,
- host_drv_type, card_drv_type);
+ drive_strength = mmc_select_drive_strength(card,
+ card->sw_caps.uhs_max_dtr,
+ card_drv_type, &drv_type);
- /* if error just use default for drive strength B */
- err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
- &card_strength);
- if (err)
- return;
+ if (drive_strength) {
+ /* if error just use default for drive strength B */
+ err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
+ &card_strength);
+ if (err)
+ return;
- card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
- card_strength |= host_drive_to_sdio_drive(drive_strength);
+ card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
+ card_strength |= host_drive_to_sdio_drive(drive_strength);
- err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
- card_strength, NULL);
+ /* if error default to drive strength B */
+ err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
+ card_strength, NULL);
+ if (err)
+ return;
+ card->drive_strength = drive_strength;
+ }
- /* if error default to drive strength B */
- if (!err)
- mmc_set_driver_type(card->host, drive_strength);
+ if (drv_type)
+ mmc_set_driver_type(card->host, drv_type);
}
@@ -934,8 +903,12 @@ static int mmc_sdio_suspend(struct mmc_host *host)
mmc_release_host(host);
}
- if (!mmc_card_keep_power(host))
+ if (!mmc_card_keep_power(host)) {
mmc_power_off(host);
+ } else if (host->retune_period) {
+ mmc_retune_timer_stop(host);
+ mmc_retune_needed(host);
+ }
return 0;
}
@@ -1056,6 +1029,12 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host)
return mmc_sdio_power_restore(host);
}
+static int mmc_sdio_reset(struct mmc_host *host)
+{
+ mmc_power_cycle(host, host->card->ocr);
+ return mmc_sdio_power_restore(host);
+}
+
static const struct mmc_bus_ops mmc_sdio_ops = {
.remove = mmc_sdio_remove,
.detect = mmc_sdio_detect,
@@ -1066,6 +1045,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
.runtime_resume = mmc_sdio_runtime_resume,
.power_restore = mmc_sdio_power_restore,
.alive = mmc_sdio_alive,
+ .reset = mmc_sdio_reset,
};
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index bee02e644d62..7e327a6dd53d 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -137,6 +137,10 @@ static int sdio_bus_probe(struct device *dev)
if (!id)
return -ENODEV;
+ ret = dev_pm_domain_attach(dev, false);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
/* Unbound SDIO functions are always suspended.
* During probe, the function is set active and the usage count
* is incremented. If the driver supports runtime PM,
@@ -166,6 +170,7 @@ static int sdio_bus_probe(struct device *dev)
disable_runtimepm:
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_noidle(dev);
+ dev_pm_domain_detach(dev, false);
return ret;
}
@@ -197,6 +202,8 @@ static int sdio_bus_remove(struct device *dev)
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_sync(dev);
+ dev_pm_domain_detach(dev, false);
+
return ret;
}
@@ -316,10 +323,8 @@ int sdio_add_func(struct sdio_func *func)
sdio_set_of_node(func);
sdio_acpi_set_handle(func);
ret = device_add(&func->dev);
- if (ret == 0) {
+ if (ret == 0)
sdio_func_set_present(func);
- dev_pm_domain_attach(&func->dev, false);
- }
return ret;
}
@@ -335,7 +340,6 @@ void sdio_remove_func(struct sdio_func *func)
if (!sdio_func_present(func))
return;
- dev_pm_domain_detach(&func->dev, false);
device_del(&func->dev);
of_node_put(func->dev.of_node);
put_device(&func->dev);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index b1f837e749fe..fd9a58e216a5 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -219,6 +219,7 @@ config MMC_SDHCI_SIRF
tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
depends on ARCH_SIRF
depends on MMC_SDHCI_PLTFM
+ select MMC_SDHCI_IO_ACCESSORS
help
This selects the SDHCI support for SiRF System-on-Chip devices.
@@ -775,3 +776,11 @@ config MMC_TOSHIBA_PCI
tristate "Toshiba Type A SD/MMC Card Interface Driver"
depends on PCI
help
+
+config MMC_MTK
+ tristate "MediaTek SD/MMC Card Interface support"
+ help
+ This selects the MediaTek(R) Secure digital and Multimedia card Interface.
+ If you have a machine with a integrated SD/MMC card reader, say Y or M here.
+ This is needed if support for any SD/SDIO/MMC devices is required.
+ If unsure, say N.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e3ab5b968651..e928d61c5f4b 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
+obj-$(CONFIG_MMC_MTK) += mtk-sd.o
obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 03d7c7521d97..9a39e0b7e583 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -1304,7 +1304,7 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->clock) {
unsigned int clock_min = ~0U;
- u32 clkdiv;
+ int clkdiv;
spin_lock_bh(&host->lock);
if (!host->mode_reg) {
@@ -1328,7 +1328,12 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
/* Calculate clock divider */
if (host->caps.has_odd_clk_div) {
clkdiv = DIV_ROUND_UP(host->bus_hz, clock_min) - 2;
- if (clkdiv > 511) {
+ if (clkdiv < 0) {
+ dev_warn(&mmc->class_dev,
+ "clock %u too fast; using %lu\n",
+ clock_min, host->bus_hz / 2);
+ clkdiv = 0;
+ } else if (clkdiv > 511) {
dev_warn(&mmc->class_dev,
"clock %u too slow; using %lu\n",
clock_min, host->bus_hz / (511 + 2));
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 1625f908dc70..ea2a2ebc6b91 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -1161,7 +1161,7 @@ static void __init init_mmcsd_host(struct mmc_davinci_host *host)
mmc_davinci_reset_ctrl(host, 0);
}
-static struct platform_device_id davinci_mmc_devtype[] = {
+static const struct platform_device_id davinci_mmc_devtype[] = {
{
.name = "dm6441-mmc",
.driver_data = MMC_CTLR_VERSION_1,
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index e761eb1b1441..1e75309898b7 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -556,4 +556,4 @@ module_platform_driver(dw_mci_exynos_pltfm_driver);
MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:dwmmc-exynos");
+MODULE_ALIAS("platform:dwmmc_exynos");
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
index 650f9cc3f7a6..63c2e2ed1288 100644
--- a/drivers/mmc/host/dw_mmc-k3.c
+++ b/drivers/mmc/host/dw_mmc-k3.c
@@ -8,16 +8,30 @@
* (at your option) any later version.
*/
-#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
#include <linux/mmc/host.h>
#include <linux/mmc/dw_mmc.h>
+#include <linux/module.h>
#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
+/*
+ * hi6220 sd only support io voltage 1.8v and 3v
+ * Also need config AO_SCTRL_SEL18 accordingly
+ */
+#define AO_SCTRL_SEL18 BIT(10)
+#define AO_SCTRL_CTRL3 0x40C
+
+struct k3_priv {
+ struct regmap *reg;
+};
+
static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
int ret;
@@ -33,8 +47,93 @@ static const struct dw_mci_drv_data k3_drv_data = {
.set_ios = dw_mci_k3_set_ios,
};
+static int dw_mci_hi6220_parse_dt(struct dw_mci *host)
+{
+ struct k3_priv *priv;
+
+ priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->reg = syscon_regmap_lookup_by_phandle(host->dev->of_node,
+ "hisilicon,peripheral-syscon");
+ if (IS_ERR(priv->reg))
+ priv->reg = NULL;
+
+ host->priv = priv;
+ return 0;
+}
+
+static int dw_mci_hi6220_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct dw_mci_slot *slot = mmc_priv(mmc);
+ struct k3_priv *priv;
+ struct dw_mci *host;
+ int min_uv, max_uv;
+ int ret;
+
+ host = slot->host;
+ priv = host->priv;
+
+ if (!priv || !priv->reg)
+ return 0;
+
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
+ ret = regmap_update_bits(priv->reg, AO_SCTRL_CTRL3,
+ AO_SCTRL_SEL18, 0);
+ min_uv = 3000000;
+ max_uv = 3000000;
+ } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+ ret = regmap_update_bits(priv->reg, AO_SCTRL_CTRL3,
+ AO_SCTRL_SEL18, AO_SCTRL_SEL18);
+ min_uv = 1800000;
+ max_uv = 1800000;
+ } else {
+ dev_dbg(host->dev, "voltage not supported\n");
+ return -EINVAL;
+ }
+
+ if (ret) {
+ dev_dbg(host->dev, "switch voltage failed\n");
+ return ret;
+ }
+
+ if (IS_ERR_OR_NULL(mmc->supply.vqmmc))
+ return 0;
+
+ ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
+ if (ret) {
+ dev_dbg(host->dev, "Regulator set error %d: %d - %d\n",
+ ret, min_uv, max_uv);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+ int ret;
+ unsigned int clock;
+
+ clock = (ios->clock <= 25000000) ? 25000000 : ios->clock;
+
+ ret = clk_set_rate(host->biu_clk, clock);
+ if (ret)
+ dev_warn(host->dev, "failed to set rate %uHz\n", clock);
+
+ host->bus_hz = clk_get_rate(host->biu_clk);
+}
+
+static const struct dw_mci_drv_data hi6220_data = {
+ .switch_voltage = dw_mci_hi6220_switch_voltage,
+ .set_ios = dw_mci_hi6220_set_ios,
+ .parse_dt = dw_mci_hi6220_parse_dt,
+};
+
static const struct of_device_id dw_mci_k3_match[] = {
{ .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, },
+ { .compatible = "hisilicon,hi6220-dw-mshc", .data = &hi6220_data, },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_k3_match);
@@ -94,4 +193,4 @@ module_platform_driver(dw_mci_k3_pltfm_driver);
MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:dwmmc-k3");
+MODULE_ALIAS("platform:dwmmc_k3");
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index dbf166f94f1b..de15121bba7d 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -153,5 +153,5 @@ module_platform_driver(dw_mci_rockchip_pltfm_driver);
MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension");
-MODULE_ALIAS("platform:dwmmc-rockchip");
+MODULE_ALIAS("platform:dwmmc_rockchip");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 38b29265cc7c..40e9d8e45f25 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -589,9 +589,11 @@ static int dw_mci_idmac_init(struct dw_mci *host)
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
/* Forward link the descriptor list */
- for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
+ for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) {
p->des3 = cpu_to_le32(host->sg_dma +
(sizeof(struct idmac_desc) * (i + 1)));
+ p->des1 = 0;
+ }
/* Set the last descriptor as the end-of-ring descriptor */
p->des3 = cpu_to_le32(host->sg_dma);
@@ -1234,11 +1236,15 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
+ const struct dw_mci_drv_data *drv_data = host->drv_data;
u32 uhs;
u32 v18 = SDMMC_UHS_18V << slot->id;
int min_uv, max_uv;
int ret;
+ if (drv_data && drv_data->switch_voltage)
+ return drv_data->switch_voltage(mmc, ios);
+
/*
* Program the voltage. Note that some instances of dw_mmc may use
* the UHS_REG for this. For other instances (like exynos) the UHS_REG
@@ -1276,10 +1282,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
int gpio_ro = mmc_gpio_get_ro(mmc);
/* Use platform get_ro function, else try on board write protect */
- if ((slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) ||
- (slot->host->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT))
- read_only = 0;
- else if (!IS_ERR_VALUE(gpio_ro))
+ if (!IS_ERR_VALUE(gpio_ro))
read_only = gpio_ro;
else
read_only =
@@ -1300,7 +1303,8 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
int gpio_cd = mmc_gpio_get_cd(mmc);
/* Use platform get_cd function, else try onboard card detect */
- if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
+ if ((brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) ||
+ (mmc->caps & MMC_CAP_NONREMOVABLE))
present = 1;
else if (!IS_ERR_VALUE(gpio_cd))
present = gpio_cd;
@@ -2277,9 +2281,10 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
}
#ifdef CONFIG_OF
-/* given a slot id, find out the device node representing that slot */
-static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
+/* given a slot, find out the device node representing that slot */
+static struct device_node *dw_mci_of_find_slot_node(struct dw_mci_slot *slot)
{
+ struct device *dev = slot->mmc->parent;
struct device_node *np;
const __be32 *addr;
int len;
@@ -2291,42 +2296,28 @@ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
addr = of_get_property(np, "reg", &len);
if (!addr || (len < sizeof(int)))
continue;
- if (be32_to_cpup(addr) == slot)
+ if (be32_to_cpup(addr) == slot->id)
return np;
}
return NULL;
}
-static struct dw_mci_of_slot_quirks {
- char *quirk;
- int id;
-} of_slot_quirks[] = {
- {
- .quirk = "disable-wp",
- .id = DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT,
- },
-};
-
-static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
+static void dw_mci_slot_of_parse(struct dw_mci_slot *slot)
{
- struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
- int quirks = 0;
- int idx;
+ struct device_node *np = dw_mci_of_find_slot_node(slot);
- /* get quirks */
- for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++)
- if (of_get_property(np, of_slot_quirks[idx].quirk, NULL)) {
- dev_warn(dev, "Slot quirk %s is deprecated\n",
- of_slot_quirks[idx].quirk);
- quirks |= of_slot_quirks[idx].id;
- }
+ if (!np)
+ return;
- return quirks;
+ if (of_property_read_bool(np, "disable-wp")) {
+ slot->mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
+ dev_warn(slot->mmc->parent,
+ "Slot quirk 'disable-wp' is deprecated\n");
+ }
}
#else /* CONFIG_OF */
-static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
+static void dw_mci_slot_of_parse(struct dw_mci_slot *slot)
{
- return 0;
}
#endif /* CONFIG_OF */
@@ -2349,8 +2340,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
slot->host = host;
host->slot[id] = slot;
- slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id);
-
mmc->ops = &dw_mci_ops;
if (of_property_read_u32_array(host->dev->of_node,
"clock-freq-min-max", freq, 2)) {
@@ -2388,6 +2377,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (host->pdata->caps2)
mmc->caps2 = host->pdata->caps2;
+ dw_mci_slot_of_parse(slot);
+
ret = mmc_of_parse(mmc);
if (ret)
goto err_host_allocated;
@@ -2615,9 +2606,6 @@ static struct dw_mci_of_quirks {
{
.quirk = "broken-cd",
.id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
- }, {
- .quirk = "disable-wp",
- .id = DW_MCI_QUIRK_NO_WRITE_PROTECT,
},
};
@@ -2938,15 +2926,15 @@ void dw_mci_remove(struct dw_mci *host)
{
int i;
- mci_writel(host, RINTSTS, 0xFFFFFFFF);
- mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
-
for (i = 0; i < host->num_slots; i++) {
dev_dbg(host->dev, "remove slot %d\n", i);
if (host->slot[i])
dw_mci_cleanup_slot(host->slot[i], i);
}
+ mci_writel(host, RINTSTS, 0xFFFFFFFF);
+ mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
/* disable clock to CIU */
mci_writel(host, CLKENA, 0);
mci_writel(host, CLKSRC, 0);
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index f45ab91de339..8ce4674730a6 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -227,7 +227,6 @@ extern int dw_mci_resume(struct dw_mci *host);
* struct dw_mci_slot - MMC slot state
* @mmc: The mmc_host representing this slot.
* @host: The MMC controller this slot is using.
- * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
* @ctype: Card type for this slot.
* @mrq: mmc_request currently being processed or waiting to be
* processed, or NULL when the slot is idle.
@@ -245,8 +244,6 @@ struct dw_mci_slot {
struct mmc_host *mmc;
struct dw_mci *host;
- int quirks;
-
u32 ctype;
struct mmc_request *mrq;
@@ -287,5 +284,7 @@ struct dw_mci_drv_data {
int (*execute_tuning)(struct dw_mci_slot *slot);
int (*prepare_hs400_tuning)(struct dw_mci *host,
struct mmc_ios *ios);
+ int (*switch_voltage)(struct mmc_host *mmc,
+ struct mmc_ios *ios);
};
#endif /* _DW_MMC_H_ */
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
new file mode 100644
index 000000000000..7153500dd007
--- /dev/null
+++ b/drivers/mmc/host/mtk-sd.c
@@ -0,0 +1,1462 @@
+/*
+ * Copyright (c) 2014-2015 MediaTek Inc.
+ * Author: Chaotian.Jing <chaotian.jing@mediatek.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.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+
+#define MAX_BD_NUM 1024
+
+/*--------------------------------------------------------------------------*/
+/* Common Definition */
+/*--------------------------------------------------------------------------*/
+#define MSDC_BUS_1BITS 0x0
+#define MSDC_BUS_4BITS 0x1
+#define MSDC_BUS_8BITS 0x2
+
+#define MSDC_BURST_64B 0x6
+
+/*--------------------------------------------------------------------------*/
+/* Register Offset */
+/*--------------------------------------------------------------------------*/
+#define MSDC_CFG 0x0
+#define MSDC_IOCON 0x04
+#define MSDC_PS 0x08
+#define MSDC_INT 0x0c
+#define MSDC_INTEN 0x10
+#define MSDC_FIFOCS 0x14
+#define SDC_CFG 0x30
+#define SDC_CMD 0x34
+#define SDC_ARG 0x38
+#define SDC_STS 0x3c
+#define SDC_RESP0 0x40
+#define SDC_RESP1 0x44
+#define SDC_RESP2 0x48
+#define SDC_RESP3 0x4c
+#define SDC_BLK_NUM 0x50
+#define SDC_ACMD_RESP 0x80
+#define MSDC_DMA_SA 0x90
+#define MSDC_DMA_CTRL 0x98
+#define MSDC_DMA_CFG 0x9c
+#define MSDC_PATCH_BIT 0xb0
+#define MSDC_PATCH_BIT1 0xb4
+#define MSDC_PAD_TUNE 0xec
+
+/*--------------------------------------------------------------------------*/
+/* Register Mask */
+/*--------------------------------------------------------------------------*/
+
+/* MSDC_CFG mask */
+#define MSDC_CFG_MODE (0x1 << 0) /* RW */
+#define MSDC_CFG_CKPDN (0x1 << 1) /* RW */
+#define MSDC_CFG_RST (0x1 << 2) /* RW */
+#define MSDC_CFG_PIO (0x1 << 3) /* RW */
+#define MSDC_CFG_CKDRVEN (0x1 << 4) /* RW */
+#define MSDC_CFG_BV18SDT (0x1 << 5) /* RW */
+#define MSDC_CFG_BV18PSS (0x1 << 6) /* R */
+#define MSDC_CFG_CKSTB (0x1 << 7) /* R */
+#define MSDC_CFG_CKDIV (0xff << 8) /* RW */
+#define MSDC_CFG_CKMOD (0x3 << 16) /* RW */
+
+/* MSDC_IOCON mask */
+#define MSDC_IOCON_SDR104CKS (0x1 << 0) /* RW */
+#define MSDC_IOCON_RSPL (0x1 << 1) /* RW */
+#define MSDC_IOCON_DSPL (0x1 << 2) /* RW */
+#define MSDC_IOCON_DDLSEL (0x1 << 3) /* RW */
+#define MSDC_IOCON_DDR50CKD (0x1 << 4) /* RW */
+#define MSDC_IOCON_DSPLSEL (0x1 << 5) /* RW */
+#define MSDC_IOCON_W_DSPL (0x1 << 8) /* RW */
+#define MSDC_IOCON_D0SPL (0x1 << 16) /* RW */
+#define MSDC_IOCON_D1SPL (0x1 << 17) /* RW */
+#define MSDC_IOCON_D2SPL (0x1 << 18) /* RW */
+#define MSDC_IOCON_D3SPL (0x1 << 19) /* RW */
+#define MSDC_IOCON_D4SPL (0x1 << 20) /* RW */
+#define MSDC_IOCON_D5SPL (0x1 << 21) /* RW */
+#define MSDC_IOCON_D6SPL (0x1 << 22) /* RW */
+#define MSDC_IOCON_D7SPL (0x1 << 23) /* RW */
+#define MSDC_IOCON_RISCSZ (0x3 << 24) /* RW */
+
+/* MSDC_PS mask */
+#define MSDC_PS_CDEN (0x1 << 0) /* RW */
+#define MSDC_PS_CDSTS (0x1 << 1) /* R */
+#define MSDC_PS_CDDEBOUNCE (0xf << 12) /* RW */
+#define MSDC_PS_DAT (0xff << 16) /* R */
+#define MSDC_PS_CMD (0x1 << 24) /* R */
+#define MSDC_PS_WP (0x1 << 31) /* R */
+
+/* MSDC_INT mask */
+#define MSDC_INT_MMCIRQ (0x1 << 0) /* W1C */
+#define MSDC_INT_CDSC (0x1 << 1) /* W1C */
+#define MSDC_INT_ACMDRDY (0x1 << 3) /* W1C */
+#define MSDC_INT_ACMDTMO (0x1 << 4) /* W1C */
+#define MSDC_INT_ACMDCRCERR (0x1 << 5) /* W1C */
+#define MSDC_INT_DMAQ_EMPTY (0x1 << 6) /* W1C */
+#define MSDC_INT_SDIOIRQ (0x1 << 7) /* W1C */
+#define MSDC_INT_CMDRDY (0x1 << 8) /* W1C */
+#define MSDC_INT_CMDTMO (0x1 << 9) /* W1C */
+#define MSDC_INT_RSPCRCERR (0x1 << 10) /* W1C */
+#define MSDC_INT_CSTA (0x1 << 11) /* R */
+#define MSDC_INT_XFER_COMPL (0x1 << 12) /* W1C */
+#define MSDC_INT_DXFER_DONE (0x1 << 13) /* W1C */
+#define MSDC_INT_DATTMO (0x1 << 14) /* W1C */
+#define MSDC_INT_DATCRCERR (0x1 << 15) /* W1C */
+#define MSDC_INT_ACMD19_DONE (0x1 << 16) /* W1C */
+#define MSDC_INT_DMA_BDCSERR (0x1 << 17) /* W1C */
+#define MSDC_INT_DMA_GPDCSERR (0x1 << 18) /* W1C */
+#define MSDC_INT_DMA_PROTECT (0x1 << 19) /* W1C */
+
+/* MSDC_INTEN mask */
+#define MSDC_INTEN_MMCIRQ (0x1 << 0) /* RW */
+#define MSDC_INTEN_CDSC (0x1 << 1) /* RW */
+#define MSDC_INTEN_ACMDRDY (0x1 << 3) /* RW */
+#define MSDC_INTEN_ACMDTMO (0x1 << 4) /* RW */
+#define MSDC_INTEN_ACMDCRCERR (0x1 << 5) /* RW */
+#define MSDC_INTEN_DMAQ_EMPTY (0x1 << 6) /* RW */
+#define MSDC_INTEN_SDIOIRQ (0x1 << 7) /* RW */
+#define MSDC_INTEN_CMDRDY (0x1 << 8) /* RW */
+#define MSDC_INTEN_CMDTMO (0x1 << 9) /* RW */
+#define MSDC_INTEN_RSPCRCERR (0x1 << 10) /* RW */
+#define MSDC_INTEN_CSTA (0x1 << 11) /* RW */
+#define MSDC_INTEN_XFER_COMPL (0x1 << 12) /* RW */
+#define MSDC_INTEN_DXFER_DONE (0x1 << 13) /* RW */
+#define MSDC_INTEN_DATTMO (0x1 << 14) /* RW */
+#define MSDC_INTEN_DATCRCERR (0x1 << 15) /* RW */
+#define MSDC_INTEN_ACMD19_DONE (0x1 << 16) /* RW */
+#define MSDC_INTEN_DMA_BDCSERR (0x1 << 17) /* RW */
+#define MSDC_INTEN_DMA_GPDCSERR (0x1 << 18) /* RW */
+#define MSDC_INTEN_DMA_PROTECT (0x1 << 19) /* RW */
+
+/* MSDC_FIFOCS mask */
+#define MSDC_FIFOCS_RXCNT (0xff << 0) /* R */
+#define MSDC_FIFOCS_TXCNT (0xff << 16) /* R */
+#define MSDC_FIFOCS_CLR (0x1 << 31) /* RW */
+
+/* SDC_CFG mask */
+#define SDC_CFG_SDIOINTWKUP (0x1 << 0) /* RW */
+#define SDC_CFG_INSWKUP (0x1 << 1) /* RW */
+#define SDC_CFG_BUSWIDTH (0x3 << 16) /* RW */
+#define SDC_CFG_SDIO (0x1 << 19) /* RW */
+#define SDC_CFG_SDIOIDE (0x1 << 20) /* RW */
+#define SDC_CFG_INTATGAP (0x1 << 21) /* RW */
+#define SDC_CFG_DTOC (0xff << 24) /* RW */
+
+/* SDC_STS mask */
+#define SDC_STS_SDCBUSY (0x1 << 0) /* RW */
+#define SDC_STS_CMDBUSY (0x1 << 1) /* RW */
+#define SDC_STS_SWR_COMPL (0x1 << 31) /* RW */
+
+/* MSDC_DMA_CTRL mask */
+#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */
+#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */
+#define MSDC_DMA_CTRL_RESUME (0x1 << 2) /* W */
+#define MSDC_DMA_CTRL_MODE (0x1 << 8) /* RW */
+#define MSDC_DMA_CTRL_LASTBUF (0x1 << 10) /* RW */
+#define MSDC_DMA_CTRL_BRUSTSZ (0x7 << 12) /* RW */
+
+/* MSDC_DMA_CFG mask */
+#define MSDC_DMA_CFG_STS (0x1 << 0) /* R */
+#define MSDC_DMA_CFG_DECSEN (0x1 << 1) /* RW */
+#define MSDC_DMA_CFG_AHBHPROT2 (0x2 << 8) /* RW */
+#define MSDC_DMA_CFG_ACTIVEEN (0x2 << 12) /* RW */
+#define MSDC_DMA_CFG_CS12B16B (0x1 << 16) /* RW */
+
+/* MSDC_PATCH_BIT mask */
+#define MSDC_PATCH_BIT_ODDSUPP (0x1 << 1) /* RW */
+#define MSDC_INT_DAT_LATCH_CK_SEL (0x7 << 7)
+#define MSDC_CKGEN_MSDC_DLY_SEL (0x1f << 10)
+#define MSDC_PATCH_BIT_IODSSEL (0x1 << 16) /* RW */
+#define MSDC_PATCH_BIT_IOINTSEL (0x1 << 17) /* RW */
+#define MSDC_PATCH_BIT_BUSYDLY (0xf << 18) /* RW */
+#define MSDC_PATCH_BIT_WDOD (0xf << 22) /* RW */
+#define MSDC_PATCH_BIT_IDRTSEL (0x1 << 26) /* RW */
+#define MSDC_PATCH_BIT_CMDFSEL (0x1 << 27) /* RW */
+#define MSDC_PATCH_BIT_INTDLSEL (0x1 << 28) /* RW */
+#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */
+#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */
+
+#define REQ_CMD_EIO (0x1 << 0)
+#define REQ_CMD_TMO (0x1 << 1)
+#define REQ_DAT_ERR (0x1 << 2)
+#define REQ_STOP_EIO (0x1 << 3)
+#define REQ_STOP_TMO (0x1 << 4)
+#define REQ_CMD_BUSY (0x1 << 5)
+
+#define MSDC_PREPARE_FLAG (0x1 << 0)
+#define MSDC_ASYNC_FLAG (0x1 << 1)
+#define MSDC_MMAP_FLAG (0x1 << 2)
+
+#define MTK_MMC_AUTOSUSPEND_DELAY 50
+#define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */
+#define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */
+
+/*--------------------------------------------------------------------------*/
+/* Descriptor Structure */
+/*--------------------------------------------------------------------------*/
+struct mt_gpdma_desc {
+ u32 gpd_info;
+#define GPDMA_DESC_HWO (0x1 << 0)
+#define GPDMA_DESC_BDP (0x1 << 1)
+#define GPDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */
+#define GPDMA_DESC_INT (0x1 << 16)
+ u32 next;
+ u32 ptr;
+ u32 gpd_data_len;
+#define GPDMA_DESC_BUFLEN (0xffff) /* bit0 ~ bit15 */
+#define GPDMA_DESC_EXTLEN (0xff << 16) /* bit16 ~ bit23 */
+ u32 arg;
+ u32 blknum;
+ u32 cmd;
+};
+
+struct mt_bdma_desc {
+ u32 bd_info;
+#define BDMA_DESC_EOL (0x1 << 0)
+#define BDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */
+#define BDMA_DESC_BLKPAD (0x1 << 17)
+#define BDMA_DESC_DWPAD (0x1 << 18)
+ u32 next;
+ u32 ptr;
+ u32 bd_data_len;
+#define BDMA_DESC_BUFLEN (0xffff) /* bit0 ~ bit15 */
+};
+
+struct msdc_dma {
+ struct scatterlist *sg; /* I/O scatter list */
+ struct mt_gpdma_desc *gpd; /* pointer to gpd array */
+ struct mt_bdma_desc *bd; /* pointer to bd array */
+ dma_addr_t gpd_addr; /* the physical address of gpd array */
+ dma_addr_t bd_addr; /* the physical address of bd array */
+};
+
+struct msdc_save_para {
+ u32 msdc_cfg;
+ u32 iocon;
+ u32 sdc_cfg;
+ u32 pad_tune;
+ u32 patch_bit0;
+ u32 patch_bit1;
+};
+
+struct msdc_host {
+ struct device *dev;
+ struct mmc_host *mmc; /* mmc structure */
+ int cmd_rsp;
+
+ spinlock_t lock;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ int error;
+
+ void __iomem *base; /* host base address */
+
+ struct msdc_dma dma; /* dma channel */
+ u64 dma_mask;
+
+ u32 timeout_ns; /* data timeout ns */
+ u32 timeout_clks; /* data timeout clks */
+
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_default;
+ struct pinctrl_state *pins_uhs;
+ struct delayed_work req_timeout;
+ int irq; /* host interrupt */
+
+ struct clk *src_clk; /* msdc source clock */
+ struct clk *h_clk; /* msdc h_clk */
+ u32 mclk; /* mmc subsystem clock frequency */
+ u32 src_clk_freq; /* source clock frequency */
+ u32 sclk; /* SD/MS bus clock frequency */
+ bool ddr;
+ bool vqmmc_enabled;
+ struct msdc_save_para save_para; /* used when gate HCLK */
+};
+
+static void sdr_set_bits(void __iomem *reg, u32 bs)
+{
+ u32 val = readl(reg);
+
+ val |= bs;
+ writel(val, reg);
+}
+
+static void sdr_clr_bits(void __iomem *reg, u32 bs)
+{
+ u32 val = readl(reg);
+
+ val &= ~bs;
+ writel(val, reg);
+}
+
+static void sdr_set_field(void __iomem *reg, u32 field, u32 val)
+{
+ unsigned int tv = readl(reg);
+
+ tv &= ~field;
+ tv |= ((val) << (ffs((unsigned int)field) - 1));
+ writel(tv, reg);
+}
+
+static void sdr_get_field(void __iomem *reg, u32 field, u32 *val)
+{
+ unsigned int tv = readl(reg);
+
+ *val = ((tv & field) >> (ffs((unsigned int)field) - 1));
+}
+
+static void msdc_reset_hw(struct msdc_host *host)
+{
+ u32 val;
+
+ sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_RST);
+ while (readl(host->base + MSDC_CFG) & MSDC_CFG_RST)
+ cpu_relax();
+
+ sdr_set_bits(host->base + MSDC_FIFOCS, MSDC_FIFOCS_CLR);
+ while (readl(host->base + MSDC_FIFOCS) & MSDC_FIFOCS_CLR)
+ cpu_relax();
+
+ val = readl(host->base + MSDC_INT);
+ writel(val, host->base + MSDC_INT);
+}
+
+static void msdc_cmd_next(struct msdc_host *host,
+ struct mmc_request *mrq, struct mmc_command *cmd);
+
+static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
+ MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR |
+ MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT;
+
+static u8 msdc_dma_calcs(u8 *buf, u32 len)
+{
+ u32 i, sum = 0;
+
+ for (i = 0; i < len; i++)
+ sum += buf[i];
+ return 0xff - (u8) sum;
+}
+
+static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
+ struct mmc_data *data)
+{
+ unsigned int j, dma_len;
+ dma_addr_t dma_address;
+ u32 dma_ctrl;
+ struct scatterlist *sg;
+ struct mt_gpdma_desc *gpd;
+ struct mt_bdma_desc *bd;
+
+ sg = data->sg;
+
+ gpd = dma->gpd;
+ bd = dma->bd;
+
+ /* modify gpd */
+ gpd->gpd_info |= GPDMA_DESC_HWO;
+ gpd->gpd_info |= GPDMA_DESC_BDP;
+ /* need to clear first. use these bits to calc checksum */
+ gpd->gpd_info &= ~GPDMA_DESC_CHECKSUM;
+ gpd->gpd_info |= msdc_dma_calcs((u8 *) gpd, 16) << 8;
+
+ /* modify bd */
+ for_each_sg(data->sg, sg, data->sg_count, j) {
+ dma_address = sg_dma_address(sg);
+ dma_len = sg_dma_len(sg);
+
+ /* init bd */
+ bd[j].bd_info &= ~BDMA_DESC_BLKPAD;
+ bd[j].bd_info &= ~BDMA_DESC_DWPAD;
+ bd[j].ptr = (u32)dma_address;
+ bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN;
+ bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN);
+
+ if (j == data->sg_count - 1) /* the last bd */
+ bd[j].bd_info |= BDMA_DESC_EOL;
+ else
+ bd[j].bd_info &= ~BDMA_DESC_EOL;
+
+ /* checksume need to clear first */
+ bd[j].bd_info &= ~BDMA_DESC_CHECKSUM;
+ bd[j].bd_info |= msdc_dma_calcs((u8 *)(&bd[j]), 16) << 8;
+ }
+
+ sdr_set_field(host->base + MSDC_DMA_CFG, MSDC_DMA_CFG_DECSEN, 1);
+ dma_ctrl = readl_relaxed(host->base + MSDC_DMA_CTRL);
+ dma_ctrl &= ~(MSDC_DMA_CTRL_BRUSTSZ | MSDC_DMA_CTRL_MODE);
+ dma_ctrl |= (MSDC_BURST_64B << 12 | 1 << 8);
+ writel_relaxed(dma_ctrl, host->base + MSDC_DMA_CTRL);
+ writel((u32)dma->gpd_addr, host->base + MSDC_DMA_SA);
+}
+
+static void msdc_prepare_data(struct msdc_host *host, struct mmc_request *mrq)
+{
+ struct mmc_data *data = mrq->data;
+
+ if (!(data->host_cookie & MSDC_PREPARE_FLAG)) {
+ bool read = (data->flags & MMC_DATA_READ) != 0;
+
+ data->host_cookie |= MSDC_PREPARE_FLAG;
+ data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len,
+ read ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ }
+}
+
+static void msdc_unprepare_data(struct msdc_host *host, struct mmc_request *mrq)
+{
+ struct mmc_data *data = mrq->data;
+
+ if (data->host_cookie & MSDC_ASYNC_FLAG)
+ return;
+
+ if (data->host_cookie & MSDC_PREPARE_FLAG) {
+ bool read = (data->flags & MMC_DATA_READ) != 0;
+
+ dma_unmap_sg(host->dev, data->sg, data->sg_len,
+ read ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ data->host_cookie &= ~MSDC_PREPARE_FLAG;
+ }
+}
+
+/* clock control primitives */
+static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
+{
+ u32 timeout, clk_ns;
+ u32 mode = 0;
+
+ host->timeout_ns = ns;
+ host->timeout_clks = clks;
+ if (host->sclk == 0) {
+ timeout = 0;
+ } else {
+ clk_ns = 1000000000UL / host->sclk;
+ timeout = (ns + clk_ns - 1) / clk_ns + clks;
+ /* in 1048576 sclk cycle unit */
+ timeout = (timeout + (0x1 << 20) - 1) >> 20;
+ sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &mode);
+ /*DDR mode will double the clk cycles for data timeout */
+ timeout = mode >= 2 ? timeout * 2 : timeout;
+ timeout = timeout > 1 ? timeout - 1 : 0;
+ timeout = timeout > 255 ? 255 : timeout;
+ }
+ sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, timeout);
+}
+
+static void msdc_gate_clock(struct msdc_host *host)
+{
+ clk_disable_unprepare(host->src_clk);
+ clk_disable_unprepare(host->h_clk);
+}
+
+static void msdc_ungate_clock(struct msdc_host *host)
+{
+ clk_prepare_enable(host->h_clk);
+ clk_prepare_enable(host->src_clk);
+ while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
+ cpu_relax();
+}
+
+static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
+{
+ u32 mode;
+ u32 flags;
+ u32 div;
+ u32 sclk;
+
+ if (!hz) {
+ dev_dbg(host->dev, "set mclk to 0\n");
+ host->mclk = 0;
+ sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
+ return;
+ }
+
+ flags = readl(host->base + MSDC_INTEN);
+ sdr_clr_bits(host->base + MSDC_INTEN, flags);
+ if (ddr) { /* may need to modify later */
+ mode = 0x2; /* ddr mode and use divisor */
+ if (hz >= (host->src_clk_freq >> 2)) {
+ div = 0; /* mean div = 1/4 */
+ sclk = host->src_clk_freq >> 2; /* sclk = clk / 4 */
+ } else {
+ div = (host->src_clk_freq + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (host->src_clk_freq >> 2) / div;
+ div = (div >> 1);
+ }
+ } else if (hz >= host->src_clk_freq) {
+ mode = 0x1; /* no divisor */
+ div = 0;
+ sclk = host->src_clk_freq;
+ } else {
+ mode = 0x0; /* use divisor */
+ if (hz >= (host->src_clk_freq >> 1)) {
+ div = 0; /* mean div = 1/2 */
+ sclk = host->src_clk_freq >> 1; /* sclk = clk / 2 */
+ } else {
+ div = (host->src_clk_freq + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (host->src_clk_freq >> 2) / div;
+ }
+ }
+ sdr_set_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD | MSDC_CFG_CKDIV,
+ (mode << 8) | (div % 0xff));
+ sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
+ while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
+ cpu_relax();
+ host->sclk = sclk;
+ host->mclk = hz;
+ host->ddr = ddr;
+ /* need because clk changed. */
+ msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
+ sdr_set_bits(host->base + MSDC_INTEN, flags);
+
+ dev_dbg(host->dev, "sclk: %d, ddr: %d\n", host->sclk, ddr);
+}
+
+static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
+ struct mmc_request *mrq, struct mmc_command *cmd)
+{
+ u32 resp;
+
+ switch (mmc_resp_type(cmd)) {
+ /* Actually, R1, R5, R6, R7 are the same */
+ case MMC_RSP_R1:
+ resp = 0x1;
+ break;
+ case MMC_RSP_R1B:
+ resp = 0x7;
+ break;
+ case MMC_RSP_R2:
+ resp = 0x2;
+ break;
+ case MMC_RSP_R3:
+ resp = 0x3;
+ break;
+ case MMC_RSP_NONE:
+ default:
+ resp = 0x0;
+ break;
+ }
+
+ return resp;
+}
+
+static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
+ struct mmc_request *mrq, struct mmc_command *cmd)
+{
+ /* rawcmd :
+ * vol_swt << 30 | auto_cmd << 28 | blklen << 16 | go_irq << 15 |
+ * stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
+ */
+ u32 opcode = cmd->opcode;
+ u32 resp = msdc_cmd_find_resp(host, mrq, cmd);
+ u32 rawcmd = (opcode & 0x3f) | ((resp & 0x7) << 7);
+
+ host->cmd_rsp = resp;
+
+ if ((opcode == SD_IO_RW_DIRECT && cmd->flags == (unsigned int) -1) ||
+ opcode == MMC_STOP_TRANSMISSION)
+ rawcmd |= (0x1 << 14);
+ else if (opcode == SD_SWITCH_VOLTAGE)
+ rawcmd |= (0x1 << 30);
+ else if (opcode == SD_APP_SEND_SCR ||
+ opcode == SD_APP_SEND_NUM_WR_BLKS ||
+ (opcode == SD_SWITCH && mmc_cmd_type(cmd) == MMC_CMD_ADTC) ||
+ (opcode == SD_APP_SD_STATUS && mmc_cmd_type(cmd) == MMC_CMD_ADTC) ||
+ (opcode == MMC_SEND_EXT_CSD && mmc_cmd_type(cmd) == MMC_CMD_ADTC))
+ rawcmd |= (0x1 << 11);
+
+ if (cmd->data) {
+ struct mmc_data *data = cmd->data;
+
+ if (mmc_op_multi(opcode)) {
+ if (mmc_card_mmc(host->mmc->card) && mrq->sbc &&
+ !(mrq->sbc->arg & 0xFFFF0000))
+ rawcmd |= 0x2 << 28; /* AutoCMD23 */
+ }
+
+ rawcmd |= ((data->blksz & 0xFFF) << 16);
+ if (data->flags & MMC_DATA_WRITE)
+ rawcmd |= (0x1 << 13);
+ if (data->blocks > 1)
+ rawcmd |= (0x2 << 11);
+ else
+ rawcmd |= (0x1 << 11);
+ /* Always use dma mode */
+ sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_PIO);
+
+ if (host->timeout_ns != data->timeout_ns ||
+ host->timeout_clks != data->timeout_clks)
+ msdc_set_timeout(host, data->timeout_ns,
+ data->timeout_clks);
+
+ writel(data->blocks, host->base + SDC_BLK_NUM);
+ }
+ return rawcmd;
+}
+
+static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq,
+ struct mmc_command *cmd, struct mmc_data *data)
+{
+ bool read;
+
+ WARN_ON(host->data);
+ host->data = data;
+ read = data->flags & MMC_DATA_READ;
+
+ mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
+ msdc_dma_setup(host, &host->dma, data);
+ sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask);
+ sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
+ dev_dbg(host->dev, "DMA start\n");
+ dev_dbg(host->dev, "%s: cmd=%d DMA data: %d blocks; read=%d\n",
+ __func__, cmd->opcode, data->blocks, read);
+}
+
+static int msdc_auto_cmd_done(struct msdc_host *host, int events,
+ struct mmc_command *cmd)
+{
+ u32 *rsp = cmd->resp;
+
+ rsp[0] = readl(host->base + SDC_ACMD_RESP);
+
+ if (events & MSDC_INT_ACMDRDY) {
+ cmd->error = 0;
+ } else {
+ msdc_reset_hw(host);
+ if (events & MSDC_INT_ACMDCRCERR) {
+ cmd->error = -EILSEQ;
+ host->error |= REQ_STOP_EIO;
+ } else if (events & MSDC_INT_ACMDTMO) {
+ cmd->error = -ETIMEDOUT;
+ host->error |= REQ_STOP_TMO;
+ }
+ dev_err(host->dev,
+ "%s: AUTO_CMD%d arg=%08X; rsp %08X; cmd_error=%d\n",
+ __func__, cmd->opcode, cmd->arg, rsp[0], cmd->error);
+ }
+ return cmd->error;
+}
+
+static void msdc_track_cmd_data(struct msdc_host *host,
+ struct mmc_command *cmd, struct mmc_data *data)
+{
+ if (host->error)
+ dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n",
+ __func__, cmd->opcode, cmd->arg, host->error);
+}
+
+static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
+{
+ unsigned long flags;
+ bool ret;
+
+ ret = cancel_delayed_work(&host->req_timeout);
+ if (!ret) {
+ /* delay work already running */
+ return;
+ }
+ spin_lock_irqsave(&host->lock, flags);
+ host->mrq = NULL;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ msdc_track_cmd_data(host, mrq->cmd, mrq->data);
+ if (mrq->data)
+ msdc_unprepare_data(host, mrq);
+ mmc_request_done(host->mmc, mrq);
+
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
+}
+
+/* returns true if command is fully handled; returns false otherwise */
+static bool msdc_cmd_done(struct msdc_host *host, int events,
+ struct mmc_request *mrq, struct mmc_command *cmd)
+{
+ bool done = false;
+ bool sbc_error;
+ unsigned long flags;
+ u32 *rsp = cmd->resp;
+
+ if (mrq->sbc && cmd == mrq->cmd &&
+ (events & (MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR
+ | MSDC_INT_ACMDTMO)))
+ msdc_auto_cmd_done(host, events, mrq->sbc);
+
+ sbc_error = mrq->sbc && mrq->sbc->error;
+
+ if (!sbc_error && !(events & (MSDC_INT_CMDRDY
+ | MSDC_INT_RSPCRCERR
+ | MSDC_INT_CMDTMO)))
+ return done;
+
+ spin_lock_irqsave(&host->lock, flags);
+ done = !host->cmd;
+ host->cmd = NULL;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (done)
+ return true;
+
+ sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
+ MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
+ MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
+ MSDC_INTEN_ACMDTMO);
+ writel(cmd->arg, host->base + SDC_ARG);
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ rsp[0] = readl(host->base + SDC_RESP3);
+ rsp[1] = readl(host->base + SDC_RESP2);
+ rsp[2] = readl(host->base + SDC_RESP1);
+ rsp[3] = readl(host->base + SDC_RESP0);
+ } else {
+ rsp[0] = readl(host->base + SDC_RESP0);
+ }
+ }
+
+ if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
+ msdc_reset_hw(host);
+ if (events & MSDC_INT_RSPCRCERR) {
+ cmd->error = -EILSEQ;
+ host->error |= REQ_CMD_EIO;
+ } else if (events & MSDC_INT_CMDTMO) {
+ cmd->error = -ETIMEDOUT;
+ host->error |= REQ_CMD_TMO;
+ }
+ }
+ if (cmd->error)
+ dev_dbg(host->dev,
+ "%s: cmd=%d arg=%08X; rsp %08X; cmd_error=%d\n",
+ __func__, cmd->opcode, cmd->arg, rsp[0],
+ cmd->error);
+
+ msdc_cmd_next(host, mrq, cmd);
+ return true;
+}
+
+/* It is the core layer's responsibility to ensure card status
+ * is correct before issue a request. but host design do below
+ * checks recommended.
+ */
+static inline bool msdc_cmd_is_ready(struct msdc_host *host,
+ struct mmc_request *mrq, struct mmc_command *cmd)
+{
+ /* The max busy time we can endure is 20ms */
+ unsigned long tmo = jiffies + msecs_to_jiffies(20);
+
+ while ((readl(host->base + SDC_STS) & SDC_STS_CMDBUSY) &&
+ time_before(jiffies, tmo))
+ cpu_relax();
+ if (readl(host->base + SDC_STS) & SDC_STS_CMDBUSY) {
+ dev_err(host->dev, "CMD bus busy detected\n");
+ host->error |= REQ_CMD_BUSY;
+ msdc_cmd_done(host, MSDC_INT_CMDTMO, mrq, cmd);
+ return false;
+ }
+
+ if (mmc_resp_type(cmd) == MMC_RSP_R1B || cmd->data) {
+ tmo = jiffies + msecs_to_jiffies(20);
+ /* R1B or with data, should check SDCBUSY */
+ while ((readl(host->base + SDC_STS) & SDC_STS_SDCBUSY) &&
+ time_before(jiffies, tmo))
+ cpu_relax();
+ if (readl(host->base + SDC_STS) & SDC_STS_SDCBUSY) {
+ dev_err(host->dev, "Controller busy detected\n");
+ host->error |= REQ_CMD_BUSY;
+ msdc_cmd_done(host, MSDC_INT_CMDTMO, mrq, cmd);
+ return false;
+ }
+ }
+ return true;
+}
+
+static void msdc_start_command(struct msdc_host *host,
+ struct mmc_request *mrq, struct mmc_command *cmd)
+{
+ u32 rawcmd;
+
+ WARN_ON(host->cmd);
+ host->cmd = cmd;
+
+ if (!msdc_cmd_is_ready(host, mrq, cmd))
+ return;
+
+ if ((readl(host->base + MSDC_FIFOCS) & MSDC_FIFOCS_TXCNT) >> 16 ||
+ readl(host->base + MSDC_FIFOCS) & MSDC_FIFOCS_RXCNT) {
+ dev_err(host->dev, "TX/RX FIFO non-empty before start of IO. Reset\n");
+ msdc_reset_hw(host);
+ }
+
+ cmd->error = 0;
+ rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
+ mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
+
+ sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
+ MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
+ MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
+ MSDC_INTEN_ACMDTMO);
+ writel(cmd->arg, host->base + SDC_ARG);
+ writel(rawcmd, host->base + SDC_CMD);
+}
+
+static void msdc_cmd_next(struct msdc_host *host,
+ struct mmc_request *mrq, struct mmc_command *cmd)
+{
+ if (cmd->error || (mrq->sbc && mrq->sbc->error))
+ msdc_request_done(host, mrq);
+ else if (cmd == mrq->sbc)
+ msdc_start_command(host, mrq, mrq->cmd);
+ else if (!cmd->data)
+ msdc_request_done(host, mrq);
+ else
+ msdc_start_data(host, mrq, cmd, cmd->data);
+}
+
+static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+
+ host->error = 0;
+ WARN_ON(host->mrq);
+ host->mrq = mrq;
+
+ pm_runtime_get_sync(host->dev);
+
+ if (mrq->data)
+ msdc_prepare_data(host, mrq);
+
+ /* if SBC is required, we have HW option and SW option.
+ * if HW option is enabled, and SBC does not have "special" flags,
+ * use HW option, otherwise use SW option
+ */
+ if (mrq->sbc && (!mmc_card_mmc(mmc->card) ||
+ (mrq->sbc->arg & 0xFFFF0000)))
+ msdc_start_command(host, mrq, mrq->sbc);
+ else
+ msdc_start_command(host, mrq, mrq->cmd);
+}
+
+static void msdc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ bool is_first_req)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (!data)
+ return;
+
+ msdc_prepare_data(host, mrq);
+ data->host_cookie |= MSDC_ASYNC_FLAG;
+}
+
+static void msdc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ int err)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct mmc_data *data;
+
+ data = mrq->data;
+ if (!data)
+ return;
+ if (data->host_cookie) {
+ data->host_cookie &= ~MSDC_ASYNC_FLAG;
+ msdc_unprepare_data(host, mrq);
+ }
+}
+
+static void msdc_data_xfer_next(struct msdc_host *host,
+ struct mmc_request *mrq, struct mmc_data *data)
+{
+ if (mmc_op_multi(mrq->cmd->opcode) && mrq->stop && !mrq->stop->error &&
+ (!data->bytes_xfered || !mrq->sbc))
+ msdc_start_command(host, mrq, mrq->stop);
+ else
+ msdc_request_done(host, mrq);
+}
+
+static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
+ struct mmc_request *mrq, struct mmc_data *data)
+{
+ struct mmc_command *stop = data->stop;
+ unsigned long flags;
+ bool done;
+ unsigned int check_data = events &
+ (MSDC_INT_XFER_COMPL | MSDC_INT_DATCRCERR | MSDC_INT_DATTMO
+ | MSDC_INT_DMA_BDCSERR | MSDC_INT_DMA_GPDCSERR
+ | MSDC_INT_DMA_PROTECT);
+
+ spin_lock_irqsave(&host->lock, flags);
+ done = !host->data;
+ if (check_data)
+ host->data = NULL;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (done)
+ return true;
+
+ if (check_data || (stop && stop->error)) {
+ dev_dbg(host->dev, "DMA status: 0x%8X\n",
+ readl(host->base + MSDC_DMA_CFG));
+ sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP,
+ 1);
+ while (readl(host->base + MSDC_DMA_CFG) & MSDC_DMA_CFG_STS)
+ cpu_relax();
+ sdr_clr_bits(host->base + MSDC_INTEN, data_ints_mask);
+ dev_dbg(host->dev, "DMA stop\n");
+
+ if ((events & MSDC_INT_XFER_COMPL) && (!stop || !stop->error)) {
+ data->bytes_xfered = data->blocks * data->blksz;
+ } else {
+ dev_err(host->dev, "interrupt events: %x\n", events);
+ msdc_reset_hw(host);
+ host->error |= REQ_DAT_ERR;
+ data->bytes_xfered = 0;
+
+ if (events & MSDC_INT_DATTMO)
+ data->error = -ETIMEDOUT;
+
+ dev_err(host->dev, "%s: cmd=%d; blocks=%d",
+ __func__, mrq->cmd->opcode, data->blocks);
+ dev_err(host->dev, "data_error=%d xfer_size=%d\n",
+ (int)data->error, data->bytes_xfered);
+ }
+
+ msdc_data_xfer_next(host, mrq, data);
+ done = true;
+ }
+ return done;
+}
+
+static void msdc_set_buswidth(struct msdc_host *host, u32 width)
+{
+ u32 val = readl(host->base + SDC_CFG);
+
+ val &= ~SDC_CFG_BUSWIDTH;
+
+ switch (width) {
+ default:
+ case MMC_BUS_WIDTH_1:
+ val |= (MSDC_BUS_1BITS << 16);
+ break;
+ case MMC_BUS_WIDTH_4:
+ val |= (MSDC_BUS_4BITS << 16);
+ break;
+ case MMC_BUS_WIDTH_8:
+ val |= (MSDC_BUS_8BITS << 16);
+ break;
+ }
+
+ writel(val, host->base + SDC_CFG);
+ dev_dbg(host->dev, "Bus Width = %d", width);
+}
+
+static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ int min_uv, max_uv;
+ int ret = 0;
+
+ if (!IS_ERR(mmc->supply.vqmmc)) {
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
+ min_uv = 3300000;
+ max_uv = 3300000;
+ } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+ min_uv = 1800000;
+ max_uv = 1800000;
+ } else {
+ dev_err(host->dev, "Unsupported signal voltage!\n");
+ return -EINVAL;
+ }
+
+ ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
+ if (ret) {
+ dev_err(host->dev,
+ "Regulator set error %d: %d - %d\n",
+ ret, min_uv, max_uv);
+ } else {
+ /* Apply different pinctrl settings for different signal voltage */
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+ pinctrl_select_state(host->pinctrl, host->pins_uhs);
+ else
+ pinctrl_select_state(host->pinctrl, host->pins_default);
+ }
+ }
+ return ret;
+}
+
+static int msdc_card_busy(struct mmc_host *mmc)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 status = readl(host->base + MSDC_PS);
+
+ /* check if any pin between dat[0:3] is low */
+ if (((status >> 16) & 0xf) != 0xf)
+ return 1;
+
+ return 0;
+}
+
+static void msdc_request_timeout(struct work_struct *work)
+{
+ struct msdc_host *host = container_of(work, struct msdc_host,
+ req_timeout.work);
+
+ /* simulate HW timeout status */
+ dev_err(host->dev, "%s: aborting cmd/data/mrq\n", __func__);
+ if (host->mrq) {
+ dev_err(host->dev, "%s: aborting mrq=%p cmd=%d\n", __func__,
+ host->mrq, host->mrq->cmd->opcode);
+ if (host->cmd) {
+ dev_err(host->dev, "%s: aborting cmd=%d\n",
+ __func__, host->cmd->opcode);
+ msdc_cmd_done(host, MSDC_INT_CMDTMO, host->mrq,
+ host->cmd);
+ } else if (host->data) {
+ dev_err(host->dev, "%s: abort data: cmd%d; %d blocks\n",
+ __func__, host->mrq->cmd->opcode,
+ host->data->blocks);
+ msdc_data_xfer_done(host, MSDC_INT_DATTMO, host->mrq,
+ host->data);
+ }
+ }
+}
+
+static irqreturn_t msdc_irq(int irq, void *dev_id)
+{
+ struct msdc_host *host = (struct msdc_host *) dev_id;
+
+ while (true) {
+ unsigned long flags;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ u32 events, event_mask;
+
+ spin_lock_irqsave(&host->lock, flags);
+ events = readl(host->base + MSDC_INT);
+ event_mask = readl(host->base + MSDC_INTEN);
+ /* clear interrupts */
+ writel(events & event_mask, host->base + MSDC_INT);
+
+ mrq = host->mrq;
+ cmd = host->cmd;
+ data = host->data;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (!(events & event_mask))
+ break;
+
+ if (!mrq) {
+ dev_err(host->dev,
+ "%s: MRQ=NULL; events=%08X; event_mask=%08X\n",
+ __func__, events, event_mask);
+ WARN_ON(1);
+ break;
+ }
+
+ dev_dbg(host->dev, "%s: events=%08X\n", __func__, events);
+
+ if (cmd)
+ msdc_cmd_done(host, events, mrq, cmd);
+ else if (data)
+ msdc_data_xfer_done(host, events, mrq, data);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void msdc_init_hw(struct msdc_host *host)
+{
+ u32 val;
+
+ /* Configure to MMC/SD mode, clock free running */
+ sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_MODE | MSDC_CFG_CKPDN);
+
+ /* Reset */
+ msdc_reset_hw(host);
+
+ /* Disable card detection */
+ sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN);
+
+ /* Disable and clear all interrupts */
+ writel(0, host->base + MSDC_INTEN);
+ val = readl(host->base + MSDC_INT);
+ writel(val, host->base + MSDC_INT);
+
+ writel(0, host->base + MSDC_PAD_TUNE);
+ writel(0, host->base + MSDC_IOCON);
+ sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+ writel(0x403c004f, host->base + MSDC_PATCH_BIT);
+ sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
+ writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
+ /* Configure to enable SDIO mode.
+ * it's must otherwise sdio cmd5 failed
+ */
+ sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO);
+
+ /* disable detect SDIO device interrupt function */
+ sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
+
+ /* Configure to default data timeout */
+ sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3);
+
+ dev_dbg(host->dev, "init hardware done!");
+}
+
+static void msdc_deinit_hw(struct msdc_host *host)
+{
+ u32 val;
+ /* Disable and clear all interrupts */
+ writel(0, host->base + MSDC_INTEN);
+
+ val = readl(host->base + MSDC_INT);
+ writel(val, host->base + MSDC_INT);
+}
+
+/* init gpd and bd list in msdc_drv_probe */
+static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma)
+{
+ struct mt_gpdma_desc *gpd = dma->gpd;
+ struct mt_bdma_desc *bd = dma->bd;
+ int i;
+
+ memset(gpd, 0, sizeof(struct mt_gpdma_desc));
+
+ gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */
+ gpd->ptr = (u32)dma->bd_addr; /* physical address */
+
+ memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM);
+ for (i = 0; i < (MAX_BD_NUM - 1); i++)
+ bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1);
+}
+
+static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ int ret;
+ u32 ddr = 0;
+
+ pm_runtime_get_sync(host->dev);
+
+ if (ios->timing == MMC_TIMING_UHS_DDR50 ||
+ ios->timing == MMC_TIMING_MMC_DDR52)
+ ddr = 1;
+
+ msdc_set_buswidth(host, ios->bus_width);
+
+ /* Suspend/Resume will do power off/on */
+ switch (ios->power_mode) {
+ case MMC_POWER_UP:
+ if (!IS_ERR(mmc->supply.vmmc)) {
+ ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
+ ios->vdd);
+ if (ret) {
+ dev_err(host->dev, "Failed to set vmmc power!\n");
+ goto end;
+ }
+ }
+ break;
+ case MMC_POWER_ON:
+ if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
+ ret = regulator_enable(mmc->supply.vqmmc);
+ if (ret)
+ dev_err(host->dev, "Failed to set vqmmc power!\n");
+ else
+ host->vqmmc_enabled = true;
+ }
+ break;
+ case MMC_POWER_OFF:
+ if (!IS_ERR(mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+
+ if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
+ regulator_disable(mmc->supply.vqmmc);
+ host->vqmmc_enabled = false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (host->mclk != ios->clock || host->ddr != ddr)
+ msdc_set_mclk(host, ddr, ios->clock);
+
+end:
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
+}
+
+static struct mmc_host_ops mt_msdc_ops = {
+ .post_req = msdc_post_req,
+ .pre_req = msdc_pre_req,
+ .request = msdc_ops_request,
+ .set_ios = msdc_ops_set_ios,
+ .start_signal_voltage_switch = msdc_ops_switch_volt,
+ .card_busy = msdc_card_busy,
+};
+
+static int msdc_drv_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct msdc_host *host;
+ struct resource *res;
+ int ret;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "No DT found\n");
+ return -EINVAL;
+ }
+ /* Allocate MMC host for this device */
+ mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
+ if (!mmc)
+ return -ENOMEM;
+
+ host = mmc_priv(mmc);
+ ret = mmc_of_parse(mmc);
+ if (ret)
+ goto host_free;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->base)) {
+ ret = PTR_ERR(host->base);
+ goto host_free;
+ }
+
+ ret = mmc_regulator_get_supply(mmc);
+ if (ret == -EPROBE_DEFER)
+ goto host_free;
+
+ host->src_clk = devm_clk_get(&pdev->dev, "source");
+ if (IS_ERR(host->src_clk)) {
+ ret = PTR_ERR(host->src_clk);
+ goto host_free;
+ }
+
+ host->h_clk = devm_clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(host->h_clk)) {
+ ret = PTR_ERR(host->h_clk);
+ goto host_free;
+ }
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq < 0) {
+ ret = -EINVAL;
+ goto host_free;
+ }
+
+ host->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR(host->pinctrl)) {
+ ret = PTR_ERR(host->pinctrl);
+ dev_err(&pdev->dev, "Cannot find pinctrl!\n");
+ goto host_free;
+ }
+
+ host->pins_default = pinctrl_lookup_state(host->pinctrl, "default");
+ if (IS_ERR(host->pins_default)) {
+ ret = PTR_ERR(host->pins_default);
+ dev_err(&pdev->dev, "Cannot find pinctrl default!\n");
+ goto host_free;
+ }
+
+ host->pins_uhs = pinctrl_lookup_state(host->pinctrl, "state_uhs");
+ if (IS_ERR(host->pins_uhs)) {
+ ret = PTR_ERR(host->pins_uhs);
+ dev_err(&pdev->dev, "Cannot find pinctrl uhs!\n");
+ goto host_free;
+ }
+
+ host->dev = &pdev->dev;
+ host->mmc = mmc;
+ host->src_clk_freq = clk_get_rate(host->src_clk);
+ /* Set host parameters to mmc */
+ mmc->ops = &mt_msdc_ops;
+ mmc->f_min = host->src_clk_freq / (4 * 255);
+
+ mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
+ /* MMC core transfer sizes tunable parameters */
+ mmc->max_segs = MAX_BD_NUM;
+ mmc->max_seg_size = BDMA_DESC_BUFLEN;
+ mmc->max_blk_size = 2048;
+ mmc->max_req_size = 512 * 1024;
+ mmc->max_blk_count = mmc->max_req_size / 512;
+ host->dma_mask = DMA_BIT_MASK(32);
+ mmc_dev(mmc)->dma_mask = &host->dma_mask;
+
+ host->timeout_clks = 3 * 1048576;
+ host->dma.gpd = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct mt_gpdma_desc),
+ &host->dma.gpd_addr, GFP_KERNEL);
+ host->dma.bd = dma_alloc_coherent(&pdev->dev,
+ MAX_BD_NUM * sizeof(struct mt_bdma_desc),
+ &host->dma.bd_addr, GFP_KERNEL);
+ if (!host->dma.gpd || !host->dma.bd) {
+ ret = -ENOMEM;
+ goto release_mem;
+ }
+ msdc_init_gpd_bd(host, &host->dma);
+ INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout);
+ spin_lock_init(&host->lock);
+
+ platform_set_drvdata(pdev, mmc);
+ msdc_ungate_clock(host);
+ msdc_init_hw(host);
+
+ ret = devm_request_irq(&pdev->dev, host->irq, msdc_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT, pdev->name, host);
+ if (ret)
+ goto release;
+
+ pm_runtime_set_active(host->dev);
+ pm_runtime_set_autosuspend_delay(host->dev, MTK_MMC_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(host->dev);
+ pm_runtime_enable(host->dev);
+ ret = mmc_add_host(mmc);
+
+ if (ret)
+ goto end;
+
+ return 0;
+end:
+ pm_runtime_disable(host->dev);
+release:
+ platform_set_drvdata(pdev, NULL);
+ msdc_deinit_hw(host);
+ msdc_gate_clock(host);
+release_mem:
+ if (host->dma.gpd)
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct mt_gpdma_desc),
+ host->dma.gpd, host->dma.gpd_addr);
+ if (host->dma.bd)
+ dma_free_coherent(&pdev->dev,
+ MAX_BD_NUM * sizeof(struct mt_bdma_desc),
+ host->dma.bd, host->dma.bd_addr);
+host_free:
+ mmc_free_host(mmc);
+
+ return ret;
+}
+
+static int msdc_drv_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct msdc_host *host;
+
+ mmc = platform_get_drvdata(pdev);
+ host = mmc_priv(mmc);
+
+ pm_runtime_get_sync(host->dev);
+
+ platform_set_drvdata(pdev, NULL);
+ mmc_remove_host(host->mmc);
+ msdc_deinit_hw(host);
+ msdc_gate_clock(host);
+
+ pm_runtime_disable(host->dev);
+ pm_runtime_put_noidle(host->dev);
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct mt_gpdma_desc),
+ host->dma.gpd, host->dma.gpd_addr);
+ dma_free_coherent(&pdev->dev, MAX_BD_NUM * sizeof(struct mt_bdma_desc),
+ host->dma.bd, host->dma.bd_addr);
+
+ mmc_free_host(host->mmc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static void msdc_save_reg(struct msdc_host *host)
+{
+ host->save_para.msdc_cfg = readl(host->base + MSDC_CFG);
+ host->save_para.iocon = readl(host->base + MSDC_IOCON);
+ host->save_para.sdc_cfg = readl(host->base + SDC_CFG);
+ host->save_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
+ host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
+ host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
+}
+
+static void msdc_restore_reg(struct msdc_host *host)
+{
+ writel(host->save_para.msdc_cfg, host->base + MSDC_CFG);
+ writel(host->save_para.iocon, host->base + MSDC_IOCON);
+ writel(host->save_para.sdc_cfg, host->base + SDC_CFG);
+ writel(host->save_para.pad_tune, host->base + MSDC_PAD_TUNE);
+ writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
+ writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
+}
+
+static int msdc_runtime_suspend(struct device *dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct msdc_host *host = mmc_priv(mmc);
+
+ msdc_save_reg(host);
+ msdc_gate_clock(host);
+ return 0;
+}
+
+static int msdc_runtime_resume(struct device *dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct msdc_host *host = mmc_priv(mmc);
+
+ msdc_ungate_clock(host);
+ msdc_restore_reg(host);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops msdc_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL)
+};
+
+static const struct of_device_id msdc_of_ids[] = {
+ { .compatible = "mediatek,mt8135-mmc", },
+ {}
+};
+
+static struct platform_driver mt_msdc_driver = {
+ .probe = msdc_drv_probe,
+ .remove = msdc_drv_remove,
+ .driver = {
+ .name = "mtk-msdc",
+ .of_match_table = msdc_of_ids,
+ .pm = &msdc_dev_pm_ops,
+ },
+};
+
+module_platform_driver(mt_msdc_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek SD/MMC Card Driver");
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 317d709f7550..d110f9e98c4b 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -605,11 +605,7 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
mxcmci_writel(host, cpu_to_le32(tmp), MMC_REG_BUFFER_ACCESS);
}
- stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
- if (stat)
- return stat;
-
- return 0;
+ return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
}
static int mxcmci_transfer_data(struct mxcmci_host *host)
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index a82411a2c024..d839147e591d 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -549,7 +549,7 @@ static const struct mmc_host_ops mxs_mmc_ops = {
.enable_sdio_irq = mxs_mmc_enable_sdio_irq,
};
-static struct platform_device_id mxs_ssp_ids[] = {
+static const struct platform_device_id mxs_ssp_ids[] = {
{
.name = "imx23-mmc",
.driver_data = IMX23_SSP,
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index 1d3d6c4bfdc6..93137483ecde 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -1474,7 +1474,7 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id rtsx_pci_sdmmc_ids[] = {
+static const struct platform_device_id rtsx_pci_sdmmc_ids[] = {
{
.name = DRV_NAME_RTSX_PCI_SDMMC,
}, {
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 88af827e086b..6c71fc9f76c7 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -1439,7 +1439,7 @@ static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id rtsx_usb_sdmmc_ids[] = {
+static const struct platform_device_id rtsx_usb_sdmmc_ids[] = {
{
.name = "rtsx_usb_sdmmc",
}, {
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 94cddf381ba3..6291d5042ef2 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -1856,7 +1856,7 @@ static int s3cmci_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id s3cmci_driver_ids[] = {
+static const struct platform_device_id s3cmci_driver_ids[] = {
{
.name = "s3c2410-sdi",
.driver_data = 0,
diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c
index 0ef0343c603a..1c65d4690e70 100644
--- a/drivers/mmc/host/sdhci-bcm2835.c
+++ b/drivers/mmc/host/sdhci-bcm2835.c
@@ -172,9 +172,19 @@ static int bcm2835_sdhci_probe(struct platform_device *pdev)
ret = PTR_ERR(pltfm_host->clk);
goto err;
}
+ ret = clk_prepare_enable(pltfm_host->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable host clk\n");
+ goto err;
+ }
- return sdhci_add_host(host);
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto err_clk;
+ return 0;
+err_clk:
+ clk_disable_unprepare(pltfm_host->clk);
err:
sdhci_pltfm_free(pdev);
return ret;
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 82f512d87cb8..faf0cb910c96 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -4,7 +4,7 @@
* derived from the OF-version.
*
* Copyright (c) 2010 Pengutronix e.K.
- * Author: Wolfram Sang <w.sang@pengutronix.de>
+ * Author: Wolfram Sang <kernel@pengutronix.de>
*
* 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
@@ -112,6 +112,14 @@
#define ESDHC_FLAG_STD_TUNING BIT(5)
/* The IP has SDHCI_CAPABILITIES_1 register */
#define ESDHC_FLAG_HAVE_CAP1 BIT(6)
+/*
+ * The IP has errata ERR004536
+ * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
+ * when reading data from the card
+ */
+#define ESDHC_FLAG_ERR004536 BIT(7)
+/* The IP supports HS200 mode */
+#define ESDHC_FLAG_HS200 BIT(8)
struct esdhc_soc_data {
u32 flags;
@@ -139,7 +147,13 @@ static struct esdhc_soc_data usdhc_imx6q_data = {
static struct esdhc_soc_data usdhc_imx6sl_data = {
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
- | ESDHC_FLAG_HAVE_CAP1,
+ | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
+ | ESDHC_FLAG_HS200,
+};
+
+static struct esdhc_soc_data usdhc_imx6sx_data = {
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
};
struct pltfm_imx_data {
@@ -161,7 +175,7 @@ struct pltfm_imx_data {
u32 is_ddr;
};
-static struct platform_device_id imx_esdhc_devtype[] = {
+static const struct platform_device_id imx_esdhc_devtype[] = {
{
.name = "sdhci-esdhc-imx25",
.driver_data = (kernel_ulong_t) &esdhc_imx25_data,
@@ -182,6 +196,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
+ { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
{ /* sentinel */ }
@@ -298,7 +313,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
u32 data;
if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
- if (val & SDHCI_INT_CARD_INT) {
+ if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
/*
* Clear and then set D3CD bit to avoid missing the
* card interrupt. This is a eSDHC controller problem
@@ -313,6 +328,11 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
data |= ESDHC_CTRL_D3CD;
writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
}
+
+ if (val & SDHCI_INT_ADMA_ERROR) {
+ val &= ~SDHCI_INT_ADMA_ERROR;
+ val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
+ }
}
if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
@@ -333,13 +353,6 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
}
}
- if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
- if (val & SDHCI_INT_ADMA_ERROR) {
- val &= ~SDHCI_INT_ADMA_ERROR;
- val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
- }
- }
-
writel(val, host->ioaddr + reg);
}
@@ -903,7 +916,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
mmc_of_parse_voltage(np, &host->ocr_mask);
- return 0;
+ /* call to generic mmc_of_parse to support additional capabilities */
+ return mmc_of_parse(host->mmc);
}
#else
static inline int
@@ -924,6 +938,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
struct esdhc_platform_data *boarddata;
int err;
struct pltfm_imx_data *imx_data;
+ bool dt = true;
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0);
if (IS_ERR(host))
@@ -991,6 +1006,16 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
host->mmc->caps |= MMC_CAP_1_8V_DDR;
+
+ if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
+ host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
+
+ /*
+ * errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
+ * TO1.1, it's harmless for MX6SL
+ */
+ writel(readl(host->ioaddr + 0x6c) | BIT(7),
+ host->ioaddr + 0x6c);
}
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
@@ -1002,6 +1027,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP,
host->ioaddr + ESDHC_TUNING_CTRL);
+ if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
+ host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
+
boarddata = &imx_data->boarddata;
if (sdhci_esdhc_imx_probe_dt(pdev, host, boarddata) < 0) {
if (!host->mmc->parent->platform_data) {
@@ -1011,11 +1039,44 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
}
imx_data->boarddata = *((struct esdhc_platform_data *)
host->mmc->parent->platform_data);
+ dt = false;
+ }
+ /* write_protect */
+ if (boarddata->wp_type == ESDHC_WP_GPIO && !dt) {
+ err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio);
+ if (err) {
+ dev_err(mmc_dev(host->mmc),
+ "failed to request write-protect gpio!\n");
+ goto disable_clk;
+ }
+ host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
}
/* card_detect */
- if (boarddata->cd_type == ESDHC_CD_CONTROLLER)
+ switch (boarddata->cd_type) {
+ case ESDHC_CD_GPIO:
+ if (dt)
+ break;
+ err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0);
+ if (err) {
+ dev_err(mmc_dev(host->mmc),
+ "failed to request card-detect gpio!\n");
+ goto disable_clk;
+ }
+ /* fall through */
+
+ case ESDHC_CD_CONTROLLER:
+ /* we have a working card_detect back */
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ break;
+
+ case ESDHC_CD_PERMANENT:
+ host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+ break;
+
+ case ESDHC_CD_NONE:
+ break;
+ }
switch (boarddata->max_bus_width) {
case 8:
@@ -1048,11 +1109,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
}
- /* call to generic mmc_of_parse to support additional capabilities */
- err = mmc_of_parse(host->mmc);
- if (err)
- goto disable_clk;
-
err = sdhci_add_host(host);
if (err)
goto disable_clk;
@@ -1151,5 +1207,5 @@ static struct platform_driver sdhci_esdhc_imx_driver = {
module_platform_driver(sdhci_esdhc_imx_driver);
MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
-MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index 6287d426c96b..21c0c08dfe54 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -20,6 +20,7 @@
*/
#include <linux/module.h>
+#include <linux/of_device.h>
#include "sdhci-pltfm.h"
#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
@@ -168,6 +169,11 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
goto clk_disable_all;
}
+ if (of_device_is_compatible(pdev->dev.of_node, "arasan,sdhci-4.9a")) {
+ host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
+ host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23;
+ }
+
sdhci_get_of_property(pdev);
pltfm_host = sdhci_priv(host);
pltfm_host->priv = sdhci_arasan;
@@ -208,6 +214,7 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
static const struct of_device_id sdhci_arasan_of_match[] = {
{ .compatible = "arasan,sdhci-8.9a" },
+ { .compatible = "arasan,sdhci-4.9a" },
{ }
};
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 22e9111b11ff..797be7549a15 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -199,7 +199,7 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
{
- int pre_div = 2;
+ int pre_div = 1;
int div = 1;
u32 temp;
@@ -229,7 +229,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
clock, host->max_clk / pre_div / div);
-
+ host->mmc->actual_clock = host->max_clk / pre_div / div;
pre_div >>= 1;
div--;
@@ -361,6 +361,13 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
sdhci_get_of_property(pdev);
np = pdev->dev.of_node;
+ if (of_device_is_compatible(np, "fsl,p5040-esdhc") ||
+ of_device_is_compatible(np, "fsl,p5020-esdhc") ||
+ of_device_is_compatible(np, "fsl,p4080-esdhc") ||
+ of_device_is_compatible(np, "fsl,p1020-esdhc") ||
+ of_device_is_compatible(np, "fsl,t1040-esdhc"))
+ host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+
if (of_device_is_compatible(np, "fsl,p2020-esdhc")) {
/*
* Freescale messed up with P2020 as it has a non-standard
diff --git a/drivers/mmc/host/sdhci-pci-data.c b/drivers/mmc/host/sdhci-pci-data.c
index a611217769f5..56fddc622a54 100644
--- a/drivers/mmc/host/sdhci-pci-data.c
+++ b/drivers/mmc/host/sdhci-pci-data.c
@@ -3,3 +3,6 @@
struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno);
EXPORT_SYMBOL_GPL(sdhci_pci_get_data);
+
+int sdhci_pci_spt_drive_strength;
+EXPORT_SYMBOL_GPL(sdhci_pci_spt_drive_strength);
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 7a3fc16d0a6c..94f54d2772e8 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
#include <linux/scatterlist.h>
#include <linux/io.h>
#include <linux/gpio.h>
@@ -266,6 +267,69 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host)
usleep_range(300, 1000);
}
+static int spt_select_drive_strength(struct sdhci_host *host,
+ struct mmc_card *card,
+ unsigned int max_dtr,
+ int host_drv, int card_drv, int *drv_type)
+{
+ int drive_strength;
+
+ if (sdhci_pci_spt_drive_strength > 0)
+ drive_strength = sdhci_pci_spt_drive_strength & 0xf;
+ else
+ drive_strength = 1; /* 33-ohm */
+
+ if ((mmc_driver_type_mask(drive_strength) & card_drv) == 0)
+ drive_strength = 0; /* Default 50-ohm */
+
+ return drive_strength;
+}
+
+/* Try to read the drive strength from the card */
+static void spt_read_drive_strength(struct sdhci_host *host)
+{
+ u32 val, i, t;
+ u16 m;
+
+ if (sdhci_pci_spt_drive_strength)
+ return;
+
+ sdhci_pci_spt_drive_strength = -1;
+
+ m = sdhci_readw(host, SDHCI_HOST_CONTROL2) & 0x7;
+ if (m != 3 && m != 5)
+ return;
+ val = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ if (val & 0x3)
+ return;
+ sdhci_writel(host, 0x007f0023, SDHCI_INT_ENABLE);
+ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+ sdhci_writew(host, 0x10, SDHCI_TRANSFER_MODE);
+ sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
+ sdhci_writew(host, 512, SDHCI_BLOCK_SIZE);
+ sdhci_writew(host, 1, SDHCI_BLOCK_COUNT);
+ sdhci_writel(host, 0, SDHCI_ARGUMENT);
+ sdhci_writew(host, 0x83b, SDHCI_COMMAND);
+ for (i = 0; i < 1000; i++) {
+ val = sdhci_readl(host, SDHCI_INT_STATUS);
+ if (val & 0xffff8000)
+ return;
+ if (val & 0x20)
+ break;
+ udelay(1);
+ }
+ val = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ if (!(val & 0x800))
+ return;
+ for (i = 0; i < 47; i++)
+ val = sdhci_readl(host, SDHCI_BUFFER);
+ t = val & 0xf00;
+ if (t != 0x200 && t != 0x300)
+ return;
+
+ sdhci_pci_spt_drive_strength = 0x10 | ((val >> 12) & 0xf);
+}
+
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
@@ -276,6 +340,10 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
slot->hw_reset = sdhci_pci_int_hw_reset;
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
+ if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_SPT_EMMC) {
+ spt_read_drive_strength(slot->host);
+ slot->select_drive_strength = spt_select_drive_strength;
+ }
return 0;
}
@@ -302,6 +370,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
.probe_slot = byt_emmc_probe_slot,
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 |
SDHCI_QUIRK2_STOP_WITH_TC,
};
@@ -655,14 +724,37 @@ static const struct sdhci_pci_fixes sdhci_rtsx = {
.probe_slot = rtsx_probe_slot,
};
+/*AMD chipset generation*/
+enum amd_chipset_gen {
+ AMD_CHIPSET_BEFORE_ML,
+ AMD_CHIPSET_CZ,
+ AMD_CHIPSET_NL,
+ AMD_CHIPSET_UNKNOWN,
+};
+
static int amd_probe(struct sdhci_pci_chip *chip)
{
struct pci_dev *smbus_dev;
+ enum amd_chipset_gen gen;
smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
+ if (smbus_dev) {
+ gen = AMD_CHIPSET_BEFORE_ML;
+ } else {
+ smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, NULL);
+ if (smbus_dev) {
+ if (smbus_dev->revision < 0x51)
+ gen = AMD_CHIPSET_CZ;
+ else
+ gen = AMD_CHIPSET_NL;
+ } else {
+ gen = AMD_CHIPSET_UNKNOWN;
+ }
+ }
- if (smbus_dev && (smbus_dev->revision < 0x51)) {
+ if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) {
chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
}
@@ -1203,6 +1295,20 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
slot->hw_reset(host);
}
+static int sdhci_pci_select_drive_strength(struct sdhci_host *host,
+ struct mmc_card *card,
+ unsigned int max_dtr, int host_drv,
+ int card_drv, int *drv_type)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+
+ if (!slot->select_drive_strength)
+ return 0;
+
+ return slot->select_drive_strength(host, card, max_dtr, host_drv,
+ card_drv, drv_type);
+}
+
static const struct sdhci_ops sdhci_pci_ops = {
.set_clock = sdhci_set_clock,
.enable_dma = sdhci_pci_enable_dma,
@@ -1210,6 +1316,7 @@ static const struct sdhci_ops sdhci_pci_ops = {
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.hw_reset = sdhci_pci_hw_reset,
+ .select_drive_strength = sdhci_pci_select_drive_strength,
};
/*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index 1ec684d06d54..541f1cad5247 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -72,6 +72,10 @@ struct sdhci_pci_slot {
bool cd_override_level;
void (*hw_reset)(struct sdhci_host *host);
+ int (*select_drive_strength)(struct sdhci_host *host,
+ struct mmc_card *card,
+ unsigned int max_dtr, int host_drv,
+ int card_drv, int *drv_type);
};
struct sdhci_pci_chip {
diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c
index f98008b5ea77..beffd8615489 100644
--- a/drivers/mmc/host/sdhci-pxav2.c
+++ b/drivers/mmc/host/sdhci-pxav2.c
@@ -252,9 +252,7 @@ static int sdhci_pxav2_remove(struct platform_device *pdev)
static struct platform_driver sdhci_pxav2_driver = {
.driver = {
.name = "sdhci-pxav2",
-#ifdef CONFIG_OF
- .of_match_table = sdhci_pxav2_of_match,
-#endif
+ .of_match_table = of_match_ptr(sdhci_pxav2_of_match),
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = sdhci_pxav2_probe,
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index b5103a247bc1..9cd5fc62f130 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -457,12 +457,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
- if (host->mmc->pm_caps & MMC_PM_KEEP_POWER) {
+ if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ)
device_init_wakeup(&pdev->dev, 1);
- host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ;
- } else {
- device_init_wakeup(&pdev->dev, 0);
- }
pm_runtime_put_autosuspend(&pdev->dev);
@@ -578,9 +574,7 @@ static const struct dev_pm_ops sdhci_pxav3_pmops = {
static struct platform_driver sdhci_pxav3_driver = {
.driver = {
.name = "sdhci-pxav3",
-#ifdef CONFIG_OF
- .of_match_table = sdhci_pxav3_of_match,
-#endif
+ .of_match_table = of_match_ptr(sdhci_pxav3_of_match),
.pm = SDHCI_PXAV3_PMOPS,
},
.probe = sdhci_pxav3_probe,
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index c6d2dd7317c1..70c724bc6fc7 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -736,7 +736,7 @@ static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)NULL)
#endif
-static struct platform_device_id sdhci_s3c_driver_ids[] = {
+static const struct platform_device_id sdhci_s3c_driver_ids[] = {
{
.name = "s3c-sdhci",
.driver_data = (kernel_ulong_t)NULL,
diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c
index 32848eb7ad80..0110bae25b7e 100644
--- a/drivers/mmc/host/sdhci-sirf.c
+++ b/drivers/mmc/host/sdhci-sirf.c
@@ -17,7 +17,7 @@
#define SDHCI_CLK_DELAY_SETTING 0x4C
#define SDHCI_SIRF_8BITBUS BIT(3)
-#define SIRF_TUNING_COUNT 128
+#define SIRF_TUNING_COUNT 16384
struct sdhci_sirf_priv {
int gpio_cd;
@@ -43,10 +43,43 @@ static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
+static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg)
+{
+ u32 val = readl(host->ioaddr + reg);
+
+ if (unlikely((reg == SDHCI_CAPABILITIES_1) &&
+ (host->mmc->caps & MMC_CAP_UHS_SDR50))) {
+ /* fake CAP_1 register */
+ val = SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING;
+ }
+
+ if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) {
+ u32 prss = val;
+ /* fake chips as V3.0 host conreoller */
+ prss &= ~(0xFF << 16);
+ val = prss | (SDHCI_SPEC_300 << 16);
+ }
+ return val;
+}
+
+static u16 sdhci_sirf_readw_le(struct sdhci_host *host, int reg)
+{
+ u16 ret = 0;
+
+ ret = readw(host->ioaddr + reg);
+
+ if (unlikely(reg == SDHCI_HOST_VERSION)) {
+ ret = readw(host->ioaddr + SDHCI_HOST_VERSION);
+ ret |= SDHCI_SPEC_300;
+ }
+
+ return ret;
+}
+
static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
{
int tuning_seq_cnt = 3;
- u8 phase, tuned_phases[SIRF_TUNING_COUNT];
+ int phase;
u8 tuned_phase_cnt = 0;
int rc = 0, longest_range = 0;
int start = -1, end = 0, tuning_value = -1, range = 0;
@@ -58,6 +91,7 @@ static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
retry:
phase = 0;
+ tuned_phase_cnt = 0;
do {
sdhci_writel(host,
clock_setting | phase,
@@ -65,7 +99,7 @@ retry:
if (!mmc_send_tuning(mmc)) {
/* Tuning is successful at this tuning point */
- tuned_phases[tuned_phase_cnt++] = phase;
+ tuned_phase_cnt++;
dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
mmc_hostname(mmc), phase);
if (start == -1)
@@ -85,7 +119,7 @@ retry:
start = -1;
end = range = 0;
}
- } while (++phase < ARRAY_SIZE(tuned_phases));
+ } while (++phase < SIRF_TUNING_COUNT);
if (tuned_phase_cnt && tuning_value > 0) {
/*
@@ -112,6 +146,8 @@ retry:
}
static struct sdhci_ops sdhci_sirf_ops = {
+ .read_l = sdhci_sirf_readl_le,
+ .read_w = sdhci_sirf_readw_le,
.platform_execute_tuning = sdhci_sirf_execute_tuning,
.set_clock = sdhci_set_clock,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c
index 682f2bb0f4bf..969c2b0d57fd 100644
--- a/drivers/mmc/host/sdhci-st.c
+++ b/drivers/mmc/host/sdhci-st.c
@@ -509,4 +509,4 @@ module_platform_driver(sdhci_st_driver);
MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs");
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:st-sdhci");
+MODULE_ALIAS("platform:sdhci-st");
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c80287a02735..bc1445238fb3 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -52,7 +52,6 @@ static void sdhci_finish_data(struct sdhci_host *);
static void sdhci_finish_command(struct sdhci_host *);
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
-static void sdhci_tuning_timer(unsigned long data);
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
struct mmc_data *data,
@@ -254,17 +253,6 @@ static void sdhci_init(struct sdhci_host *host, int soft)
static void sdhci_reinit(struct sdhci_host *host)
{
sdhci_init(host, 0);
- /*
- * Retuning stuffs are affected by different cards inserted and only
- * applicable to UHS-I cards. So reset these fields to their initial
- * value when card is removed.
- */
- if (host->flags & SDHCI_USING_RETUNING_TIMER) {
- host->flags &= ~SDHCI_USING_RETUNING_TIMER;
-
- del_timer_sync(&host->tuning_timer);
- host->flags &= ~SDHCI_NEEDS_RETUNING;
- }
sdhci_enable_card_detection(host);
}
@@ -328,8 +316,7 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
local_irq_save(flags);
while (blksize) {
- if (!sg_miter_next(&host->sg_miter))
- BUG();
+ BUG_ON(!sg_miter_next(&host->sg_miter));
len = min(host->sg_miter.length, blksize);
@@ -374,8 +361,7 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
local_irq_save(flags);
while (blksize) {
- if (!sg_miter_next(&host->sg_miter))
- BUG();
+ BUG_ON(!sg_miter_next(&host->sg_miter));
len = min(host->sg_miter.length, blksize);
@@ -848,7 +834,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
int sg_cnt;
sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
- if (sg_cnt == 0) {
+ if (sg_cnt <= 0) {
/*
* This only happens when someone fed
* us an invalid request.
@@ -1353,7 +1339,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct sdhci_host *host;
int present;
unsigned long flags;
- u32 tuning_opcode;
host = mmc_priv(mmc);
@@ -1387,39 +1372,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
} else {
- u32 present_state;
-
- present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
- /*
- * Check if the re-tuning timer has already expired and there
- * is no on-going data transfer and DAT0 is not busy. If so,
- * we need to execute tuning procedure before sending command.
- */
- if ((host->flags & SDHCI_NEEDS_RETUNING) &&
- !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ)) &&
- (present_state & SDHCI_DATA_0_LVL_MASK)) {
- if (mmc->card) {
- /* eMMC uses cmd21 but sd and sdio use cmd19 */
- tuning_opcode =
- mmc->card->type == MMC_TYPE_MMC ?
- MMC_SEND_TUNING_BLOCK_HS200 :
- MMC_SEND_TUNING_BLOCK;
-
- /* Here we need to set the host->mrq to NULL,
- * in case the pending finish_tasklet
- * finishes it incorrectly.
- */
- host->mrq = NULL;
-
- spin_unlock_irqrestore(&host->lock, flags);
- sdhci_execute_tuning(mmc, tuning_opcode);
- spin_lock_irqsave(&host->lock, flags);
-
- /* Restore original mmc_request structure */
- host->mrq = mrq;
- }
- }
-
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
sdhci_send_command(host, mrq->sbc);
else
@@ -1562,8 +1514,17 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;
if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
+ else if (ios->drv_type == MMC_SET_DRIVER_TYPE_B)
+ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;
else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C)
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;
+ else if (ios->drv_type == MMC_SET_DRIVER_TYPE_D)
+ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_D;
+ else {
+ pr_warn("%s: invalid driver type, default to "
+ "driver type B\n", mmc_hostname(mmc));
+ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;
+ }
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
} else {
@@ -2065,23 +2026,18 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
}
out:
- host->flags &= ~SDHCI_NEEDS_RETUNING;
-
if (tuning_count) {
- host->flags |= SDHCI_USING_RETUNING_TIMER;
- mod_timer(&host->tuning_timer, jiffies + tuning_count * HZ);
+ /*
+ * In case tuning fails, host controllers which support
+ * re-tuning can try tuning again at a later time, when the
+ * re-tuning timer expires. So for these controllers, we
+ * return 0. Since there might be other controllers who do not
+ * have this capability, we return error for them.
+ */
+ err = 0;
}
- /*
- * In case tuning fails, host controllers which support re-tuning can
- * try tuning again at a later time, when the re-tuning timer expires.
- * So for these controllers, we return 0. Since there might be other
- * controllers who do not have this capability, we return error for
- * them. SDHCI_USING_RETUNING_TIMER means the host is currently using
- * a retuning timer to do the retuning for the card.
- */
- if (err && (host->flags & SDHCI_USING_RETUNING_TIMER))
- err = 0;
+ host->mmc->retune_period = err ? 0 : tuning_count;
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
@@ -2092,6 +2048,18 @@ out_unlock:
return err;
}
+static int sdhci_select_drive_strength(struct mmc_card *card,
+ unsigned int max_dtr, int host_drv,
+ int card_drv, int *drv_type)
+{
+ struct sdhci_host *host = mmc_priv(card->host);
+
+ if (!host->ops->select_drive_strength)
+ return 0;
+
+ return host->ops->select_drive_strength(host, card, max_dtr, host_drv,
+ card_drv, drv_type);
+}
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
{
@@ -2236,6 +2204,7 @@ static const struct mmc_host_ops sdhci_ops = {
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.prepare_hs400_tuning = sdhci_prepare_hs400_tuning,
.execute_tuning = sdhci_execute_tuning,
+ .select_drive_strength = sdhci_select_drive_strength,
.card_event = sdhci_card_event,
.card_busy = sdhci_card_busy,
};
@@ -2337,20 +2306,6 @@ static void sdhci_timeout_timer(unsigned long data)
spin_unlock_irqrestore(&host->lock, flags);
}
-static void sdhci_tuning_timer(unsigned long data)
-{
- struct sdhci_host *host;
- unsigned long flags;
-
- host = (struct sdhci_host *)data;
-
- spin_lock_irqsave(&host->lock, flags);
-
- host->flags |= SDHCI_NEEDS_RETUNING;
-
- spin_unlock_irqrestore(&host->lock, flags);
-}
-
/*****************************************************************************\
* *
* Interrupt handling *
@@ -2728,11 +2683,8 @@ int sdhci_suspend_host(struct sdhci_host *host)
{
sdhci_disable_card_detection(host);
- /* Disable tuning since we are suspending */
- if (host->flags & SDHCI_USING_RETUNING_TIMER) {
- del_timer_sync(&host->tuning_timer);
- host->flags &= ~SDHCI_NEEDS_RETUNING;
- }
+ mmc_retune_timer_stop(host->mmc);
+ mmc_retune_needed(host->mmc);
if (!device_may_wakeup(mmc_dev(host->mmc))) {
host->ier = 0;
@@ -2782,10 +2734,6 @@ int sdhci_resume_host(struct sdhci_host *host)
sdhci_enable_card_detection(host);
- /* Set the re-tuning expiration flag */
- if (host->flags & SDHCI_USING_RETUNING_TIMER)
- host->flags |= SDHCI_NEEDS_RETUNING;
-
return ret;
}
@@ -2822,11 +2770,8 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
{
unsigned long flags;
- /* Disable tuning since we are suspending */
- if (host->flags & SDHCI_USING_RETUNING_TIMER) {
- del_timer_sync(&host->tuning_timer);
- host->flags &= ~SDHCI_NEEDS_RETUNING;
- }
+ mmc_retune_timer_stop(host->mmc);
+ mmc_retune_needed(host->mmc);
spin_lock_irqsave(&host->lock, flags);
host->ier &= SDHCI_INT_CARD_INT;
@@ -2869,10 +2814,6 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
spin_unlock_irqrestore(&host->lock, flags);
}
- /* Set the re-tuning expiration flag */
- if (host->flags & SDHCI_USING_RETUNING_TIMER)
- host->flags |= SDHCI_NEEDS_RETUNING;
-
spin_lock_irqsave(&host->lock, flags);
host->runtime_suspended = false;
@@ -3315,13 +3256,14 @@ int sdhci_add_host(struct sdhci_host *host)
SDHCI_MAX_CURRENT_MULTIPLIER;
}
- /* If OCR set by external regulators, use it instead */
+ /* If OCR set by host, use it instead. */
+ if (host->ocr_mask)
+ ocr_avail = host->ocr_mask;
+
+ /* If OCR set by external regulators, give it highest prio. */
if (mmc->ocr_avail)
ocr_avail = mmc->ocr_avail;
- if (host->ocr_mask)
- ocr_avail &= host->ocr_mask;
-
mmc->ocr_avail = ocr_avail;
mmc->ocr_avail_sdio = ocr_avail;
if (host->ocr_avail_sdio)
@@ -3408,13 +3350,6 @@ int sdhci_add_host(struct sdhci_host *host)
init_waitqueue_head(&host->buf_ready_int);
- if (host->version >= SDHCI_SPEC_300) {
- /* Initialize re-tuning timer */
- init_timer(&host->tuning_timer);
- host->tuning_timer.data = (unsigned long)host;
- host->tuning_timer.function = sdhci_tuning_timer;
- }
-
sdhci_init(host, 0);
ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index e639b7f435e5..5521d29368e4 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -432,13 +432,11 @@ struct sdhci_host {
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
-#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
-#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
@@ -504,7 +502,6 @@ struct sdhci_host {
unsigned int tuning_count; /* Timer count for re-tuning */
unsigned int tuning_mode; /* Re-tuning mode supported by host */
#define SDHCI_TUNING_MODE_1 0
- struct timer_list tuning_timer; /* Timer for tuning */
struct sdhci_host_next next_data;
unsigned long private[0] ____cacheline_aligned;
@@ -541,6 +538,10 @@ struct sdhci_ops {
void (*platform_init)(struct sdhci_host *host);
void (*card_event)(struct sdhci_host *host);
void (*voltage_switch)(struct sdhci_host *host);
+ int (*select_drive_strength)(struct sdhci_host *host,
+ struct mmc_card *card,
+ unsigned int max_dtr, int host_drv,
+ int card_drv, int *drv_type);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
index 2fe8b91481b3..983b8b32ef96 100644
--- a/drivers/mmc/host/sdhci_f_sdh30.c
+++ b/drivers/mmc/host/sdhci_f_sdh30.c
@@ -49,7 +49,7 @@ struct f_sdhost_priv {
struct device *dev;
};
-void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
+static void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
{
struct f_sdhost_priv *priv = sdhci_priv(host);
u32 ctrl = 0;
@@ -77,12 +77,12 @@ void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
}
-unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
+static unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
{
return F_SDH30_MIN_CLOCK;
}
-void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
+static void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
{
if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0)
sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
@@ -114,8 +114,7 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
return irq;
}
- host = sdhci_alloc_host(dev, sizeof(struct sdhci_host) +
- sizeof(struct f_sdhost_priv));
+ host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv));
if (IS_ERR(host))
return PTR_ERR(host);
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 2b6ef6bd5d5f..5a1fdd405b1a 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -57,6 +57,7 @@
#include <linux/mmc/slot-gpio.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
+#include <linux/of_device.h>
#include <linux/pagemap.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
@@ -205,14 +206,14 @@
#define CLKDEV_MMC_DATA 20000000 /* 20MHz */
#define CLKDEV_INIT 400000 /* 400 KHz */
-enum mmcif_state {
+enum sh_mmcif_state {
STATE_IDLE,
STATE_REQUEST,
STATE_IOS,
STATE_TIMEOUT,
};
-enum mmcif_wait_for {
+enum sh_mmcif_wait_for {
MMCIF_WAIT_FOR_REQUEST,
MMCIF_WAIT_FOR_CMD,
MMCIF_WAIT_FOR_MREAD,
@@ -224,12 +225,14 @@ enum mmcif_wait_for {
MMCIF_WAIT_FOR_STOP,
};
+/*
+ * difference for each SoC
+ */
struct sh_mmcif_host {
struct mmc_host *mmc;
struct mmc_request *mrq;
struct platform_device *pd;
- struct clk *hclk;
- unsigned int clk;
+ struct clk *clk;
int bus_width;
unsigned char timing;
bool sd_error;
@@ -238,8 +241,8 @@ struct sh_mmcif_host {
void __iomem *addr;
u32 *pio_ptr;
spinlock_t lock; /* protect sh_mmcif_host::state */
- enum mmcif_state state;
- enum mmcif_wait_for wait_for;
+ enum sh_mmcif_state state;
+ enum sh_mmcif_wait_for wait_for;
struct delayed_work timeout_work;
size_t blocksize;
int sg_idx;
@@ -249,6 +252,7 @@ struct sh_mmcif_host {
bool ccs_enable; /* Command Completion Signal support */
bool clk_ctrl2_enable;
struct mutex thread_lock;
+ u32 clkdiv_map; /* see CE_CLK_CTRL::CLKDIV */
/* DMA support */
struct dma_chan *chan_rx;
@@ -257,6 +261,14 @@ struct sh_mmcif_host {
bool dma_active;
};
+static const struct of_device_id sh_mmcif_of_match[] = {
+ { .compatible = "renesas,sh-mmcif" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sh_mmcif_of_match);
+
+#define sh_mmcif_host_to_dev(host) (&host->pd->dev)
+
static inline void sh_mmcif_bitset(struct sh_mmcif_host *host,
unsigned int reg, u32 val)
{
@@ -269,15 +281,16 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host,
writel(~val & readl(host->addr + reg), host->addr + reg);
}
-static void mmcif_dma_complete(void *arg)
+static void sh_mmcif_dma_complete(void *arg)
{
struct sh_mmcif_host *host = arg;
struct mmc_request *mrq = host->mrq;
+ struct device *dev = sh_mmcif_host_to_dev(host);
- dev_dbg(&host->pd->dev, "Command completed\n");
+ dev_dbg(dev, "Command completed\n");
if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion!\n",
- dev_name(&host->pd->dev)))
+ dev_name(dev)))
return;
complete(&host->dma_complete);
@@ -289,6 +302,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
struct scatterlist *sg = data->sg;
struct dma_async_tx_descriptor *desc = NULL;
struct dma_chan *chan = host->chan_rx;
+ struct device *dev = sh_mmcif_host_to_dev(host);
dma_cookie_t cookie = -EINVAL;
int ret;
@@ -301,13 +315,13 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
}
if (desc) {
- desc->callback = mmcif_dma_complete;
+ desc->callback = sh_mmcif_dma_complete;
desc->callback_param = host;
cookie = dmaengine_submit(desc);
sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN);
dma_async_issue_pending(chan);
}
- dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n",
+ dev_dbg(dev, "%s(): mapped %d -> %d, cookie %d\n",
__func__, data->sg_len, ret, cookie);
if (!desc) {
@@ -323,12 +337,12 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
host->chan_tx = NULL;
dma_release_channel(chan);
}
- dev_warn(&host->pd->dev,
+ dev_warn(dev,
"DMA failed: %d, falling back to PIO\n", ret);
sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
}
- dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__,
+ dev_dbg(dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__,
desc, cookie, data->sg_len);
}
@@ -338,6 +352,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
struct scatterlist *sg = data->sg;
struct dma_async_tx_descriptor *desc = NULL;
struct dma_chan *chan = host->chan_tx;
+ struct device *dev = sh_mmcif_host_to_dev(host);
dma_cookie_t cookie = -EINVAL;
int ret;
@@ -350,13 +365,13 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
}
if (desc) {
- desc->callback = mmcif_dma_complete;
+ desc->callback = sh_mmcif_dma_complete;
desc->callback_param = host;
cookie = dmaengine_submit(desc);
sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAWEN);
dma_async_issue_pending(chan);
}
- dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n",
+ dev_dbg(dev, "%s(): mapped %d -> %d, cookie %d\n",
__func__, data->sg_len, ret, cookie);
if (!desc) {
@@ -372,12 +387,12 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
host->chan_rx = NULL;
dma_release_channel(chan);
}
- dev_warn(&host->pd->dev,
+ dev_warn(dev,
"DMA failed: %d, falling back to PIO\n", ret);
sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
}
- dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d\n", __func__,
+ dev_dbg(dev, "%s(): desc %p, cookie %d\n", __func__,
desc, cookie);
}
@@ -390,6 +405,7 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
struct dma_chan *chan;
void *slave_data = NULL;
struct resource *res;
+ struct device *dev = sh_mmcif_host_to_dev(host);
dma_cap_mask_t mask;
int ret;
@@ -402,10 +418,10 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
(void *)pdata->slave_id_rx;
chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
- slave_data, &host->pd->dev,
+ slave_data, dev,
direction == DMA_MEM_TO_DEV ? "tx" : "rx");
- dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__,
+ dev_dbg(dev, "%s: %s: got channel %p\n", __func__,
direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan);
if (!chan)
@@ -435,12 +451,13 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
struct sh_mmcif_plat_data *pdata)
{
+ struct device *dev = sh_mmcif_host_to_dev(host);
host->dma_active = false;
if (pdata) {
if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0)
return;
- } else if (!host->pd->dev.of_node) {
+ } else if (!dev->of_node) {
return;
}
@@ -476,21 +493,59 @@ static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
{
- struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
+ struct device *dev = sh_mmcif_host_to_dev(host);
+ struct sh_mmcif_plat_data *p = dev->platform_data;
bool sup_pclk = p ? p->sup_pclk : false;
+ unsigned int current_clk = clk_get_rate(host->clk);
+ unsigned int clkdiv;
sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR);
if (!clk)
return;
- if (sup_pclk && clk == host->clk)
- sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK);
- else
- sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR &
- ((fls(DIV_ROUND_UP(host->clk,
- clk) - 1) - 1) << 16));
+ if (host->clkdiv_map) {
+ unsigned int freq, best_freq, myclk, div, diff_min, diff;
+ int i;
+
+ clkdiv = 0;
+ diff_min = ~0;
+ best_freq = 0;
+ for (i = 31; i >= 0; i--) {
+ if (!((1 << i) & host->clkdiv_map))
+ continue;
+
+ /*
+ * clk = parent_freq / div
+ * -> parent_freq = clk x div
+ */
+
+ div = 1 << (i + 1);
+ freq = clk_round_rate(host->clk, clk * div);
+ myclk = freq / div;
+ diff = (myclk > clk) ? myclk - clk : clk - myclk;
+
+ if (diff <= diff_min) {
+ best_freq = freq;
+ clkdiv = i;
+ diff_min = diff;
+ }
+ }
+
+ dev_dbg(dev, "clk %u/%u (%u, 0x%x)\n",
+ (best_freq / (1 << (clkdiv + 1))), clk,
+ best_freq, clkdiv);
+
+ clk_set_rate(host->clk, best_freq);
+ clkdiv = clkdiv << 16;
+ } else if (sup_pclk && clk == current_clk) {
+ clkdiv = CLK_SUP_PCLK;
+ } else {
+ clkdiv = (fls(DIV_ROUND_UP(current_clk, clk) - 1) - 1) << 16;
+ }
+
+ sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & clkdiv);
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
}
@@ -514,6 +569,7 @@ static void sh_mmcif_sync_reset(struct sh_mmcif_host *host)
static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
{
+ struct device *dev = sh_mmcif_host_to_dev(host);
u32 state1, state2;
int ret, timeout;
@@ -521,8 +577,8 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
state1 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1);
state2 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS2);
- dev_dbg(&host->pd->dev, "ERR HOST_STS1 = %08x\n", state1);
- dev_dbg(&host->pd->dev, "ERR HOST_STS2 = %08x\n", state2);
+ dev_dbg(dev, "ERR HOST_STS1 = %08x\n", state1);
+ dev_dbg(dev, "ERR HOST_STS2 = %08x\n", state2);
if (state1 & STS1_CMDSEQ) {
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK);
@@ -534,25 +590,25 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
mdelay(1);
}
if (!timeout) {
- dev_err(&host->pd->dev,
+ dev_err(dev,
"Forced end of command sequence timeout err\n");
return -EIO;
}
sh_mmcif_sync_reset(host);
- dev_dbg(&host->pd->dev, "Forced end of command sequence\n");
+ dev_dbg(dev, "Forced end of command sequence\n");
return -EIO;
}
if (state2 & STS2_CRC_ERR) {
- dev_err(&host->pd->dev, " CRC error: state %u, wait %u\n",
+ dev_err(dev, " CRC error: state %u, wait %u\n",
host->state, host->wait_for);
ret = -EIO;
} else if (state2 & STS2_TIMEOUT_ERR) {
- dev_err(&host->pd->dev, " Timeout: state %u, wait %u\n",
+ dev_err(dev, " Timeout: state %u, wait %u\n",
host->state, host->wait_for);
ret = -ETIMEDOUT;
} else {
- dev_dbg(&host->pd->dev, " End/Index error: state %u, wait %u\n",
+ dev_dbg(dev, " End/Index error: state %u, wait %u\n",
host->state, host->wait_for);
ret = -EIO;
}
@@ -593,13 +649,14 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host,
static bool sh_mmcif_read_block(struct sh_mmcif_host *host)
{
+ struct device *dev = sh_mmcif_host_to_dev(host);
struct mmc_data *data = host->mrq->data;
u32 *p = sg_virt(data->sg);
int i;
if (host->sd_error) {
data->error = sh_mmcif_error_manage(host);
- dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
+ dev_dbg(dev, "%s(): %d\n", __func__, data->error);
return false;
}
@@ -634,13 +691,14 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host,
static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
{
+ struct device *dev = sh_mmcif_host_to_dev(host);
struct mmc_data *data = host->mrq->data;
u32 *p = host->pio_ptr;
int i;
if (host->sd_error) {
data->error = sh_mmcif_error_manage(host);
- dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
+ dev_dbg(dev, "%s(): %d\n", __func__, data->error);
return false;
}
@@ -671,13 +729,14 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host,
static bool sh_mmcif_write_block(struct sh_mmcif_host *host)
{
+ struct device *dev = sh_mmcif_host_to_dev(host);
struct mmc_data *data = host->mrq->data;
u32 *p = sg_virt(data->sg);
int i;
if (host->sd_error) {
data->error = sh_mmcif_error_manage(host);
- dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
+ dev_dbg(dev, "%s(): %d\n", __func__, data->error);
return false;
}
@@ -712,13 +771,14 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host,
static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
{
+ struct device *dev = sh_mmcif_host_to_dev(host);
struct mmc_data *data = host->mrq->data;
u32 *p = host->pio_ptr;
int i;
if (host->sd_error) {
data->error = sh_mmcif_error_manage(host);
- dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
+ dev_dbg(dev, "%s(): %d\n", __func__, data->error);
return false;
}
@@ -756,6 +816,7 @@ static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host,
static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
struct mmc_request *mrq)
{
+ struct device *dev = sh_mmcif_host_to_dev(host);
struct mmc_data *data = mrq->data;
struct mmc_command *cmd = mrq->cmd;
u32 opc = cmd->opcode;
@@ -775,7 +836,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
tmp |= CMD_SET_RTYP_17B;
break;
default:
- dev_err(&host->pd->dev, "Unsupported response type.\n");
+ dev_err(dev, "Unsupported response type.\n");
break;
}
switch (opc) {
@@ -803,7 +864,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
tmp |= CMD_SET_DATW_8;
break;
default:
- dev_err(&host->pd->dev, "Unsupported bus width.\n");
+ dev_err(dev, "Unsupported bus width.\n");
break;
}
switch (host->timing) {
@@ -846,6 +907,8 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
static int sh_mmcif_data_trans(struct sh_mmcif_host *host,
struct mmc_request *mrq, u32 opc)
{
+ struct device *dev = sh_mmcif_host_to_dev(host);
+
switch (opc) {
case MMC_READ_MULTIPLE_BLOCK:
sh_mmcif_multi_read(host, mrq);
@@ -861,7 +924,7 @@ static int sh_mmcif_data_trans(struct sh_mmcif_host *host,
sh_mmcif_single_read(host, mrq);
return 0;
default:
- dev_err(&host->pd->dev, "Unsupported CMD%d\n", opc);
+ dev_err(dev, "Unsupported CMD%d\n", opc);
return -EINVAL;
}
}
@@ -918,6 +981,8 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
struct mmc_request *mrq)
{
+ struct device *dev = sh_mmcif_host_to_dev(host);
+
switch (mrq->cmd->opcode) {
case MMC_READ_MULTIPLE_BLOCK:
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE);
@@ -926,7 +991,7 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE);
break;
default:
- dev_err(&host->pd->dev, "unsupported stop cmd\n");
+ dev_err(dev, "unsupported stop cmd\n");
mrq->stop->error = sh_mmcif_error_manage(host);
return;
}
@@ -937,11 +1002,13 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sh_mmcif_host *host = mmc_priv(mmc);
+ struct device *dev = sh_mmcif_host_to_dev(host);
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
if (host->state != STATE_IDLE) {
- dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state);
+ dev_dbg(dev, "%s() rejected, state %u\n",
+ __func__, host->state);
spin_unlock_irqrestore(&host->lock, flags);
mrq->cmd->error = -EAGAIN;
mmc_request_done(mmc, mrq);
@@ -972,17 +1039,37 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
sh_mmcif_start_cmd(host, mrq);
}
-static int sh_mmcif_clk_update(struct sh_mmcif_host *host)
+static void sh_mmcif_clk_setup(struct sh_mmcif_host *host)
{
- int ret = clk_prepare_enable(host->hclk);
+ struct device *dev = sh_mmcif_host_to_dev(host);
+
+ if (host->mmc->f_max) {
+ unsigned int f_max, f_min = 0, f_min_old;
+
+ f_max = host->mmc->f_max;
+ for (f_min_old = f_max; f_min_old > 2;) {
+ f_min = clk_round_rate(host->clk, f_min_old / 2);
+ if (f_min == f_min_old)
+ break;
+ f_min_old = f_min;
+ }
+
+ /*
+ * This driver assumes this SoC is R-Car Gen2 or later
+ */
+ host->clkdiv_map = 0x3ff;
+
+ host->mmc->f_max = f_max / (1 << ffs(host->clkdiv_map));
+ host->mmc->f_min = f_min / (1 << fls(host->clkdiv_map));
+ } else {
+ unsigned int clk = clk_get_rate(host->clk);
- if (!ret) {
- host->clk = clk_get_rate(host->hclk);
- host->mmc->f_max = host->clk / 2;
- host->mmc->f_min = host->clk / 512;
+ host->mmc->f_max = clk / 2;
+ host->mmc->f_min = clk / 512;
}
- return ret;
+ dev_dbg(dev, "clk max/min = %d/%d\n",
+ host->mmc->f_max, host->mmc->f_min);
}
static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios)
@@ -998,11 +1085,13 @@ static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios)
static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sh_mmcif_host *host = mmc_priv(mmc);
+ struct device *dev = sh_mmcif_host_to_dev(host);
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
if (host->state != STATE_IDLE) {
- dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state);
+ dev_dbg(dev, "%s() rejected, state %u\n",
+ __func__, host->state);
spin_unlock_irqrestore(&host->lock, flags);
return;
}
@@ -1013,7 +1102,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->power_mode == MMC_POWER_UP) {
if (!host->card_present) {
/* See if we also get DMA */
- sh_mmcif_request_dma(host, host->pd->dev.platform_data);
+ sh_mmcif_request_dma(host, dev->platform_data);
host->card_present = true;
}
sh_mmcif_set_power(host, ios);
@@ -1027,8 +1116,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
}
if (host->power) {
- pm_runtime_put_sync(&host->pd->dev);
- clk_disable_unprepare(host->hclk);
+ pm_runtime_put_sync(dev);
+ clk_disable_unprepare(host->clk);
host->power = false;
if (ios->power_mode == MMC_POWER_OFF)
sh_mmcif_set_power(host, ios);
@@ -1039,8 +1128,9 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->clock) {
if (!host->power) {
- sh_mmcif_clk_update(host);
- pm_runtime_get_sync(&host->pd->dev);
+ clk_prepare_enable(host->clk);
+
+ pm_runtime_get_sync(dev);
host->power = true;
sh_mmcif_sync_reset(host);
}
@@ -1055,7 +1145,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static int sh_mmcif_get_cd(struct mmc_host *mmc)
{
struct sh_mmcif_host *host = mmc_priv(mmc);
- struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
+ struct device *dev = sh_mmcif_host_to_dev(host);
+ struct sh_mmcif_plat_data *p = dev->platform_data;
int ret = mmc_gpio_get_cd(mmc);
if (ret >= 0)
@@ -1077,6 +1168,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
{
struct mmc_command *cmd = host->mrq->cmd;
struct mmc_data *data = host->mrq->data;
+ struct device *dev = sh_mmcif_host_to_dev(host);
long time;
if (host->sd_error) {
@@ -1090,7 +1182,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
cmd->error = sh_mmcif_error_manage(host);
break;
}
- dev_dbg(&host->pd->dev, "CMD%d error %d\n",
+ dev_dbg(dev, "CMD%d error %d\n",
cmd->opcode, cmd->error);
host->sd_error = false;
return false;
@@ -1170,6 +1262,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
{
struct sh_mmcif_host *host = dev_id;
struct mmc_request *mrq;
+ struct device *dev = sh_mmcif_host_to_dev(host);
bool wait = false;
unsigned long flags;
int wait_work;
@@ -1184,7 +1277,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
mrq = host->mrq;
if (!mrq) {
- dev_dbg(&host->pd->dev, "IRQ thread state %u, wait %u: NULL mrq!\n",
+ dev_dbg(dev, "IRQ thread state %u, wait %u: NULL mrq!\n",
host->state, host->wait_for);
mutex_unlock(&host->thread_lock);
return IRQ_HANDLED;
@@ -1222,7 +1315,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
case MMCIF_WAIT_FOR_STOP:
if (host->sd_error) {
mrq->stop->error = sh_mmcif_error_manage(host);
- dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->stop->error);
+ dev_dbg(dev, "%s(): %d\n", __func__, mrq->stop->error);
break;
}
sh_mmcif_get_cmd12response(host, mrq->stop);
@@ -1232,7 +1325,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
case MMCIF_WAIT_FOR_WRITE_END:
if (host->sd_error) {
mrq->data->error = sh_mmcif_error_manage(host);
- dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->data->error);
+ dev_dbg(dev, "%s(): %d\n", __func__, mrq->data->error);
}
break;
default:
@@ -1275,6 +1368,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
{
struct sh_mmcif_host *host = dev_id;
+ struct device *dev = sh_mmcif_host_to_dev(host);
u32 state, mask;
state = sh_mmcif_readl(host->addr, MMCIF_CE_INT);
@@ -1286,32 +1380,33 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN);
if (state & ~MASK_CLEAN)
- dev_dbg(&host->pd->dev, "IRQ state = 0x%08x incompletely cleared\n",
+ dev_dbg(dev, "IRQ state = 0x%08x incompletely cleared\n",
state);
if (state & INT_ERR_STS || state & ~INT_ALL) {
host->sd_error = true;
- dev_dbg(&host->pd->dev, "int err state = 0x%08x\n", state);
+ dev_dbg(dev, "int err state = 0x%08x\n", state);
}
if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) {
if (!host->mrq)
- dev_dbg(&host->pd->dev, "NULL IRQ state = 0x%08x\n", state);
+ dev_dbg(dev, "NULL IRQ state = 0x%08x\n", state);
if (!host->dma_active)
return IRQ_WAKE_THREAD;
else if (host->sd_error)
- mmcif_dma_complete(host);
+ sh_mmcif_dma_complete(host);
} else {
- dev_dbg(&host->pd->dev, "Unexpected IRQ 0x%x\n", state);
+ dev_dbg(dev, "Unexpected IRQ 0x%x\n", state);
}
return IRQ_HANDLED;
}
-static void mmcif_timeout_work(struct work_struct *work)
+static void sh_mmcif_timeout_work(struct work_struct *work)
{
struct delayed_work *d = container_of(work, struct delayed_work, work);
struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work);
struct mmc_request *mrq = host->mrq;
+ struct device *dev = sh_mmcif_host_to_dev(host);
unsigned long flags;
if (host->dying)
@@ -1324,7 +1419,7 @@ static void mmcif_timeout_work(struct work_struct *work)
return;
}
- dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n",
+ dev_err(dev, "Timeout waiting for %u on CMD%u\n",
host->wait_for, mrq->cmd->opcode);
host->state = STATE_TIMEOUT;
@@ -1361,7 +1456,8 @@ static void mmcif_timeout_work(struct work_struct *work)
static void sh_mmcif_init_ocr(struct sh_mmcif_host *host)
{
- struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data;
+ struct device *dev = sh_mmcif_host_to_dev(host);
+ struct sh_mmcif_plat_data *pd = dev->platform_data;
struct mmc_host *mmc = host->mmc;
mmc_regulator_get_supply(mmc);
@@ -1380,7 +1476,8 @@ static int sh_mmcif_probe(struct platform_device *pdev)
int ret = 0, irq[2];
struct mmc_host *mmc;
struct sh_mmcif_host *host;
- struct sh_mmcif_plat_data *pd = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct sh_mmcif_plat_data *pd = dev->platform_data;
struct resource *res;
void __iomem *reg;
const char *name;
@@ -1388,16 +1485,16 @@ static int sh_mmcif_probe(struct platform_device *pdev)
irq[0] = platform_get_irq(pdev, 0);
irq[1] = platform_get_irq(pdev, 1);
if (irq[0] < 0) {
- dev_err(&pdev->dev, "Get irq error\n");
+ dev_err(dev, "Get irq error\n");
return -ENXIO;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- reg = devm_ioremap_resource(&pdev->dev, res);
+ reg = devm_ioremap_resource(dev, res);
if (IS_ERR(reg))
return PTR_ERR(reg);
- mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev);
+ mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), dev);
if (!mmc)
return -ENOMEM;
@@ -1408,7 +1505,7 @@ static int sh_mmcif_probe(struct platform_device *pdev)
host = mmc_priv(mmc);
host->mmc = mmc;
host->addr = reg;
- host->timeout = msecs_to_jiffies(1000);
+ host->timeout = msecs_to_jiffies(10000);
host->ccs_enable = !pd || !pd->ccs_unsupported;
host->clk_ctrl2_enable = pd && pd->clk_ctrl2_present;
@@ -1430,41 +1527,44 @@ static int sh_mmcif_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
- pm_runtime_enable(&pdev->dev);
+ pm_runtime_enable(dev);
host->power = false;
- host->hclk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(host->hclk)) {
- ret = PTR_ERR(host->hclk);
- dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
+ host->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(host->clk)) {
+ ret = PTR_ERR(host->clk);
+ dev_err(dev, "cannot get clock: %d\n", ret);
goto err_pm;
}
- ret = sh_mmcif_clk_update(host);
+
+ ret = clk_prepare_enable(host->clk);
if (ret < 0)
goto err_pm;
- ret = pm_runtime_resume(&pdev->dev);
+ sh_mmcif_clk_setup(host);
+
+ ret = pm_runtime_resume(dev);
if (ret < 0)
goto err_clk;
- INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work);
+ INIT_DELAYED_WORK(&host->timeout_work, sh_mmcif_timeout_work);
sh_mmcif_sync_reset(host);
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
- name = irq[1] < 0 ? dev_name(&pdev->dev) : "sh_mmc:error";
- ret = devm_request_threaded_irq(&pdev->dev, irq[0], sh_mmcif_intr,
+ name = irq[1] < 0 ? dev_name(dev) : "sh_mmc:error";
+ ret = devm_request_threaded_irq(dev, irq[0], sh_mmcif_intr,
sh_mmcif_irqt, 0, name, host);
if (ret) {
- dev_err(&pdev->dev, "request_irq error (%s)\n", name);
+ dev_err(dev, "request_irq error (%s)\n", name);
goto err_clk;
}
if (irq[1] >= 0) {
- ret = devm_request_threaded_irq(&pdev->dev, irq[1],
+ ret = devm_request_threaded_irq(dev, irq[1],
sh_mmcif_intr, sh_mmcif_irqt,
0, "sh_mmc:int", host);
if (ret) {
- dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
+ dev_err(dev, "request_irq error (sh_mmc:int)\n");
goto err_clk;
}
}
@@ -1481,19 +1581,19 @@ static int sh_mmcif_probe(struct platform_device *pdev)
if (ret < 0)
goto err_clk;
- dev_pm_qos_expose_latency_limit(&pdev->dev, 100);
+ dev_pm_qos_expose_latency_limit(dev, 100);
- dev_info(&pdev->dev, "Chip version 0x%04x, clock rate %luMHz\n",
+ dev_info(dev, "Chip version 0x%04x, clock rate %luMHz\n",
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0xffff,
- clk_get_rate(host->hclk) / 1000000UL);
+ clk_get_rate(host->clk) / 1000000UL);
- clk_disable_unprepare(host->hclk);
+ clk_disable_unprepare(host->clk);
return ret;
err_clk:
- clk_disable_unprepare(host->hclk);
+ clk_disable_unprepare(host->clk);
err_pm:
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_disable(dev);
err_host:
mmc_free_host(mmc);
return ret;
@@ -1504,7 +1604,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
host->dying = true;
- clk_prepare_enable(host->hclk);
+ clk_prepare_enable(host->clk);
pm_runtime_get_sync(&pdev->dev);
dev_pm_qos_hide_latency_limit(&pdev->dev);
@@ -1519,7 +1619,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)
*/
cancel_delayed_work_sync(&host->timeout_work);
- clk_disable_unprepare(host->hclk);
+ clk_disable_unprepare(host->clk);
mmc_free_host(host->mmc);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -1543,12 +1643,6 @@ static int sh_mmcif_resume(struct device *dev)
}
#endif
-static const struct of_device_id mmcif_of_match[] = {
- { .compatible = "renesas,sh-mmcif" },
- { }
-};
-MODULE_DEVICE_TABLE(of, mmcif_of_match);
-
static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
};
@@ -1559,7 +1653,7 @@ static struct platform_driver sh_mmcif_driver = {
.driver = {
.name = DRIVER_NAME,
.pm = &sh_mmcif_dev_pm_ops,
- .of_match_table = mmcif_of_match,
+ .of_match_table = sh_mmcif_of_match,
},
};
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index f746df493892..e897e7fc3b14 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -85,8 +85,10 @@ static int tmio_mmc_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
+ if (!res) {
+ ret = -EINVAL;
+ goto cell_disable;
+ }
pdata->flags |= TMIO_MMC_HAVE_HIGH_REG;
@@ -101,7 +103,8 @@ static int tmio_mmc_probe(struct platform_device *pdev)
if (ret)
goto host_free;
- ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING,
+ ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq,
+ IRQF_TRIGGER_FALLING,
dev_name(&pdev->dev), host);
if (ret)
goto host_remove;
@@ -129,7 +132,6 @@ static int tmio_mmc_remove(struct platform_device *pdev)
if (mmc) {
struct tmio_mmc_host *host = mmc_priv(mmc);
- free_irq(platform_get_irq(pdev, 0), host);
tmio_mmc_host_remove(host);
if (cell->disable)
cell->disable(pdev);
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index dba7e1c19dd7..e3dcf31a8bd6 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -1108,7 +1108,8 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
if (ret < 0)
goto host_free;
- _host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
+ _host->ctl = devm_ioremap(&pdev->dev,
+ res_ctl->start, resource_size(res_ctl));
if (!_host->ctl) {
ret = -ENOMEM;
goto host_free;
@@ -1230,8 +1231,6 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- iounmap(host->ctl);
}
EXPORT_SYMBOL(tmio_mmc_host_remove);
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index 9f02c28c0204..54479c481a7a 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -16,6 +16,7 @@ config MTD_CFI
config MTD_JEDECPROBE
tristate "Detect non-CFI AMD/JEDEC-compatible flash chips"
select MTD_GEN_PROBE
+ select MTD_CFI_UTIL
help
This option enables JEDEC-style probing of flash chips which are not
compatible with the Common Flash Interface, but will use the common
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index c50d8cf0f60d..c3624eb571d1 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -1295,7 +1295,7 @@ static int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr,
unsigned long bus_ofs = adr & ~(map_bankwidth(map)-1);
int gap = adr - bus_ofs;
int n = min_t(int, len, map_bankwidth(map) - gap);
- map_word datum;
+ map_word datum = map_word_ff(map);
if (n != map_bankwidth(map)) {
/* partial write of a word, load old contents */
diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c
index 09c79bd0b4f4..6f16552cd59f 100644
--- a/drivers/mtd/chips/cfi_util.c
+++ b/drivers/mtd/chips/cfi_util.c
@@ -23,6 +23,194 @@
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
+void cfi_udelay(int us)
+{
+ if (us >= 1000) {
+ msleep((us+999)/1000);
+ } else {
+ udelay(us);
+ cond_resched();
+ }
+}
+EXPORT_SYMBOL(cfi_udelay);
+
+/*
+ * Returns the command address according to the given geometry.
+ */
+uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
+ struct map_info *map, struct cfi_private *cfi)
+{
+ unsigned bankwidth = map_bankwidth(map);
+ unsigned interleave = cfi_interleave(cfi);
+ unsigned type = cfi->device_type;
+ uint32_t addr;
+
+ addr = (cmd_ofs * type) * interleave;
+
+ /* Modify the unlock address if we are in compatibility mode.
+ * For 16bit devices on 8 bit busses
+ * and 32bit devices on 16 bit busses
+ * set the low bit of the alternating bit sequence of the address.
+ */
+ if (((type * interleave) > bankwidth) && ((cmd_ofs & 0xff) == 0xaa))
+ addr |= (type >> 1)*interleave;
+
+ return addr;
+}
+EXPORT_SYMBOL(cfi_build_cmd_addr);
+
+/*
+ * Transforms the CFI command for the given geometry (bus width & interleave).
+ * It looks too long to be inline, but in the common case it should almost all
+ * get optimised away.
+ */
+map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
+{
+ map_word val = { {0} };
+ int wordwidth, words_per_bus, chip_mode, chips_per_word;
+ unsigned long onecmd;
+ int i;
+
+ /* We do it this way to give the compiler a fighting chance
+ of optimising away all the crap for 'bankwidth' larger than
+ an unsigned long, in the common case where that support is
+ disabled */
+ if (map_bankwidth_is_large(map)) {
+ wordwidth = sizeof(unsigned long);
+ words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
+ } else {
+ wordwidth = map_bankwidth(map);
+ words_per_bus = 1;
+ }
+
+ chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
+ chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+
+ /* First, determine what the bit-pattern should be for a single
+ device, according to chip mode and endianness... */
+ switch (chip_mode) {
+ default: BUG();
+ case 1:
+ onecmd = cmd;
+ break;
+ case 2:
+ onecmd = cpu_to_cfi16(map, cmd);
+ break;
+ case 4:
+ onecmd = cpu_to_cfi32(map, cmd);
+ break;
+ }
+
+ /* Now replicate it across the size of an unsigned long, or
+ just to the bus width as appropriate */
+ switch (chips_per_word) {
+ default: BUG();
+#if BITS_PER_LONG >= 64
+ case 8:
+ onecmd |= (onecmd << (chip_mode * 32));
+#endif
+ case 4:
+ onecmd |= (onecmd << (chip_mode * 16));
+ case 2:
+ onecmd |= (onecmd << (chip_mode * 8));
+ case 1:
+ ;
+ }
+
+ /* And finally, for the multi-word case, replicate it
+ in all words in the structure */
+ for (i=0; i < words_per_bus; i++) {
+ val.x[i] = onecmd;
+ }
+
+ return val;
+}
+EXPORT_SYMBOL(cfi_build_cmd);
+
+unsigned long cfi_merge_status(map_word val, struct map_info *map,
+ struct cfi_private *cfi)
+{
+ int wordwidth, words_per_bus, chip_mode, chips_per_word;
+ unsigned long onestat, res = 0;
+ int i;
+
+ /* We do it this way to give the compiler a fighting chance
+ of optimising away all the crap for 'bankwidth' larger than
+ an unsigned long, in the common case where that support is
+ disabled */
+ if (map_bankwidth_is_large(map)) {
+ wordwidth = sizeof(unsigned long);
+ words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
+ } else {
+ wordwidth = map_bankwidth(map);
+ words_per_bus = 1;
+ }
+
+ chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
+ chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+
+ onestat = val.x[0];
+ /* Or all status words together */
+ for (i=1; i < words_per_bus; i++) {
+ onestat |= val.x[i];
+ }
+
+ res = onestat;
+ switch(chips_per_word) {
+ default: BUG();
+#if BITS_PER_LONG >= 64
+ case 8:
+ res |= (onestat >> (chip_mode * 32));
+#endif
+ case 4:
+ res |= (onestat >> (chip_mode * 16));
+ case 2:
+ res |= (onestat >> (chip_mode * 8));
+ case 1:
+ ;
+ }
+
+ /* Last, determine what the bit-pattern should be for a single
+ device, according to chip mode and endianness... */
+ switch (chip_mode) {
+ case 1:
+ break;
+ case 2:
+ res = cfi16_to_cpu(map, res);
+ break;
+ case 4:
+ res = cfi32_to_cpu(map, res);
+ break;
+ default: BUG();
+ }
+ return res;
+}
+EXPORT_SYMBOL(cfi_merge_status);
+
+/*
+ * Sends a CFI command to a bank of flash for the given geometry.
+ *
+ * Returns the offset in flash where the command was written.
+ * If prev_val is non-null, it will be set to the value at the command address,
+ * before the command was written.
+ */
+uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
+ struct map_info *map, struct cfi_private *cfi,
+ int type, map_word *prev_val)
+{
+ map_word val;
+ uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, map, cfi);
+ val = cfi_build_cmd(cmd, map, cfi);
+
+ if (prev_val)
+ *prev_val = map_read(map, addr);
+
+ map_write(map, val, addr);
+
+ return addr - base;
+}
+EXPORT_SYMBOL(cfi_send_gen_cmd);
+
int __xipram cfi_qry_present(struct map_info *map, __u32 base,
struct cfi_private *cfi)
{
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 866d31904475..5e67b4acde78 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -1815,7 +1815,7 @@ static void doc_dbg_unregister(struct docg3 *docg3)
* @chip_id: The chip ID of the supported chip
* @mtd: The structure to fill
*/
-static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
+static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
{
struct docg3 *docg3 = mtd->priv;
int cfg;
@@ -1828,6 +1828,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
case DOC_CHIPID_G3:
mtd->name = kasprintf(GFP_KERNEL, "docg3.%d",
docg3->device_id);
+ if (!mtd->name)
+ return -ENOMEM;
docg3->max_block = 2047;
break;
}
@@ -1850,6 +1852,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
mtd->_block_isbad = doc_block_isbad;
mtd->ecclayout = &docg3_oobinfo;
mtd->ecc_strength = DOC_ECC_BCH_T;
+
+ return 0;
}
/**
@@ -1900,7 +1904,7 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
ret = 0;
if (chip_id != (u16)(~chip_id_inv)) {
- goto nomem3;
+ goto nomem4;
}
switch (chip_id) {
@@ -1910,15 +1914,19 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
break;
default:
doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
- goto nomem3;
+ goto nomem4;
}
- doc_set_driver_info(chip_id, mtd);
+ ret = doc_set_driver_info(chip_id, mtd);
+ if (ret)
+ goto nomem4;
doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ);
doc_reload_bbt(docg3);
return mtd;
+nomem4:
+ kfree(docg3->bbt);
nomem3:
kfree(mtd);
nomem2:
@@ -2117,7 +2125,7 @@ static int docg3_release(struct platform_device *pdev)
}
#ifdef CONFIG_OF
-static struct of_device_id docg3_dt_ids[] = {
+static const struct of_device_id docg3_dt_ids[] = {
{ .compatible = "m-systems,diskonchip-g3" },
{}
};
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 7c8b1694a134..d313f948b96c 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -223,7 +223,7 @@ static int m25p_probe(struct spi_device *spi)
*/
if (data && data->type)
flash_name = data->type;
- else if (!strcmp(spi->modalias, "nor-jedec"))
+ else if (!strcmp(spi->modalias, "spi-nor"))
flash_name = NULL; /* auto-detect */
else
flash_name = spi->modalias;
@@ -255,57 +255,45 @@ static int m25p_remove(struct spi_device *spi)
* since most of these flash are compatible to some extent, and their
* differences can often be differentiated by the JEDEC read-ID command, we
* encourage new users to add support to the spi-nor library, and simply bind
- * against a generic string here (e.g., "nor-jedec").
+ * against a generic string here (e.g., "jedec,spi-nor").
*
* Many flash names are kept here in this list (as well as in spi-nor.c) to
* keep them available as module aliases for existing platforms.
*/
static const struct spi_device_id m25p_ids[] = {
- {"at25fs010"}, {"at25fs040"}, {"at25df041a"}, {"at25df321a"},
- {"at25df641"}, {"at26f004"}, {"at26df081a"}, {"at26df161a"},
- {"at26df321"}, {"at45db081d"},
- {"en25f32"}, {"en25p32"}, {"en25q32b"}, {"en25p64"},
- {"en25q64"}, {"en25qh128"}, {"en25qh256"},
- {"f25l32pa"},
- {"mr25h256"}, {"mr25h10"},
- {"gd25q32"}, {"gd25q64"},
- {"160s33b"}, {"320s33b"}, {"640s33b"},
- {"mx25l2005a"}, {"mx25l4005a"}, {"mx25l8005"}, {"mx25l1606e"},
- {"mx25l3205d"}, {"mx25l3255e"}, {"mx25l6405d"}, {"mx25l12805d"},
- {"mx25l12855e"},{"mx25l25635e"},{"mx25l25655e"},{"mx66l51235l"},
- {"mx66l1g55g"},
- {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q256a"},
- {"n25q512a"}, {"n25q512ax3"}, {"n25q00"},
- {"pm25lv512"}, {"pm25lv010"}, {"pm25lq032"},
- {"s25sl032p"}, {"s25sl064p"}, {"s25fl256s0"}, {"s25fl256s1"},
- {"s25fl512s"}, {"s70fl01gs"}, {"s25sl12800"}, {"s25sl12801"},
- {"s25fl129p0"}, {"s25fl129p1"}, {"s25sl004a"}, {"s25sl008a"},
- {"s25sl016a"}, {"s25sl032a"}, {"s25sl064a"}, {"s25fl008k"},
- {"s25fl016k"}, {"s25fl064k"}, {"s25fl132k"},
- {"sst25vf040b"},{"sst25vf080b"},{"sst25vf016b"},{"sst25vf032b"},
- {"sst25vf064c"},{"sst25wf512"}, {"sst25wf010"}, {"sst25wf020"},
- {"sst25wf040"},
- {"m25p05"}, {"m25p10"}, {"m25p20"}, {"m25p40"},
- {"m25p80"}, {"m25p16"}, {"m25p32"}, {"m25p64"},
- {"m25p128"}, {"n25q032"},
+ /*
+ * Entries not used in DTs that should be safe to drop after replacing
+ * them with "nor-jedec" in platform data.
+ */
+ {"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"},
+
+ /*
+ * Entries that were used in DTs without "nor-jedec" fallback and should
+ * be kept for backward compatibility.
+ */
+ {"at25df321a"}, {"at25df641"}, {"at26df081a"},
+ {"mr25h256"},
+ {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
+ {"mx25l25635e"},{"mx66l51235l"},
+ {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
+ {"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"},
+ {"s25fl064k"},
+ {"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
+ {"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"},
+ {"m25p64"}, {"m25p128"},
+ {"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
+ {"w25q80bl"}, {"w25q128"}, {"w25q256"},
+
+ /* Flashes that can't be detected using JEDEC */
{"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"},
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
{"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"},
- {"m45pe10"}, {"m45pe80"}, {"m45pe16"},
- {"m25pe20"}, {"m25pe80"}, {"m25pe16"},
- {"m25px16"}, {"m25px32"}, {"m25px32-s0"}, {"m25px32-s1"},
- {"m25px64"}, {"m25px80"},
- {"w25x10"}, {"w25x20"}, {"w25x40"}, {"w25x80"},
- {"w25x16"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
- {"w25x64"}, {"w25q64"}, {"w25q80"}, {"w25q80bl"},
- {"w25q128"}, {"w25q256"}, {"cat25c11"},
- {"cat25c03"}, {"cat25c09"}, {"cat25c17"}, {"cat25128"},
/*
* Generic support for SPI NOR that can be identified by the JEDEC READ
* ID opcode (0x9F). Use this, if possible.
*/
- {"nor-jedec"},
+ {"spi-nor"},
{ },
};
MODULE_DEVICE_TABLE(spi, m25p_ids);
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
index 508bab3bd0c4..04b24d2b03f2 100644
--- a/drivers/mtd/devices/spear_smi.c
+++ b/drivers/mtd/devices/spear_smi.c
@@ -1,8 +1,8 @@
/*
* SMI (Serial Memory Controller) device driver for Serial NOR Flash on
* SPEAr platform
- * The serial nor interface is largely based on drivers/mtd/m25p80.c,
- * however the SPI interface has been replaced by SMI.
+ * The serial nor interface is largely based on m25p80.c, however the SPI
+ * interface has been replaced by SMI.
*
* Copyright © 2010 STMicroelectronics.
* Ashish Priyadarshi
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index e715ae90632f..7c95a656f9e4 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -326,7 +326,7 @@ config MTD_BFIN_ASYNC
config MTD_GPIO_ADDR
tristate "GPIO-assisted Flash Chip Support"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
depends on MTD_COMPLEX_MAPPINGS
help
Map driver which allows flashes to be partially physically addressed
diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c
index f7207b0a76dc..f2b68667ea59 100644
--- a/drivers/mtd/maps/amd76xrom.c
+++ b/drivers/mtd/maps/amd76xrom.c
@@ -138,7 +138,7 @@ static int amd76xrom_init_one(struct pci_dev *pdev,
/*
* Try to reserve the window mem region. If this fails then
* it is likely due to a fragment of the window being
- * "reseved" by the BIOS. In the case that the
+ * "reserved" by the BIOS. In the case that the
* request_mem_region() fails then once the rom size is
* discovered we will try to reserve the unreserved fragment.
*/
diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c
index f8a7dd14cee0..70a3db3ab856 100644
--- a/drivers/mtd/maps/dc21285.c
+++ b/drivers/mtd/maps/dc21285.c
@@ -38,9 +38,9 @@ static void nw_en_write(void)
* we want to write a bit pattern XXX1 to Xilinx to enable
* the write gate, which will be open for about the next 2ms.
*/
- spin_lock_irqsave(&nw_gpio_lock, flags);
+ raw_spin_lock_irqsave(&nw_gpio_lock, flags);
nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
- spin_unlock_irqrestore(&nw_gpio_lock, flags);
+ raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
/*
* let the ISA bus to catch on...
diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c
index f784cf0caa13..76ed651b515b 100644
--- a/drivers/mtd/maps/esb2rom.c
+++ b/drivers/mtd/maps/esb2rom.c
@@ -234,7 +234,7 @@ static int esb2rom_init_one(struct pci_dev *pdev,
/*
* Try to reserve the window mem region. If this fails then
- * it is likely due to the window being "reseved" by the BIOS.
+ * it is likely due to the window being "reserved" by the BIOS.
*/
window->rsrc.name = MOD_NAME;
window->rsrc.start = window->phys;
diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c
index c7478e18f485..8636bba42200 100644
--- a/drivers/mtd/maps/ichxrom.c
+++ b/drivers/mtd/maps/ichxrom.c
@@ -167,7 +167,7 @@ static int ichxrom_init_one(struct pci_dev *pdev,
/*
* Try to reserve the window mem region. If this fails then
- * it is likely due to the window being "reseved" by the BIOS.
+ * it is likely due to the window being "reserved" by the BIOS.
*/
window->rsrc.name = MOD_NAME;
window->rsrc.start = window->phys;
diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c
index 33d26f5bee54..e2f878216048 100644
--- a/drivers/mtd/maps/lantiq-flash.c
+++ b/drivers/mtd/maps/lantiq-flash.c
@@ -45,7 +45,6 @@ struct ltq_mtd {
};
static const char ltq_map_name[] = "ltq_nor";
-static const char * const ltq_probe_types[] = { "cmdlinepart", "ofpart", NULL };
static map_word
ltq_read16(struct map_info *map, unsigned long adr)
@@ -168,8 +167,7 @@ ltq_mtd_probe(struct platform_device *pdev)
cfi->addr_unlock2 ^= 1;
ppdata.of_node = pdev->dev.of_node;
- err = mtd_device_parse_register(ltq_mtd->mtd, ltq_probe_types,
- &ppdata, NULL, 0);
+ err = mtd_device_parse_register(ltq_mtd->mtd, NULL, &ppdata, NULL, 0);
if (err) {
dev_err(&pdev->dev, "failed to add partitions\n");
goto err_destroy;
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index ff26e979b1a1..774b32fd29e6 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -147,7 +147,7 @@ static void of_free_probes(const char * const *probes)
kfree(probes);
}
-static struct of_device_id of_flash_match[];
+static const struct of_device_id of_flash_match[];
static int of_flash_probe(struct platform_device *dev)
{
const char * const *part_probe_types;
@@ -327,7 +327,7 @@ err_flash_remove:
return err;
}
-static struct of_device_id of_flash_match[] = {
+static const struct of_device_id of_flash_match[] = {
{
.compatible = "cfi-flash",
.data = (void *)"cfi_probe",
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 2b0c52870999..41acc507b22e 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -197,6 +197,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
mutex_lock(&dev->lock);
+ mutex_lock(&mtd_table_mutex);
if (dev->open)
goto unlock;
@@ -220,6 +221,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
unlock:
dev->open++;
+ mutex_unlock(&mtd_table_mutex);
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
return ret;
@@ -230,6 +232,7 @@ error_release:
error_put:
module_put(dev->tr->owner);
kref_put(&dev->ref, blktrans_dev_release);
+ mutex_unlock(&mtd_table_mutex);
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
return ret;
@@ -243,6 +246,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
return;
mutex_lock(&dev->lock);
+ mutex_lock(&mtd_table_mutex);
if (--dev->open)
goto unlock;
@@ -256,6 +260,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
__put_mtd_device(dev->mtd);
}
unlock:
+ mutex_unlock(&mtd_table_mutex);
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
}
@@ -273,7 +278,7 @@ static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
if (!dev->mtd)
goto unlock;
- ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0;
+ ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : -ENOTTY;
unlock:
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index d172195fbd15..8bbbb751bf45 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -48,14 +48,34 @@
static struct backing_dev_info mtd_bdi = {
};
-static int mtd_cls_suspend(struct device *dev, pm_message_t state);
-static int mtd_cls_resume(struct device *dev);
+#ifdef CONFIG_PM_SLEEP
+
+static int mtd_cls_suspend(struct device *dev)
+{
+ struct mtd_info *mtd = dev_get_drvdata(dev);
+
+ return mtd ? mtd_suspend(mtd) : 0;
+}
+
+static int mtd_cls_resume(struct device *dev)
+{
+ struct mtd_info *mtd = dev_get_drvdata(dev);
+
+ if (mtd)
+ mtd_resume(mtd);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mtd_cls_pm_ops, mtd_cls_suspend, mtd_cls_resume);
+#define MTD_CLS_PM_OPS (&mtd_cls_pm_ops)
+#else
+#define MTD_CLS_PM_OPS NULL
+#endif
static struct class mtd_class = {
.name = "mtd",
.owner = THIS_MODULE,
- .suspend = mtd_cls_suspend,
- .resume = mtd_cls_resume,
+ .pm = MTD_CLS_PM_OPS,
};
static DEFINE_IDR(mtd_idr);
@@ -88,22 +108,6 @@ static void mtd_release(struct device *dev)
device_destroy(&mtd_class, index + 1);
}
-static int mtd_cls_suspend(struct device *dev, pm_message_t state)
-{
- struct mtd_info *mtd = dev_get_drvdata(dev);
-
- return mtd ? mtd_suspend(mtd) : 0;
-}
-
-static int mtd_cls_resume(struct device *dev)
-{
- struct mtd_info *mtd = dev_get_drvdata(dev);
-
- if (mtd)
- mtd_resume(mtd);
- return 0;
-}
-
static ssize_t mtd_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -375,8 +379,7 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
*
* Add a device to the list of MTD devices present in the system, and
* notify each currently active MTD 'user' of its arrival. Returns
- * zero on success or 1 on failure, which currently will only happen
- * if there is insufficient memory or a sysfs error.
+ * zero on success or non-zero on failure.
*/
int add_mtd_device(struct mtd_info *mtd)
@@ -390,8 +393,10 @@ int add_mtd_device(struct mtd_info *mtd)
mutex_lock(&mtd_table_mutex);
i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
- if (i < 0)
+ if (i < 0) {
+ error = i;
goto fail_locked;
+ }
mtd->index = i;
mtd->usecount = 0;
@@ -420,6 +425,8 @@ int add_mtd_device(struct mtd_info *mtd)
printk(KERN_WARNING
"%s: unlock failed, writes may not work\n",
mtd->name);
+ /* Ignore unlock failures? */
+ error = 0;
}
/* Caller should have set dev.parent to match the
@@ -430,7 +437,8 @@ int add_mtd_device(struct mtd_info *mtd)
mtd->dev.devt = MTD_DEVT(i);
dev_set_name(&mtd->dev, "mtd%d", i);
dev_set_drvdata(&mtd->dev, mtd);
- if (device_register(&mtd->dev) != 0)
+ error = device_register(&mtd->dev);
+ if (error)
goto fail_added;
device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
@@ -454,7 +462,7 @@ fail_added:
idr_remove(&mtd_idr, i);
fail_locked:
mutex_unlock(&mtd_table_mutex);
- return 1;
+ return error;
}
/**
@@ -510,8 +518,8 @@ static int mtd_add_device_partitions(struct mtd_info *mtd,
if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
ret = add_mtd_device(mtd);
- if (ret == 1)
- return -ENODEV;
+ if (ret)
+ return ret;
}
if (nbparts > 0) {
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 5897d8d8fa5a..5b2806a7e5f7 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -76,7 +76,7 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
config MTD_NAND_GPIO
tristate "GPIO assisted NAND Flash driver"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
This enables a NAND flash driver where control signals are
connected to GPIO pins, and commands and data are communicated
@@ -394,6 +394,14 @@ config MTD_NAND_GPMI_NAND
block, such as SD card. So pay attention to it when you enable
the GPMI.
+config MTD_NAND_BRCMNAND
+ tristate "Broadcom STB NAND controller"
+ depends on ARM || MIPS
+ help
+ Enables the Broadcom NAND controller driver. The controller was
+ originally designed for Set-Top Box but is used on various BCM7xxx,
+ BCM3xxx, BCM63xxx, iProc/Cygnus and more.
+
config MTD_NAND_BCM47XXNFLASH
tristate "Support for NAND flash on BCM4706 BCMA bus"
depends on BCMA_NFLASH
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 582bbd05aff7..1f897ec3c242 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -52,5 +52,6 @@ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
+obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/drivers/mtd/nand/brcmnand/Makefile b/drivers/mtd/nand/brcmnand/Makefile
new file mode 100644
index 000000000000..3b1fbfd27d4f
--- /dev/null
+++ b/drivers/mtd/nand/brcmnand/Makefile
@@ -0,0 +1,6 @@
+# link order matters; don't link the more generic brcmstb_nand.o before the
+# more specific iproc_nand.o, for instance
+obj-$(CONFIG_MTD_NAND_BRCMNAND) += iproc_nand.o
+obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm63138_nand.o
+obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o
+obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o
diff --git a/drivers/mtd/nand/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/brcmnand/bcm63138_nand.c
new file mode 100644
index 000000000000..3f4c44c24e14
--- /dev/null
+++ b/drivers/mtd/nand/brcmnand/bcm63138_nand.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright © 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "brcmnand.h"
+
+struct bcm63138_nand_soc_priv {
+ void __iomem *base;
+};
+
+#define BCM63138_NAND_INT_STATUS 0x00
+#define BCM63138_NAND_INT_EN 0x04
+
+enum {
+ BCM63138_CTLRDY = BIT(4),
+};
+
+static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc)
+{
+ struct bcm63138_nand_soc_priv *priv = soc->priv;
+ void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS;
+ u32 val = brcmnand_readl(mmio);
+
+ if (val & BCM63138_CTLRDY) {
+ brcmnand_writel(val & ~BCM63138_CTLRDY, mmio);
+ return true;
+ }
+
+ return false;
+}
+
+static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en)
+{
+ struct bcm63138_nand_soc_priv *priv = soc->priv;
+ void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN;
+ u32 val = brcmnand_readl(mmio);
+
+ if (en)
+ val |= BCM63138_CTLRDY;
+ else
+ val &= ~BCM63138_CTLRDY;
+
+ brcmnand_writel(val, mmio);
+}
+
+static int bcm63138_nand_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bcm63138_nand_soc_priv *priv;
+ struct brcmnand_soc *soc;
+ struct resource *res;
+
+ soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL);
+ if (!soc)
+ return -ENOMEM;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base");
+ priv->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ soc->pdev = pdev;
+ soc->priv = priv;
+ soc->ctlrdy_ack = bcm63138_nand_intc_ack;
+ soc->ctlrdy_set_enabled = bcm63138_nand_intc_set;
+
+ return brcmnand_probe(pdev, soc);
+}
+
+static const struct of_device_id bcm63138_nand_of_match[] = {
+ { .compatible = "brcm,nand-bcm63138" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match);
+
+static struct platform_driver bcm63138_nand_driver = {
+ .probe = bcm63138_nand_probe,
+ .remove = brcmnand_remove,
+ .driver = {
+ .name = "bcm63138_nand",
+ .pm = &brcmnand_pm_ops,
+ .of_match_table = bcm63138_nand_of_match,
+ }
+};
+module_platform_driver(bcm63138_nand_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Brian Norris");
+MODULE_DESCRIPTION("NAND driver for BCM63138");
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
new file mode 100644
index 000000000000..fddb795eeb71
--- /dev/null
+++ b/drivers/mtd/nand/brcmnand/brcmnand.c
@@ -0,0 +1,2246 @@
+/*
+ * Copyright © 2010-2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/ioport.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/mm.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of.h>
+#include <linux/of_mtd.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/log2.h>
+
+#include "brcmnand.h"
+
+/*
+ * This flag controls if WP stays on between erase/write commands to mitigate
+ * flash corruption due to power glitches. Values:
+ * 0: NAND_WP is not used or not available
+ * 1: NAND_WP is set by default, cleared for erase/write operations
+ * 2: NAND_WP is always cleared
+ */
+static int wp_on = 1;
+module_param(wp_on, int, 0444);
+
+/***********************************************************************
+ * Definitions
+ ***********************************************************************/
+
+#define DRV_NAME "brcmnand"
+
+#define CMD_NULL 0x00
+#define CMD_PAGE_READ 0x01
+#define CMD_SPARE_AREA_READ 0x02
+#define CMD_STATUS_READ 0x03
+#define CMD_PROGRAM_PAGE 0x04
+#define CMD_PROGRAM_SPARE_AREA 0x05
+#define CMD_COPY_BACK 0x06
+#define CMD_DEVICE_ID_READ 0x07
+#define CMD_BLOCK_ERASE 0x08
+#define CMD_FLASH_RESET 0x09
+#define CMD_BLOCKS_LOCK 0x0a
+#define CMD_BLOCKS_LOCK_DOWN 0x0b
+#define CMD_BLOCKS_UNLOCK 0x0c
+#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d
+#define CMD_PARAMETER_READ 0x0e
+#define CMD_PARAMETER_CHANGE_COL 0x0f
+#define CMD_LOW_LEVEL_OP 0x10
+
+struct brcm_nand_dma_desc {
+ u32 next_desc;
+ u32 next_desc_ext;
+ u32 cmd_irq;
+ u32 dram_addr;
+ u32 dram_addr_ext;
+ u32 tfr_len;
+ u32 total_len;
+ u32 flash_addr;
+ u32 flash_addr_ext;
+ u32 cs;
+ u32 pad2[5];
+ u32 status_valid;
+} __packed;
+
+/* Bitfields for brcm_nand_dma_desc::status_valid */
+#define FLASH_DMA_ECC_ERROR (1 << 8)
+#define FLASH_DMA_CORR_ERROR (1 << 9)
+
+/* 512B flash cache in the NAND controller HW */
+#define FC_SHIFT 9U
+#define FC_BYTES 512U
+#define FC_WORDS (FC_BYTES >> 2)
+
+#define BRCMNAND_MIN_PAGESIZE 512
+#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024)
+#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024)
+
+/* Controller feature flags */
+enum {
+ BRCMNAND_HAS_1K_SECTORS = BIT(0),
+ BRCMNAND_HAS_PREFETCH = BIT(1),
+ BRCMNAND_HAS_CACHE_MODE = BIT(2),
+ BRCMNAND_HAS_WP = BIT(3),
+};
+
+struct brcmnand_controller {
+ struct device *dev;
+ struct nand_hw_control controller;
+ void __iomem *nand_base;
+ void __iomem *nand_fc; /* flash cache */
+ void __iomem *flash_dma_base;
+ unsigned int irq;
+ unsigned int dma_irq;
+ int nand_version;
+
+ /* Some SoCs provide custom interrupt status register(s) */
+ struct brcmnand_soc *soc;
+
+ int cmd_pending;
+ bool dma_pending;
+ struct completion done;
+ struct completion dma_done;
+
+ /* List of NAND hosts (one for each chip-select) */
+ struct list_head host_list;
+
+ struct brcm_nand_dma_desc *dma_desc;
+ dma_addr_t dma_pa;
+
+ /* in-memory cache of the FLASH_CACHE, used only for some commands */
+ u32 flash_cache[FC_WORDS];
+
+ /* Controller revision details */
+ const u16 *reg_offsets;
+ unsigned int reg_spacing; /* between CS1, CS2, ... regs */
+ const u8 *cs_offsets; /* within each chip-select */
+ const u8 *cs0_offsets; /* within CS0, if different */
+ unsigned int max_block_size;
+ const unsigned int *block_sizes;
+ unsigned int max_page_size;
+ const unsigned int *page_sizes;
+ unsigned int max_oob;
+ u32 features;
+
+ /* for low-power standby/resume only */
+ u32 nand_cs_nand_select;
+ u32 nand_cs_nand_xor;
+ u32 corr_stat_threshold;
+ u32 flash_dma_mode;
+};
+
+struct brcmnand_cfg {
+ u64 device_size;
+ unsigned int block_size;
+ unsigned int page_size;
+ unsigned int spare_area_size;
+ unsigned int device_width;
+ unsigned int col_adr_bytes;
+ unsigned int blk_adr_bytes;
+ unsigned int ful_adr_bytes;
+ unsigned int sector_size_1k;
+ unsigned int ecc_level;
+ /* use for low-power standby/resume only */
+ u32 acc_control;
+ u32 config;
+ u32 config_ext;
+ u32 timing_1;
+ u32 timing_2;
+};
+
+struct brcmnand_host {
+ struct list_head node;
+ struct device_node *of_node;
+
+ struct nand_chip chip;
+ struct mtd_info mtd;
+ struct platform_device *pdev;
+ int cs;
+
+ unsigned int last_cmd;
+ unsigned int last_byte;
+ u64 last_addr;
+ struct brcmnand_cfg hwcfg;
+ struct brcmnand_controller *ctrl;
+};
+
+enum brcmnand_reg {
+ BRCMNAND_CMD_START = 0,
+ BRCMNAND_CMD_EXT_ADDRESS,
+ BRCMNAND_CMD_ADDRESS,
+ BRCMNAND_INTFC_STATUS,
+ BRCMNAND_CS_SELECT,
+ BRCMNAND_CS_XOR,
+ BRCMNAND_LL_OP,
+ BRCMNAND_CS0_BASE,
+ BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */
+ BRCMNAND_CORR_THRESHOLD,
+ BRCMNAND_CORR_THRESHOLD_EXT,
+ BRCMNAND_UNCORR_COUNT,
+ BRCMNAND_CORR_COUNT,
+ BRCMNAND_CORR_EXT_ADDR,
+ BRCMNAND_CORR_ADDR,
+ BRCMNAND_UNCORR_EXT_ADDR,
+ BRCMNAND_UNCORR_ADDR,
+ BRCMNAND_SEMAPHORE,
+ BRCMNAND_ID,
+ BRCMNAND_ID_EXT,
+ BRCMNAND_LL_RDATA,
+ BRCMNAND_OOB_READ_BASE,
+ BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */
+ BRCMNAND_OOB_WRITE_BASE,
+ BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */
+ BRCMNAND_FC_BASE,
+};
+
+/* BRCMNAND v4.0 */
+static const u16 brcmnand_regs_v40[] = {
+ [BRCMNAND_CMD_START] = 0x04,
+ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
+ [BRCMNAND_CMD_ADDRESS] = 0x0c,
+ [BRCMNAND_INTFC_STATUS] = 0x6c,
+ [BRCMNAND_CS_SELECT] = 0x14,
+ [BRCMNAND_CS_XOR] = 0x18,
+ [BRCMNAND_LL_OP] = 0x178,
+ [BRCMNAND_CS0_BASE] = 0x40,
+ [BRCMNAND_CS1_BASE] = 0xd0,
+ [BRCMNAND_CORR_THRESHOLD] = 0x84,
+ [BRCMNAND_CORR_THRESHOLD_EXT] = 0,
+ [BRCMNAND_UNCORR_COUNT] = 0,
+ [BRCMNAND_CORR_COUNT] = 0,
+ [BRCMNAND_CORR_EXT_ADDR] = 0x70,
+ [BRCMNAND_CORR_ADDR] = 0x74,
+ [BRCMNAND_UNCORR_EXT_ADDR] = 0x78,
+ [BRCMNAND_UNCORR_ADDR] = 0x7c,
+ [BRCMNAND_SEMAPHORE] = 0x58,
+ [BRCMNAND_ID] = 0x60,
+ [BRCMNAND_ID_EXT] = 0x64,
+ [BRCMNAND_LL_RDATA] = 0x17c,
+ [BRCMNAND_OOB_READ_BASE] = 0x20,
+ [BRCMNAND_OOB_READ_10_BASE] = 0x130,
+ [BRCMNAND_OOB_WRITE_BASE] = 0x30,
+ [BRCMNAND_OOB_WRITE_10_BASE] = 0,
+ [BRCMNAND_FC_BASE] = 0x200,
+};
+
+/* BRCMNAND v5.0 */
+static const u16 brcmnand_regs_v50[] = {
+ [BRCMNAND_CMD_START] = 0x04,
+ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
+ [BRCMNAND_CMD_ADDRESS] = 0x0c,
+ [BRCMNAND_INTFC_STATUS] = 0x6c,
+ [BRCMNAND_CS_SELECT] = 0x14,
+ [BRCMNAND_CS_XOR] = 0x18,
+ [BRCMNAND_LL_OP] = 0x178,
+ [BRCMNAND_CS0_BASE] = 0x40,
+ [BRCMNAND_CS1_BASE] = 0xd0,
+ [BRCMNAND_CORR_THRESHOLD] = 0x84,
+ [BRCMNAND_CORR_THRESHOLD_EXT] = 0,
+ [BRCMNAND_UNCORR_COUNT] = 0,
+ [BRCMNAND_CORR_COUNT] = 0,
+ [BRCMNAND_CORR_EXT_ADDR] = 0x70,
+ [BRCMNAND_CORR_ADDR] = 0x74,
+ [BRCMNAND_UNCORR_EXT_ADDR] = 0x78,
+ [BRCMNAND_UNCORR_ADDR] = 0x7c,
+ [BRCMNAND_SEMAPHORE] = 0x58,
+ [BRCMNAND_ID] = 0x60,
+ [BRCMNAND_ID_EXT] = 0x64,
+ [BRCMNAND_LL_RDATA] = 0x17c,
+ [BRCMNAND_OOB_READ_BASE] = 0x20,
+ [BRCMNAND_OOB_READ_10_BASE] = 0x130,
+ [BRCMNAND_OOB_WRITE_BASE] = 0x30,
+ [BRCMNAND_OOB_WRITE_10_BASE] = 0x140,
+ [BRCMNAND_FC_BASE] = 0x200,
+};
+
+/* BRCMNAND v6.0 - v7.1 */
+static const u16 brcmnand_regs_v60[] = {
+ [BRCMNAND_CMD_START] = 0x04,
+ [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
+ [BRCMNAND_CMD_ADDRESS] = 0x0c,
+ [BRCMNAND_INTFC_STATUS] = 0x14,
+ [BRCMNAND_CS_SELECT] = 0x18,
+ [BRCMNAND_CS_XOR] = 0x1c,
+ [BRCMNAND_LL_OP] = 0x20,
+ [BRCMNAND_CS0_BASE] = 0x50,
+ [BRCMNAND_CS1_BASE] = 0,
+ [BRCMNAND_CORR_THRESHOLD] = 0xc0,
+ [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4,
+ [BRCMNAND_UNCORR_COUNT] = 0xfc,
+ [BRCMNAND_CORR_COUNT] = 0x100,
+ [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
+ [BRCMNAND_CORR_ADDR] = 0x110,
+ [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
+ [BRCMNAND_UNCORR_ADDR] = 0x118,
+ [BRCMNAND_SEMAPHORE] = 0x150,
+ [BRCMNAND_ID] = 0x194,
+ [BRCMNAND_ID_EXT] = 0x198,
+ [BRCMNAND_LL_RDATA] = 0x19c,
+ [BRCMNAND_OOB_READ_BASE] = 0x200,
+ [BRCMNAND_OOB_READ_10_BASE] = 0,
+ [BRCMNAND_OOB_WRITE_BASE] = 0x280,
+ [BRCMNAND_OOB_WRITE_10_BASE] = 0,
+ [BRCMNAND_FC_BASE] = 0x400,
+};
+
+enum brcmnand_cs_reg {
+ BRCMNAND_CS_CFG_EXT = 0,
+ BRCMNAND_CS_CFG,
+ BRCMNAND_CS_ACC_CONTROL,
+ BRCMNAND_CS_TIMING1,
+ BRCMNAND_CS_TIMING2,
+};
+
+/* Per chip-select offsets for v7.1 */
+static const u8 brcmnand_cs_offsets_v71[] = {
+ [BRCMNAND_CS_ACC_CONTROL] = 0x00,
+ [BRCMNAND_CS_CFG_EXT] = 0x04,
+ [BRCMNAND_CS_CFG] = 0x08,
+ [BRCMNAND_CS_TIMING1] = 0x0c,
+ [BRCMNAND_CS_TIMING2] = 0x10,
+};
+
+/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */
+static const u8 brcmnand_cs_offsets[] = {
+ [BRCMNAND_CS_ACC_CONTROL] = 0x00,
+ [BRCMNAND_CS_CFG_EXT] = 0x04,
+ [BRCMNAND_CS_CFG] = 0x04,
+ [BRCMNAND_CS_TIMING1] = 0x08,
+ [BRCMNAND_CS_TIMING2] = 0x0c,
+};
+
+/* Per chip-select offset for <= v5.0 on CS0 only */
+static const u8 brcmnand_cs_offsets_cs0[] = {
+ [BRCMNAND_CS_ACC_CONTROL] = 0x00,
+ [BRCMNAND_CS_CFG_EXT] = 0x08,
+ [BRCMNAND_CS_CFG] = 0x08,
+ [BRCMNAND_CS_TIMING1] = 0x10,
+ [BRCMNAND_CS_TIMING2] = 0x14,
+};
+
+/* BRCMNAND_INTFC_STATUS */
+enum {
+ INTFC_FLASH_STATUS = GENMASK(7, 0),
+
+ INTFC_ERASED = BIT(27),
+ INTFC_OOB_VALID = BIT(28),
+ INTFC_CACHE_VALID = BIT(29),
+ INTFC_FLASH_READY = BIT(30),
+ INTFC_CTLR_READY = BIT(31),
+};
+
+static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
+{
+ return brcmnand_readl(ctrl->nand_base + offs);
+}
+
+static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
+ u32 val)
+{
+ brcmnand_writel(val, ctrl->nand_base + offs);
+}
+
+static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
+{
+ static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 };
+ static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 };
+ static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 };
+
+ ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
+
+ /* Only support v4.0+? */
+ if (ctrl->nand_version < 0x0400) {
+ dev_err(ctrl->dev, "version %#x not supported\n",
+ ctrl->nand_version);
+ return -ENODEV;
+ }
+
+ /* Register offsets */
+ if (ctrl->nand_version >= 0x0600)
+ ctrl->reg_offsets = brcmnand_regs_v60;
+ else if (ctrl->nand_version >= 0x0500)
+ ctrl->reg_offsets = brcmnand_regs_v50;
+ else if (ctrl->nand_version >= 0x0400)
+ ctrl->reg_offsets = brcmnand_regs_v40;
+
+ /* Chip-select stride */
+ if (ctrl->nand_version >= 0x0701)
+ ctrl->reg_spacing = 0x14;
+ else
+ ctrl->reg_spacing = 0x10;
+
+ /* Per chip-select registers */
+ if (ctrl->nand_version >= 0x0701) {
+ ctrl->cs_offsets = brcmnand_cs_offsets_v71;
+ } else {
+ ctrl->cs_offsets = brcmnand_cs_offsets;
+
+ /* v5.0 and earlier has a different CS0 offset layout */
+ if (ctrl->nand_version <= 0x0500)
+ ctrl->cs0_offsets = brcmnand_cs_offsets_cs0;
+ }
+
+ /* Page / block sizes */
+ if (ctrl->nand_version >= 0x0701) {
+ /* >= v7.1 use nice power-of-2 values! */
+ ctrl->max_page_size = 16 * 1024;
+ ctrl->max_block_size = 2 * 1024 * 1024;
+ } else {
+ ctrl->page_sizes = page_sizes;
+ if (ctrl->nand_version >= 0x0600)
+ ctrl->block_sizes = block_sizes_v6;
+ else
+ ctrl->block_sizes = block_sizes_v4;
+
+ if (ctrl->nand_version < 0x0400) {
+ ctrl->max_page_size = 4096;
+ ctrl->max_block_size = 512 * 1024;
+ }
+ }
+
+ /* Maximum spare area sector size (per 512B) */
+ if (ctrl->nand_version >= 0x0600)
+ ctrl->max_oob = 64;
+ else if (ctrl->nand_version >= 0x0500)
+ ctrl->max_oob = 32;
+ else
+ ctrl->max_oob = 16;
+
+ /* v6.0 and newer (except v6.1) have prefetch support */
+ if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601)
+ ctrl->features |= BRCMNAND_HAS_PREFETCH;
+
+ /*
+ * v6.x has cache mode, but it's implemented differently. Ignore it for
+ * now.
+ */
+ if (ctrl->nand_version >= 0x0700)
+ ctrl->features |= BRCMNAND_HAS_CACHE_MODE;
+
+ if (ctrl->nand_version >= 0x0500)
+ ctrl->features |= BRCMNAND_HAS_1K_SECTORS;
+
+ if (ctrl->nand_version >= 0x0700)
+ ctrl->features |= BRCMNAND_HAS_WP;
+ else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp"))
+ ctrl->features |= BRCMNAND_HAS_WP;
+
+ return 0;
+}
+
+static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl,
+ enum brcmnand_reg reg)
+{
+ u16 offs = ctrl->reg_offsets[reg];
+
+ if (offs)
+ return nand_readreg(ctrl, offs);
+ else
+ return 0;
+}
+
+static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl,
+ enum brcmnand_reg reg, u32 val)
+{
+ u16 offs = ctrl->reg_offsets[reg];
+
+ if (offs)
+ nand_writereg(ctrl, offs, val);
+}
+
+static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
+ enum brcmnand_reg reg, u32 mask, unsigned
+ int shift, u32 val)
+{
+ u32 tmp = brcmnand_read_reg(ctrl, reg);
+
+ tmp &= ~mask;
+ tmp |= val << shift;
+ brcmnand_write_reg(ctrl, reg, tmp);
+}
+
+static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
+{
+ return __raw_readl(ctrl->nand_fc + word * 4);
+}
+
+static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
+ int word, u32 val)
+{
+ __raw_writel(val, ctrl->nand_fc + word * 4);
+}
+
+static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs,
+ enum brcmnand_cs_reg reg)
+{
+ u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE];
+ u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE];
+ u8 cs_offs;
+
+ if (cs == 0 && ctrl->cs0_offsets)
+ cs_offs = ctrl->cs0_offsets[reg];
+ else
+ cs_offs = ctrl->cs_offsets[reg];
+
+ if (cs && offs_cs1)
+ return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs;
+
+ return offs_cs0 + cs * ctrl->reg_spacing + cs_offs;
+}
+
+static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl)
+{
+ if (ctrl->nand_version < 0x0600)
+ return 1;
+ return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT);
+}
+
+static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ unsigned int shift = 0, bits;
+ enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
+ int cs = host->cs;
+
+ if (ctrl->nand_version >= 0x0600)
+ bits = 6;
+ else if (ctrl->nand_version >= 0x0500)
+ bits = 5;
+ else
+ bits = 4;
+
+ if (ctrl->nand_version >= 0x0600) {
+ if (cs >= 5)
+ reg = BRCMNAND_CORR_THRESHOLD_EXT;
+ shift = (cs % 5) * bits;
+ }
+ brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val);
+}
+
+static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
+{
+ if (ctrl->nand_version < 0x0700)
+ return 24;
+ return 0;
+}
+
+/***********************************************************************
+ * NAND ACC CONTROL bitfield
+ *
+ * Some bits have remained constant throughout hardware revision, while
+ * others have shifted around.
+ ***********************************************************************/
+
+/* Constant for all versions (where supported) */
+enum {
+ /* See BRCMNAND_HAS_CACHE_MODE */
+ ACC_CONTROL_CACHE_MODE = BIT(22),
+
+ /* See BRCMNAND_HAS_PREFETCH */
+ ACC_CONTROL_PREFETCH = BIT(23),
+
+ ACC_CONTROL_PAGE_HIT = BIT(24),
+ ACC_CONTROL_WR_PREEMPT = BIT(25),
+ ACC_CONTROL_PARTIAL_PAGE = BIT(26),
+ ACC_CONTROL_RD_ERASED = BIT(27),
+ ACC_CONTROL_FAST_PGM_RDIN = BIT(28),
+ ACC_CONTROL_WR_ECC = BIT(30),
+ ACC_CONTROL_RD_ECC = BIT(31),
+};
+
+static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
+{
+ if (ctrl->nand_version >= 0x0600)
+ return GENMASK(6, 0);
+ else
+ return GENMASK(5, 0);
+}
+
+#define NAND_ACC_CONTROL_ECC_SHIFT 16
+
+static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
+{
+ u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
+
+ return mask << NAND_ACC_CONTROL_ECC_SHIFT;
+}
+
+static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
+ u32 acc_control = nand_readreg(ctrl, offs);
+ u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC;
+
+ if (en) {
+ acc_control |= ecc_flags; /* enable RD/WR ECC */
+ acc_control |= host->hwcfg.ecc_level
+ << NAND_ACC_CONTROL_ECC_SHIFT;
+ } else {
+ acc_control &= ~ecc_flags; /* disable RD/WR ECC */
+ acc_control &= ~brcmnand_ecc_level_mask(ctrl);
+ }
+
+ nand_writereg(ctrl, offs, acc_control);
+}
+
+static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
+{
+ if (ctrl->nand_version >= 0x0600)
+ return 7;
+ else if (ctrl->nand_version >= 0x0500)
+ return 6;
+ else
+ return -1;
+}
+
+static int brcmnand_get_sector_size_1k(struct brcmnand_host *host)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ int shift = brcmnand_sector_1k_shift(ctrl);
+ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_ACC_CONTROL);
+
+ if (shift < 0)
+ return 0;
+
+ return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1;
+}
+
+static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ int shift = brcmnand_sector_1k_shift(ctrl);
+ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_ACC_CONTROL);
+ u32 tmp;
+
+ if (shift < 0)
+ return;
+
+ tmp = nand_readreg(ctrl, acc_control_offs);
+ tmp &= ~(1 << shift);
+ tmp |= (!!val) << shift;
+ nand_writereg(ctrl, acc_control_offs, tmp);
+}
+
+/***********************************************************************
+ * CS_NAND_SELECT
+ ***********************************************************************/
+
+enum {
+ CS_SELECT_NAND_WP = BIT(29),
+ CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30),
+};
+
+static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
+{
+ u32 val = en ? CS_SELECT_NAND_WP : 0;
+
+ brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val);
+}
+
+/***********************************************************************
+ * Flash DMA
+ ***********************************************************************/
+
+enum flash_dma_reg {
+ FLASH_DMA_REVISION = 0x00,
+ FLASH_DMA_FIRST_DESC = 0x04,
+ FLASH_DMA_FIRST_DESC_EXT = 0x08,
+ FLASH_DMA_CTRL = 0x0c,
+ FLASH_DMA_MODE = 0x10,
+ FLASH_DMA_STATUS = 0x14,
+ FLASH_DMA_INTERRUPT_DESC = 0x18,
+ FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c,
+ FLASH_DMA_ERROR_STATUS = 0x20,
+ FLASH_DMA_CURRENT_DESC = 0x24,
+ FLASH_DMA_CURRENT_DESC_EXT = 0x28,
+};
+
+static inline bool has_flash_dma(struct brcmnand_controller *ctrl)
+{
+ return ctrl->flash_dma_base;
+}
+
+static inline bool flash_dma_buf_ok(const void *buf)
+{
+ return buf && !is_vmalloc_addr(buf) &&
+ likely(IS_ALIGNED((uintptr_t)buf, 4));
+}
+
+static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs,
+ u32 val)
+{
+ brcmnand_writel(val, ctrl->flash_dma_base + offs);
+}
+
+static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs)
+{
+ return brcmnand_readl(ctrl->flash_dma_base + offs);
+}
+
+/* Low-level operation types: command, address, write, or read */
+enum brcmnand_llop_type {
+ LL_OP_CMD,
+ LL_OP_ADDR,
+ LL_OP_WR,
+ LL_OP_RD,
+};
+
+/***********************************************************************
+ * Internal support functions
+ ***********************************************************************/
+
+static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg)
+{
+ return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
+ cfg->ecc_level == 15;
+}
+
+/*
+ * Returns a nand_ecclayout strucutre for the given layout/configuration.
+ * Returns NULL on failure.
+ */
+static struct nand_ecclayout *brcmnand_create_layout(int ecc_level,
+ struct brcmnand_host *host)
+{
+ struct brcmnand_cfg *cfg = &host->hwcfg;
+ int i, j;
+ struct nand_ecclayout *layout;
+ int req;
+ int sectors;
+ int sas;
+ int idx1, idx2;
+
+ layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL);
+ if (!layout)
+ return NULL;
+
+ sectors = cfg->page_size / (512 << cfg->sector_size_1k);
+ sas = cfg->spare_area_size << cfg->sector_size_1k;
+
+ /* Hamming */
+ if (is_hamming_ecc(cfg)) {
+ for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
+ /* First sector of each page may have BBI */
+ if (i == 0) {
+ layout->oobfree[idx2].offset = i * sas + 1;
+ /* Small-page NAND use byte 6 for BBI */
+ if (cfg->page_size == 512)
+ layout->oobfree[idx2].offset--;
+ layout->oobfree[idx2].length = 5;
+ } else {
+ layout->oobfree[idx2].offset = i * sas;
+ layout->oobfree[idx2].length = 6;
+ }
+ idx2++;
+ layout->eccpos[idx1++] = i * sas + 6;
+ layout->eccpos[idx1++] = i * sas + 7;
+ layout->eccpos[idx1++] = i * sas + 8;
+ layout->oobfree[idx2].offset = i * sas + 9;
+ layout->oobfree[idx2].length = 7;
+ idx2++;
+ /* Leave zero-terminated entry for OOBFREE */
+ if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
+ idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
+ break;
+ }
+ goto out;
+ }
+
+ /*
+ * CONTROLLER_VERSION:
+ * < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
+ * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
+ * But we will just be conservative.
+ */
+ req = DIV_ROUND_UP(ecc_level * 14, 8);
+ if (req >= sas) {
+ dev_err(&host->pdev->dev,
+ "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
+ req, sas);
+ return NULL;
+ }
+
+ layout->eccbytes = req * sectors;
+ for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
+ for (j = sas - req; j < sas && idx1 <
+ MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++)
+ layout->eccpos[idx1] = i * sas + j;
+
+ /* First sector of each page may have BBI */
+ if (i == 0) {
+ if (cfg->page_size == 512 && (sas - req >= 6)) {
+ /* Small-page NAND use byte 6 for BBI */
+ layout->oobfree[idx2].offset = 0;
+ layout->oobfree[idx2].length = 5;
+ idx2++;
+ if (sas - req > 6) {
+ layout->oobfree[idx2].offset = 6;
+ layout->oobfree[idx2].length =
+ sas - req - 6;
+ idx2++;
+ }
+ } else if (sas > req + 1) {
+ layout->oobfree[idx2].offset = i * sas + 1;
+ layout->oobfree[idx2].length = sas - req - 1;
+ idx2++;
+ }
+ } else if (sas > req) {
+ layout->oobfree[idx2].offset = i * sas;
+ layout->oobfree[idx2].length = sas - req;
+ idx2++;
+ }
+ /* Leave zero-terminated entry for OOBFREE */
+ if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
+ idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
+ break;
+ }
+out:
+ /* Sum available OOB */
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE; i++)
+ layout->oobavail += layout->oobfree[i].length;
+ return layout;
+}
+
+static struct nand_ecclayout *brcmstb_choose_ecc_layout(
+ struct brcmnand_host *host)
+{
+ struct nand_ecclayout *layout;
+ struct brcmnand_cfg *p = &host->hwcfg;
+ unsigned int ecc_level = p->ecc_level;
+
+ if (p->sector_size_1k)
+ ecc_level <<= 1;
+
+ layout = brcmnand_create_layout(ecc_level, host);
+ if (!layout) {
+ dev_err(&host->pdev->dev,
+ "no proper ecc_layout for this NAND cfg\n");
+ return NULL;
+ }
+
+ return layout;
+}
+
+static void brcmnand_wp(struct mtd_info *mtd, int wp)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct brcmnand_host *host = chip->priv;
+ struct brcmnand_controller *ctrl = host->ctrl;
+
+ if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
+ static int old_wp = -1;
+
+ if (old_wp != wp) {
+ dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
+ old_wp = wp;
+ }
+ brcmnand_set_wp(ctrl, wp);
+ }
+}
+
+/* Helper functions for reading and writing OOB registers */
+static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs)
+{
+ u16 offset0, offset10, reg_offs;
+
+ offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE];
+ offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE];
+
+ if (offs >= ctrl->max_oob)
+ return 0x77;
+
+ if (offs >= 16 && offset10)
+ reg_offs = offset10 + ((offs - 0x10) & ~0x03);
+ else
+ reg_offs = offset0 + (offs & ~0x03);
+
+ return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3));
+}
+
+static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs,
+ u32 data)
+{
+ u16 offset0, offset10, reg_offs;
+
+ offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE];
+ offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE];
+
+ if (offs >= ctrl->max_oob)
+ return;
+
+ if (offs >= 16 && offset10)
+ reg_offs = offset10 + ((offs - 0x10) & ~0x03);
+ else
+ reg_offs = offset0 + (offs & ~0x03);
+
+ nand_writereg(ctrl, reg_offs, data);
+}
+
+/*
+ * read_oob_from_regs - read data from OOB registers
+ * @ctrl: NAND controller
+ * @i: sub-page sector index
+ * @oob: buffer to read to
+ * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
+ * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
+ */
+static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob,
+ int sas, int sector_1k)
+{
+ int tbytes = sas << sector_1k;
+ int j;
+
+ /* Adjust OOB values for 1K sector size */
+ if (sector_1k && (i & 0x01))
+ tbytes = max(0, tbytes - (int)ctrl->max_oob);
+ tbytes = min_t(int, tbytes, ctrl->max_oob);
+
+ for (j = 0; j < tbytes; j++)
+ oob[j] = oob_reg_read(ctrl, j);
+ return tbytes;
+}
+
+/*
+ * write_oob_to_regs - write data to OOB registers
+ * @i: sub-page sector index
+ * @oob: buffer to write from
+ * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
+ * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
+ */
+static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i,
+ const u8 *oob, int sas, int sector_1k)
+{
+ int tbytes = sas << sector_1k;
+ int j;
+
+ /* Adjust OOB values for 1K sector size */
+ if (sector_1k && (i & 0x01))
+ tbytes = max(0, tbytes - (int)ctrl->max_oob);
+ tbytes = min_t(int, tbytes, ctrl->max_oob);
+
+ for (j = 0; j < tbytes; j += 4)
+ oob_reg_write(ctrl, j,
+ (oob[j + 0] << 24) |
+ (oob[j + 1] << 16) |
+ (oob[j + 2] << 8) |
+ (oob[j + 3] << 0));
+ return tbytes;
+}
+
+static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data)
+{
+ struct brcmnand_controller *ctrl = data;
+
+ /* Discard all NAND_CTLRDY interrupts during DMA */
+ if (ctrl->dma_pending)
+ return IRQ_HANDLED;
+
+ complete(&ctrl->done);
+ return IRQ_HANDLED;
+}
+
+/* Handle SoC-specific interrupt hardware */
+static irqreturn_t brcmnand_irq(int irq, void *data)
+{
+ struct brcmnand_controller *ctrl = data;
+
+ if (ctrl->soc->ctlrdy_ack(ctrl->soc))
+ return brcmnand_ctlrdy_irq(irq, data);
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t brcmnand_dma_irq(int irq, void *data)
+{
+ struct brcmnand_controller *ctrl = data;
+
+ complete(&ctrl->dma_done);
+
+ return IRQ_HANDLED;
+}
+
+static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u32 intfc;
+
+ dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd,
+ brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS));
+ BUG_ON(ctrl->cmd_pending != 0);
+ ctrl->cmd_pending = cmd;
+
+ intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
+ BUG_ON(!(intfc & INTFC_CTLR_READY));
+
+ mb(); /* flush previous writes */
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
+ cmd << brcmnand_cmd_shift(ctrl));
+}
+
+/***********************************************************************
+ * NAND MTD API: read/program/erase
+ ***********************************************************************/
+
+static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat,
+ unsigned int ctrl)
+{
+ /* intentionally left blank */
+}
+
+static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct brcmnand_host *host = chip->priv;
+ struct brcmnand_controller *ctrl = host->ctrl;
+ unsigned long timeo = msecs_to_jiffies(100);
+
+ dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending);
+ if (ctrl->cmd_pending &&
+ wait_for_completion_timeout(&ctrl->done, timeo) <= 0) {
+ u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START)
+ >> brcmnand_cmd_shift(ctrl);
+
+ dev_err_ratelimited(ctrl->dev,
+ "timeout waiting for command %#02x\n", cmd);
+ dev_err_ratelimited(ctrl->dev, "intfc status %08x\n",
+ brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS));
+ }
+ ctrl->cmd_pending = 0;
+ return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
+ INTFC_FLASH_STATUS;
+}
+
+enum {
+ LLOP_RE = BIT(16),
+ LLOP_WE = BIT(17),
+ LLOP_ALE = BIT(18),
+ LLOP_CLE = BIT(19),
+ LLOP_RETURN_IDLE = BIT(31),
+
+ LLOP_DATA_MASK = GENMASK(15, 0),
+};
+
+static int brcmnand_low_level_op(struct brcmnand_host *host,
+ enum brcmnand_llop_type type, u32 data,
+ bool last_op)
+{
+ struct mtd_info *mtd = &host->mtd;
+ struct nand_chip *chip = &host->chip;
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u32 tmp;
+
+ tmp = data & LLOP_DATA_MASK;
+ switch (type) {
+ case LL_OP_CMD:
+ tmp |= LLOP_WE | LLOP_CLE;
+ break;
+ case LL_OP_ADDR:
+ /* WE | ALE */
+ tmp |= LLOP_WE | LLOP_ALE;
+ break;
+ case LL_OP_WR:
+ /* WE */
+ tmp |= LLOP_WE;
+ break;
+ case LL_OP_RD:
+ /* RE */
+ tmp |= LLOP_RE;
+ break;
+ }
+ if (last_op)
+ /* RETURN_IDLE */
+ tmp |= LLOP_RETURN_IDLE;
+
+ dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp);
+
+ brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp);
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP);
+
+ brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP);
+ return brcmnand_waitfunc(mtd, chip);
+}
+
+static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct brcmnand_host *host = chip->priv;
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u64 addr = (u64)page_addr << chip->page_shift;
+ int native_cmd = 0;
+
+ if (command == NAND_CMD_READID || command == NAND_CMD_PARAM ||
+ command == NAND_CMD_RNDOUT)
+ addr = (u64)column;
+ /* Avoid propagating a negative, don't-care address */
+ else if (page_addr < 0)
+ addr = 0;
+
+ dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command,
+ (unsigned long long)addr);
+
+ host->last_cmd = command;
+ host->last_byte = 0;
+ host->last_addr = addr;
+
+ switch (command) {
+ case NAND_CMD_RESET:
+ native_cmd = CMD_FLASH_RESET;
+ break;
+ case NAND_CMD_STATUS:
+ native_cmd = CMD_STATUS_READ;
+ break;
+ case NAND_CMD_READID:
+ native_cmd = CMD_DEVICE_ID_READ;
+ break;
+ case NAND_CMD_READOOB:
+ native_cmd = CMD_SPARE_AREA_READ;
+ break;
+ case NAND_CMD_ERASE1:
+ native_cmd = CMD_BLOCK_ERASE;
+ brcmnand_wp(mtd, 0);
+ break;
+ case NAND_CMD_PARAM:
+ native_cmd = CMD_PARAMETER_READ;
+ break;
+ case NAND_CMD_SET_FEATURES:
+ case NAND_CMD_GET_FEATURES:
+ brcmnand_low_level_op(host, LL_OP_CMD, command, false);
+ brcmnand_low_level_op(host, LL_OP_ADDR, column, false);
+ break;
+ case NAND_CMD_RNDOUT:
+ native_cmd = CMD_PARAMETER_CHANGE_COL;
+ addr &= ~((u64)(FC_BYTES - 1));
+ /*
+ * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0
+ * NB: hwcfg.sector_size_1k may not be initialized yet
+ */
+ if (brcmnand_get_sector_size_1k(host)) {
+ host->hwcfg.sector_size_1k =
+ brcmnand_get_sector_size_1k(host);
+ brcmnand_set_sector_size_1k(host, 0);
+ }
+ break;
+ }
+
+ if (!native_cmd)
+ return;
+
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
+ (host->cs << 16) | ((addr >> 32) & 0xffff));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+
+ brcmnand_send_cmd(host, native_cmd);
+ brcmnand_waitfunc(mtd, chip);
+
+ if (native_cmd == CMD_PARAMETER_READ ||
+ native_cmd == CMD_PARAMETER_CHANGE_COL) {
+ int i;
+
+ brcmnand_soc_data_bus_prepare(ctrl->soc);
+
+ /*
+ * Must cache the FLASH_CACHE now, since changes in
+ * SECTOR_SIZE_1K may invalidate it
+ */
+ for (i = 0; i < FC_WORDS; i++)
+ ctrl->flash_cache[i] = brcmnand_read_fc(ctrl, i);
+
+ brcmnand_soc_data_bus_unprepare(ctrl->soc);
+
+ /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
+ if (host->hwcfg.sector_size_1k)
+ brcmnand_set_sector_size_1k(host,
+ host->hwcfg.sector_size_1k);
+ }
+
+ /* Re-enable protection is necessary only after erase */
+ if (command == NAND_CMD_ERASE1)
+ brcmnand_wp(mtd, 1);
+}
+
+static uint8_t brcmnand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct brcmnand_host *host = chip->priv;
+ struct brcmnand_controller *ctrl = host->ctrl;
+ uint8_t ret = 0;
+ int addr, offs;
+
+ switch (host->last_cmd) {
+ case NAND_CMD_READID:
+ if (host->last_byte < 4)
+ ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >>
+ (24 - (host->last_byte << 3));
+ else if (host->last_byte < 8)
+ ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >>
+ (56 - (host->last_byte << 3));
+ break;
+
+ case NAND_CMD_READOOB:
+ ret = oob_reg_read(ctrl, host->last_byte);
+ break;
+
+ case NAND_CMD_STATUS:
+ ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
+ INTFC_FLASH_STATUS;
+ if (wp_on) /* hide WP status */
+ ret |= NAND_STATUS_WP;
+ break;
+
+ case NAND_CMD_PARAM:
+ case NAND_CMD_RNDOUT:
+ addr = host->last_addr + host->last_byte;
+ offs = addr & (FC_BYTES - 1);
+
+ /* At FC_BYTES boundary, switch to next column */
+ if (host->last_byte > 0 && offs == 0)
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, addr, -1);
+
+ ret = ctrl->flash_cache[offs >> 2] >>
+ (24 - ((offs & 0x03) << 3));
+ break;
+ case NAND_CMD_GET_FEATURES:
+ if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) {
+ ret = 0;
+ } else {
+ bool last = host->last_byte ==
+ ONFI_SUBFEATURE_PARAM_LEN - 1;
+ brcmnand_low_level_op(host, LL_OP_RD, 0, last);
+ ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff;
+ }
+ }
+
+ dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret);
+ host->last_byte++;
+
+ return ret;
+}
+
+static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++, buf++)
+ *buf = brcmnand_read_byte(mtd);
+}
+
+static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+ struct brcmnand_host *host = chip->priv;
+
+ switch (host->last_cmd) {
+ case NAND_CMD_SET_FEATURES:
+ for (i = 0; i < len; i++)
+ brcmnand_low_level_op(host, LL_OP_WR, buf[i],
+ (i + 1) == len);
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+/**
+ * Construct a FLASH_DMA descriptor as part of a linked list. You must know the
+ * following ahead of time:
+ * - Is this descriptor the beginning or end of a linked list?
+ * - What is the (DMA) address of the next descriptor in the linked list?
+ */
+static int brcmnand_fill_dma_desc(struct brcmnand_host *host,
+ struct brcm_nand_dma_desc *desc, u64 addr,
+ dma_addr_t buf, u32 len, u8 dma_cmd,
+ bool begin, bool end,
+ dma_addr_t next_desc)
+{
+ memset(desc, 0, sizeof(*desc));
+ /* Descriptors are written in native byte order (wordwise) */
+ desc->next_desc = lower_32_bits(next_desc);
+ desc->next_desc_ext = upper_32_bits(next_desc);
+ desc->cmd_irq = (dma_cmd << 24) |
+ (end ? (0x03 << 8) : 0) | /* IRQ | STOP */
+ (!!begin) | ((!!end) << 1); /* head, tail */
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ desc->cmd_irq |= 0x01 << 12;
+#endif
+ desc->dram_addr = lower_32_bits(buf);
+ desc->dram_addr_ext = upper_32_bits(buf);
+ desc->tfr_len = len;
+ desc->total_len = len;
+ desc->flash_addr = lower_32_bits(addr);
+ desc->flash_addr_ext = upper_32_bits(addr);
+ desc->cs = host->cs;
+ desc->status_valid = 0x01;
+ return 0;
+}
+
+/**
+ * Kick the FLASH_DMA engine, with a given DMA descriptor
+ */
+static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ unsigned long timeo = msecs_to_jiffies(100);
+
+ flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc));
+ (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC);
+ flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc));
+ (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT);
+
+ /* Start FLASH_DMA engine */
+ ctrl->dma_pending = true;
+ mb(); /* flush previous writes */
+ flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */
+
+ if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) {
+ dev_err(ctrl->dev,
+ "timeout waiting for DMA; status %#x, error status %#x\n",
+ flash_dma_readl(ctrl, FLASH_DMA_STATUS),
+ flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS));
+ }
+ ctrl->dma_pending = false;
+ flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */
+}
+
+static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
+ u32 len, u8 dma_cmd)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ dma_addr_t buf_pa;
+ int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ buf_pa = dma_map_single(ctrl->dev, buf, len, dir);
+ if (dma_mapping_error(ctrl->dev, buf_pa)) {
+ dev_err(ctrl->dev, "unable to map buffer for DMA\n");
+ return -ENOMEM;
+ }
+
+ brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len,
+ dma_cmd, true, true, 0);
+
+ brcmnand_dma_run(host, ctrl->dma_pa);
+
+ dma_unmap_single(ctrl->dev, buf_pa, len, dir);
+
+ if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR)
+ return -EBADMSG;
+ else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR)
+ return -EUCLEAN;
+
+ return 0;
+}
+
+/*
+ * Assumes proper CS is already set
+ */
+static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
+ u64 addr, unsigned int trans, u32 *buf,
+ u8 *oob, u64 *err_addr)
+{
+ struct brcmnand_host *host = chip->priv;
+ struct brcmnand_controller *ctrl = host->ctrl;
+ int i, j, ret = 0;
+
+ /* Clear error addresses */
+ brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0);
+ brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0);
+
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
+ (host->cs << 16) | ((addr >> 32) & 0xffff));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
+
+ for (i = 0; i < trans; i++, addr += FC_BYTES) {
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
+ lower_32_bits(addr));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+ /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */
+ brcmnand_send_cmd(host, CMD_PAGE_READ);
+ brcmnand_waitfunc(mtd, chip);
+
+ if (likely(buf)) {
+ brcmnand_soc_data_bus_prepare(ctrl->soc);
+
+ for (j = 0; j < FC_WORDS; j++, buf++)
+ *buf = brcmnand_read_fc(ctrl, j);
+
+ brcmnand_soc_data_bus_unprepare(ctrl->soc);
+ }
+
+ if (oob)
+ oob += read_oob_from_regs(ctrl, i, oob,
+ mtd->oobsize / trans,
+ host->hwcfg.sector_size_1k);
+
+ if (!ret) {
+ *err_addr = brcmnand_read_reg(ctrl,
+ BRCMNAND_UNCORR_ADDR) |
+ ((u64)(brcmnand_read_reg(ctrl,
+ BRCMNAND_UNCORR_EXT_ADDR)
+ & 0xffff) << 32);
+ if (*err_addr)
+ ret = -EBADMSG;
+ }
+
+ if (!ret) {
+ *err_addr = brcmnand_read_reg(ctrl,
+ BRCMNAND_CORR_ADDR) |
+ ((u64)(brcmnand_read_reg(ctrl,
+ BRCMNAND_CORR_EXT_ADDR)
+ & 0xffff) << 32);
+ if (*err_addr)
+ ret = -EUCLEAN;
+ }
+ }
+
+ return ret;
+}
+
+static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
+ u64 addr, unsigned int trans, u32 *buf, u8 *oob)
+{
+ struct brcmnand_host *host = chip->priv;
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u64 err_addr = 0;
+ int err;
+
+ dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
+
+ brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0);
+
+ if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
+ err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES,
+ CMD_PAGE_READ);
+ if (err) {
+ if (mtd_is_bitflip_or_eccerr(err))
+ err_addr = addr;
+ else
+ return -EIO;
+ }
+ } else {
+ if (oob)
+ memset(oob, 0x99, mtd->oobsize);
+
+ err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
+ oob, &err_addr);
+ }
+
+ if (mtd_is_eccerr(err)) {
+ dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
+ (unsigned long long)err_addr);
+ mtd->ecc_stats.failed++;
+ /* NAND layer expects zero on ECC errors */
+ return 0;
+ }
+
+ if (mtd_is_bitflip(err)) {
+ unsigned int corrected = brcmnand_count_corrected(ctrl);
+
+ dev_dbg(ctrl->dev, "corrected error at 0x%llx\n",
+ (unsigned long long)err_addr);
+ mtd->ecc_stats.corrected += corrected;
+ /* Always exceed the software-imposed threshold */
+ return max(mtd->bitflip_threshold, corrected);
+ }
+
+ return 0;
+}
+
+static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct brcmnand_host *host = chip->priv;
+ u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
+
+ return brcmnand_read(mtd, chip, host->last_addr,
+ mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
+}
+
+static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct brcmnand_host *host = chip->priv;
+ u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
+ int ret;
+
+ brcmnand_set_ecc_enabled(host, 0);
+ ret = brcmnand_read(mtd, chip, host->last_addr,
+ mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
+ brcmnand_set_ecc_enabled(host, 1);
+ return ret;
+}
+
+static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
+ mtd->writesize >> FC_SHIFT,
+ NULL, (u8 *)chip->oob_poi);
+}
+
+static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct brcmnand_host *host = chip->priv;
+
+ brcmnand_set_ecc_enabled(host, 0);
+ brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
+ mtd->writesize >> FC_SHIFT,
+ NULL, (u8 *)chip->oob_poi);
+ brcmnand_set_ecc_enabled(host, 1);
+ return 0;
+}
+
+static int brcmnand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t data_offs, uint32_t readlen,
+ uint8_t *bufpoi, int page)
+{
+ struct brcmnand_host *host = chip->priv;
+
+ return brcmnand_read(mtd, chip, host->last_addr + data_offs,
+ readlen >> FC_SHIFT, (u32 *)bufpoi, NULL);
+}
+
+static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
+ u64 addr, const u32 *buf, u8 *oob)
+{
+ struct brcmnand_host *host = chip->priv;
+ struct brcmnand_controller *ctrl = host->ctrl;
+ unsigned int i, j, trans = mtd->writesize >> FC_SHIFT;
+ int status, ret = 0;
+
+ dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf);
+
+ if (unlikely((u32)buf & 0x03)) {
+ dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf);
+ buf = (u32 *)((u32)buf & ~0x03);
+ }
+
+ brcmnand_wp(mtd, 0);
+
+ for (i = 0; i < ctrl->max_oob; i += 4)
+ oob_reg_write(ctrl, i, 0xffffffff);
+
+ if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
+ if (brcmnand_dma_trans(host, addr, (u32 *)buf,
+ mtd->writesize, CMD_PROGRAM_PAGE))
+ ret = -EIO;
+ goto out;
+ }
+
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
+ (host->cs << 16) | ((addr >> 32) & 0xffff));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
+
+ for (i = 0; i < trans; i++, addr += FC_BYTES) {
+ /* full address MUST be set before populating FC */
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
+ lower_32_bits(addr));
+ (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+
+ if (buf) {
+ brcmnand_soc_data_bus_prepare(ctrl->soc);
+
+ for (j = 0; j < FC_WORDS; j++, buf++)
+ brcmnand_write_fc(ctrl, j, *buf);
+
+ brcmnand_soc_data_bus_unprepare(ctrl->soc);
+ } else if (oob) {
+ for (j = 0; j < FC_WORDS; j++)
+ brcmnand_write_fc(ctrl, j, 0xffffffff);
+ }
+
+ if (oob) {
+ oob += write_oob_to_regs(ctrl, i, oob,
+ mtd->oobsize / trans,
+ host->hwcfg.sector_size_1k);
+ }
+
+ /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */
+ brcmnand_send_cmd(host, CMD_PROGRAM_PAGE);
+ status = brcmnand_waitfunc(mtd, chip);
+
+ if (status & NAND_STATUS_FAIL) {
+ dev_info(ctrl->dev, "program failed at %llx\n",
+ (unsigned long long)addr);
+ ret = -EIO;
+ goto out;
+ }
+ }
+out:
+ brcmnand_wp(mtd, 1);
+ return ret;
+}
+
+static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
+{
+ struct brcmnand_host *host = chip->priv;
+ void *oob = oob_required ? chip->oob_poi : NULL;
+
+ brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
+ return 0;
+}
+
+static int brcmnand_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required)
+{
+ struct brcmnand_host *host = chip->priv;
+ void *oob = oob_required ? chip->oob_poi : NULL;
+
+ brcmnand_set_ecc_enabled(host, 0);
+ brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
+ brcmnand_set_ecc_enabled(host, 1);
+ return 0;
+}
+
+static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return brcmnand_write(mtd, chip, (u64)page << chip->page_shift,
+ NULL, chip->oob_poi);
+}
+
+static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct brcmnand_host *host = chip->priv;
+ int ret;
+
+ brcmnand_set_ecc_enabled(host, 0);
+ ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL,
+ (u8 *)chip->oob_poi);
+ brcmnand_set_ecc_enabled(host, 1);
+
+ return ret;
+}
+
+/***********************************************************************
+ * Per-CS setup (1 NAND device)
+ ***********************************************************************/
+
+static int brcmnand_set_cfg(struct brcmnand_host *host,
+ struct brcmnand_cfg *cfg)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ struct nand_chip *chip = &host->chip;
+ u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
+ u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_CFG_EXT);
+ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_ACC_CONTROL);
+ u8 block_size = 0, page_size = 0, device_size = 0;
+ u32 tmp;
+
+ if (ctrl->block_sizes) {
+ int i, found;
+
+ for (i = 0, found = 0; ctrl->block_sizes[i]; i++)
+ if (ctrl->block_sizes[i] * 1024 == cfg->block_size) {
+ block_size = i;
+ found = 1;
+ }
+ if (!found) {
+ dev_warn(ctrl->dev, "invalid block size %u\n",
+ cfg->block_size);
+ return -EINVAL;
+ }
+ } else {
+ block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE);
+ }
+
+ if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size &&
+ cfg->block_size > ctrl->max_block_size)) {
+ dev_warn(ctrl->dev, "invalid block size %u\n",
+ cfg->block_size);
+ block_size = 0;
+ }
+
+ if (ctrl->page_sizes) {
+ int i, found;
+
+ for (i = 0, found = 0; ctrl->page_sizes[i]; i++)
+ if (ctrl->page_sizes[i] == cfg->page_size) {
+ page_size = i;
+ found = 1;
+ }
+ if (!found) {
+ dev_warn(ctrl->dev, "invalid page size %u\n",
+ cfg->page_size);
+ return -EINVAL;
+ }
+ } else {
+ page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE);
+ }
+
+ if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size &&
+ cfg->page_size > ctrl->max_page_size)) {
+ dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size);
+ return -EINVAL;
+ }
+
+ if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) {
+ dev_warn(ctrl->dev, "invalid device size 0x%llx\n",
+ (unsigned long long)cfg->device_size);
+ return -EINVAL;
+ }
+ device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE);
+
+ tmp = (cfg->blk_adr_bytes << 8) |
+ (cfg->col_adr_bytes << 12) |
+ (cfg->ful_adr_bytes << 16) |
+ (!!(cfg->device_width == 16) << 23) |
+ (device_size << 24);
+ if (cfg_offs == cfg_ext_offs) {
+ tmp |= (page_size << 20) | (block_size << 28);
+ nand_writereg(ctrl, cfg_offs, tmp);
+ } else {
+ nand_writereg(ctrl, cfg_offs, tmp);
+ tmp = page_size | (block_size << 4);
+ nand_writereg(ctrl, cfg_ext_offs, tmp);
+ }
+
+ tmp = nand_readreg(ctrl, acc_control_offs);
+ tmp &= ~brcmnand_ecc_level_mask(ctrl);
+ tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT;
+ tmp &= ~brcmnand_spare_area_mask(ctrl);
+ tmp |= cfg->spare_area_size;
+ nand_writereg(ctrl, acc_control_offs, tmp);
+
+ brcmnand_set_sector_size_1k(host, cfg->sector_size_1k);
+
+ /* threshold = ceil(BCH-level * 0.75) */
+ brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4));
+
+ return 0;
+}
+
+static void brcmnand_print_cfg(char *buf, struct brcmnand_cfg *cfg)
+{
+ buf += sprintf(buf,
+ "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit",
+ (unsigned long long)cfg->device_size >> 20,
+ cfg->block_size >> 10,
+ cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size,
+ cfg->page_size >= 1024 ? "KiB" : "B",
+ cfg->spare_area_size, cfg->device_width);
+
+ /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */
+ if (is_hamming_ecc(cfg))
+ sprintf(buf, ", Hamming ECC");
+ else if (cfg->sector_size_1k)
+ sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
+ else
+ sprintf(buf, ", BCH-%u", cfg->ecc_level);
+}
+
+/*
+ * Minimum number of bytes to address a page. Calculated as:
+ * roundup(log2(size / page-size) / 8)
+ *
+ * NB: the following does not "round up" for non-power-of-2 'size'; but this is
+ * OK because many other things will break if 'size' is irregular...
+ */
+static inline int get_blk_adr_bytes(u64 size, u32 writesize)
+{
+ return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3;
+}
+
+static int brcmnand_setup_dev(struct brcmnand_host *host)
+{
+ struct mtd_info *mtd = &host->mtd;
+ struct nand_chip *chip = &host->chip;
+ struct brcmnand_controller *ctrl = host->ctrl;
+ struct brcmnand_cfg *cfg = &host->hwcfg;
+ char msg[128];
+ u32 offs, tmp, oob_sector;
+ int ret;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ ret = of_property_read_u32(chip->dn, "brcm,nand-oob-sector-size",
+ &oob_sector);
+ if (ret) {
+ /* Use detected size */
+ cfg->spare_area_size = mtd->oobsize /
+ (mtd->writesize >> FC_SHIFT);
+ } else {
+ cfg->spare_area_size = oob_sector;
+ }
+ if (cfg->spare_area_size > ctrl->max_oob)
+ cfg->spare_area_size = ctrl->max_oob;
+ /*
+ * Set oobsize to be consistent with controller's spare_area_size, as
+ * the rest is inaccessible.
+ */
+ mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT);
+
+ cfg->device_size = mtd->size;
+ cfg->block_size = mtd->erasesize;
+ cfg->page_size = mtd->writesize;
+ cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8;
+ cfg->col_adr_bytes = 2;
+ cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize);
+
+ switch (chip->ecc.size) {
+ case 512:
+ if (chip->ecc.strength == 1) /* Hamming */
+ cfg->ecc_level = 15;
+ else
+ cfg->ecc_level = chip->ecc.strength;
+ cfg->sector_size_1k = 0;
+ break;
+ case 1024:
+ if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) {
+ dev_err(ctrl->dev, "1KB sectors not supported\n");
+ return -EINVAL;
+ }
+ if (chip->ecc.strength & 0x1) {
+ dev_err(ctrl->dev,
+ "odd ECC not supported with 1KB sectors\n");
+ return -EINVAL;
+ }
+
+ cfg->ecc_level = chip->ecc.strength >> 1;
+ cfg->sector_size_1k = 1;
+ break;
+ default:
+ dev_err(ctrl->dev, "unsupported ECC size: %d\n",
+ chip->ecc.size);
+ return -EINVAL;
+ }
+
+ cfg->ful_adr_bytes = cfg->blk_adr_bytes;
+ if (mtd->writesize > 512)
+ cfg->ful_adr_bytes += cfg->col_adr_bytes;
+ else
+ cfg->ful_adr_bytes += 1;
+
+ ret = brcmnand_set_cfg(host, cfg);
+ if (ret)
+ return ret;
+
+ brcmnand_set_ecc_enabled(host, 1);
+
+ brcmnand_print_cfg(msg, cfg);
+ dev_info(ctrl->dev, "detected %s\n", msg);
+
+ /* Configure ACC_CONTROL */
+ offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
+ tmp = nand_readreg(ctrl, offs);
+ tmp &= ~ACC_CONTROL_PARTIAL_PAGE;
+ tmp &= ~ACC_CONTROL_RD_ERASED;
+ tmp &= ~ACC_CONTROL_FAST_PGM_RDIN;
+ if (ctrl->features & BRCMNAND_HAS_PREFETCH) {
+ /*
+ * FIXME: Flash DMA + prefetch may see spurious erased-page ECC
+ * errors
+ */
+ if (has_flash_dma(ctrl))
+ tmp &= ~ACC_CONTROL_PREFETCH;
+ else
+ tmp |= ACC_CONTROL_PREFETCH;
+ }
+ nand_writereg(ctrl, offs, tmp);
+
+ return 0;
+}
+
+static int brcmnand_init_cs(struct brcmnand_host *host)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ struct device_node *dn = host->of_node;
+ struct platform_device *pdev = host->pdev;
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ int ret;
+ struct mtd_part_parser_data ppdata = { .of_node = dn };
+
+ ret = of_property_read_u32(dn, "reg", &host->cs);
+ if (ret) {
+ dev_err(&pdev->dev, "can't get chip-select\n");
+ return -ENXIO;
+ }
+
+ mtd = &host->mtd;
+ chip = &host->chip;
+
+ chip->dn = dn;
+ chip->priv = host;
+ mtd->priv = chip;
+ mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
+ host->cs);
+ mtd->owner = THIS_MODULE;
+ mtd->dev.parent = &pdev->dev;
+
+ chip->IO_ADDR_R = (void __iomem *)0xdeadbeef;
+ chip->IO_ADDR_W = (void __iomem *)0xdeadbeef;
+
+ chip->cmd_ctrl = brcmnand_cmd_ctrl;
+ chip->cmdfunc = brcmnand_cmdfunc;
+ chip->waitfunc = brcmnand_waitfunc;
+ chip->read_byte = brcmnand_read_byte;
+ chip->read_buf = brcmnand_read_buf;
+ chip->write_buf = brcmnand_write_buf;
+
+ chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.read_page = brcmnand_read_page;
+ chip->ecc.read_subpage = brcmnand_read_subpage;
+ chip->ecc.write_page = brcmnand_write_page;
+ chip->ecc.read_page_raw = brcmnand_read_page_raw;
+ chip->ecc.write_page_raw = brcmnand_write_page_raw;
+ chip->ecc.write_oob_raw = brcmnand_write_oob_raw;
+ chip->ecc.read_oob_raw = brcmnand_read_oob_raw;
+ chip->ecc.read_oob = brcmnand_read_oob;
+ chip->ecc.write_oob = brcmnand_write_oob;
+
+ chip->controller = &ctrl->controller;
+
+ if (nand_scan_ident(mtd, 1, NULL))
+ return -ENXIO;
+
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+ /*
+ * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
+ * to/from, and have nand_base pass us a bounce buffer instead, as
+ * needed.
+ */
+ chip->options |= NAND_USE_BOUNCE_BUFFER;
+
+ if (of_get_nand_on_flash_bbt(dn))
+ chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+
+ if (brcmnand_setup_dev(host))
+ return -ENXIO;
+
+ chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
+ /* only use our internal HW threshold */
+ mtd->bitflip_threshold = 1;
+
+ chip->ecc.layout = brcmstb_choose_ecc_layout(host);
+ if (!chip->ecc.layout)
+ return -ENXIO;
+
+ if (nand_scan_tail(mtd))
+ return -ENXIO;
+
+ return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+}
+
+static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
+ int restore)
+{
+ struct brcmnand_controller *ctrl = host->ctrl;
+ u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
+ u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_CFG_EXT);
+ u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+ BRCMNAND_CS_ACC_CONTROL);
+ u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1);
+ u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2);
+
+ if (restore) {
+ nand_writereg(ctrl, cfg_offs, host->hwcfg.config);
+ if (cfg_offs != cfg_ext_offs)
+ nand_writereg(ctrl, cfg_ext_offs,
+ host->hwcfg.config_ext);
+ nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control);
+ nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1);
+ nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2);
+ } else {
+ host->hwcfg.config = nand_readreg(ctrl, cfg_offs);
+ if (cfg_offs != cfg_ext_offs)
+ host->hwcfg.config_ext =
+ nand_readreg(ctrl, cfg_ext_offs);
+ host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs);
+ host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs);
+ host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs);
+ }
+}
+
+static int brcmnand_suspend(struct device *dev)
+{
+ struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
+ struct brcmnand_host *host;
+
+ list_for_each_entry(host, &ctrl->host_list, node)
+ brcmnand_save_restore_cs_config(host, 0);
+
+ ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT);
+ ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR);
+ ctrl->corr_stat_threshold =
+ brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD);
+
+ if (has_flash_dma(ctrl))
+ ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE);
+
+ return 0;
+}
+
+static int brcmnand_resume(struct device *dev)
+{
+ struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
+ struct brcmnand_host *host;
+
+ if (has_flash_dma(ctrl)) {
+ flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode);
+ flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
+ }
+
+ brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select);
+ brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor);
+ brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD,
+ ctrl->corr_stat_threshold);
+ if (ctrl->soc) {
+ /* Clear/re-enable interrupt */
+ ctrl->soc->ctlrdy_ack(ctrl->soc);
+ ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
+ }
+
+ list_for_each_entry(host, &ctrl->host_list, node) {
+ struct mtd_info *mtd = &host->mtd;
+ struct nand_chip *chip = mtd->priv;
+
+ brcmnand_save_restore_cs_config(host, 1);
+
+ /* Reset the chip, required by some chips after power-up */
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ }
+
+ return 0;
+}
+
+const struct dev_pm_ops brcmnand_pm_ops = {
+ .suspend = brcmnand_suspend,
+ .resume = brcmnand_resume,
+};
+EXPORT_SYMBOL_GPL(brcmnand_pm_ops);
+
+static const struct of_device_id brcmnand_of_match[] = {
+ { .compatible = "brcm,brcmnand-v4.0" },
+ { .compatible = "brcm,brcmnand-v5.0" },
+ { .compatible = "brcm,brcmnand-v6.0" },
+ { .compatible = "brcm,brcmnand-v6.1" },
+ { .compatible = "brcm,brcmnand-v7.0" },
+ { .compatible = "brcm,brcmnand-v7.1" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, brcmnand_of_match);
+
+/***********************************************************************
+ * Platform driver setup (per controller)
+ ***********************************************************************/
+
+int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *dn = dev->of_node, *child;
+ struct brcmnand_controller *ctrl;
+ struct resource *res;
+ int ret;
+
+ /* We only support device-tree instantiation */
+ if (!dn)
+ return -ENODEV;
+
+ if (!of_match_node(brcmnand_of_match, dn))
+ return -ENODEV;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, ctrl);
+ ctrl->dev = dev;
+
+ init_completion(&ctrl->done);
+ init_completion(&ctrl->dma_done);
+ spin_lock_init(&ctrl->controller.lock);
+ init_waitqueue_head(&ctrl->controller.wq);
+ INIT_LIST_HEAD(&ctrl->host_list);
+
+ /* NAND register range */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctrl->nand_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctrl->nand_base))
+ return PTR_ERR(ctrl->nand_base);
+
+ /* Initialize NAND revision */
+ ret = brcmnand_revision_init(ctrl);
+ if (ret)
+ return ret;
+
+ /*
+ * Most chips have this cache at a fixed offset within 'nand' block.
+ * Some must specify this region separately.
+ */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
+ if (res) {
+ ctrl->nand_fc = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctrl->nand_fc))
+ return PTR_ERR(ctrl->nand_fc);
+ } else {
+ ctrl->nand_fc = ctrl->nand_base +
+ ctrl->reg_offsets[BRCMNAND_FC_BASE];
+ }
+
+ /* FLASH_DMA */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
+ if (res) {
+ ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctrl->flash_dma_base))
+ return PTR_ERR(ctrl->flash_dma_base);
+
+ flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */
+ flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
+
+ /* Allocate descriptor(s) */
+ ctrl->dma_desc = dmam_alloc_coherent(dev,
+ sizeof(*ctrl->dma_desc),
+ &ctrl->dma_pa, GFP_KERNEL);
+ if (!ctrl->dma_desc)
+ return -ENOMEM;
+
+ ctrl->dma_irq = platform_get_irq(pdev, 1);
+ if ((int)ctrl->dma_irq < 0) {
+ dev_err(dev, "missing FLASH_DMA IRQ\n");
+ return -ENODEV;
+ }
+
+ ret = devm_request_irq(dev, ctrl->dma_irq,
+ brcmnand_dma_irq, 0, DRV_NAME,
+ ctrl);
+ if (ret < 0) {
+ dev_err(dev, "can't allocate IRQ %d: error %d\n",
+ ctrl->dma_irq, ret);
+ return ret;
+ }
+
+ dev_info(dev, "enabling FLASH_DMA\n");
+ }
+
+ /* Disable automatic device ID config, direct addressing */
+ brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT,
+ CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0);
+ /* Disable XOR addressing */
+ brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
+
+ if (ctrl->features & BRCMNAND_HAS_WP) {
+ /* Permanently disable write protection */
+ if (wp_on == 2)
+ brcmnand_set_wp(ctrl, false);
+ } else {
+ wp_on = 0;
+ }
+
+ /* IRQ */
+ ctrl->irq = platform_get_irq(pdev, 0);
+ if ((int)ctrl->irq < 0) {
+ dev_err(dev, "no IRQ defined\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Some SoCs integrate this controller (e.g., its interrupt bits) in
+ * interesting ways
+ */
+ if (soc) {
+ ctrl->soc = soc;
+
+ ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
+ DRV_NAME, ctrl);
+
+ /* Enable interrupt */
+ ctrl->soc->ctlrdy_ack(ctrl->soc);
+ ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
+ } else {
+ /* Use standard interrupt infrastructure */
+ ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
+ DRV_NAME, ctrl);
+ }
+ if (ret < 0) {
+ dev_err(dev, "can't allocate IRQ %d: error %d\n",
+ ctrl->irq, ret);
+ return ret;
+ }
+
+ for_each_available_child_of_node(dn, child) {
+ if (of_device_is_compatible(child, "brcm,nandcs")) {
+ struct brcmnand_host *host;
+
+ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
+ host->pdev = pdev;
+ host->ctrl = ctrl;
+ host->of_node = child;
+
+ ret = brcmnand_init_cs(host);
+ if (ret)
+ continue; /* Try all chip-selects */
+
+ list_add_tail(&host->node, &ctrl->host_list);
+ }
+ }
+
+ /* No chip-selects could initialize properly */
+ if (list_empty(&ctrl->host_list))
+ return -ENODEV;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(brcmnand_probe);
+
+int brcmnand_remove(struct platform_device *pdev)
+{
+ struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
+ struct brcmnand_host *host;
+
+ list_for_each_entry(host, &ctrl->host_list, node)
+ nand_release(&host->mtd);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(brcmnand_remove);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kevin Cernekee");
+MODULE_AUTHOR("Brian Norris");
+MODULE_DESCRIPTION("NAND driver for Broadcom chips");
+MODULE_ALIAS("platform:brcmnand");
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.h b/drivers/mtd/nand/brcmnand/brcmnand.h
new file mode 100644
index 000000000000..a20c73630b7b
--- /dev/null
+++ b/drivers/mtd/nand/brcmnand/brcmnand.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __BRCMNAND_H__
+#define __BRCMNAND_H__
+
+#include <linux/types.h>
+#include <linux/io.h>
+
+struct platform_device;
+struct dev_pm_ops;
+
+struct brcmnand_soc {
+ struct platform_device *pdev;
+ void *priv;
+ bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
+ void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
+ void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare);
+};
+
+static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc)
+{
+ if (soc && soc->prepare_data_bus)
+ soc->prepare_data_bus(soc, true);
+}
+
+static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc)
+{
+ if (soc && soc->prepare_data_bus)
+ soc->prepare_data_bus(soc, false);
+}
+
+static inline u32 brcmnand_readl(void __iomem *addr)
+{
+ /*
+ * MIPS endianness is configured by boot strap, which also reverses all
+ * bus endianness (i.e., big-endian CPU + big endian bus ==> native
+ * endian I/O).
+ *
+ * Other architectures (e.g., ARM) either do not support big endian, or
+ * else leave I/O in little endian mode.
+ */
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+ return __raw_readl(addr);
+ else
+ return readl_relaxed(addr);
+}
+
+static inline void brcmnand_writel(u32 val, void __iomem *addr)
+{
+ /* See brcmnand_readl() comments */
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+ __raw_writel(val, addr);
+ else
+ writel_relaxed(val, addr);
+}
+
+int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc);
+int brcmnand_remove(struct platform_device *pdev);
+
+extern const struct dev_pm_ops brcmnand_pm_ops;
+
+#endif /* __BRCMNAND_H__ */
diff --git a/drivers/mtd/nand/brcmnand/brcmstb_nand.c b/drivers/mtd/nand/brcmnand/brcmstb_nand.c
new file mode 100644
index 000000000000..5c271077ac87
--- /dev/null
+++ b/drivers/mtd/nand/brcmnand/brcmstb_nand.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "brcmnand.h"
+
+static const struct of_device_id brcmstb_nand_of_match[] = {
+ { .compatible = "brcm,brcmnand" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, brcmstb_nand_of_match);
+
+static int brcmstb_nand_probe(struct platform_device *pdev)
+{
+ return brcmnand_probe(pdev, NULL);
+}
+
+static struct platform_driver brcmstb_nand_driver = {
+ .probe = brcmstb_nand_probe,
+ .remove = brcmnand_remove,
+ .driver = {
+ .name = "brcmstb_nand",
+ .pm = &brcmnand_pm_ops,
+ .of_match_table = brcmstb_nand_of_match,
+ }
+};
+module_platform_driver(brcmstb_nand_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Brian Norris");
+MODULE_DESCRIPTION("NAND driver for Broadcom STB chips");
diff --git a/drivers/mtd/nand/brcmnand/iproc_nand.c b/drivers/mtd/nand/brcmnand/iproc_nand.c
new file mode 100644
index 000000000000..683495c74620
--- /dev/null
+++ b/drivers/mtd/nand/brcmnand/iproc_nand.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright © 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "brcmnand.h"
+
+struct iproc_nand_soc_priv {
+ void __iomem *idm_base;
+ void __iomem *ext_base;
+ spinlock_t idm_lock;
+};
+
+#define IPROC_NAND_CTLR_READY_OFFSET 0x10
+#define IPROC_NAND_CTLR_READY BIT(0)
+
+#define IPROC_NAND_IO_CTRL_OFFSET 0x00
+#define IPROC_NAND_APB_LE_MODE BIT(24)
+#define IPROC_NAND_INT_CTRL_READ_ENABLE BIT(6)
+
+static bool iproc_nand_intc_ack(struct brcmnand_soc *soc)
+{
+ struct iproc_nand_soc_priv *priv = soc->priv;
+ void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET;
+ u32 val = brcmnand_readl(mmio);
+
+ if (val & IPROC_NAND_CTLR_READY) {
+ brcmnand_writel(IPROC_NAND_CTLR_READY, mmio);
+ return true;
+ }
+
+ return false;
+}
+
+static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)
+{
+ struct iproc_nand_soc_priv *priv = soc->priv;
+ void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->idm_lock, flags);
+
+ val = brcmnand_readl(mmio);
+
+ if (en)
+ val |= IPROC_NAND_INT_CTRL_READ_ENABLE;
+ else
+ val &= ~IPROC_NAND_INT_CTRL_READ_ENABLE;
+
+ brcmnand_writel(val, mmio);
+
+ spin_unlock_irqrestore(&priv->idm_lock, flags);
+}
+
+static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare)
+{
+ struct iproc_nand_soc_priv *priv = soc->priv;
+ void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->idm_lock, flags);
+
+ val = brcmnand_readl(mmio);
+
+ if (prepare)
+ val |= IPROC_NAND_APB_LE_MODE;
+ else
+ val &= ~IPROC_NAND_APB_LE_MODE;
+
+ brcmnand_writel(val, mmio);
+
+ spin_unlock_irqrestore(&priv->idm_lock, flags);
+}
+
+static int iproc_nand_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iproc_nand_soc_priv *priv;
+ struct brcmnand_soc *soc;
+ struct resource *res;
+
+ soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL);
+ if (!soc)
+ return -ENOMEM;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->idm_lock);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm");
+ priv->idm_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->idm_base))
+ return PTR_ERR(priv->idm_base);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext");
+ priv->ext_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->ext_base))
+ return PTR_ERR(priv->ext_base);
+
+ soc->pdev = pdev;
+ soc->priv = priv;
+ soc->ctlrdy_ack = iproc_nand_intc_ack;
+ soc->ctlrdy_set_enabled = iproc_nand_intc_set;
+ soc->prepare_data_bus = iproc_nand_apb_access;
+
+ return brcmnand_probe(pdev, soc);
+}
+
+static const struct of_device_id iproc_nand_of_match[] = {
+ { .compatible = "brcm,nand-iproc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, iproc_nand_of_match);
+
+static struct platform_driver iproc_nand_driver = {
+ .probe = iproc_nand_probe,
+ .remove = brcmnand_remove,
+ .driver = {
+ .name = "iproc_nand",
+ .pm = &brcmnand_pm_ops,
+ .of_match_table = iproc_nand_of_match,
+ }
+};
+module_platform_driver(iproc_nand_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Brian Norris");
+MODULE_AUTHOR("Ray Jui");
+MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs");
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
index 88109d375ae7..aec6045058c7 100644
--- a/drivers/mtd/nand/cs553x_nand.c
+++ b/drivers/mtd/nand/cs553x_nand.c
@@ -237,17 +237,23 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
/* Enable the following for a flash based bad block table */
this->bbt_options = NAND_BBT_USE_FLASH;
+ new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
+ if (!new_mtd->name) {
+ err = -ENOMEM;
+ goto out_ior;
+ }
+
/* Scan to find existence of the device */
if (nand_scan(new_mtd, 1)) {
err = -ENXIO;
- goto out_ior;
+ goto out_free;
}
- new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
-
cs553x_mtd[cs] = new_mtd;
goto out;
+out_free:
+ kfree(new_mtd->name);
out_ior:
iounmap(this->IO_ADDR_R);
out_mtd:
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index f68a7bccecdc..7da266a53979 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -69,6 +69,9 @@ struct doc_priv {
int mh0_page;
int mh1_page;
struct mtd_info *nextdoc;
+
+ /* Handle the last stage of initialization (BBT scan, partitioning) */
+ int (*late_init)(struct mtd_info *mtd);
};
/* This is the syndrome computed by the HW ecc generator upon reading an empty
@@ -1294,14 +1297,11 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd)
this->bbt_md = NULL;
}
- /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
- At least as nand_bbt.c is currently written. */
- if ((ret = nand_scan_bbt(mtd, NULL)))
+ ret = this->scan_bbt(mtd);
+ if (ret)
return ret;
- mtd_device_register(mtd, NULL, 0);
- if (!no_autopart)
- mtd_device_register(mtd, parts, numparts);
- return 0;
+
+ return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
}
static int __init inftl_scan_bbt(struct mtd_info *mtd)
@@ -1344,10 +1344,10 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
this->bbt_md->pattern = "TBB_SYSM";
}
- /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
- At least as nand_bbt.c is currently written. */
- if ((ret = nand_scan_bbt(mtd, NULL)))
+ ret = this->scan_bbt(mtd);
+ if (ret)
return ret;
+
memset((char *)parts, 0, sizeof(parts));
numparts = inftl_partscan(mtd, parts);
/* At least for now, require the INFTL Media Header. We could probably
@@ -1355,10 +1355,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
autopartitioning, but I want to give it more thought. */
if (!numparts)
return -EIO;
- mtd_device_register(mtd, NULL, 0);
- if (!no_autopart)
- mtd_device_register(mtd, parts, numparts);
- return 0;
+ return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
}
static inline int __init doc2000_init(struct mtd_info *mtd)
@@ -1369,7 +1366,7 @@ static inline int __init doc2000_init(struct mtd_info *mtd)
this->read_byte = doc2000_read_byte;
this->write_buf = doc2000_writebuf;
this->read_buf = doc2000_readbuf;
- this->scan_bbt = nftl_scan_bbt;
+ doc->late_init = nftl_scan_bbt;
doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
doc2000_count_chips(mtd);
@@ -1396,13 +1393,13 @@ static inline int __init doc2001_init(struct mtd_info *mtd)
can have multiple chips. */
doc2000_count_chips(mtd);
mtd->name = "DiskOnChip 2000 (INFTL Model)";
- this->scan_bbt = inftl_scan_bbt;
+ doc->late_init = inftl_scan_bbt;
return (4 * doc->chips_per_floor);
} else {
/* Bog-standard Millennium */
doc->chips_per_floor = 1;
mtd->name = "DiskOnChip Millennium";
- this->scan_bbt = nftl_scan_bbt;
+ doc->late_init = nftl_scan_bbt;
return 1;
}
}
@@ -1415,7 +1412,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)
this->read_byte = doc2001plus_read_byte;
this->write_buf = doc2001plus_writebuf;
this->read_buf = doc2001plus_readbuf;
- this->scan_bbt = inftl_scan_bbt;
+ doc->late_init = inftl_scan_bbt;
this->cmd_ctrl = NULL;
this->select_chip = doc2001plus_select_chip;
this->cmdfunc = doc2001plus_command;
@@ -1591,6 +1588,8 @@ static int __init doc_probe(unsigned long physadr)
nand->ecc.bytes = 6;
nand->ecc.strength = 2;
nand->bbt_options = NAND_BBT_USE_FLASH;
+ /* Skip the automatic BBT scan so we can run it manually */
+ nand->options |= NAND_SKIP_BBTSCAN;
doc->physadr = physadr;
doc->virtadr = virtadr;
@@ -1608,7 +1607,7 @@ static int __init doc_probe(unsigned long physadr)
else
numchips = doc2001_init(mtd);
- if ((ret = nand_scan(mtd, numchips))) {
+ if ((ret = nand_scan(mtd, numchips)) || (ret = doc->late_init(mtd))) {
/* DBB note: i believe nand_release is necessary here, as
buffers may have been allocated in nand_base. Check with
Thomas. FIX ME! */
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
index e58af4bfa8c8..793872f18065 100644
--- a/drivers/mtd/nand/fsmc_nand.c
+++ b/drivers/mtd/nand/fsmc_nand.c
@@ -562,6 +562,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
dma_cookie_t cookie;
unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
int ret;
+ unsigned long time_left;
if (direction == DMA_TO_DEVICE)
chan = host->write_dma_chan;
@@ -601,14 +602,13 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
dma_async_issue_pending(chan);
- ret =
+ time_left =
wait_for_completion_timeout(&host->dma_access_complete,
msecs_to_jiffies(3000));
- if (ret <= 0) {
+ if (time_left == 0) {
dmaengine_terminate_all(chan);
dev_err(host->dev, "wait_for_completion_timeout\n");
- if (!ret)
- ret = -ETIMEDOUT;
+ ret = -ETIMEDOUT;
goto unmap_dma;
}
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index 1f12e5bfbced..2a49b53c8db9 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -837,7 +837,7 @@ static int mpc5121_nfc_remove(struct platform_device *op)
return 0;
}
-static struct of_device_id mpc5121_nfc_match[] = {
+static const struct of_device_id mpc5121_nfc_match[] = {
{ .compatible = "fsl,mpc5121-nfc", },
{},
};
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 372e0e38f59b..2426db88db36 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -189,6 +189,7 @@ struct mxc_nand_host {
int clk_act;
int irq;
int eccsize;
+ int used_oobsize;
int active_cs;
struct completion op_completion;
@@ -280,12 +281,44 @@ static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
*t++ = __raw_readl(s++);
}
+static void memcpy16_fromio(void *trg, const void __iomem *src, size_t size)
+{
+ int i;
+ u16 *t = trg;
+ const __iomem u16 *s = src;
+
+ /* We assume that src (IO) is always 32bit aligned */
+ if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) {
+ memcpy32_fromio(trg, src, size);
+ return;
+ }
+
+ for (i = 0; i < (size >> 1); i++)
+ *t++ = __raw_readw(s++);
+}
+
static inline void memcpy32_toio(void __iomem *trg, const void *src, int size)
{
/* __iowrite32_copy use 32bit size values so divide by 4 */
__iowrite32_copy(trg, src, size / 4);
}
+static void memcpy16_toio(void __iomem *trg, const void *src, int size)
+{
+ int i;
+ __iomem u16 *t = trg;
+ const u16 *s = src;
+
+ /* We assume that trg (IO) is always 32bit aligned */
+ if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) {
+ memcpy32_toio(trg, src, size);
+ return;
+ }
+
+ for (i = 0; i < (size >> 1); i++)
+ __raw_writew(*s++, t++);
+}
+
static int check_int_v3(struct mxc_nand_host *host)
{
uint32_t tmp;
@@ -807,32 +840,48 @@ static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
}
/*
- * Function to transfer data to/from spare area.
+ * The controller splits a page into data chunks of 512 bytes + partial oob.
+ * There are writesize / 512 such chunks, the size of the partial oob parts is
+ * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then
+ * contains additionally the byte lost by rounding (if any).
+ * This function handles the needed shuffling between host->data_buf (which
+ * holds a page in natural order, i.e. writesize bytes data + oobsize bytes
+ * spare) and the NFC buffer.
*/
static void copy_spare(struct mtd_info *mtd, bool bfrom)
{
struct nand_chip *this = mtd->priv;
struct mxc_nand_host *host = this->priv;
- u16 i, j;
- u16 n = mtd->writesize >> 9;
+ u16 i, oob_chunk_size;
+ u16 num_chunks = mtd->writesize / 512;
+
u8 *d = host->data_buf + mtd->writesize;
u8 __iomem *s = host->spare0;
- u16 t = host->devtype_data->spare_len;
+ u16 sparebuf_size = host->devtype_data->spare_len;
- j = (mtd->oobsize / n >> 1) << 1;
+ /* size of oob chunk for all but possibly the last one */
+ oob_chunk_size = (host->used_oobsize / num_chunks) & ~1;
if (bfrom) {
- for (i = 0; i < n - 1; i++)
- memcpy32_fromio(d + i * j, s + i * t, j);
-
- /* the last section */
- memcpy32_fromio(d + i * j, s + i * t, mtd->oobsize - i * j);
+ for (i = 0; i < num_chunks - 1; i++)
+ memcpy16_fromio(d + i * oob_chunk_size,
+ s + i * sparebuf_size,
+ oob_chunk_size);
+
+ /* the last chunk */
+ memcpy16_fromio(d + i * oob_chunk_size,
+ s + i * sparebuf_size,
+ host->used_oobsize - i * oob_chunk_size);
} else {
- for (i = 0; i < n - 1; i++)
- memcpy32_toio(&s[i * t], &d[i * j], j);
-
- /* the last section */
- memcpy32_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j);
+ for (i = 0; i < num_chunks - 1; i++)
+ memcpy16_toio(&s[i * sparebuf_size],
+ &d[i * oob_chunk_size],
+ oob_chunk_size);
+
+ /* the last chunk */
+ memcpy16_toio(&s[oob_chunk_size * sparebuf_size],
+ &d[i * oob_chunk_size],
+ host->used_oobsize - i * oob_chunk_size);
}
}
@@ -911,6 +960,23 @@ static int get_eccsize(struct mtd_info *mtd)
return 8;
}
+static void ecc_8bit_layout_4k(struct nand_ecclayout *layout)
+{
+ int i, j;
+
+ layout->eccbytes = 8*18;
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 18; j++)
+ layout->eccpos[i*18 + j] = i*26 + j + 7;
+
+ layout->oobfree[0].offset = 2;
+ layout->oobfree[0].length = 4;
+ for (i = 1; i < 8; i++) {
+ layout->oobfree[i].offset = i*26;
+ layout->oobfree[i].length = 7;
+ }
+}
+
static void preset_v1(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -1350,7 +1416,7 @@ static inline int is_imx53_nfc(struct mxc_nand_host *host)
return host->devtype_data == &imx53_nand_devtype_data;
}
-static struct platform_device_id mxcnd_devtype[] = {
+static const struct platform_device_id mxcnd_devtype[] = {
{
.name = "imx21-nand",
.driver_data = (kernel_ulong_t) &imx21_nand_devtype_data,
@@ -1587,8 +1653,20 @@ static int mxcnd_probe(struct platform_device *pdev)
if (mtd->writesize == 2048)
this->ecc.layout = host->devtype_data->ecclayout_2k;
- else if (mtd->writesize == 4096)
+ else if (mtd->writesize == 4096) {
this->ecc.layout = host->devtype_data->ecclayout_4k;
+ if (get_eccsize(mtd) == 8)
+ ecc_8bit_layout_4k(this->ecc.layout);
+ }
+
+ /*
+ * Experimentation shows that i.MX NFC can only handle up to 218 oob
+ * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare()
+ * into copying invalid data to/from the spare IO buffer, as this
+ * might cause ECC data corruption when doing sub-page write to a
+ * partially written page.
+ */
+ host->used_oobsize = min(mtd->oobsize, 218U);
if (this->ecc.mode == NAND_ECC_HW) {
if (is_imx21_nfc(host) || is_imx27_nfc(host))
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index c2e1232cd45c..ceb68ca8277a 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand.c
- *
* Overview:
* This is the generic MTD driver for NAND flash devices. It should be
* capable of working with almost all NAND chips currently available.
@@ -48,6 +46,7 @@
#include <linux/leds.h>
#include <linux/io.h>
#include <linux/mtd/partitions.h>
+#include <linux/of_mtd.h>
/* Define default oob placement schemes for large and small page devices */
static struct nand_ecclayout nand_oob_8 = {
@@ -2928,9 +2927,6 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
& ONFI_OPT_CMD_SET_GET_FEATURES))
return -EINVAL;
- /* clear the sub feature parameters */
- memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
-
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
*subfeature_param++ = chip->read_byte(mtd);
@@ -3689,7 +3685,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
if (find_full_id_nand(mtd, chip, type, id_data, &busw))
goto ident_done;
} else if (*dev_id == type->dev_id) {
- break;
+ break;
}
}
@@ -3798,6 +3794,39 @@ ident_done:
return type;
}
+static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip,
+ struct device_node *dn)
+{
+ int ecc_mode, ecc_strength, ecc_step;
+
+ if (of_get_nand_bus_width(dn) == 16)
+ chip->options |= NAND_BUSWIDTH_16;
+
+ if (of_get_nand_on_flash_bbt(dn))
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+ ecc_mode = of_get_nand_ecc_mode(dn);
+ ecc_strength = of_get_nand_ecc_strength(dn);
+ ecc_step = of_get_nand_ecc_step_size(dn);
+
+ if ((ecc_step >= 0 && !(ecc_strength >= 0)) ||
+ (!(ecc_step >= 0) && ecc_strength >= 0)) {
+ pr_err("must set both strength and step size in DT\n");
+ return -EINVAL;
+ }
+
+ if (ecc_mode >= 0)
+ chip->ecc.mode = ecc_mode;
+
+ if (ecc_strength >= 0)
+ chip->ecc.strength = ecc_strength;
+
+ if (ecc_step > 0)
+ chip->ecc.size = ecc_step;
+
+ return 0;
+}
+
/**
* nand_scan_ident - [NAND Interface] Scan for the NAND device
* @mtd: MTD device structure
@@ -3815,6 +3844,13 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
int i, nand_maf_id, nand_dev_id;
struct nand_chip *chip = mtd->priv;
struct nand_flash_dev *type;
+ int ret;
+
+ if (chip->dn) {
+ ret = nand_dt_init(mtd, chip, chip->dn);
+ if (ret)
+ return ret;
+ }
/* Set the default functions */
nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 9bb8453d224e..63a1a36a3f4b 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nand_bbt.c
- *
* Overview:
* Bad block table support for the NAND driver
*
@@ -64,7 +62,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/bbm.h>
#include <linux/mtd/nand.h>
-#include <linux/mtd/nand_ecc.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/vmalloc.h>
@@ -720,7 +717,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
/* Must we save the block contents? */
if (td->options & NAND_BBT_SAVECONTENT) {
/* Make it block aligned */
- to &= ~((loff_t)((1 << this->bbt_erase_shift) - 1));
+ to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1);
len = 1 << this->bbt_erase_shift;
res = mtd_read(mtd, to, len, &retlen, buf);
if (res < 0) {
@@ -1075,10 +1072,10 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
* The bad block table memory is allocated here. It must be freed by calling
* the nand_free_bbt function.
*/
-int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
- int len, res = 0;
+ int len, res;
uint8_t *buf;
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
@@ -1099,10 +1096,9 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
if (!td) {
if ((res = nand_memory_bbt(mtd, bd))) {
pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
- kfree(this->bbt);
- this->bbt = NULL;
+ goto err;
}
- return res;
+ return 0;
}
verify_bbt_descr(mtd, td);
verify_bbt_descr(mtd, md);
@@ -1112,9 +1108,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
len += (len >> this->page_shift) * mtd->oobsize;
buf = vmalloc(len);
if (!buf) {
- kfree(this->bbt);
- this->bbt = NULL;
- return -ENOMEM;
+ res = -ENOMEM;
+ goto err;
}
/* Is the bbt at a given page? */
@@ -1126,6 +1121,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
}
res = check_create(mtd, buf, bd);
+ if (res)
+ goto err;
/* Prevent the bbt regions from erasing / writing */
mark_bbt_region(mtd, td);
@@ -1133,6 +1130,11 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
mark_bbt_region(mtd, md);
vfree(buf);
+ return 0;
+
+err:
+ kfree(this->bbt);
+ this->bbt = NULL;
return res;
}
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index dd620c19c619..7124400d903b 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/nandids.c
- *
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index f2324271b94e..52c0c1a3899c 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -743,6 +743,11 @@ static int init_nandsim(struct mtd_info *mtd)
goto error;
}
ns->partitions[i].name = get_partition_name(i);
+ if (!ns->partitions[i].name) {
+ NS_ERR("unable to allocate memory.\n");
+ ret = -ENOMEM;
+ goto error;
+ }
ns->partitions[i].offset = next_offset;
ns->partitions[i].size = part_sz;
next_offset += ns->partitions[i].size;
@@ -756,6 +761,11 @@ static int init_nandsim(struct mtd_info *mtd)
goto error;
}
ns->partitions[i].name = get_partition_name(i);
+ if (!ns->partitions[i].name) {
+ NS_ERR("unable to allocate memory.\n");
+ ret = -ENOMEM;
+ goto error;
+ }
ns->partitions[i].offset = next_offset;
ns->partitions[i].size = remains;
ns->nbparts += 1;
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index 3187c6b92d9a..67a1b3f911cf 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -1,6 +1,4 @@
/*
- * drivers/mtd/ndfc.c
- *
* Overview:
* Platform independent driver for NDFC (NanD Flash Controller)
* integrated into EP440 cores
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c
index 4535c263fae5..717cf623fcde 100644
--- a/drivers/mtd/nand/plat_nand.c
+++ b/drivers/mtd/nand/plat_nand.c
@@ -24,8 +24,6 @@ struct plat_nand_data {
void __iomem *io_base;
};
-static const char *part_probe_types[] = { "cmdlinepart", NULL };
-
/*
* Probe for the NAND device.
*/
@@ -95,7 +93,7 @@ static int plat_nand_probe(struct platform_device *pdev)
goto out;
}
- part_types = pdata->chip.part_probe_types ? : part_probe_types;
+ part_types = pdata->chip.part_probe_types;
ppdata.of_node = pdev->dev.of_node;
err = mtd_device_parse_register(&data->mtd, part_types, &ppdata,
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index a4615fcc3d00..1259cc558ce9 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -22,13 +22,14 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mtd.h>
-#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP)
+#if defined(CONFIG_ARM) && (defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP))
#define ARCH_HAS_DMA
#endif
@@ -483,7 +484,8 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
{
if (info->ecc_bch) {
- int timeout;
+ u32 val;
+ int ret;
/*
* According to the datasheet, when reading from NDDB
@@ -494,18 +496,14 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
* the polling on the last read.
*/
while (len > 8) {
- __raw_readsl(info->mmio_base + NDDB, data, 8);
-
- for (timeout = 0;
- !(nand_readl(info, NDSR) & NDSR_RDDREQ);
- timeout++) {
- if (timeout >= 5) {
- dev_err(&info->pdev->dev,
- "Timeout on RDDREQ while draining the FIFO\n");
- return;
- }
-
- mdelay(1);
+ readsl(info->mmio_base + NDDB, data, 8);
+
+ ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val,
+ val & NDSR_RDDREQ, 1000, 5000);
+ if (ret) {
+ dev_err(&info->pdev->dev,
+ "Timeout on RDDREQ while draining the FIFO\n");
+ return;
}
data += 32;
@@ -513,7 +511,7 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
}
}
- __raw_readsl(info->mmio_base + NDDB, data, len);
+ readsl(info->mmio_base + NDDB, data, len);
}
static void handle_data_pio(struct pxa3xx_nand_info *info)
@@ -522,14 +520,14 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
switch (info->state) {
case STATE_PIO_WRITING:
- __raw_writesl(info->mmio_base + NDDB,
- info->data_buff + info->data_buff_pos,
- DIV_ROUND_UP(do_bytes, 4));
+ writesl(info->mmio_base + NDDB,
+ info->data_buff + info->data_buff_pos,
+ DIV_ROUND_UP(do_bytes, 4));
if (info->oob_size > 0)
- __raw_writesl(info->mmio_base + NDDB,
- info->oob_buff + info->oob_buff_pos,
- DIV_ROUND_UP(info->oob_size, 4));
+ writesl(info->mmio_base + NDDB,
+ info->oob_buff + info->oob_buff_pos,
+ DIV_ROUND_UP(info->oob_size, 4));
break;
case STATE_PIO_READING:
drain_fifo(info,
@@ -1630,8 +1628,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
info->pdev = pdev;
info->variant = pxa3xx_nand_get_variant(pdev);
for (cs = 0; cs < pdata->num_cs; cs++) {
- mtd = (struct mtd_info *)((unsigned int)&info[1] +
- (sizeof(*mtd) + sizeof(*host)) * cs);
+ mtd = (void *)&info[1] + (sizeof(*mtd) + sizeof(*host)) * cs;
chip = (struct nand_chip *)(&mtd[1]);
host = (struct pxa3xx_nand_host *)chip;
info->host[cs] = host;
diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c
index baea83f4dea8..77e96d2df96c 100644
--- a/drivers/mtd/nand/r852.c
+++ b/drivers/mtd/nand/r852.c
@@ -653,11 +653,15 @@ static int r852_register_nand_device(struct r852_device *dev)
if (sm_register_device(dev->mtd, dev->sm))
goto error2;
- if (device_create_file(&dev->mtd->dev, &dev_attr_media_type))
+ if (device_create_file(&dev->mtd->dev, &dev_attr_media_type)) {
message("can't create media type sysfs attribute");
+ goto error3;
+ }
dev->card_registred = 1;
return 0;
+error3:
+ nand_release(dev->mtd);
error2:
kfree(dev->mtd);
error1:
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 0e02be47ce1d..381f67ac6b5a 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -1105,7 +1105,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
/* driver device registration */
-static struct platform_device_id s3c24xx_driver_ids[] = {
+static const struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-nand",
.driver_data = TYPE_S3C2410,
diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/xway_nand.c
index 3f81dc8f214c..3b28db458ea0 100644
--- a/drivers/mtd/nand/xway_nand.c
+++ b/drivers/mtd/nand/xway_nand.c
@@ -160,14 +160,10 @@ static int xway_nand_probe(struct platform_device *pdev)
return 0;
}
-/* allow users to override the partition in DT using the cmdline */
-static const char *part_probes[] = { "cmdlinepart", "ofpart", NULL };
-
static struct platform_nand_data xway_nand_data = {
.chip = {
.nr_chips = 1,
.chip_delay = 30,
- .part_probe_types = part_probes,
},
.ctrl = {
.probe = xway_nand_probe,
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c
index 19cfb97adbc0..739259513055 100644
--- a/drivers/mtd/onenand/samsung.c
+++ b/drivers/mtd/onenand/samsung.c
@@ -1083,7 +1083,7 @@ static const struct dev_pm_ops s3c_pm_ops = {
.resume = s3c_pm_ops_resume,
};
-static struct platform_device_id s3c_onenand_driver_ids[] = {
+static const struct platform_device_id s3c_onenand_driver_ids[] = {
{
.name = "s3c6400-onenand",
.driver_data = TYPE_S3C6400,
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 5d5d36272bb5..52a872fa1b6e 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -662,7 +662,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
return 0;
}
-static struct of_device_id fsl_qspi_dt_ids[] = {
+static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, },
{ .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, },
{ /* sentinel */ }
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 14a5d2325dac..d78831b4422b 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -513,6 +513,13 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
/* NOTE: double check command sets and memory organization when you add
* more nor chips. This current list focusses on newer chips, which
* have been converging on command sets which including JEDEC ID.
+ *
+ * All newly added entries should describe *hardware* and should use SECT_4K
+ * (or SECT_4K_PMC) if hardware supports erasing 4 KiB sectors. For usage
+ * scenarios excluding small sectors there is config option that can be
+ * disabled: CONFIG_MTD_SPI_NOR_USE_4K_SECTORS.
+ * For historical (and compatibility) reasons (before we got above config) some
+ * old entries may be missing 4K flag.
*/
static const struct spi_device_id spi_nor_ids[] = {
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
@@ -538,7 +545,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
{ "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) },
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
- { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, 0) },
+ { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) },
/* ESMT */
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
@@ -560,7 +567,11 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
+ /* ISSI */
+ { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2, SECT_4K) },
+
/* Macronix */
+ { "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) },
{ "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) },
{ "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
{ "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) },
@@ -602,7 +613,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
- { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
+ { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
@@ -613,7 +624,8 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
- { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, 0) },
+ { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
+ { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
diff --git a/drivers/mtd/tests/readtest.c b/drivers/mtd/tests/readtest.c
index a3196b750a22..58df07acdbdb 100644
--- a/drivers/mtd/tests/readtest.c
+++ b/drivers/mtd/tests/readtest.c
@@ -191,9 +191,11 @@ static int __init mtd_readtest_init(void)
err = ret;
}
- err = mtdtest_relax();
- if (err)
+ ret = mtdtest_relax();
+ if (ret) {
+ err = ret;
goto out;
+ }
}
if (err)
diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c
index db2c05b6fe7f..c9eb78f10a0d 100644
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -310,6 +310,8 @@ static void ubiblock_do_work(struct work_struct *work)
blk_rq_map_sg(req->q, req, pdu->usgl.sg);
ret = ubiblock_read(pdu);
+ rq_flush_dcache_pages(req);
+
blk_mq_end_request(req, ret);
}
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 3a10551d64cf..d5fe5d5f490f 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -4544,6 +4544,8 @@ unsigned int bond_get_num_tx_queues(void)
int bond_create(struct net *net, const char *name)
{
struct net_device *bond_dev;
+ struct bonding *bond;
+ struct alb_bond_info *bond_info;
int res;
rtnl_lock();
@@ -4557,6 +4559,14 @@ int bond_create(struct net *net, const char *name)
return -ENOMEM;
}
+ /*
+ * Initialize rx_hashtbl_used_head to RLB_NULL_INDEX.
+ * It is set to 0 by default which is wrong.
+ */
+ bond = netdev_priv(bond_dev);
+ bond_info = &(BOND_ALB_INFO(bond));
+ bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX;
+
dev_net_set(bond_dev, net);
bond_dev->rtnl_link_ops = &bond_link_ops;
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 4df28943d222..e8d3c1d35453 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -624,7 +624,7 @@ int __bond_opt_set(struct bonding *bond,
out:
if (ret)
bond_opt_error_interpret(bond, opt, ret, val);
- else
+ else if (bond->dev->reg_state == NETREG_REGISTERED)
call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
return ret;
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
index 6bddfe062b51..fc55e8e0351d 100644
--- a/drivers/net/can/xilinx_can.c
+++ b/drivers/net/can/xilinx_can.c
@@ -509,10 +509,11 @@ static int xcan_rx(struct net_device *ndev)
cf->can_id |= CAN_RTR_FLAG;
}
- if (!(id_xcan & XCAN_IDR_SRR_MASK)) {
- data[0] = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET);
- data[1] = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET);
+ /* DW1/DW2 must always be read to remove message from RXFIFO */
+ data[0] = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET);
+ data[1] = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET);
+ if (!(cf->can_id & CAN_RTR_FLAG)) {
/* Change Xilinx CAN data format to socketCAN data format */
if (cf->can_dlc > 0)
*(__be32 *)(cf->data) = cpu_to_be32(data[0]);
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index af639ab4c55b..cf309aa92802 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -1469,6 +1469,9 @@ static void __exit mv88e6xxx_cleanup(void)
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
unregister_switch_driver(&mv88e6171_switch_driver);
#endif
+#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
+ unregister_switch_driver(&mv88e6352_switch_driver);
+#endif
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
unregister_switch_driver(&mv88e6123_61_65_switch_driver);
#endif
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index 0533c051a3e5..da48e66377b5 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -391,6 +391,12 @@ static int tse_rx(struct altera_tse_private *priv, int limit)
"RCV pktstatus %08X pktlength %08X\n",
pktstatus, pktlength);
+ /* DMA trasfer from TSE starts with 2 aditional bytes for
+ * IP payload alignment. Status returned by get_rx_status()
+ * contains DMA transfer length. Packet is 2 bytes shorter.
+ */
+ pktlength -= 2;
+
count++;
next_entry = (++priv->rx_cons) % priv->rx_ring_size;
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index 089c269637b7..426916036151 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -180,6 +180,7 @@ config SUNLANCE
config AMD_XGBE
tristate "AMD 10GbE Ethernet driver"
depends on (OF_NET || ACPI) && HAS_IOMEM && HAS_DMA
+ depends on ARM64 || COMPILE_TEST
select PHYLIB
select AMD_XGBE_PHY
select BITREVERSE
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index db84ddcfec84..9fd6c69a8bac 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -423,7 +423,7 @@ static void xgbe_tx_timer(unsigned long data)
if (napi_schedule_prep(napi)) {
/* Disable Tx and Rx interrupts */
if (pdata->per_channel_irq)
- disable_irq(channel->dma_irq);
+ disable_irq_nosync(channel->dma_irq);
else
xgbe_disable_rx_tx_ints(pdata);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
index 714905384900..6d2c702c8e4a 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
@@ -168,13 +168,8 @@ static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata)
#ifdef CONFIG_ACPI
static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
{
- struct acpi_device *adev = pdata->adev;
struct device *dev = pdata->dev;
u32 property;
- acpi_handle handle;
- acpi_status status;
- unsigned long long data;
- int cca;
int ret;
/* Obtain the system clock setting */
@@ -195,24 +190,6 @@ static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
}
pdata->ptpclk_rate = property;
- /* Retrieve the device cache coherency value */
- handle = adev->handle;
- do {
- status = acpi_evaluate_integer(handle, "_CCA", NULL, &data);
- if (!ACPI_FAILURE(status)) {
- cca = data;
- break;
- }
-
- status = acpi_get_parent(handle, &handle);
- } while (!ACPI_FAILURE(status));
-
- if (ACPI_FAILURE(status)) {
- dev_err(dev, "error obtaining acpi coherency value\n");
- return -EINVAL;
- }
- pdata->coherent = !!cca;
-
return 0;
}
#else /* CONFIG_ACPI */
@@ -243,9 +220,6 @@ static int xgbe_of_support(struct xgbe_prv_data *pdata)
}
pdata->ptpclk_rate = clk_get_rate(pdata->ptpclk);
- /* Retrieve the device cache coherency value */
- pdata->coherent = of_dma_is_coherent(dev->of_node);
-
return 0;
}
#else /* CONFIG_OF */
@@ -364,6 +338,7 @@ static int xgbe_probe(struct platform_device *pdev)
goto err_io;
/* Set the DMA coherency values */
+ pdata->coherent = device_dma_is_coherent(pdata->dev);
if (pdata->coherent) {
pdata->axdomain = XGBE_DMA_OS_AXDOMAIN;
pdata->arcache = XGBE_DMA_OS_ARCACHE;
diff --git a/drivers/net/ethernet/apm/xgene/Kconfig b/drivers/net/ethernet/apm/xgene/Kconfig
index f4054d242f3c..19e38afbc5ee 100644
--- a/drivers/net/ethernet/apm/xgene/Kconfig
+++ b/drivers/net/ethernet/apm/xgene/Kconfig
@@ -1,6 +1,7 @@
config NET_XGENE
tristate "APM X-Gene SoC Ethernet Driver"
depends on HAS_DMA
+ depends on ARCH_XGENE || COMPILE_TEST
select PHYLIB
help
This is the Ethernet driver for the on-chip ethernet interface on the
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_hw.h b/drivers/net/ethernet/atheros/atl1e/atl1e_hw.h
index 74df16aef793..88a6271de5bc 100644
--- a/drivers/net/ethernet/atheros/atl1e/atl1e_hw.h
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_hw.h
@@ -129,7 +129,7 @@ s32 atl1e_restart_autoneg(struct atl1e_hw *hw);
#define TWSI_CTRL_LD_SLV_ADDR_SHIFT 8
#define TWSI_CTRL_SW_LDSTART 0x800
#define TWSI_CTRL_HW_LDSTART 0x1000
-#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x0x7F
+#define TWSI_CTRL_SMB_SLV_ADDR_MASK 0x7F
#define TWSI_CTRL_SMB_SLV_ADDR_SHIFT 15
#define TWSI_CTRL_LD_EXIST 0x400000
#define TWSI_CTRL_READ_FREQ_SEL_MASK 0x3
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index 77363d680532..a3b1c07ae0af 100644
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -2464,6 +2464,7 @@ err_out_powerdown:
ssb_bus_may_powerdown(sdev->bus);
err_out_free_dev:
+ netif_napi_del(&bp->napi);
free_netdev(dev);
out:
@@ -2480,6 +2481,7 @@ static void b44_remove_one(struct ssb_device *sdev)
b44_unregister_phy_one(bp);
ssb_device_disable(sdev, 0);
ssb_bus_may_powerdown(sdev->bus);
+ netif_napi_del(&bp->napi);
free_netdev(dev);
ssb_pcihost_set_power_state(sdev, PCI_D3hot);
ssb_set_drvdata(sdev, NULL);
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h
index 7e3d87a88c76..e2c043eabbf3 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.h
+++ b/drivers/net/ethernet/broadcom/bcmsysport.h
@@ -543,7 +543,7 @@ struct bcm_sysport_tx_counters {
u32 jbr; /* RO # of xmited jabber count*/
u32 bytes; /* RO # of xmited byte count */
u32 pok; /* RO # of xmited good pkt */
- u32 uc; /* RO (0x0x4f0)# of xmited unitcast pkt */
+ u32 uc; /* RO (0x4f0) # of xmited unicast pkt */
};
struct bcm_sysport_mib {
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index 355d5fea5be9..1f82a04ce01a 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -521,6 +521,7 @@ struct bnx2x_fp_txdata {
};
enum bnx2x_tpa_mode_t {
+ TPA_MODE_DISABLED,
TPA_MODE_LRO,
TPA_MODE_GRO
};
@@ -589,7 +590,6 @@ struct bnx2x_fastpath {
/* TPA related */
struct bnx2x_agg_info *tpa_info;
- u8 disable_tpa;
#ifdef BNX2X_STOP_ON_ERROR
u64 tpa_queue_used;
#endif
@@ -1545,9 +1545,7 @@ struct bnx2x {
#define USING_MSIX_FLAG (1 << 5)
#define USING_MSI_FLAG (1 << 6)
#define DISABLE_MSI_FLAG (1 << 7)
-#define TPA_ENABLE_FLAG (1 << 8)
#define NO_MCP_FLAG (1 << 9)
-#define GRO_ENABLE_FLAG (1 << 10)
#define MF_FUNC_DIS (1 << 11)
#define OWN_CNIC_IRQ (1 << 12)
#define NO_ISCSI_OOO_FLAG (1 << 13)
@@ -1776,7 +1774,7 @@ struct bnx2x {
int stats_state;
/* used for synchronization of concurrent threads statistics handling */
- struct mutex stats_lock;
+ struct semaphore stats_lock;
/* used by dmae command loader */
struct dmae_command stats_dmae;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index 3558a36b1c2d..ec56a9b65dc3 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -947,10 +947,10 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
u16 frag_size, pages;
#ifdef BNX2X_STOP_ON_ERROR
/* sanity check */
- if (fp->disable_tpa &&
+ if (fp->mode == TPA_MODE_DISABLED &&
(CQE_TYPE_START(cqe_fp_type) ||
CQE_TYPE_STOP(cqe_fp_type)))
- BNX2X_ERR("START/STOP packet while disable_tpa type %x\n",
+ BNX2X_ERR("START/STOP packet while TPA disabled, type %x\n",
CQE_TYPE(cqe_fp_type));
#endif
@@ -1396,7 +1396,7 @@ void bnx2x_init_rx_rings(struct bnx2x *bp)
DP(NETIF_MSG_IFUP,
"mtu %d rx_buf_size %d\n", bp->dev->mtu, fp->rx_buf_size);
- if (!fp->disable_tpa) {
+ if (fp->mode != TPA_MODE_DISABLED) {
/* Fill the per-aggregation pool */
for (i = 0; i < MAX_AGG_QS(bp); i++) {
struct bnx2x_agg_info *tpa_info =
@@ -1410,7 +1410,7 @@ void bnx2x_init_rx_rings(struct bnx2x *bp)
BNX2X_ERR("Failed to allocate TPA skb pool for queue[%d] - disabling TPA on this queue!\n",
j);
bnx2x_free_tpa_pool(bp, fp, i);
- fp->disable_tpa = 1;
+ fp->mode = TPA_MODE_DISABLED;
break;
}
dma_unmap_addr_set(first_buf, mapping, 0);
@@ -1438,7 +1438,7 @@ void bnx2x_init_rx_rings(struct bnx2x *bp)
ring_prod);
bnx2x_free_tpa_pool(bp, fp,
MAX_AGG_QS(bp));
- fp->disable_tpa = 1;
+ fp->mode = TPA_MODE_DISABLED;
ring_prod = 0;
break;
}
@@ -1560,7 +1560,7 @@ static void bnx2x_free_rx_skbs(struct bnx2x *bp)
bnx2x_free_rx_bds(fp);
- if (!fp->disable_tpa)
+ if (fp->mode != TPA_MODE_DISABLED)
bnx2x_free_tpa_pool(bp, fp, MAX_AGG_QS(bp));
}
}
@@ -2477,19 +2477,19 @@ static void bnx2x_bz_fp(struct bnx2x *bp, int index)
/* set the tpa flag for each queue. The tpa flag determines the queue
* minimal size so it must be set prior to queue memory allocation
*/
- fp->disable_tpa = !(bp->flags & TPA_ENABLE_FLAG ||
- (bp->flags & GRO_ENABLE_FLAG &&
- bnx2x_mtu_allows_gro(bp->dev->mtu)));
- if (bp->flags & TPA_ENABLE_FLAG)
+ if (bp->dev->features & NETIF_F_LRO)
fp->mode = TPA_MODE_LRO;
- else if (bp->flags & GRO_ENABLE_FLAG)
+ else if (bp->dev->features & NETIF_F_GRO &&
+ bnx2x_mtu_allows_gro(bp->dev->mtu))
fp->mode = TPA_MODE_GRO;
+ else
+ fp->mode = TPA_MODE_DISABLED;
/* We don't want TPA if it's disabled in bp
* or if this is an FCoE L2 ring.
*/
if (bp->disable_tpa || IS_FCOE_FP(fp))
- fp->disable_tpa = 1;
+ fp->mode = TPA_MODE_DISABLED;
}
int bnx2x_load_cnic(struct bnx2x *bp)
@@ -2610,7 +2610,7 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
/*
* Zero fastpath structures preserving invariants like napi, which are
* allocated only once, fp index, max_cos, bp pointer.
- * Also set fp->disable_tpa and txdata_ptr.
+ * Also set fp->mode and txdata_ptr.
*/
DP(NETIF_MSG_IFUP, "num queues: %d", bp->num_queues);
for_each_queue(bp, i)
@@ -3249,7 +3249,7 @@ int bnx2x_low_latency_recv(struct napi_struct *napi)
if ((bp->state == BNX2X_STATE_CLOSED) ||
(bp->state == BNX2X_STATE_ERROR) ||
- (bp->flags & (TPA_ENABLE_FLAG | GRO_ENABLE_FLAG)))
+ (bp->dev->features & (NETIF_F_LRO | NETIF_F_GRO)))
return LL_FLUSH_FAILED;
if (!bnx2x_fp_lock_poll(fp))
@@ -4545,7 +4545,7 @@ alloc_mem_err:
* In these cases we disable the queue
* Min size is different for OOO, TPA and non-TPA queues
*/
- if (ring_size < (fp->disable_tpa ?
+ if (ring_size < (fp->mode == TPA_MODE_DISABLED ?
MIN_RX_SIZE_NONTPA : MIN_RX_SIZE_TPA)) {
/* release memory allocated for this queue */
bnx2x_free_fp_mem_at(bp, index);
@@ -4786,6 +4786,11 @@ int bnx2x_change_mtu(struct net_device *dev, int new_mtu)
{
struct bnx2x *bp = netdev_priv(dev);
+ if (pci_num_vf(bp->pdev)) {
+ DP(BNX2X_MSG_IOV, "VFs are enabled, can not change MTU\n");
+ return -EPERM;
+ }
+
if (bp->recovery_state != BNX2X_RECOVERY_DONE) {
BNX2X_ERR("Can't perform change MTU during parity recovery\n");
return -EAGAIN;
@@ -4834,29 +4839,15 @@ netdev_features_t bnx2x_fix_features(struct net_device *dev,
features &= ~NETIF_F_GRO;
}
- /* Note: do not disable SW GRO in kernel when HW GRO is off */
- if (bp->disable_tpa)
- features &= ~NETIF_F_LRO;
-
return features;
}
int bnx2x_set_features(struct net_device *dev, netdev_features_t features)
{
struct bnx2x *bp = netdev_priv(dev);
- u32 flags = bp->flags;
- u32 changes;
+ netdev_features_t changes = features ^ dev->features;
bool bnx2x_reload = false;
-
- if (features & NETIF_F_LRO)
- flags |= TPA_ENABLE_FLAG;
- else
- flags &= ~TPA_ENABLE_FLAG;
-
- if (features & NETIF_F_GRO)
- flags |= GRO_ENABLE_FLAG;
- else
- flags &= ~GRO_ENABLE_FLAG;
+ int rc;
/* VFs or non SRIOV PFs should be able to change loopback feature */
if (!pci_num_vf(bp->pdev)) {
@@ -4873,24 +4864,23 @@ int bnx2x_set_features(struct net_device *dev, netdev_features_t features)
}
}
- changes = flags ^ bp->flags;
-
/* if GRO is changed while LRO is enabled, don't force a reload */
- if ((changes & GRO_ENABLE_FLAG) && (flags & TPA_ENABLE_FLAG))
- changes &= ~GRO_ENABLE_FLAG;
+ if ((changes & NETIF_F_GRO) && (features & NETIF_F_LRO))
+ changes &= ~NETIF_F_GRO;
/* if GRO is changed while HW TPA is off, don't force a reload */
- if ((changes & GRO_ENABLE_FLAG) && bp->disable_tpa)
- changes &= ~GRO_ENABLE_FLAG;
+ if ((changes & NETIF_F_GRO) && bp->disable_tpa)
+ changes &= ~NETIF_F_GRO;
if (changes)
bnx2x_reload = true;
- bp->flags = flags;
-
if (bnx2x_reload) {
- if (bp->recovery_state == BNX2X_RECOVERY_DONE)
- return bnx2x_reload_if_running(dev);
+ if (bp->recovery_state == BNX2X_RECOVERY_DONE) {
+ dev->features = features;
+ rc = bnx2x_reload_if_running(dev);
+ return rc ? rc : 1;
+ }
/* else: bnx2x_nic_load() will be called at end of recovery */
}
@@ -4953,11 +4943,6 @@ int bnx2x_resume(struct pci_dev *pdev)
}
bp = netdev_priv(dev);
- if (pci_num_vf(bp->pdev)) {
- DP(BNX2X_MSG_IOV, "VFs are enabled, can not change MTU\n");
- return -EPERM;
- }
-
if (bp->recovery_state != BNX2X_RECOVERY_DONE) {
BNX2X_ERR("Handling parity error recovery. Try again later\n");
return -EAGAIN;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
index adcacda7af7b..d7a71758e876 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
@@ -969,7 +969,7 @@ static inline void bnx2x_free_rx_sge_range(struct bnx2x *bp,
{
int i;
- if (fp->disable_tpa)
+ if (fp->mode == TPA_MODE_DISABLED)
return;
for (i = 0; i < last; i++)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index b9f85fccb419..33501bcddc48 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -3128,7 +3128,7 @@ static unsigned long bnx2x_get_q_flags(struct bnx2x *bp,
__set_bit(BNX2X_Q_FLG_FORCE_DEFAULT_PRI, &flags);
}
- if (!fp->disable_tpa) {
+ if (fp->mode != TPA_MODE_DISABLED) {
__set_bit(BNX2X_Q_FLG_TPA, &flags);
__set_bit(BNX2X_Q_FLG_TPA_IPV6, &flags);
if (fp->mode == TPA_MODE_GRO)
@@ -3176,7 +3176,7 @@ static void bnx2x_pf_rx_q_prep(struct bnx2x *bp,
u16 sge_sz = 0;
u16 tpa_agg_size = 0;
- if (!fp->disable_tpa) {
+ if (fp->mode != TPA_MODE_DISABLED) {
pause->sge_th_lo = SGE_TH_LO(bp);
pause->sge_th_hi = SGE_TH_HI(bp);
@@ -3304,7 +3304,7 @@ static void bnx2x_pf_init(struct bnx2x *bp)
/* This flag is relevant for E1x only.
* E2 doesn't have a TPA configuration in a function level.
*/
- flags |= (bp->flags & TPA_ENABLE_FLAG) ? FUNC_FLG_TPA : 0;
+ flags |= (bp->dev->features & NETIF_F_LRO) ? FUNC_FLG_TPA : 0;
func_init.func_flgs = flags;
func_init.pf_id = BP_FUNC(bp);
@@ -12054,7 +12054,7 @@ static int bnx2x_init_bp(struct bnx2x *bp)
mutex_init(&bp->port.phy_mutex);
mutex_init(&bp->fw_mb_mutex);
mutex_init(&bp->drv_info_mutex);
- mutex_init(&bp->stats_lock);
+ sema_init(&bp->stats_lock, 1);
bp->drv_info_mng_owner = false;
INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task);
@@ -12107,11 +12107,8 @@ static int bnx2x_init_bp(struct bnx2x *bp)
/* Set TPA flags */
if (bp->disable_tpa) {
- bp->flags &= ~(TPA_ENABLE_FLAG | GRO_ENABLE_FLAG);
+ bp->dev->hw_features &= ~NETIF_F_LRO;
bp->dev->features &= ~NETIF_F_LRO;
- } else {
- bp->flags |= (TPA_ENABLE_FLAG | GRO_ENABLE_FLAG);
- bp->dev->features |= NETIF_F_LRO;
}
if (CHIP_IS_E1(bp))
@@ -13371,6 +13368,17 @@ static int bnx2x_init_one(struct pci_dev *pdev,
bool is_vf;
int cnic_cnt;
+ /* Management FW 'remembers' living interfaces. Allow it some time
+ * to forget previously living interfaces, allowing a proper re-load.
+ */
+ if (is_kdump_kernel()) {
+ ktime_t now = ktime_get_boottime();
+ ktime_t fw_ready_time = ktime_set(5, 0);
+
+ if (ktime_before(now, fw_ready_time))
+ msleep(ktime_ms_delta(fw_ready_time, now));
+ }
+
/* An estimated maximum supported CoS number according to the chip
* version.
* We will try to roughly estimate the maximum number of CoSes this chip
@@ -13682,9 +13690,10 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
cancel_delayed_work_sync(&bp->sp_task);
cancel_delayed_work_sync(&bp->period_task);
- mutex_lock(&bp->stats_lock);
- bp->stats_state = STATS_STATE_DISABLED;
- mutex_unlock(&bp->stats_lock);
+ if (!down_timeout(&bp->stats_lock, HZ / 10)) {
+ bp->stats_state = STATS_STATE_DISABLED;
+ up(&bp->stats_lock);
+ }
bnx2x_save_statistics(bp);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
index 266b055c2360..69d699f0730a 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
@@ -1372,19 +1372,23 @@ void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event)
* that context in case someone is in the middle of a transition.
* For other events, wait a bit until lock is taken.
*/
- if (!mutex_trylock(&bp->stats_lock)) {
+ if (down_trylock(&bp->stats_lock)) {
if (event == STATS_EVENT_UPDATE)
return;
DP(BNX2X_MSG_STATS,
"Unlikely stats' lock contention [event %d]\n", event);
- mutex_lock(&bp->stats_lock);
+ if (unlikely(down_timeout(&bp->stats_lock, HZ / 10))) {
+ BNX2X_ERR("Failed to take stats lock [event %d]\n",
+ event);
+ return;
+ }
}
bnx2x_stats_stm[state][event].action(bp);
bp->stats_state = bnx2x_stats_stm[state][event].next_state;
- mutex_unlock(&bp->stats_lock);
+ up(&bp->stats_lock);
if ((event != STATS_EVENT_UPDATE) || netif_msg_timer(bp))
DP(BNX2X_MSG_STATS, "state %d -> event %d -> state %d\n",
@@ -1970,7 +1974,11 @@ int bnx2x_stats_safe_exec(struct bnx2x *bp,
/* Wait for statistics to end [while blocking further requests],
* then run supplied function 'safely'.
*/
- mutex_lock(&bp->stats_lock);
+ rc = down_timeout(&bp->stats_lock, HZ / 10);
+ if (unlikely(rc)) {
+ BNX2X_ERR("Failed to take statistics lock for safe execution\n");
+ goto out_no_lock;
+ }
bnx2x_stats_comp(bp);
while (bp->stats_pending && cnt--)
@@ -1988,7 +1996,7 @@ out:
/* No need to restart statistics - if they're enabled, the timer
* will restart the statistics.
*/
- mutex_unlock(&bp->stats_lock);
-
+ up(&bp->stats_lock);
+out_no_lock:
return rc;
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
index 15b2d1647560..06b8c0d8fd3b 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
@@ -594,7 +594,7 @@ int bnx2x_vfpf_setup_q(struct bnx2x *bp, struct bnx2x_fastpath *fp,
bnx2x_vfpf_prep(bp, &req->first_tlv, CHANNEL_TLV_SETUP_Q, sizeof(*req));
/* select tpa mode to request */
- if (!fp->disable_tpa) {
+ if (fp->mode != TPA_MODE_DISABLED) {
flags |= VFPF_QUEUE_FLG_TPA;
flags |= VFPF_QUEUE_FLG_TPA_IPV6;
if (fp->mode == TPA_MODE_GRO)
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index e7651b3c6c57..420949cc55aa 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -299,9 +299,6 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
phy_name = "external RGMII (no delay)";
else
phy_name = "external RGMII (TX delay)";
- reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
- reg |= RGMII_MODE_EN | id_mode_dis;
- bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
bcmgenet_sys_writel(priv,
PORT_MODE_EXT_GPHY, SYS_PORT_CTRL);
break;
@@ -310,6 +307,15 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
return -EINVAL;
}
+ /* This is an external PHY (xMII), so we need to enable the RGMII
+ * block for the interface to work
+ */
+ if (priv->ext_phy) {
+ reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
+ reg |= RGMII_MODE_EN | id_mode_dis;
+ bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
+ }
+
if (init)
dev_info(kdev, "configuring instance for %s\n", phy_name);
diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c
index 594a2ab36d31..68f3c13c9ef6 100644
--- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c
+++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c
@@ -2414,7 +2414,7 @@ bfa_ioc_boot(struct bfa_ioc *ioc, enum bfi_fwboot_type boot_type,
if (status == BFA_STATUS_OK)
bfa_ioc_lpu_start(ioc);
else
- bfa_nw_iocpf_timeout(ioc);
+ bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_TIMEOUT);
return status;
}
@@ -3029,7 +3029,7 @@ bfa_ioc_poll_fwinit(struct bfa_ioc *ioc)
}
if (ioc->iocpf.poll_time >= BFA_IOC_TOV) {
- bfa_nw_iocpf_timeout(ioc);
+ bfa_fsm_send_event(&ioc->iocpf, IOCPF_E_TIMEOUT);
} else {
ioc->iocpf.poll_time += BFA_IOC_POLL_TOV;
mod_timer(&ioc->iocpf_timer, jiffies +
diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c
index 37072a83f9d6..caae6cb2bc1a 100644
--- a/drivers/net/ethernet/brocade/bna/bnad.c
+++ b/drivers/net/ethernet/brocade/bna/bnad.c
@@ -3701,10 +3701,6 @@ bnad_pci_probe(struct pci_dev *pdev,
setup_timer(&bnad->bna.ioceth.ioc.sem_timer, bnad_iocpf_sem_timeout,
((unsigned long)bnad));
- /* Now start the timer before calling IOC */
- mod_timer(&bnad->bna.ioceth.ioc.iocpf_timer,
- jiffies + msecs_to_jiffies(BNA_IOC_TIMER_FREQ));
-
/*
* Start the chip
* If the call back comes with error, we bail out.
diff --git a/drivers/net/ethernet/brocade/bna/cna_fwimg.c b/drivers/net/ethernet/brocade/bna/cna_fwimg.c
index ebf462d8082f..badea368bdc8 100644
--- a/drivers/net/ethernet/brocade/bna/cna_fwimg.c
+++ b/drivers/net/ethernet/brocade/bna/cna_fwimg.c
@@ -30,6 +30,7 @@ cna_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
u32 *bfi_image_size, char *fw_name)
{
const struct firmware *fw;
+ u32 n;
if (request_firmware(&fw, fw_name, &pdev->dev)) {
pr_alert("Can't locate firmware %s\n", fw_name);
@@ -40,6 +41,12 @@ cna_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
*bfi_image_size = fw->size/sizeof(u32);
bfi_fw = fw;
+ /* Convert loaded firmware to host order as it is stored in file
+ * as sequence of LE32 integers.
+ */
+ for (n = 0; n < *bfi_image_size; n++)
+ le32_to_cpus(*bfi_image + n);
+
return *bfi_image;
error:
return NULL;
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 665c29098e3c..fc646a41d548 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -350,6 +350,9 @@ static int macb_mii_probe(struct net_device *dev)
else
phydev->supported &= PHY_BASIC_FEATURES;
+ if (bp->caps & MACB_CAPS_NO_GIGABIT_HALF)
+ phydev->supported &= ~SUPPORTED_1000baseT_Half;
+
phydev->advertising = phydev->supported;
bp->link = 0;
@@ -707,6 +710,9 @@ static void gem_rx_refill(struct macb *bp)
/* properly align Ethernet header */
skb_reserve(skb, NET_IP_ALIGN);
+ } else {
+ bp->rx_ring[entry].addr &= ~MACB_BIT(RX_USED);
+ bp->rx_ring[entry].ctrl = 0;
}
}
@@ -978,7 +984,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
struct macb_queue *queue = dev_id;
struct macb *bp = queue->bp;
struct net_device *dev = bp->dev;
- u32 status;
+ u32 status, ctrl;
status = queue_readl(queue, ISR);
@@ -1034,6 +1040,21 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
* add that if/when we get our hands on a full-blown MII PHY.
*/
+ /* There is a hardware issue under heavy load where DMA can
+ * stop, this causes endless "used buffer descriptor read"
+ * interrupts but it can be cleared by re-enabling RX. See
+ * the at91 manual, section 41.3.1 or the Zynq manual
+ * section 16.7.4 for details.
+ */
+ if (status & MACB_BIT(RXUBR)) {
+ ctrl = macb_readl(bp, NCR);
+ macb_writel(bp, NCR, ctrl & ~MACB_BIT(RE));
+ macb_writel(bp, NCR, ctrl | MACB_BIT(RE));
+
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ macb_writel(bp, ISR, MACB_BIT(RXUBR));
+ }
+
if (status & MACB_BIT(ISR_ROVR)) {
/* We missed at least one packet */
if (macb_is_gem(bp))
@@ -2681,6 +2702,14 @@ static const struct macb_config emac_config = {
.init = at91ether_init,
};
+static const struct macb_config zynq_config = {
+ .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE |
+ MACB_CAPS_NO_GIGABIT_HALF,
+ .dma_burst_length = 16,
+ .clk_init = macb_clk_init,
+ .init = macb_init,
+};
+
static const struct of_device_id macb_dt_ids[] = {
{ .compatible = "cdns,at32ap7000-macb" },
{ .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config },
@@ -2691,6 +2720,7 @@ static const struct of_device_id macb_dt_ids[] = {
{ .compatible = "atmel,sama5d4-gem", .data = &sama5d4_config },
{ .compatible = "cdns,at91rm9200-emac", .data = &emac_config },
{ .compatible = "cdns,emac", .data = &emac_config },
+ { .compatible = "cdns,zynq-gem", .data = &zynq_config },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, macb_dt_ids);
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index eb7d76f7bf6a..24b1d9bcd865 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -393,6 +393,7 @@
#define MACB_CAPS_ISR_CLEAR_ON_WRITE 0x00000001
#define MACB_CAPS_USRIO_HAS_CLKEN 0x00000002
#define MACB_CAPS_USRIO_DEFAULT_IS_MII 0x00000004
+#define MACB_CAPS_NO_GIGABIT_HALF 0x00000008
#define MACB_CAPS_FIFO_MODE 0x10000000
#define MACB_CAPS_GIGABIT_MODE_AVAILABLE 0x20000000
#define MACB_CAPS_SG_DISABLED 0x40000000
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 524d11098c56..e052f05558c7 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -1185,6 +1185,7 @@ enum t4_bar2_qtype { T4_BAR2_QTYPE_EGRESS, T4_BAR2_QTYPE_INGRESS };
int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter,
unsigned int qid,
enum t4_bar2_qtype qtype,
+ int user,
u64 *pbar2_qoffset,
unsigned int *pbar2_qid);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 803d91beec6f..a9355593e65e 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -2145,6 +2145,7 @@ EXPORT_SYMBOL(cxgb4_read_sge_timestamp);
int cxgb4_bar2_sge_qregs(struct net_device *dev,
unsigned int qid,
enum cxgb4_bar2_qtype qtype,
+ int user,
u64 *pbar2_qoffset,
unsigned int *pbar2_qid)
{
@@ -2153,6 +2154,7 @@ int cxgb4_bar2_sge_qregs(struct net_device *dev,
(qtype == CXGB4_BAR2_QTYPE_EGRESS
? T4_BAR2_QTYPE_EGRESS
: T4_BAR2_QTYPE_INGRESS),
+ user,
pbar2_qoffset,
pbar2_qid);
}
@@ -2351,7 +2353,7 @@ static void process_db_drop(struct work_struct *work)
int ret;
ret = cxgb4_t4_bar2_sge_qregs(adap, qid, T4_BAR2_QTYPE_EGRESS,
- &bar2_qoffset, &bar2_qid);
+ 0, &bar2_qoffset, &bar2_qid);
if (ret)
dev_err(adap->pdev_dev, "doorbell drop recovery: "
"qid=%d, pidx_inc=%d\n", qid, pidx_inc);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
index 78ab4d406ce2..e33934a6f59e 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
@@ -306,6 +306,7 @@ enum cxgb4_bar2_qtype { CXGB4_BAR2_QTYPE_EGRESS, CXGB4_BAR2_QTYPE_INGRESS };
int cxgb4_bar2_sge_qregs(struct net_device *dev,
unsigned int qid,
enum cxgb4_bar2_qtype qtype,
+ int user,
u64 *pbar2_qoffset,
unsigned int *pbar2_qid);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index 0d2eddab04ef..1b99aecde736 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -2429,8 +2429,8 @@ static void __iomem *bar2_address(struct adapter *adapter,
u64 bar2_qoffset;
int ret;
- ret = cxgb4_t4_bar2_sge_qregs(adapter, qid, qtype,
- &bar2_qoffset, pbar2_qid);
+ ret = cxgb4_t4_bar2_sge_qregs(adapter, qid, qtype, 0,
+ &bar2_qoffset, pbar2_qid);
if (ret)
return NULL;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 5959e3ae72da..61d8b3ec959e 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -492,7 +492,7 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr,
memoffset = (mtype * (edc_size * 1024 * 1024));
else {
mc_size = EXT_MEM0_SIZE_G(t4_read_reg(adap,
- MA_EXT_MEMORY1_BAR_A));
+ MA_EXT_MEMORY0_BAR_A));
memoffset = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024;
}
@@ -5102,6 +5102,7 @@ int t4_prep_adapter(struct adapter *adapter)
* @adapter: the adapter
* @qid: the Queue ID
* @qtype: the Ingress or Egress type for @qid
+ * @user: true if this request is for a user mode queue
* @pbar2_qoffset: BAR2 Queue Offset
* @pbar2_qid: BAR2 Queue ID or 0 for Queue ID inferred SGE Queues
*
@@ -5125,6 +5126,7 @@ int t4_prep_adapter(struct adapter *adapter)
int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter,
unsigned int qid,
enum t4_bar2_qtype qtype,
+ int user,
u64 *pbar2_qoffset,
unsigned int *pbar2_qid)
{
@@ -5132,9 +5134,8 @@ int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter,
u64 bar2_page_offset, bar2_qoffset;
unsigned int bar2_qid, bar2_qid_offset, bar2_qinferred;
- /* T4 doesn't support BAR2 SGE Queue registers.
- */
- if (is_t4(adapter->params.chip))
+ /* T4 doesn't support BAR2 SGE Queue registers for kernel mode queues */
+ if (!user && is_t4(adapter->params.chip))
return -EINVAL;
/* Get our SGE Page Size parameters.
diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c
index 28d9ca675a27..68d47b196dae 100644
--- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c
+++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c
@@ -131,8 +131,15 @@ static void enic_get_drvinfo(struct net_device *netdev,
{
struct enic *enic = netdev_priv(netdev);
struct vnic_devcmd_fw_info *fw_info;
+ int err;
- enic_dev_fw_info(enic, &fw_info);
+ err = enic_dev_fw_info(enic, &fw_info);
+ /* return only when pci_zalloc_consistent fails in vnic_dev_fw_info
+ * For other failures, like devcmd failure, we return previously
+ * recorded info.
+ */
+ if (err == -ENOMEM)
+ return;
strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version));
@@ -181,8 +188,15 @@ static void enic_get_ethtool_stats(struct net_device *netdev,
struct enic *enic = netdev_priv(netdev);
struct vnic_stats *vstats;
unsigned int i;
-
- enic_dev_stats_dump(enic, &vstats);
+ int err;
+
+ err = enic_dev_stats_dump(enic, &vstats);
+ /* return only when pci_zalloc_consistent fails in vnic_dev_stats_dump
+ * For other failures, like devcmd failure, we return previously
+ * recorded stats.
+ */
+ if (err == -ENOMEM)
+ return;
for (i = 0; i < enic_n_tx_stats; i++)
*(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].index];
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index 204bd182473b..eadae1b412c6 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -615,8 +615,15 @@ static struct rtnl_link_stats64 *enic_get_stats(struct net_device *netdev,
{
struct enic *enic = netdev_priv(netdev);
struct vnic_stats *stats;
+ int err;
- enic_dev_stats_dump(enic, &stats);
+ err = enic_dev_stats_dump(enic, &stats);
+ /* return only when pci_zalloc_consistent fails in vnic_dev_stats_dump
+ * For other failures, like devcmd failure, we return previously
+ * recorded stats.
+ */
+ if (err == -ENOMEM)
+ return net_stats;
net_stats->tx_packets = stats->tx.tx_frames_ok;
net_stats->tx_bytes = stats->tx.tx_bytes_ok;
@@ -1407,6 +1414,7 @@ static int enic_poll_msix_rq(struct napi_struct *napi, int budget)
*/
enic_calc_int_moderation(enic, &enic->rq[rq]);
+ enic_poll_unlock_napi(&enic->rq[rq]);
if (work_done < work_to_do) {
/* Some work done, but not enough to stay in polling,
@@ -1418,7 +1426,6 @@ static int enic_poll_msix_rq(struct napi_struct *napi, int budget)
enic_set_int_moderation(enic, &enic->rq[rq]);
vnic_intr_unmask(&enic->intr[intr]);
}
- enic_poll_unlock_napi(&enic->rq[rq]);
return work_done;
}
diff --git a/drivers/net/ethernet/cisco/enic/vnic_rq.c b/drivers/net/ethernet/cisco/enic/vnic_rq.c
index 36a2ed606c91..c4b2183bf352 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_rq.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_rq.c
@@ -188,16 +188,15 @@ void vnic_rq_clean(struct vnic_rq *rq,
struct vnic_rq_buf *buf;
u32 fetch_index;
unsigned int count = rq->ring.desc_count;
+ int i;
buf = rq->to_clean;
- while (vnic_rq_desc_used(rq) > 0) {
-
+ for (i = 0; i < rq->ring.desc_count; i++) {
(*buf_clean)(rq, buf);
-
- buf = rq->to_clean = buf->next;
- rq->ring.desc_avail++;
+ buf = buf->next;
}
+ rq->ring.desc_avail = rq->ring.desc_count - 1;
/* Use current fetch_index as the ring starting point */
fetch_index = ioread32(&rq->ctrl->fetch_index);
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index fb140faeafb1..c5e1d0ac75f9 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -1720,9 +1720,9 @@ int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
total_size = buf_len;
get_fat_cmd.size = sizeof(struct be_cmd_req_get_fat) + 60*1024;
- get_fat_cmd.va = pci_alloc_consistent(adapter->pdev,
- get_fat_cmd.size,
- &get_fat_cmd.dma);
+ get_fat_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+ get_fat_cmd.size,
+ &get_fat_cmd.dma, GFP_ATOMIC);
if (!get_fat_cmd.va) {
dev_err(&adapter->pdev->dev,
"Memory allocation failure while reading FAT data\n");
@@ -1767,8 +1767,8 @@ int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
log_offset += buf_size;
}
err:
- pci_free_consistent(adapter->pdev, get_fat_cmd.size,
- get_fat_cmd.va, get_fat_cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, get_fat_cmd.size,
+ get_fat_cmd.va, get_fat_cmd.dma);
spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -2215,12 +2215,12 @@ int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
return -EINVAL;
cmd.size = sizeof(struct be_cmd_resp_port_type);
- cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+ cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+ GFP_ATOMIC);
if (!cmd.va) {
dev_err(&adapter->pdev->dev, "Memory allocation failed\n");
return -ENOMEM;
}
- memset(cmd.va, 0, cmd.size);
spin_lock_bh(&adapter->mcc_lock);
@@ -2245,7 +2245,7 @@ int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
}
err:
spin_unlock_bh(&adapter->mcc_lock);
- pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va, cmd.dma);
return status;
}
@@ -2720,7 +2720,8 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
goto err;
}
cmd.size = sizeof(struct be_cmd_req_get_phy_info);
- cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+ cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+ GFP_ATOMIC);
if (!cmd.va) {
dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
status = -ENOMEM;
@@ -2754,7 +2755,7 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
BE_SUPPORTED_SPEED_1GBPS;
}
}
- pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va, cmd.dma);
err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
@@ -2805,8 +2806,9 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
memset(&attribs_cmd, 0, sizeof(struct be_dma_mem));
attribs_cmd.size = sizeof(struct be_cmd_resp_cntl_attribs);
- attribs_cmd.va = pci_alloc_consistent(adapter->pdev, attribs_cmd.size,
- &attribs_cmd.dma);
+ attribs_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+ attribs_cmd.size,
+ &attribs_cmd.dma, GFP_ATOMIC);
if (!attribs_cmd.va) {
dev_err(&adapter->pdev->dev, "Memory allocation failure\n");
status = -ENOMEM;
@@ -2833,8 +2835,8 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
err:
mutex_unlock(&adapter->mbox_lock);
if (attribs_cmd.va)
- pci_free_consistent(adapter->pdev, attribs_cmd.size,
- attribs_cmd.va, attribs_cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, attribs_cmd.size,
+ attribs_cmd.va, attribs_cmd.dma);
return status;
}
@@ -2972,9 +2974,10 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
memset(&get_mac_list_cmd, 0, sizeof(struct be_dma_mem));
get_mac_list_cmd.size = sizeof(struct be_cmd_resp_get_mac_list);
- get_mac_list_cmd.va = pci_alloc_consistent(adapter->pdev,
- get_mac_list_cmd.size,
- &get_mac_list_cmd.dma);
+ get_mac_list_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+ get_mac_list_cmd.size,
+ &get_mac_list_cmd.dma,
+ GFP_ATOMIC);
if (!get_mac_list_cmd.va) {
dev_err(&adapter->pdev->dev,
@@ -3047,8 +3050,8 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
out:
spin_unlock_bh(&adapter->mcc_lock);
- pci_free_consistent(adapter->pdev, get_mac_list_cmd.size,
- get_mac_list_cmd.va, get_mac_list_cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, get_mac_list_cmd.size,
+ get_mac_list_cmd.va, get_mac_list_cmd.dma);
return status;
}
@@ -3101,8 +3104,8 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
memset(&cmd, 0, sizeof(struct be_dma_mem));
cmd.size = sizeof(struct be_cmd_req_set_mac_list);
- cmd.va = dma_alloc_coherent(&adapter->pdev->dev, cmd.size,
- &cmd.dma, GFP_KERNEL);
+ cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+ GFP_KERNEL);
if (!cmd.va)
return -ENOMEM;
@@ -3291,7 +3294,8 @@ int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter)
memset(&cmd, 0, sizeof(struct be_dma_mem));
cmd.size = sizeof(struct be_cmd_resp_acpi_wol_magic_config_v1);
- cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+ cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+ GFP_ATOMIC);
if (!cmd.va) {
dev_err(&adapter->pdev->dev, "Memory allocation failure\n");
status = -ENOMEM;
@@ -3326,7 +3330,8 @@ int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter)
err:
mutex_unlock(&adapter->mbox_lock);
if (cmd.va)
- pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va,
+ cmd.dma);
return status;
}
@@ -3340,8 +3345,9 @@ int be_cmd_set_fw_log_level(struct be_adapter *adapter, u32 level)
memset(&extfat_cmd, 0, sizeof(struct be_dma_mem));
extfat_cmd.size = sizeof(struct be_cmd_resp_get_ext_fat_caps);
- extfat_cmd.va = pci_alloc_consistent(adapter->pdev, extfat_cmd.size,
- &extfat_cmd.dma);
+ extfat_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+ extfat_cmd.size, &extfat_cmd.dma,
+ GFP_ATOMIC);
if (!extfat_cmd.va)
return -ENOMEM;
@@ -3363,8 +3369,8 @@ int be_cmd_set_fw_log_level(struct be_adapter *adapter, u32 level)
status = be_cmd_set_ext_fat_capabilites(adapter, &extfat_cmd, cfgs);
err:
- pci_free_consistent(adapter->pdev, extfat_cmd.size, extfat_cmd.va,
- extfat_cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, extfat_cmd.size, extfat_cmd.va,
+ extfat_cmd.dma);
return status;
}
@@ -3377,8 +3383,9 @@ int be_cmd_get_fw_log_level(struct be_adapter *adapter)
memset(&extfat_cmd, 0, sizeof(struct be_dma_mem));
extfat_cmd.size = sizeof(struct be_cmd_resp_get_ext_fat_caps);
- extfat_cmd.va = pci_alloc_consistent(adapter->pdev, extfat_cmd.size,
- &extfat_cmd.dma);
+ extfat_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+ extfat_cmd.size, &extfat_cmd.dma,
+ GFP_ATOMIC);
if (!extfat_cmd.va) {
dev_err(&adapter->pdev->dev, "%s: Memory allocation failure\n",
@@ -3396,8 +3403,8 @@ int be_cmd_get_fw_log_level(struct be_adapter *adapter)
level = cfgs->module[0].trace_lvl[j].dbg_lvl;
}
}
- pci_free_consistent(adapter->pdev, extfat_cmd.size, extfat_cmd.va,
- extfat_cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, extfat_cmd.size, extfat_cmd.va,
+ extfat_cmd.dma);
err:
return level;
}
@@ -3595,7 +3602,8 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res)
memset(&cmd, 0, sizeof(struct be_dma_mem));
cmd.size = sizeof(struct be_cmd_resp_get_func_config);
- cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+ cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+ GFP_ATOMIC);
if (!cmd.va) {
dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
status = -ENOMEM;
@@ -3635,7 +3643,8 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res)
err:
mutex_unlock(&adapter->mbox_lock);
if (cmd.va)
- pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va,
+ cmd.dma);
return status;
}
@@ -3656,7 +3665,8 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
memset(&cmd, 0, sizeof(struct be_dma_mem));
cmd.size = sizeof(struct be_cmd_resp_get_profile_config);
- cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+ cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+ GFP_ATOMIC);
if (!cmd.va)
return -ENOMEM;
@@ -3702,7 +3712,8 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
res->vf_if_cap_flags = vf_res->cap_flags;
err:
if (cmd.va)
- pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va,
+ cmd.dma);
return status;
}
@@ -3717,7 +3728,8 @@ static int be_cmd_set_profile_config(struct be_adapter *adapter, void *desc,
memset(&cmd, 0, sizeof(struct be_dma_mem));
cmd.size = sizeof(struct be_cmd_req_set_profile_config);
- cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+ cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+ GFP_ATOMIC);
if (!cmd.va)
return -ENOMEM;
@@ -3733,7 +3745,8 @@ static int be_cmd_set_profile_config(struct be_adapter *adapter, void *desc,
status = be_cmd_notify_wait(adapter, &wrb);
if (cmd.va)
- pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va,
+ cmd.dma);
return status;
}
diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c
index b765c24625bf..2835dee5dc39 100644
--- a/drivers/net/ethernet/emulex/benet/be_ethtool.c
+++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c
@@ -264,8 +264,8 @@ static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
int status = 0;
read_cmd.size = LANCER_READ_FILE_CHUNK;
- read_cmd.va = pci_alloc_consistent(adapter->pdev, read_cmd.size,
- &read_cmd.dma);
+ read_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, read_cmd.size,
+ &read_cmd.dma, GFP_ATOMIC);
if (!read_cmd.va) {
dev_err(&adapter->pdev->dev,
@@ -289,8 +289,8 @@ static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
break;
}
}
- pci_free_consistent(adapter->pdev, read_cmd.size, read_cmd.va,
- read_cmd.dma);
+ dma_free_coherent(&adapter->pdev->dev, read_cmd.size, read_cmd.va,
+ read_cmd.dma);
return status;
}
@@ -818,8 +818,9 @@ static int be_test_ddr_dma(struct be_adapter *adapter)
};
ddrdma_cmd.size = sizeof(struct be_cmd_req_ddrdma_test);
- ddrdma_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, ddrdma_cmd.size,
- &ddrdma_cmd.dma, GFP_KERNEL);
+ ddrdma_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+ ddrdma_cmd.size, &ddrdma_cmd.dma,
+ GFP_KERNEL);
if (!ddrdma_cmd.va)
return -ENOMEM;
@@ -941,8 +942,9 @@ static int be_read_eeprom(struct net_device *netdev,
memset(&eeprom_cmd, 0, sizeof(struct be_dma_mem));
eeprom_cmd.size = sizeof(struct be_cmd_req_seeprom_read);
- eeprom_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, eeprom_cmd.size,
- &eeprom_cmd.dma, GFP_KERNEL);
+ eeprom_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+ eeprom_cmd.size, &eeprom_cmd.dma,
+ GFP_KERNEL);
if (!eeprom_cmd.va)
return -ENOMEM;
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index fb0bc3c3620e..e43cc8a73ea7 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -2358,11 +2358,11 @@ static int be_evt_queues_create(struct be_adapter *adapter)
adapter->cfg_num_qs);
for_all_evt_queues(adapter, eqo, i) {
+ int numa_node = dev_to_node(&adapter->pdev->dev);
if (!zalloc_cpumask_var(&eqo->affinity_mask, GFP_KERNEL))
return -ENOMEM;
- cpumask_set_cpu_local_first(i, dev_to_node(&adapter->pdev->dev),
- eqo->affinity_mask);
-
+ cpumask_set_cpu(cpumask_local_spread(i, numa_node),
+ eqo->affinity_mask);
netif_napi_add(adapter->netdev, &eqo->napi, be_poll,
BE_NAPI_WEIGHT);
napi_hash_add(&eqo->napi);
@@ -4605,8 +4605,8 @@ static int lancer_fw_download(struct be_adapter *adapter,
flash_cmd.size = sizeof(struct lancer_cmd_req_write_object)
+ LANCER_FW_DOWNLOAD_CHUNK;
- flash_cmd.va = dma_alloc_coherent(dev, flash_cmd.size,
- &flash_cmd.dma, GFP_KERNEL);
+ flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size,
+ &flash_cmd.dma, GFP_KERNEL);
if (!flash_cmd.va)
return -ENOMEM;
@@ -4739,8 +4739,8 @@ static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw)
}
flash_cmd.size = sizeof(struct be_cmd_write_flashrom);
- flash_cmd.va = dma_alloc_coherent(dev, flash_cmd.size, &flash_cmd.dma,
- GFP_KERNEL);
+ flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size, &flash_cmd.dma,
+ GFP_KERNEL);
if (!flash_cmd.va)
return -ENOMEM;
@@ -4846,7 +4846,8 @@ err:
}
static int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
- struct net_device *dev, u32 filter_mask)
+ struct net_device *dev, u32 filter_mask,
+ int nlflags)
{
struct be_adapter *adapter = netdev_priv(dev);
int status = 0;
@@ -4868,7 +4869,7 @@ static int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
hsw_mode == PORT_FWD_TYPE_VEPA ?
BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB,
- 0, 0);
+ 0, 0, nlflags);
}
#ifdef CONFIG_BE2NET_VXLAN
@@ -5290,16 +5291,15 @@ static int be_drv_init(struct be_adapter *adapter)
int status = 0;
mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16;
- mbox_mem_alloc->va = dma_alloc_coherent(dev, mbox_mem_alloc->size,
- &mbox_mem_alloc->dma,
- GFP_KERNEL);
+ mbox_mem_alloc->va = dma_zalloc_coherent(dev, mbox_mem_alloc->size,
+ &mbox_mem_alloc->dma,
+ GFP_KERNEL);
if (!mbox_mem_alloc->va)
return -ENOMEM;
mbox_mem_align->size = sizeof(struct be_mcc_mailbox);
mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16);
mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16);
- memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox));
rx_filter->size = sizeof(struct be_cmd_req_rx_filter);
rx_filter->va = dma_zalloc_coherent(dev, rx_filter->size,
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index f6a3a7abd468..66d47e448e4d 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -988,7 +988,10 @@ fec_restart(struct net_device *ndev)
rcntl |= 0x40000000 | 0x00000020;
/* RGMII, RMII or MII */
- if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII)
+ if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII ||
+ fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
+ fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID)
rcntl |= (1 << 6);
else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
rcntl |= (1 << 8);
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index de7919322190..b9df0cbd0a38 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -2084,12 +2084,8 @@ static void emac_ethtool_get_pauseparam(struct net_device *ndev,
static int emac_get_regs_len(struct emac_instance *dev)
{
- if (emac_has_feature(dev, EMAC_FTR_EMAC4))
- return sizeof(struct emac_ethtool_regs_subhdr) +
- EMAC4_ETHTOOL_REGS_SIZE(dev);
- else
return sizeof(struct emac_ethtool_regs_subhdr) +
- EMAC_ETHTOOL_REGS_SIZE(dev);
+ sizeof(struct emac_regs);
}
static int emac_ethtool_get_regs_len(struct net_device *ndev)
@@ -2114,15 +2110,15 @@ static void *emac_dump_regs(struct emac_instance *dev, void *buf)
struct emac_ethtool_regs_subhdr *hdr = buf;
hdr->index = dev->cell_index;
- if (emac_has_feature(dev, EMAC_FTR_EMAC4)) {
+ if (emac_has_feature(dev, EMAC_FTR_EMAC4SYNC)) {
+ hdr->version = EMAC4SYNC_ETHTOOL_REGS_VER;
+ } else if (emac_has_feature(dev, EMAC_FTR_EMAC4)) {
hdr->version = EMAC4_ETHTOOL_REGS_VER;
- memcpy_fromio(hdr + 1, dev->emacp, EMAC4_ETHTOOL_REGS_SIZE(dev));
- return (void *)(hdr + 1) + EMAC4_ETHTOOL_REGS_SIZE(dev);
} else {
hdr->version = EMAC_ETHTOOL_REGS_VER;
- memcpy_fromio(hdr + 1, dev->emacp, EMAC_ETHTOOL_REGS_SIZE(dev));
- return (void *)(hdr + 1) + EMAC_ETHTOOL_REGS_SIZE(dev);
}
+ memcpy_fromio(hdr + 1, dev->emacp, sizeof(struct emac_regs));
+ return (void *)(hdr + 1) + sizeof(struct emac_regs);
}
static void emac_ethtool_get_regs(struct net_device *ndev,
diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h
index 67f342a9f65e..28df37420da9 100644
--- a/drivers/net/ethernet/ibm/emac/core.h
+++ b/drivers/net/ethernet/ibm/emac/core.h
@@ -461,10 +461,7 @@ struct emac_ethtool_regs_subhdr {
};
#define EMAC_ETHTOOL_REGS_VER 0
-#define EMAC_ETHTOOL_REGS_SIZE(dev) ((dev)->rsrc_regs.end - \
- (dev)->rsrc_regs.start + 1)
-#define EMAC4_ETHTOOL_REGS_VER 1
-#define EMAC4_ETHTOOL_REGS_SIZE(dev) ((dev)->rsrc_regs.end - \
- (dev)->rsrc_regs.start + 1)
+#define EMAC4_ETHTOOL_REGS_VER 1
+#define EMAC4SYNC_ETHTOOL_REGS_VER 2
#endif /* __IBM_NEWEMAC_CORE_H */
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index 5d9ceb17b4cb..0abc942c966e 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -40,6 +40,7 @@
#include <linux/ptp_classify.h>
#include <linux/mii.h>
#include <linux/mdio.h>
+#include <linux/pm_qos.h>
#include "hw.h"
struct e1000_info;
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c
index 1b0661e3573b..c754b2027281 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c
@@ -610,7 +610,7 @@ static bool fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector,
unsigned int total_bytes = 0, total_packets = 0;
u16 cleaned_count = fm10k_desc_unused(rx_ring);
- do {
+ while (likely(total_packets < budget)) {
union fm10k_rx_desc *rx_desc;
/* return some buffers to hardware, one at a time is too slow */
@@ -659,7 +659,7 @@ static bool fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector,
/* update budget accounting */
total_packets++;
- } while (likely(total_packets < budget));
+ }
/* place incomplete frames back on ring for completion */
rx_ring->skb = skb;
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 33c35d3b7420..5d47307121ab 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -317,6 +317,7 @@ struct i40e_pf {
#endif
#define I40E_FLAG_PORT_ID_VALID (u64)(1 << 28)
#define I40E_FLAG_DCB_CAPABLE (u64)(1 << 29)
+#define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40)
/* tracks features that get auto disabled by errors */
u64 auto_disable_flags;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
index 34170eabca7d..da0faf478af0 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
@@ -1021,6 +1021,15 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
goto command_write_done;
}
+ /* By default we are in VEPA mode, if this is the first VF/VMDq
+ * VSI to be added switch to VEB mode.
+ */
+ if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
+ pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
+ i40e_do_reset_safe(pf,
+ BIT_ULL(__I40E_PF_RESET_REQUESTED));
+ }
+
vsi = i40e_vsi_setup(pf, I40E_VSI_VMDQ2, vsi_seid, 0);
if (vsi)
dev_info(&pf->pdev->dev, "added VSI %d to relay %d\n",
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 24481cd7e59a..5b5bea159bd5 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -6097,6 +6097,10 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb)
if (ret)
goto end_reconstitute;
+ if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED)
+ veb->bridge_mode = BRIDGE_MODE_VEB;
+ else
+ veb->bridge_mode = BRIDGE_MODE_VEPA;
i40e_config_bridge_mode(veb);
/* create the remaining VSIs attached to this VEB */
@@ -8031,7 +8035,12 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev,
} else if (mode != veb->bridge_mode) {
/* Existing HW bridge but different mode needs reset */
veb->bridge_mode = mode;
- i40e_do_reset(pf, (1 << __I40E_PF_RESET_REQUESTED));
+ /* TODO: If no VFs or VMDq VSIs, disallow VEB mode */
+ if (mode == BRIDGE_MODE_VEB)
+ pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
+ else
+ pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED;
+ i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED));
break;
}
}
@@ -8053,10 +8062,10 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev,
#ifdef HAVE_BRIDGE_FILTER
static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev,
- u32 __always_unused filter_mask)
+ u32 __always_unused filter_mask, int nlflags)
#else
static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
- struct net_device *dev)
+ struct net_device *dev, int nlflags)
#endif /* HAVE_BRIDGE_FILTER */
{
struct i40e_netdev_priv *np = netdev_priv(dev);
@@ -8078,7 +8087,8 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
if (!veb)
return 0;
- return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode);
+ return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode,
+ nlflags);
}
#endif /* HAVE_BRIDGE_ATTRIBS */
@@ -8342,11 +8352,12 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
ctxt.uplink_seid = vsi->uplink_seid;
ctxt.connection_type = I40E_AQ_VSI_CONN_TYPE_NORMAL;
ctxt.flags = I40E_AQ_VSI_TYPE_PF;
- if (i40e_is_vsi_uplink_mode_veb(vsi)) {
+ if ((pf->flags & I40E_FLAG_VEB_MODE_ENABLED) &&
+ (i40e_is_vsi_uplink_mode_veb(vsi))) {
ctxt.info.valid_sections |=
- cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);
+ cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);
ctxt.info.switch_id =
- cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
+ cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
}
i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, true);
break;
@@ -8745,6 +8756,14 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
__func__);
return NULL;
}
+ /* We come up by default in VEPA mode if SRIOV is not
+ * already enabled, in which case we can't force VEPA
+ * mode.
+ */
+ if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
+ veb->bridge_mode = BRIDGE_MODE_VEPA;
+ pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED;
+ }
i40e_config_bridge_mode(veb);
}
for (i = 0; i < I40E_MAX_VEB && !veb; i++) {
@@ -9855,6 +9874,15 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_switch_setup;
}
+#ifdef CONFIG_PCI_IOV
+ /* prep for VF support */
+ if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
+ (pf->flags & I40E_FLAG_MSIX_ENABLED) &&
+ !test_bit(__I40E_BAD_EEPROM, &pf->state)) {
+ if (pci_num_vf(pdev))
+ pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
+ }
+#endif
err = i40e_setup_pf_switch(pf, false);
if (err) {
dev_info(&pdev->dev, "setup_pf_switch failed: %d\n", err);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index 4bd3a80aba82..9d95042d5a0f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -2410,14 +2410,12 @@ static int i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size)
* i40e_chk_linearize - Check if there are more than 8 fragments per packet
* @skb: send buffer
* @tx_flags: collected send information
- * @hdr_len: size of the packet header
*
* Note: Our HW can't scatter-gather more than 8 fragments to build
* a packet on the wire and so we need to figure out the cases where we
* need to linearize the skb.
**/
-static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
- const u8 hdr_len)
+static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags)
{
struct skb_frag_struct *frag;
bool linearize = false;
@@ -2429,7 +2427,7 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
gso_segs = skb_shinfo(skb)->gso_segs;
if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) {
- u16 j = 1;
+ u16 j = 0;
if (num_frags < (I40E_MAX_BUFFER_TXD))
goto linearize_chk_done;
@@ -2440,21 +2438,18 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
goto linearize_chk_done;
}
frag = &skb_shinfo(skb)->frags[0];
- size = hdr_len;
/* we might still have more fragments per segment */
do {
size += skb_frag_size(frag);
frag++; j++;
+ if ((size >= skb_shinfo(skb)->gso_size) &&
+ (j < I40E_MAX_BUFFER_TXD)) {
+ size = (size % skb_shinfo(skb)->gso_size);
+ j = (size) ? 1 : 0;
+ }
if (j == I40E_MAX_BUFFER_TXD) {
- if (size < skb_shinfo(skb)->gso_size) {
- linearize = true;
- break;
- }
- j = 1;
- size -= skb_shinfo(skb)->gso_size;
- if (size)
- j++;
- size += hdr_len;
+ linearize = true;
+ break;
}
num_frags--;
} while (num_frags);
@@ -2724,7 +2719,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
if (tsyn)
tx_flags |= I40E_TX_FLAGS_TSYN;
- if (i40e_chk_linearize(skb, tx_flags, hdr_len))
+ if (i40e_chk_linearize(skb, tx_flags))
if (skb_linearize(skb))
goto out_drop;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index 78d1c4ff565e..4e9376da0518 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -1018,11 +1018,19 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
struct i40e_pf *pf = pci_get_drvdata(pdev);
- if (num_vfs)
+ if (num_vfs) {
+ if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
+ pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
+ i40e_do_reset_safe(pf,
+ BIT_ULL(__I40E_PF_RESET_REQUESTED));
+ }
return i40e_pci_sriov_enable(pdev, num_vfs);
+ }
if (!pci_vfs_assigned(pf->pdev)) {
i40e_free_vfs(pf);
+ pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED;
+ i40e_do_reset_safe(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED));
} else {
dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n");
return -EINVAL;
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
index b077e02a0cc7..458fbb421090 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
@@ -1619,14 +1619,12 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring,
* i40e_chk_linearize - Check if there are more than 8 fragments per packet
* @skb: send buffer
* @tx_flags: collected send information
- * @hdr_len: size of the packet header
*
* Note: Our HW can't scatter-gather more than 8 fragments to build
* a packet on the wire and so we need to figure out the cases where we
* need to linearize the skb.
**/
-static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
- const u8 hdr_len)
+static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags)
{
struct skb_frag_struct *frag;
bool linearize = false;
@@ -1638,7 +1636,7 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
gso_segs = skb_shinfo(skb)->gso_segs;
if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) {
- u16 j = 1;
+ u16 j = 0;
if (num_frags < (I40E_MAX_BUFFER_TXD))
goto linearize_chk_done;
@@ -1649,21 +1647,18 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
goto linearize_chk_done;
}
frag = &skb_shinfo(skb)->frags[0];
- size = hdr_len;
/* we might still have more fragments per segment */
do {
size += skb_frag_size(frag);
frag++; j++;
+ if ((size >= skb_shinfo(skb)->gso_size) &&
+ (j < I40E_MAX_BUFFER_TXD)) {
+ size = (size % skb_shinfo(skb)->gso_size);
+ j = (size) ? 1 : 0;
+ }
if (j == I40E_MAX_BUFFER_TXD) {
- if (size < skb_shinfo(skb)->gso_size) {
- linearize = true;
- break;
- }
- j = 1;
- size -= skb_shinfo(skb)->gso_size;
- if (size)
- j++;
- size += hdr_len;
+ linearize = true;
+ break;
}
num_frags--;
} while (num_frags);
@@ -1950,7 +1945,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
else if (tso)
tx_flags |= I40E_TX_FLAGS_TSO;
- if (i40e_chk_linearize(skb, tx_flags, hdr_len))
+ if (i40e_chk_linearize(skb, tx_flags))
if (skb_linearize(skb))
goto out_drop;
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 8457d0306e3a..a0a9b1fcb5e8 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -1036,7 +1036,7 @@ static void igb_reset_q_vector(struct igb_adapter *adapter, int v_idx)
adapter->tx_ring[q_vector->tx.ring->queue_index] = NULL;
if (q_vector->rx.ring)
- adapter->tx_ring[q_vector->rx.ring->queue_index] = NULL;
+ adapter->rx_ring[q_vector->rx.ring->queue_index] = NULL;
netif_napi_del(&q_vector->napi);
@@ -1207,6 +1207,8 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter,
q_vector = adapter->q_vector[v_idx];
if (!q_vector)
q_vector = kzalloc(size, GFP_KERNEL);
+ else
+ memset(q_vector, 0, size);
if (!q_vector)
return -ENOMEM;
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index e3b9b63ad010..c3a9392cbc19 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -538,8 +538,8 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp,
igb->perout[i].start.tv_nsec = rq->perout.start.nsec;
igb->perout[i].period.tv_sec = ts.tv_sec;
igb->perout[i].period.tv_nsec = ts.tv_nsec;
- wr32(trgttiml, rq->perout.start.sec);
- wr32(trgttimh, rq->perout.start.nsec);
+ wr32(trgttimh, rq->perout.start.sec);
+ wr32(trgttiml, rq->perout.start.nsec);
tsauxc |= tsauxc_mask;
tsim |= tsim_mask;
} else {
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index d3f4b0ceb3f7..5be12a00e1f4 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -8044,7 +8044,7 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev,
- u32 filter_mask)
+ u32 filter_mask, int nlflags)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
@@ -8052,7 +8052,7 @@ static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return 0;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
- adapter->bridge_mode, 0, 0);
+ adapter->bridge_mode, 0, 0, nlflags);
}
static void *ixgbe_fwd_add(struct net_device *pdev, struct net_device *vdev)
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index a16d267fbce4..e71cdde9cb01 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -3612,7 +3612,7 @@ static int ixgbevf_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
u8 *dst_mac = skb_header_pointer(skb, 0, 0, NULL);
if (!dst_mac || is_link_local_ether_addr(dst_mac)) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c
index 4f7dc044601e..529ef0594b90 100644
--- a/drivers/net/ethernet/mellanox/mlx4/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c
@@ -714,8 +714,13 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
msecs_to_jiffies(timeout))) {
mlx4_warn(dev, "command 0x%x timed out (go bit not cleared)\n",
op);
- err = -EIO;
- goto out_reset;
+ if (op == MLX4_CMD_NOP) {
+ err = -EBUSY;
+ goto out;
+ } else {
+ err = -EIO;
+ goto out_reset;
+ }
}
err = context->result;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 0f1afc085d58..cf467a9f6cc7 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -1467,6 +1467,7 @@ static void mlx4_en_service_task(struct work_struct *work)
if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
mlx4_en_ptp_overflow_check(mdev);
+ mlx4_en_recover_from_oom(priv);
queue_delayed_work(mdev->workqueue, &priv->service_task,
SERVICE_TASK_DELAY);
}
@@ -1500,17 +1501,13 @@ static int mlx4_en_init_affinity_hint(struct mlx4_en_priv *priv, int ring_idx)
{
struct mlx4_en_rx_ring *ring = priv->rx_ring[ring_idx];
int numa_node = priv->mdev->dev->numa_node;
- int ret = 0;
if (!zalloc_cpumask_var(&ring->affinity_mask, GFP_KERNEL))
return -ENOMEM;
- ret = cpumask_set_cpu_local_first(ring_idx, numa_node,
- ring->affinity_mask);
- if (ret)
- free_cpumask_var(ring->affinity_mask);
-
- return ret;
+ cpumask_set_cpu(cpumask_local_spread(ring_idx, numa_node),
+ ring->affinity_mask);
+ return 0;
}
static void mlx4_en_free_affinity_hint(struct mlx4_en_priv *priv, int ring_idx)
@@ -1721,7 +1718,7 @@ mac_err:
cq_err:
while (rx_index--) {
mlx4_en_deactivate_cq(priv, priv->rx_cq[rx_index]);
- mlx4_en_free_affinity_hint(priv, i);
+ mlx4_en_free_affinity_hint(priv, rx_index);
}
for (i = 0; i < priv->rx_ring_num; i++)
mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c
index 54f0e5ab2e55..0a56f010c846 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c
@@ -139,7 +139,7 @@ static unsigned long en_stats_adder(__be64 *start, __be64 *next, int num)
int i;
int offset = next - start;
- for (i = 0; i <= num; i++) {
+ for (i = 0; i < num; i++) {
ret += be64_to_cpu(*curr);
curr += offset;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index 4fdd3c37e47b..2a77a6b19121 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -244,6 +244,12 @@ static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv,
return mlx4_en_alloc_frags(priv, rx_desc, frags, ring->page_alloc, gfp);
}
+static inline bool mlx4_en_is_ring_empty(struct mlx4_en_rx_ring *ring)
+{
+ BUG_ON((u32)(ring->prod - ring->cons) > ring->actual_size);
+ return ring->prod == ring->cons;
+}
+
static inline void mlx4_en_update_rx_prod_db(struct mlx4_en_rx_ring *ring)
{
*ring->wqres.db.db = cpu_to_be32(ring->prod & 0xffff);
@@ -315,8 +321,7 @@ static void mlx4_en_free_rx_buf(struct mlx4_en_priv *priv,
ring->cons, ring->prod);
/* Unmap and free Rx buffers */
- BUG_ON((u32) (ring->prod - ring->cons) > ring->actual_size);
- while (ring->cons != ring->prod) {
+ while (!mlx4_en_is_ring_empty(ring)) {
index = ring->cons & ring->size_mask;
en_dbg(DRV, priv, "Processing descriptor:%d\n", index);
mlx4_en_free_rx_desc(priv, ring, index);
@@ -491,6 +496,23 @@ err_allocator:
return err;
}
+/* We recover from out of memory by scheduling our napi poll
+ * function (mlx4_en_process_cq), which tries to allocate
+ * all missing RX buffers (call to mlx4_en_refill_rx_buffers).
+ */
+void mlx4_en_recover_from_oom(struct mlx4_en_priv *priv)
+{
+ int ring;
+
+ if (!priv->port_up)
+ return;
+
+ for (ring = 0; ring < priv->rx_ring_num; ring++) {
+ if (mlx4_en_is_ring_empty(priv->rx_ring[ring]))
+ napi_reschedule(&priv->rx_cq[ring]->napi);
+ }
+}
+
void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_rx_ring **pring,
u32 size, u16 stride)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index 1783705273d8..7bed3a88579f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -143,8 +143,10 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
ring->hwtstamp_tx_type = priv->hwtstamp_config.tx_type;
ring->queue_index = queue_index;
- if (queue_index < priv->num_tx_rings_p_up && cpu_online(queue_index))
- cpumask_set_cpu(queue_index, &ring->affinity_mask);
+ if (queue_index < priv->num_tx_rings_p_up)
+ cpumask_set_cpu(cpumask_local_spread(queue_index,
+ priv->mdev->dev->numa_node),
+ &ring->affinity_mask);
*pring = ring;
return 0;
@@ -213,7 +215,7 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
err = mlx4_qp_to_ready(mdev->dev, &ring->wqres.mtt, &ring->context,
&ring->qp, &ring->qp_state);
- if (!user_prio && cpu_online(ring->queue_index))
+ if (!cpumask_empty(&ring->affinity_mask))
netif_set_xps_queue(priv->dev, &ring->affinity_mask,
ring->queue_index);
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c
index a4079811b176..e30bf57ad7a1 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.c
@@ -56,11 +56,13 @@ MODULE_PARM_DESC(enable_qos, "Enable Enhanced QoS support (default: on)");
#define MLX4_GET(dest, source, offset) \
do { \
void *__p = (char *) (source) + (offset); \
+ u64 val; \
switch (sizeof (dest)) { \
case 1: (dest) = *(u8 *) __p; break; \
case 2: (dest) = be16_to_cpup(__p); break; \
case 4: (dest) = be32_to_cpup(__p); break; \
- case 8: (dest) = be64_to_cpup(__p); break; \
+ case 8: val = get_unaligned((u64 *)__p); \
+ (dest) = be64_to_cpu(val); break; \
default: __buggy_use_of_MLX4_GET(); \
} \
} while (0)
@@ -1605,9 +1607,17 @@ static void get_board_id(void *vsd, char *board_id)
* swaps each 4-byte word before passing it back to
* us. Therefore we need to swab it before printing.
*/
- for (i = 0; i < 4; ++i)
- ((u32 *) board_id)[i] =
- swab32(*(u32 *) (vsd + VSD_OFFSET_MLX_BOARD_ID + i * 4));
+ u32 *bid_u32 = (u32 *)board_id;
+
+ for (i = 0; i < 4; ++i) {
+ u32 *addr;
+ u32 val;
+
+ addr = (u32 *) (vsd + VSD_OFFSET_MLX_BOARD_ID + i * 4);
+ val = get_unaligned(addr);
+ val = swab32(val);
+ put_unaligned(val, &bid_u32[i]);
+ }
}
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index ced5ecab5aa7..70de39c6a397 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -1674,6 +1674,25 @@ static int map_internal_clock(struct mlx4_dev *dev)
return 0;
}
+int mlx4_get_internal_clock_params(struct mlx4_dev *dev,
+ struct mlx4_clock_params *params)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ if (mlx4_is_slave(dev))
+ return -ENOTSUPP;
+
+ if (!params)
+ return -EINVAL;
+
+ params->bar = priv->fw.clock_bar;
+ params->offset = priv->fw.clock_offset;
+ params->size = MLX4_CLOCK_SIZE;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mlx4_get_internal_clock_params);
+
static void unmap_internal_clock(struct mlx4_dev *dev)
{
struct mlx4_priv *priv = mlx4_priv(dev);
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index 9de30216b146..d021f079f181 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -774,6 +774,7 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
void mlx4_en_deactivate_tx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_tx_ring *ring);
void mlx4_en_set_num_rx_rings(struct mlx4_en_dev *mdev);
+void mlx4_en_recover_from_oom(struct mlx4_en_priv *priv);
int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_rx_ring **pring,
u32 size, u16 stride, int node);
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index c7f28bf4b8e2..bafe2180cf0c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -2845,7 +2845,7 @@ int mlx4_SW2HW_EQ_wrapper(struct mlx4_dev *dev, int slave,
{
int err;
int eqn = vhcr->in_modifier;
- int res_id = (slave << 8) | eqn;
+ int res_id = (slave << 10) | eqn;
struct mlx4_eq_context *eqc = inbox->buf;
int mtt_base = eq_get_mtt_addr(eqc) / dev->caps.mtt_entry_sz;
int mtt_size = eq_get_mtt_size(eqc);
@@ -3051,7 +3051,7 @@ int mlx4_HW2SW_EQ_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_cmd_info *cmd)
{
int eqn = vhcr->in_modifier;
- int res_id = eqn | (slave << 8);
+ int res_id = eqn | (slave << 10);
struct res_eq *eq;
int err;
@@ -3108,7 +3108,7 @@ int mlx4_GEN_EQE(struct mlx4_dev *dev, int slave, struct mlx4_eqe *eqe)
return 0;
mutex_lock(&priv->mfunc.master.gen_eqe_mutex[slave]);
- res_id = (slave << 8) | event_eq->eqn;
+ res_id = (slave << 10) | event_eq->eqn;
err = get_res(dev, slave, res_id, RES_EQ, &req);
if (err)
goto unlock;
@@ -3131,7 +3131,7 @@ int mlx4_GEN_EQE(struct mlx4_dev *dev, int slave, struct mlx4_eqe *eqe)
memcpy(mailbox->buf, (u8 *) eqe, 28);
- in_modifier = (slave & 0xff) | ((event_eq->eqn & 0xff) << 16);
+ in_modifier = (slave & 0xff) | ((event_eq->eqn & 0x3ff) << 16);
err = mlx4_cmd(dev, mailbox->dma, in_modifier, 0,
MLX4_CMD_GEN_EQE, MLX4_CMD_TIME_CLASS_B,
@@ -3157,7 +3157,7 @@ int mlx4_QUERY_EQ_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_cmd_info *cmd)
{
int eqn = vhcr->in_modifier;
- int res_id = eqn | (slave << 8);
+ int res_id = eqn | (slave << 10);
struct res_eq *eq;
int err;
@@ -3187,7 +3187,7 @@ int mlx4_SW2HW_CQ_wrapper(struct mlx4_dev *dev, int slave,
int cqn = vhcr->in_modifier;
struct mlx4_cq_context *cqc = inbox->buf;
int mtt_base = cq_get_mtt_addr(cqc) / dev->caps.mtt_entry_sz;
- struct res_cq *cq;
+ struct res_cq *cq = NULL;
struct res_mtt *mtt;
err = cq_res_start_move_to(dev, slave, cqn, RES_CQ_HW, &cq);
@@ -3223,7 +3223,7 @@ int mlx4_HW2SW_CQ_wrapper(struct mlx4_dev *dev, int slave,
{
int err;
int cqn = vhcr->in_modifier;
- struct res_cq *cq;
+ struct res_cq *cq = NULL;
err = cq_res_start_move_to(dev, slave, cqn, RES_CQ_ALLOCATED, &cq);
if (err)
@@ -3362,7 +3362,7 @@ int mlx4_SW2HW_SRQ_wrapper(struct mlx4_dev *dev, int slave,
int err;
int srqn = vhcr->in_modifier;
struct res_mtt *mtt;
- struct res_srq *srq;
+ struct res_srq *srq = NULL;
struct mlx4_srq_context *srqc = inbox->buf;
int mtt_base = srq_get_mtt_addr(srqc) / dev->caps.mtt_entry_sz;
@@ -3406,7 +3406,7 @@ int mlx4_HW2SW_SRQ_wrapper(struct mlx4_dev *dev, int slave,
{
int err;
int srqn = vhcr->in_modifier;
- struct res_srq *srq;
+ struct res_srq *srq = NULL;
err = srq_res_start_move_to(dev, slave, srqn, RES_SRQ_ALLOCATED, &srq);
if (err)
@@ -4714,13 +4714,13 @@ static void rem_slave_eqs(struct mlx4_dev *dev, int slave)
break;
case RES_EQ_HW:
- err = mlx4_cmd(dev, slave, eqn & 0xff,
+ err = mlx4_cmd(dev, slave, eqn & 0x3ff,
1, MLX4_CMD_HW2SW_EQ,
MLX4_CMD_TIME_CLASS_A,
MLX4_CMD_NATIVE);
if (err)
mlx4_dbg(dev, "rem_slave_eqs: failed to move slave %d eqs %d to SW ownership\n",
- slave, eqn);
+ slave, eqn & 0x3ff);
atomic_dec(&eq->mtt->ref_count);
state = RES_EQ_RESERVED;
break;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mad.c b/drivers/net/ethernet/mellanox/mlx5/core/mad.c
index ee1b0b965f34..1368dac00da0 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mad.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mad.c
@@ -36,7 +36,7 @@
#include <linux/mlx5/cmd.h>
#include "mlx5_core.h"
-int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, void *inb, void *outb,
+int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, const void *inb, void *outb,
u16 opmod, u8 port)
{
struct mlx5_mad_ifc_mbox_in *in = NULL;
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
index 5c4068353f66..7b43a3b4abdc 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
@@ -135,7 +135,7 @@ void netxen_release_tx_buffers(struct netxen_adapter *adapter)
int i, j;
struct nx_host_tx_ring *tx_ring = adapter->tx_ring;
- spin_lock(&adapter->tx_clean_lock);
+ spin_lock_bh(&adapter->tx_clean_lock);
cmd_buf = tx_ring->cmd_buf_arr;
for (i = 0; i < tx_ring->num_desc; i++) {
buffrag = cmd_buf->frag_array;
@@ -159,7 +159,7 @@ void netxen_release_tx_buffers(struct netxen_adapter *adapter)
}
cmd_buf++;
}
- spin_unlock(&adapter->tx_clean_lock);
+ spin_unlock_bh(&adapter->tx_clean_lock);
}
void netxen_free_sw_resources(struct netxen_adapter *adapter)
@@ -1764,7 +1764,7 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter)
int done = 0;
struct nx_host_tx_ring *tx_ring = adapter->tx_ring;
- if (!spin_trylock(&adapter->tx_clean_lock))
+ if (!spin_trylock_bh(&adapter->tx_clean_lock))
return 1;
sw_consumer = tx_ring->sw_consumer;
@@ -1819,7 +1819,7 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter)
*/
hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
done = (sw_consumer == hw_consumer);
- spin_unlock(&adapter->tx_clean_lock);
+ spin_unlock_bh(&adapter->tx_clean_lock);
return done;
}
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
index e0c31e3947d1..6409a06bbdf6 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
@@ -3025,9 +3025,9 @@ netxen_sysfs_read_dimm(struct file *filp, struct kobject *kobj,
u8 dw, rows, cols, banks, ranks;
u32 val;
- if (size != sizeof(struct netxen_dimm_cfg)) {
+ if (size < attr->size) {
netdev_err(netdev, "Invalid size\n");
- return -1;
+ return -EINVAL;
}
memset(&dimm, 0, sizeof(struct netxen_dimm_cfg));
@@ -3137,7 +3137,7 @@ out:
static struct bin_attribute bin_attr_dimm = {
.attr = { .name = "dimm", .mode = (S_IRUGO | S_IWUSR) },
- .size = 0,
+ .size = sizeof(struct netxen_dimm_cfg),
.read = netxen_sysfs_read_dimm,
};
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index f66641d961e3..6af028d5f9bc 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -912,6 +912,8 @@ qca_spi_probe(struct spi_device *spi_device)
qca->spi_dev = spi_device;
qca->legacy_mode = legacy_mode;
+ spi_set_drvdata(spi_device, qcaspi_devs);
+
mac = of_get_mac_address(spi_device->dev.of_node);
if (mac)
@@ -944,8 +946,6 @@ qca_spi_probe(struct spi_device *spi_device)
return -EFAULT;
}
- spi_set_drvdata(spi_device, qcaspi_devs);
-
qcaspi_init_device_debugfs(qca);
return 0;
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index c70ab40d8698..3df51faf18ae 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -6884,7 +6884,7 @@ static void r8169_csum_workaround(struct rtl8169_private *tp,
rtl8169_start_xmit(nskb, tp->dev);
} while (segs);
- dev_kfree_skb(skb);
+ dev_consume_skb_any(skb);
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
if (skb_checksum_help(skb) < 0)
goto drop;
@@ -6896,7 +6896,7 @@ static void r8169_csum_workaround(struct rtl8169_private *tp,
drop:
stats = &tp->dev->stats;
stats->tx_dropped++;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
}
}
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index a570a60533be..cf98cc9bbc8d 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -2921,10 +2921,11 @@ static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port,
struct neighbour *n = __ipv4_neigh_lookup(dev, (__force u32)ip_addr);
int err = 0;
- if (!n)
+ if (!n) {
n = neigh_create(&arp_tbl, &ip_addr, dev);
- if (!n)
- return -ENOMEM;
+ if (IS_ERR(n))
+ return IS_ERR(n);
+ }
/* If the neigh is already resolved, then go ahead and
* install the entry, otherwise start the ARP process to
@@ -2936,6 +2937,7 @@ static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port,
else
neigh_event_send(n, NULL);
+ neigh_release(n);
return err;
}
@@ -4176,14 +4178,15 @@ static int rocker_port_bridge_setlink(struct net_device *dev,
static int rocker_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev,
- u32 filter_mask)
+ u32 filter_mask, int nlflags)
{
struct rocker_port *rocker_port = netdev_priv(dev);
u16 mode = BRIDGE_MODE_UNDEF;
u32 mask = BR_LEARNING | BR_LEARNING_SYNC;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode,
- rocker_port->brport_flags, mask);
+ rocker_port->brport_flags, mask,
+ nlflags);
}
static int rocker_port_get_phys_port_name(struct net_device *dev,
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 4b00545a3ace..65944dd8bf6b 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -1304,7 +1304,7 @@ static unsigned int efx_wanted_parallelism(struct efx_nic *efx)
if (!cpumask_test_cpu(cpu, thread_mask)) {
++count;
cpumask_or(thread_mask, thread_mask,
- topology_thread_cpumask(cpu));
+ topology_sibling_cpumask(cpu));
}
}
diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c
index c0ad95d2f63d..809ea4610a77 100644
--- a/drivers/net/ethernet/sfc/rx.c
+++ b/drivers/net/ethernet/sfc/rx.c
@@ -224,12 +224,17 @@ static void efx_unmap_rx_buffer(struct efx_nic *efx,
}
}
-static void efx_free_rx_buffer(struct efx_rx_buffer *rx_buf)
+static void efx_free_rx_buffers(struct efx_rx_queue *rx_queue,
+ struct efx_rx_buffer *rx_buf,
+ unsigned int num_bufs)
{
- if (rx_buf->page) {
- put_page(rx_buf->page);
- rx_buf->page = NULL;
- }
+ do {
+ if (rx_buf->page) {
+ put_page(rx_buf->page);
+ rx_buf->page = NULL;
+ }
+ rx_buf = efx_rx_buf_next(rx_queue, rx_buf);
+ } while (--num_bufs);
}
/* Attempt to recycle the page if there is an RX recycle ring; the page can
@@ -278,7 +283,7 @@ static void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue,
/* If this is the last buffer in a page, unmap and free it. */
if (rx_buf->flags & EFX_RX_BUF_LAST_IN_PAGE) {
efx_unmap_rx_buffer(rx_queue->efx, rx_buf);
- efx_free_rx_buffer(rx_buf);
+ efx_free_rx_buffers(rx_queue, rx_buf, 1);
}
rx_buf->page = NULL;
}
@@ -304,10 +309,7 @@ static void efx_discard_rx_packet(struct efx_channel *channel,
efx_recycle_rx_pages(channel, rx_buf, n_frags);
- do {
- efx_free_rx_buffer(rx_buf);
- rx_buf = efx_rx_buf_next(rx_queue, rx_buf);
- } while (--n_frags);
+ efx_free_rx_buffers(rx_queue, rx_buf, n_frags);
}
/**
@@ -431,11 +433,10 @@ efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf,
skb = napi_get_frags(napi);
if (unlikely(!skb)) {
- while (n_frags--) {
- put_page(rx_buf->page);
- rx_buf->page = NULL;
- rx_buf = efx_rx_buf_next(&channel->rx_queue, rx_buf);
- }
+ struct efx_rx_queue *rx_queue;
+
+ rx_queue = efx_channel_get_rx_queue(channel);
+ efx_free_rx_buffers(rx_queue, rx_buf, n_frags);
return;
}
@@ -622,7 +623,10 @@ static void efx_rx_deliver(struct efx_channel *channel, u8 *eh,
skb = efx_rx_mk_skb(channel, rx_buf, n_frags, eh, hdr_len);
if (unlikely(skb == NULL)) {
- efx_free_rx_buffer(rx_buf);
+ struct efx_rx_queue *rx_queue;
+
+ rx_queue = efx_channel_get_rx_queue(channel);
+ efx_free_rx_buffers(rx_queue, rx_buf, n_frags);
return;
}
skb_record_rx_queue(skb, channel->rx_queue.core_index);
@@ -661,8 +665,12 @@ void __efx_rx_packet(struct efx_channel *channel)
* loopback layer, and free the rx_buf here
*/
if (unlikely(efx->loopback_selftest)) {
+ struct efx_rx_queue *rx_queue;
+
efx_loopback_rx_packet(efx, eh, rx_buf->len);
- efx_free_rx_buffer(rx_buf);
+ rx_queue = efx_channel_get_rx_queue(channel);
+ efx_free_rx_buffers(rx_queue, rx_buf,
+ channel->rx_pkt_n_frags);
goto out;
}
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index 14b363a25c02..630f0b7800e4 100644
--- a/drivers/net/ethernet/smsc/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -2238,9 +2238,10 @@ static int smc_drv_probe(struct platform_device *pdev)
const struct of_device_id *match = NULL;
struct smc_local *lp;
struct net_device *ndev;
- struct resource *res, *ires;
+ struct resource *res;
unsigned int __iomem *addr;
unsigned long irq_flags = SMC_IRQ_FLAGS;
+ unsigned long irq_resflags;
int ret;
ndev = alloc_etherdev(sizeof(struct smc_local));
@@ -2332,16 +2333,19 @@ static int smc_drv_probe(struct platform_device *pdev)
goto out_free_netdev;
}
- ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!ires) {
+ ndev->irq = platform_get_irq(pdev, 0);
+ if (ndev->irq <= 0) {
ret = -ENODEV;
goto out_release_io;
}
-
- ndev->irq = ires->start;
-
- if (irq_flags == -1 || ires->flags & IRQF_TRIGGER_MASK)
- irq_flags = ires->flags & IRQF_TRIGGER_MASK;
+ /*
+ * If this platform does not specify any special irqflags, or if
+ * the resource supplies a trigger, override the irqflags with
+ * the trigger flags from the resource.
+ */
+ irq_resflags = irqd_get_trigger_type(irq_get_irq_data(ndev->irq));
+ if (irq_flags == -1 || irq_resflags & IRQF_TRIGGER_MASK)
+ irq_flags = irq_resflags & IRQF_TRIGGER_MASK;
ret = smc_request_attrib(pdev, ndev);
if (ret)
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index 41047c9143d0..959aeeade0c9 100644
--- a/drivers/net/ethernet/smsc/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -2418,9 +2418,9 @@ static int smsc911x_drv_probe(struct platform_device *pdev)
struct net_device *dev;
struct smsc911x_data *pdata;
struct smsc911x_platform_config *config = dev_get_platdata(&pdev->dev);
- struct resource *res, *irq_res;
+ struct resource *res;
unsigned int intcfg = 0;
- int res_size, irq_flags;
+ int res_size, irq, irq_flags;
int retval;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
@@ -2434,8 +2434,8 @@ static int smsc911x_drv_probe(struct platform_device *pdev)
}
res_size = resource_size(res);
- irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq_res) {
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
pr_warn("Could not allocate irq resource\n");
retval = -ENODEV;
goto out_0;
@@ -2455,8 +2455,8 @@ static int smsc911x_drv_probe(struct platform_device *pdev)
SET_NETDEV_DEV(dev, &pdev->dev);
pdata = netdev_priv(dev);
- dev->irq = irq_res->start;
- irq_flags = irq_res->flags & IRQF_TRIGGER_MASK;
+ dev->irq = irq;
+ irq_flags = irq_get_trigger_type(irq);
pdata->ioaddr = ioremap_nocache(res->start, res_size);
pdata->dev = dev;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 2ac9552d1fa3..73bab983edd9 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -117,6 +117,12 @@ struct stmmac_priv {
int use_riwt;
int irq_wake;
spinlock_t ptp_lock;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dbgfs_dir;
+ struct dentry *dbgfs_rings_status;
+ struct dentry *dbgfs_dma_cap;
+#endif
};
int stmmac_mdio_unregister(struct net_device *ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 05c146f718a3..2c5ce2baca87 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -118,7 +118,7 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
#ifdef CONFIG_DEBUG_FS
static int stmmac_init_fs(struct net_device *dev);
-static void stmmac_exit_fs(void);
+static void stmmac_exit_fs(struct net_device *dev);
#endif
#define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x))
@@ -1916,7 +1916,7 @@ static int stmmac_release(struct net_device *dev)
netif_carrier_off(dev);
#ifdef CONFIG_DEBUG_FS
- stmmac_exit_fs();
+ stmmac_exit_fs(dev);
#endif
stmmac_release_ptp(priv);
@@ -2508,8 +2508,6 @@ static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
#ifdef CONFIG_DEBUG_FS
static struct dentry *stmmac_fs_dir;
-static struct dentry *stmmac_rings_status;
-static struct dentry *stmmac_dma_cap;
static void sysfs_display_ring(void *head, int size, int extend_desc,
struct seq_file *seq)
@@ -2648,36 +2646,39 @@ static const struct file_operations stmmac_dma_cap_fops = {
static int stmmac_init_fs(struct net_device *dev)
{
- /* Create debugfs entries */
- stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
+ struct stmmac_priv *priv = netdev_priv(dev);
+
+ /* Create per netdev entries */
+ priv->dbgfs_dir = debugfs_create_dir(dev->name, stmmac_fs_dir);
- if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
- pr_err("ERROR %s, debugfs create directory failed\n",
- STMMAC_RESOURCE_NAME);
+ if (!priv->dbgfs_dir || IS_ERR(priv->dbgfs_dir)) {
+ pr_err("ERROR %s/%s, debugfs create directory failed\n",
+ STMMAC_RESOURCE_NAME, dev->name);
return -ENOMEM;
}
/* Entry to report DMA RX/TX rings */
- stmmac_rings_status = debugfs_create_file("descriptors_status",
- S_IRUGO, stmmac_fs_dir, dev,
- &stmmac_rings_status_fops);
+ priv->dbgfs_rings_status =
+ debugfs_create_file("descriptors_status", S_IRUGO,
+ priv->dbgfs_dir, dev,
+ &stmmac_rings_status_fops);
- if (!stmmac_rings_status || IS_ERR(stmmac_rings_status)) {
+ if (!priv->dbgfs_rings_status || IS_ERR(priv->dbgfs_rings_status)) {
pr_info("ERROR creating stmmac ring debugfs file\n");
- debugfs_remove(stmmac_fs_dir);
+ debugfs_remove_recursive(priv->dbgfs_dir);
return -ENOMEM;
}
/* Entry to report the DMA HW features */
- stmmac_dma_cap = debugfs_create_file("dma_cap", S_IRUGO, stmmac_fs_dir,
- dev, &stmmac_dma_cap_fops);
+ priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", S_IRUGO,
+ priv->dbgfs_dir,
+ dev, &stmmac_dma_cap_fops);
- if (!stmmac_dma_cap || IS_ERR(stmmac_dma_cap)) {
+ if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) {
pr_info("ERROR creating stmmac MMC debugfs file\n");
- debugfs_remove(stmmac_rings_status);
- debugfs_remove(stmmac_fs_dir);
+ debugfs_remove_recursive(priv->dbgfs_dir);
return -ENOMEM;
}
@@ -2685,11 +2686,11 @@ static int stmmac_init_fs(struct net_device *dev)
return 0;
}
-static void stmmac_exit_fs(void)
+static void stmmac_exit_fs(struct net_device *dev)
{
- debugfs_remove(stmmac_rings_status);
- debugfs_remove(stmmac_dma_cap);
- debugfs_remove(stmmac_fs_dir);
+ struct stmmac_priv *priv = netdev_priv(dev);
+
+ debugfs_remove_recursive(priv->dbgfs_dir);
}
#endif /* CONFIG_DEBUG_FS */
@@ -3149,6 +3150,35 @@ err:
__setup("stmmaceth=", stmmac_cmdline_opt);
#endif /* MODULE */
+static int __init stmmac_init(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ /* Create debugfs main directory if it doesn't exist yet */
+ if (!stmmac_fs_dir) {
+ stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
+
+ if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
+ pr_err("ERROR %s, debugfs create directory failed\n",
+ STMMAC_RESOURCE_NAME);
+
+ return -ENOMEM;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+static void __exit stmmac_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(stmmac_fs_dir);
+#endif
+}
+
+module_init(stmmac_init)
+module_exit(stmmac_exit)
+
MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet device driver");
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 705bbdf93940..68aec5c460db 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -23,6 +23,7 @@
*******************************************************************************/
#include <linux/platform_device.h>
+#include <linux/module.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_net.h>
diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c
index 3dc1f68b322d..6ce973187225 100644
--- a/drivers/net/ethernet/sun/cassini.c
+++ b/drivers/net/ethernet/sun/cassini.c
@@ -3058,7 +3058,6 @@ static void cas_init_mac(struct cas *cp)
/* setup core arbitration weight register */
writel(CAWR_RR_DIS, cp->regs + REG_CAWR);
- /* XXX Use pci_dma_burst_advice() */
#if !defined(CONFIG_SPARC64) && !defined(CONFIG_ALPHA)
/* set the infinite burst register for chips that don't have
* pci issues.
diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c
index 2bef655279f3..9b7e0a34c98b 100644
--- a/drivers/net/ethernet/ti/netcp_ethss.c
+++ b/drivers/net/ethernet/ti/netcp_ethss.c
@@ -1765,7 +1765,9 @@ static void netcp_ethss_link_state_action(struct gbe_priv *gbe_dev,
ALE_PORT_STATE,
ALE_PORT_STATE_FORWARD);
- if (ndev && slave->open)
+ if (ndev && slave->open &&
+ slave->link_interface != SGMII_LINK_MAC_PHY &&
+ slave->link_interface != XGMII_LINK_MAC_PHY)
netif_carrier_on(ndev);
} else {
writel(mac_control, GBE_REG_ADDR(slave, emac_regs,
@@ -1773,7 +1775,9 @@ static void netcp_ethss_link_state_action(struct gbe_priv *gbe_dev,
cpsw_ale_control_set(gbe_dev->ale, slave->port_num,
ALE_PORT_STATE,
ALE_PORT_STATE_DISABLE);
- if (ndev)
+ if (ndev &&
+ slave->link_interface != SGMII_LINK_MAC_PHY &&
+ slave->link_interface != XGMII_LINK_MAC_PHY)
netif_carrier_off(ndev);
}
diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c
index 690a4c36b316..af2694dc6f90 100644
--- a/drivers/net/ethernet/xilinx/ll_temac_main.c
+++ b/drivers/net/ethernet/xilinx/ll_temac_main.c
@@ -707,8 +707,8 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
cur_p->app0 |= STS_CTRL_APP0_SOP;
cur_p->len = skb_headlen(skb);
- cur_p->phys = dma_map_single(ndev->dev.parent, skb->data, skb->len,
- DMA_TO_DEVICE);
+ cur_p->phys = dma_map_single(ndev->dev.parent, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
cur_p->app4 = (unsigned long)skb;
for (ii = 0; ii < num_frag; ii++) {
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index a10b31664709..41071d32bc8e 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -128,7 +128,6 @@ struct ndis_tcp_ip_checksum_info;
struct hv_netvsc_packet {
/* Bookkeeping stuff */
u32 status;
- bool part_of_skb;
bool is_data_pkt;
bool xmit_more; /* from skb */
@@ -612,6 +611,15 @@ struct multi_send_data {
u32 count; /* counter of batched packets */
};
+/* The context of the netvsc device */
+struct net_device_context {
+ /* point back to our device context */
+ struct hv_device *device_ctx;
+ struct delayed_work dwork;
+ struct work_struct work;
+ u32 msg_enable; /* debug level */
+};
+
/* Per netvsc device */
struct netvsc_device {
struct hv_device *dev;
@@ -667,6 +675,9 @@ struct netvsc_device {
struct multi_send_data msd[NR_CPUS];
u32 max_pkt; /* max number of pkt in one send, e.g. 8 */
u32 pkt_align; /* alignment bytes, e.g. 8 */
+
+ /* The net device context */
+ struct net_device_context *nd_ctx;
};
/* NdisInitialize message */
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 2e8ad0636b46..1e09243d5449 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/if_ether.h>
+#include <linux/vmalloc.h>
#include <asm/sync_bitops.h>
#include "hyperv_net.h"
@@ -826,7 +827,6 @@ int netvsc_send(struct hv_device *device,
u16 q_idx = packet->q_idx;
u32 pktlen = packet->total_data_buflen, msd_len = 0;
unsigned int section_index = NETVSC_INVALID_INDEX;
- struct sk_buff *skb = NULL;
unsigned long flag;
struct multi_send_data *msdp;
struct hv_netvsc_packet *msd_send = NULL, *cur_send = NULL;
@@ -889,11 +889,6 @@ int netvsc_send(struct hv_device *device,
} else {
packet->page_buf_cnt = 0;
packet->total_data_buflen += msd_len;
- if (!packet->part_of_skb) {
- skb = (struct sk_buff *)(unsigned long)packet->
- send_completion_tid;
- packet->send_completion_tid = 0;
- }
}
if (msdp->pkt)
@@ -929,12 +924,8 @@ int netvsc_send(struct hv_device *device,
if (cur_send)
ret = netvsc_send_pkt(cur_send, net_device);
- if (ret != 0) {
- if (section_index != NETVSC_INVALID_INDEX)
- netvsc_free_send_slot(net_device, section_index);
- } else if (skb) {
- dev_kfree_skb_any(skb);
- }
+ if (ret != 0 && section_index != NETVSC_INVALID_INDEX)
+ netvsc_free_send_slot(net_device, section_index);
return ret;
}
@@ -1197,6 +1188,9 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
*/
ndev = net_device->ndev;
+ /* Add netvsc_device context to netvsc_device */
+ net_device->nd_ctx = netdev_priv(ndev);
+
/* Initialize the NetVSC channel extension */
init_completion(&net_device->channel_init_wait);
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index a3a9d3898a6e..5993c7e2d723 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -40,18 +40,21 @@
#include "hyperv_net.h"
-struct net_device_context {
- /* point back to our device context */
- struct hv_device *device_ctx;
- struct delayed_work dwork;
- struct work_struct work;
-};
#define RING_SIZE_MIN 64
static int ring_size = 128;
module_param(ring_size, int, S_IRUGO);
MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
+static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
+ NETIF_MSG_LINK | NETIF_MSG_IFUP |
+ NETIF_MSG_IFDOWN | NETIF_MSG_RX_ERR |
+ NETIF_MSG_TX_ERR;
+
+static int debug = -1;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
static void do_set_multicast(struct work_struct *w)
{
struct net_device_context *ndevctx =
@@ -235,9 +238,6 @@ void netvsc_xmit_completion(void *context)
struct sk_buff *skb = (struct sk_buff *)
(unsigned long)packet->send_completion_tid;
- if (!packet->part_of_skb)
- kfree(packet);
-
if (skb)
dev_kfree_skb_any(skb);
}
@@ -389,7 +389,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
u32 net_trans_info;
u32 hash;
u32 skb_length;
- u32 head_room;
u32 pkt_sz;
struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
@@ -402,7 +401,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
check_size:
skb_length = skb->len;
- head_room = skb_headroom(skb);
num_data_pgs = netvsc_get_slots(skb) + 2;
if (num_data_pgs > MAX_PAGE_BUFFER_COUNT && linear) {
net_alert_ratelimited("packet too big: %u pages (%u bytes)\n",
@@ -421,20 +419,14 @@ check_size:
pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE;
- if (head_room < pkt_sz) {
- packet = kmalloc(pkt_sz, GFP_ATOMIC);
- if (!packet) {
- /* out of memory, drop packet */
- netdev_err(net, "unable to alloc hv_netvsc_packet\n");
- ret = -ENOMEM;
- goto drop;
- }
- packet->part_of_skb = false;
- } else {
- /* Use the headroom for building up the packet */
- packet = (struct hv_netvsc_packet *)skb->head;
- packet->part_of_skb = true;
+ ret = skb_cow_head(skb, pkt_sz);
+ if (ret) {
+ netdev_err(net, "unable to alloc hv_netvsc_packet\n");
+ ret = -ENOMEM;
+ goto drop;
}
+ /* Use the headroom for building up the packet */
+ packet = (struct hv_netvsc_packet *)skb->head;
packet->status = 0;
packet->xmit_more = skb->xmit_more;
@@ -591,8 +583,6 @@ drop:
net->stats.tx_bytes += skb_length;
net->stats.tx_packets++;
} else {
- if (packet && !packet->part_of_skb)
- kfree(packet);
if (ret != -EAGAIN) {
dev_kfree_skb_any(skb);
net->stats.tx_dropped++;
@@ -888,6 +878,11 @@ static int netvsc_probe(struct hv_device *dev,
net_device_ctx = netdev_priv(net);
net_device_ctx->device_ctx = dev;
+ net_device_ctx->msg_enable = netif_msg_init(debug, default_msg);
+ if (netif_msg_probe(net_device_ctx))
+ netdev_dbg(net, "netvsc msg_enable: %d\n",
+ net_device_ctx->msg_enable);
+
hv_set_drvdata(dev, net);
INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change);
INIT_WORK(&net_device_ctx->work, do_set_multicast);
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index 0d92efefd796..35a482d526d9 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -27,6 +27,7 @@
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
#include <linux/nls.h>
+#include <linux/vmalloc.h>
#include "hyperv_net.h"
@@ -429,7 +430,8 @@ int rndis_filter_receive(struct hv_device *dev,
rndis_msg = pkt->data;
- dump_rndis_message(dev, rndis_msg);
+ if (netif_msg_rx_err(net_dev->nd_ctx))
+ dump_rndis_message(dev, rndis_msg);
switch (rndis_msg->ndis_msg_type) {
case RNDIS_MSG_PACKET:
diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c
index 38026650c038..67d00fbc2e0e 100644
--- a/drivers/net/ieee802154/at86rf230.c
+++ b/drivers/net/ieee802154/at86rf230.c
@@ -85,6 +85,7 @@ struct at86rf230_local {
struct ieee802154_hw *hw;
struct at86rf2xx_chip_data *data;
struct regmap *regmap;
+ int slp_tr;
struct completion state_complete;
struct at86rf230_state_change state;
@@ -95,163 +96,164 @@ struct at86rf230_local {
unsigned long cal_timeout;
s8 max_frame_retries;
bool is_tx;
+ bool is_tx_from_off;
u8 tx_retry;
struct sk_buff *tx_skb;
struct at86rf230_state_change tx;
};
-#define RG_TRX_STATUS (0x01)
-#define SR_TRX_STATUS 0x01, 0x1f, 0
-#define SR_RESERVED_01_3 0x01, 0x20, 5
-#define SR_CCA_STATUS 0x01, 0x40, 6
-#define SR_CCA_DONE 0x01, 0x80, 7
-#define RG_TRX_STATE (0x02)
-#define SR_TRX_CMD 0x02, 0x1f, 0
-#define SR_TRAC_STATUS 0x02, 0xe0, 5
-#define RG_TRX_CTRL_0 (0x03)
-#define SR_CLKM_CTRL 0x03, 0x07, 0
-#define SR_CLKM_SHA_SEL 0x03, 0x08, 3
-#define SR_PAD_IO_CLKM 0x03, 0x30, 4
-#define SR_PAD_IO 0x03, 0xc0, 6
-#define RG_TRX_CTRL_1 (0x04)
-#define SR_IRQ_POLARITY 0x04, 0x01, 0
-#define SR_IRQ_MASK_MODE 0x04, 0x02, 1
-#define SR_SPI_CMD_MODE 0x04, 0x0c, 2
-#define SR_RX_BL_CTRL 0x04, 0x10, 4
-#define SR_TX_AUTO_CRC_ON 0x04, 0x20, 5
-#define SR_IRQ_2_EXT_EN 0x04, 0x40, 6
-#define SR_PA_EXT_EN 0x04, 0x80, 7
-#define RG_PHY_TX_PWR (0x05)
-#define SR_TX_PWR 0x05, 0x0f, 0
-#define SR_PA_LT 0x05, 0x30, 4
-#define SR_PA_BUF_LT 0x05, 0xc0, 6
-#define RG_PHY_RSSI (0x06)
-#define SR_RSSI 0x06, 0x1f, 0
-#define SR_RND_VALUE 0x06, 0x60, 5
-#define SR_RX_CRC_VALID 0x06, 0x80, 7
-#define RG_PHY_ED_LEVEL (0x07)
-#define SR_ED_LEVEL 0x07, 0xff, 0
-#define RG_PHY_CC_CCA (0x08)
-#define SR_CHANNEL 0x08, 0x1f, 0
-#define SR_CCA_MODE 0x08, 0x60, 5
-#define SR_CCA_REQUEST 0x08, 0x80, 7
-#define RG_CCA_THRES (0x09)
-#define SR_CCA_ED_THRES 0x09, 0x0f, 0
-#define SR_RESERVED_09_1 0x09, 0xf0, 4
-#define RG_RX_CTRL (0x0a)
-#define SR_PDT_THRES 0x0a, 0x0f, 0
-#define SR_RESERVED_0a_1 0x0a, 0xf0, 4
-#define RG_SFD_VALUE (0x0b)
-#define SR_SFD_VALUE 0x0b, 0xff, 0
-#define RG_TRX_CTRL_2 (0x0c)
-#define SR_OQPSK_DATA_RATE 0x0c, 0x03, 0
-#define SR_SUB_MODE 0x0c, 0x04, 2
-#define SR_BPSK_QPSK 0x0c, 0x08, 3
-#define SR_OQPSK_SUB1_RC_EN 0x0c, 0x10, 4
-#define SR_RESERVED_0c_5 0x0c, 0x60, 5
-#define SR_RX_SAFE_MODE 0x0c, 0x80, 7
-#define RG_ANT_DIV (0x0d)
-#define SR_ANT_CTRL 0x0d, 0x03, 0
-#define SR_ANT_EXT_SW_EN 0x0d, 0x04, 2
-#define SR_ANT_DIV_EN 0x0d, 0x08, 3
-#define SR_RESERVED_0d_2 0x0d, 0x70, 4
-#define SR_ANT_SEL 0x0d, 0x80, 7
-#define RG_IRQ_MASK (0x0e)
-#define SR_IRQ_MASK 0x0e, 0xff, 0
-#define RG_IRQ_STATUS (0x0f)
-#define SR_IRQ_0_PLL_LOCK 0x0f, 0x01, 0
-#define SR_IRQ_1_PLL_UNLOCK 0x0f, 0x02, 1
-#define SR_IRQ_2_RX_START 0x0f, 0x04, 2
-#define SR_IRQ_3_TRX_END 0x0f, 0x08, 3
-#define SR_IRQ_4_CCA_ED_DONE 0x0f, 0x10, 4
-#define SR_IRQ_5_AMI 0x0f, 0x20, 5
-#define SR_IRQ_6_TRX_UR 0x0f, 0x40, 6
-#define SR_IRQ_7_BAT_LOW 0x0f, 0x80, 7
-#define RG_VREG_CTRL (0x10)
-#define SR_RESERVED_10_6 0x10, 0x03, 0
-#define SR_DVDD_OK 0x10, 0x04, 2
-#define SR_DVREG_EXT 0x10, 0x08, 3
-#define SR_RESERVED_10_3 0x10, 0x30, 4
-#define SR_AVDD_OK 0x10, 0x40, 6
-#define SR_AVREG_EXT 0x10, 0x80, 7
-#define RG_BATMON (0x11)
-#define SR_BATMON_VTH 0x11, 0x0f, 0
-#define SR_BATMON_HR 0x11, 0x10, 4
-#define SR_BATMON_OK 0x11, 0x20, 5
-#define SR_RESERVED_11_1 0x11, 0xc0, 6
-#define RG_XOSC_CTRL (0x12)
-#define SR_XTAL_TRIM 0x12, 0x0f, 0
-#define SR_XTAL_MODE 0x12, 0xf0, 4
-#define RG_RX_SYN (0x15)
-#define SR_RX_PDT_LEVEL 0x15, 0x0f, 0
-#define SR_RESERVED_15_2 0x15, 0x70, 4
-#define SR_RX_PDT_DIS 0x15, 0x80, 7
-#define RG_XAH_CTRL_1 (0x17)
-#define SR_RESERVED_17_8 0x17, 0x01, 0
-#define SR_AACK_PROM_MODE 0x17, 0x02, 1
-#define SR_AACK_ACK_TIME 0x17, 0x04, 2
-#define SR_RESERVED_17_5 0x17, 0x08, 3
-#define SR_AACK_UPLD_RES_FT 0x17, 0x10, 4
-#define SR_AACK_FLTR_RES_FT 0x17, 0x20, 5
-#define SR_CSMA_LBT_MODE 0x17, 0x40, 6
-#define SR_RESERVED_17_1 0x17, 0x80, 7
-#define RG_FTN_CTRL (0x18)
-#define SR_RESERVED_18_2 0x18, 0x7f, 0
-#define SR_FTN_START 0x18, 0x80, 7
-#define RG_PLL_CF (0x1a)
-#define SR_RESERVED_1a_2 0x1a, 0x7f, 0
-#define SR_PLL_CF_START 0x1a, 0x80, 7
-#define RG_PLL_DCU (0x1b)
-#define SR_RESERVED_1b_3 0x1b, 0x3f, 0
-#define SR_RESERVED_1b_2 0x1b, 0x40, 6
-#define SR_PLL_DCU_START 0x1b, 0x80, 7
-#define RG_PART_NUM (0x1c)
-#define SR_PART_NUM 0x1c, 0xff, 0
-#define RG_VERSION_NUM (0x1d)
-#define SR_VERSION_NUM 0x1d, 0xff, 0
-#define RG_MAN_ID_0 (0x1e)
-#define SR_MAN_ID_0 0x1e, 0xff, 0
-#define RG_MAN_ID_1 (0x1f)
-#define SR_MAN_ID_1 0x1f, 0xff, 0
-#define RG_SHORT_ADDR_0 (0x20)
-#define SR_SHORT_ADDR_0 0x20, 0xff, 0
-#define RG_SHORT_ADDR_1 (0x21)
-#define SR_SHORT_ADDR_1 0x21, 0xff, 0
-#define RG_PAN_ID_0 (0x22)
-#define SR_PAN_ID_0 0x22, 0xff, 0
-#define RG_PAN_ID_1 (0x23)
-#define SR_PAN_ID_1 0x23, 0xff, 0
-#define RG_IEEE_ADDR_0 (0x24)
-#define SR_IEEE_ADDR_0 0x24, 0xff, 0
-#define RG_IEEE_ADDR_1 (0x25)
-#define SR_IEEE_ADDR_1 0x25, 0xff, 0
-#define RG_IEEE_ADDR_2 (0x26)
-#define SR_IEEE_ADDR_2 0x26, 0xff, 0
-#define RG_IEEE_ADDR_3 (0x27)
-#define SR_IEEE_ADDR_3 0x27, 0xff, 0
-#define RG_IEEE_ADDR_4 (0x28)
-#define SR_IEEE_ADDR_4 0x28, 0xff, 0
-#define RG_IEEE_ADDR_5 (0x29)
-#define SR_IEEE_ADDR_5 0x29, 0xff, 0
-#define RG_IEEE_ADDR_6 (0x2a)
-#define SR_IEEE_ADDR_6 0x2a, 0xff, 0
-#define RG_IEEE_ADDR_7 (0x2b)
-#define SR_IEEE_ADDR_7 0x2b, 0xff, 0
-#define RG_XAH_CTRL_0 (0x2c)
-#define SR_SLOTTED_OPERATION 0x2c, 0x01, 0
-#define SR_MAX_CSMA_RETRIES 0x2c, 0x0e, 1
-#define SR_MAX_FRAME_RETRIES 0x2c, 0xf0, 4
-#define RG_CSMA_SEED_0 (0x2d)
-#define SR_CSMA_SEED_0 0x2d, 0xff, 0
-#define RG_CSMA_SEED_1 (0x2e)
-#define SR_CSMA_SEED_1 0x2e, 0x07, 0
-#define SR_AACK_I_AM_COORD 0x2e, 0x08, 3
-#define SR_AACK_DIS_ACK 0x2e, 0x10, 4
-#define SR_AACK_SET_PD 0x2e, 0x20, 5
-#define SR_AACK_FVN_MODE 0x2e, 0xc0, 6
-#define RG_CSMA_BE (0x2f)
-#define SR_MIN_BE 0x2f, 0x0f, 0
-#define SR_MAX_BE 0x2f, 0xf0, 4
+#define RG_TRX_STATUS (0x01)
+#define SR_TRX_STATUS 0x01, 0x1f, 0
+#define SR_RESERVED_01_3 0x01, 0x20, 5
+#define SR_CCA_STATUS 0x01, 0x40, 6
+#define SR_CCA_DONE 0x01, 0x80, 7
+#define RG_TRX_STATE (0x02)
+#define SR_TRX_CMD 0x02, 0x1f, 0
+#define SR_TRAC_STATUS 0x02, 0xe0, 5
+#define RG_TRX_CTRL_0 (0x03)
+#define SR_CLKM_CTRL 0x03, 0x07, 0
+#define SR_CLKM_SHA_SEL 0x03, 0x08, 3
+#define SR_PAD_IO_CLKM 0x03, 0x30, 4
+#define SR_PAD_IO 0x03, 0xc0, 6
+#define RG_TRX_CTRL_1 (0x04)
+#define SR_IRQ_POLARITY 0x04, 0x01, 0
+#define SR_IRQ_MASK_MODE 0x04, 0x02, 1
+#define SR_SPI_CMD_MODE 0x04, 0x0c, 2
+#define SR_RX_BL_CTRL 0x04, 0x10, 4
+#define SR_TX_AUTO_CRC_ON 0x04, 0x20, 5
+#define SR_IRQ_2_EXT_EN 0x04, 0x40, 6
+#define SR_PA_EXT_EN 0x04, 0x80, 7
+#define RG_PHY_TX_PWR (0x05)
+#define SR_TX_PWR 0x05, 0x0f, 0
+#define SR_PA_LT 0x05, 0x30, 4
+#define SR_PA_BUF_LT 0x05, 0xc0, 6
+#define RG_PHY_RSSI (0x06)
+#define SR_RSSI 0x06, 0x1f, 0
+#define SR_RND_VALUE 0x06, 0x60, 5
+#define SR_RX_CRC_VALID 0x06, 0x80, 7
+#define RG_PHY_ED_LEVEL (0x07)
+#define SR_ED_LEVEL 0x07, 0xff, 0
+#define RG_PHY_CC_CCA (0x08)
+#define SR_CHANNEL 0x08, 0x1f, 0
+#define SR_CCA_MODE 0x08, 0x60, 5
+#define SR_CCA_REQUEST 0x08, 0x80, 7
+#define RG_CCA_THRES (0x09)
+#define SR_CCA_ED_THRES 0x09, 0x0f, 0
+#define SR_RESERVED_09_1 0x09, 0xf0, 4
+#define RG_RX_CTRL (0x0a)
+#define SR_PDT_THRES 0x0a, 0x0f, 0
+#define SR_RESERVED_0a_1 0x0a, 0xf0, 4
+#define RG_SFD_VALUE (0x0b)
+#define SR_SFD_VALUE 0x0b, 0xff, 0
+#define RG_TRX_CTRL_2 (0x0c)
+#define SR_OQPSK_DATA_RATE 0x0c, 0x03, 0
+#define SR_SUB_MODE 0x0c, 0x04, 2
+#define SR_BPSK_QPSK 0x0c, 0x08, 3
+#define SR_OQPSK_SUB1_RC_EN 0x0c, 0x10, 4
+#define SR_RESERVED_0c_5 0x0c, 0x60, 5
+#define SR_RX_SAFE_MODE 0x0c, 0x80, 7
+#define RG_ANT_DIV (0x0d)
+#define SR_ANT_CTRL 0x0d, 0x03, 0
+#define SR_ANT_EXT_SW_EN 0x0d, 0x04, 2
+#define SR_ANT_DIV_EN 0x0d, 0x08, 3
+#define SR_RESERVED_0d_2 0x0d, 0x70, 4
+#define SR_ANT_SEL 0x0d, 0x80, 7
+#define RG_IRQ_MASK (0x0e)
+#define SR_IRQ_MASK 0x0e, 0xff, 0
+#define RG_IRQ_STATUS (0x0f)
+#define SR_IRQ_0_PLL_LOCK 0x0f, 0x01, 0
+#define SR_IRQ_1_PLL_UNLOCK 0x0f, 0x02, 1
+#define SR_IRQ_2_RX_START 0x0f, 0x04, 2
+#define SR_IRQ_3_TRX_END 0x0f, 0x08, 3
+#define SR_IRQ_4_CCA_ED_DONE 0x0f, 0x10, 4
+#define SR_IRQ_5_AMI 0x0f, 0x20, 5
+#define SR_IRQ_6_TRX_UR 0x0f, 0x40, 6
+#define SR_IRQ_7_BAT_LOW 0x0f, 0x80, 7
+#define RG_VREG_CTRL (0x10)
+#define SR_RESERVED_10_6 0x10, 0x03, 0
+#define SR_DVDD_OK 0x10, 0x04, 2
+#define SR_DVREG_EXT 0x10, 0x08, 3
+#define SR_RESERVED_10_3 0x10, 0x30, 4
+#define SR_AVDD_OK 0x10, 0x40, 6
+#define SR_AVREG_EXT 0x10, 0x80, 7
+#define RG_BATMON (0x11)
+#define SR_BATMON_VTH 0x11, 0x0f, 0
+#define SR_BATMON_HR 0x11, 0x10, 4
+#define SR_BATMON_OK 0x11, 0x20, 5
+#define SR_RESERVED_11_1 0x11, 0xc0, 6
+#define RG_XOSC_CTRL (0x12)
+#define SR_XTAL_TRIM 0x12, 0x0f, 0
+#define SR_XTAL_MODE 0x12, 0xf0, 4
+#define RG_RX_SYN (0x15)
+#define SR_RX_PDT_LEVEL 0x15, 0x0f, 0
+#define SR_RESERVED_15_2 0x15, 0x70, 4
+#define SR_RX_PDT_DIS 0x15, 0x80, 7
+#define RG_XAH_CTRL_1 (0x17)
+#define SR_RESERVED_17_8 0x17, 0x01, 0
+#define SR_AACK_PROM_MODE 0x17, 0x02, 1
+#define SR_AACK_ACK_TIME 0x17, 0x04, 2
+#define SR_RESERVED_17_5 0x17, 0x08, 3
+#define SR_AACK_UPLD_RES_FT 0x17, 0x10, 4
+#define SR_AACK_FLTR_RES_FT 0x17, 0x20, 5
+#define SR_CSMA_LBT_MODE 0x17, 0x40, 6
+#define SR_RESERVED_17_1 0x17, 0x80, 7
+#define RG_FTN_CTRL (0x18)
+#define SR_RESERVED_18_2 0x18, 0x7f, 0
+#define SR_FTN_START 0x18, 0x80, 7
+#define RG_PLL_CF (0x1a)
+#define SR_RESERVED_1a_2 0x1a, 0x7f, 0
+#define SR_PLL_CF_START 0x1a, 0x80, 7
+#define RG_PLL_DCU (0x1b)
+#define SR_RESERVED_1b_3 0x1b, 0x3f, 0
+#define SR_RESERVED_1b_2 0x1b, 0x40, 6
+#define SR_PLL_DCU_START 0x1b, 0x80, 7
+#define RG_PART_NUM (0x1c)
+#define SR_PART_NUM 0x1c, 0xff, 0
+#define RG_VERSION_NUM (0x1d)
+#define SR_VERSION_NUM 0x1d, 0xff, 0
+#define RG_MAN_ID_0 (0x1e)
+#define SR_MAN_ID_0 0x1e, 0xff, 0
+#define RG_MAN_ID_1 (0x1f)
+#define SR_MAN_ID_1 0x1f, 0xff, 0
+#define RG_SHORT_ADDR_0 (0x20)
+#define SR_SHORT_ADDR_0 0x20, 0xff, 0
+#define RG_SHORT_ADDR_1 (0x21)
+#define SR_SHORT_ADDR_1 0x21, 0xff, 0
+#define RG_PAN_ID_0 (0x22)
+#define SR_PAN_ID_0 0x22, 0xff, 0
+#define RG_PAN_ID_1 (0x23)
+#define SR_PAN_ID_1 0x23, 0xff, 0
+#define RG_IEEE_ADDR_0 (0x24)
+#define SR_IEEE_ADDR_0 0x24, 0xff, 0
+#define RG_IEEE_ADDR_1 (0x25)
+#define SR_IEEE_ADDR_1 0x25, 0xff, 0
+#define RG_IEEE_ADDR_2 (0x26)
+#define SR_IEEE_ADDR_2 0x26, 0xff, 0
+#define RG_IEEE_ADDR_3 (0x27)
+#define SR_IEEE_ADDR_3 0x27, 0xff, 0
+#define RG_IEEE_ADDR_4 (0x28)
+#define SR_IEEE_ADDR_4 0x28, 0xff, 0
+#define RG_IEEE_ADDR_5 (0x29)
+#define SR_IEEE_ADDR_5 0x29, 0xff, 0
+#define RG_IEEE_ADDR_6 (0x2a)
+#define SR_IEEE_ADDR_6 0x2a, 0xff, 0
+#define RG_IEEE_ADDR_7 (0x2b)
+#define SR_IEEE_ADDR_7 0x2b, 0xff, 0
+#define RG_XAH_CTRL_0 (0x2c)
+#define SR_SLOTTED_OPERATION 0x2c, 0x01, 0
+#define SR_MAX_CSMA_RETRIES 0x2c, 0x0e, 1
+#define SR_MAX_FRAME_RETRIES 0x2c, 0xf0, 4
+#define RG_CSMA_SEED_0 (0x2d)
+#define SR_CSMA_SEED_0 0x2d, 0xff, 0
+#define RG_CSMA_SEED_1 (0x2e)
+#define SR_CSMA_SEED_1 0x2e, 0x07, 0
+#define SR_AACK_I_AM_COORD 0x2e, 0x08, 3
+#define SR_AACK_DIS_ACK 0x2e, 0x10, 4
+#define SR_AACK_SET_PD 0x2e, 0x20, 5
+#define SR_AACK_FVN_MODE 0x2e, 0xc0, 6
+#define RG_CSMA_BE (0x2f)
+#define SR_MIN_BE 0x2f, 0x0f, 0
+#define SR_MAX_BE 0x2f, 0xf0, 4
#define CMD_REG 0x80
#define CMD_REG_MASK 0x3f
@@ -292,6 +294,8 @@ struct at86rf230_local {
#define STATE_BUSY_RX_AACK_NOCLK 0x1E
#define STATE_TRANSITION_IN_PROGRESS 0x1F
+#define TRX_STATE_MASK (0x1F)
+
#define AT86RF2XX_NUMREGS 0x3F
static void
@@ -336,6 +340,14 @@ at86rf230_write_subreg(struct at86rf230_local *lp,
return regmap_update_bits(lp->regmap, addr, mask, data << shift);
}
+static inline void
+at86rf230_slp_tr_rising_edge(struct at86rf230_local *lp)
+{
+ gpio_set_value(lp->slp_tr, 1);
+ udelay(1);
+ gpio_set_value(lp->slp_tr, 0);
+}
+
static bool
at86rf230_reg_writeable(struct device *dev, unsigned int reg)
{
@@ -509,7 +521,7 @@ at86rf230_async_state_assert(void *context)
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
const u8 *buf = ctx->buf;
- const u8 trx_state = buf[1] & 0x1f;
+ const u8 trx_state = buf[1] & TRX_STATE_MASK;
/* Assert state change */
if (trx_state != ctx->to_state) {
@@ -609,11 +621,17 @@ at86rf230_async_state_delay(void *context)
switch (ctx->to_state) {
case STATE_RX_AACK_ON:
tim = ktime_set(0, c->t_off_to_aack * NSEC_PER_USEC);
+ /* state change from TRX_OFF to RX_AACK_ON to do a
+ * calibration, we need to reset the timeout for the
+ * next one.
+ */
+ lp->cal_timeout = jiffies + AT86RF2XX_CAL_LOOP_TIMEOUT;
goto change;
+ case STATE_TX_ARET_ON:
case STATE_TX_ON:
tim = ktime_set(0, c->t_off_to_tx_on * NSEC_PER_USEC);
- /* state change from TRX_OFF to TX_ON to do a
- * calibration, we need to reset the timeout for the
+ /* state change from TRX_OFF to TX_ON or ARET_ON to do
+ * a calibration, we need to reset the timeout for the
* next one.
*/
lp->cal_timeout = jiffies + AT86RF2XX_CAL_LOOP_TIMEOUT;
@@ -667,7 +685,7 @@ at86rf230_async_state_change_start(void *context)
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
u8 *buf = ctx->buf;
- const u8 trx_state = buf[1] & 0x1f;
+ const u8 trx_state = buf[1] & TRX_STATE_MASK;
int rc;
/* Check for "possible" STATE_TRANSITION_IN_PROGRESS */
@@ -773,16 +791,6 @@ at86rf230_tx_on(void *context)
}
static void
-at86rf230_tx_trac_error(void *context)
-{
- struct at86rf230_state_change *ctx = context;
- struct at86rf230_local *lp = ctx->lp;
-
- at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
- at86rf230_tx_on, true);
-}
-
-static void
at86rf230_tx_trac_check(void *context)
{
struct at86rf230_state_change *ctx = context;
@@ -791,12 +799,12 @@ at86rf230_tx_trac_check(void *context)
const u8 trac = (buf[1] & 0xe0) >> 5;
/* If trac status is different than zero we need to do a state change
- * to STATE_FORCE_TRX_OFF then STATE_TX_ON to recover the transceiver
- * state to TX_ON.
+ * to STATE_FORCE_TRX_OFF then STATE_RX_AACK_ON to recover the
+ * transceiver.
*/
if (trac)
at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
- at86rf230_tx_trac_error, true);
+ at86rf230_tx_on, true);
else
at86rf230_tx_on(context);
}
@@ -941,13 +949,18 @@ at86rf230_write_frame_complete(void *context)
u8 *buf = ctx->buf;
int rc;
- buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
- buf[1] = STATE_BUSY_TX;
ctx->trx.len = 2;
- ctx->msg.complete = NULL;
- rc = spi_async(lp->spi, &ctx->msg);
- if (rc)
- at86rf230_async_error(lp, ctx, rc);
+
+ if (gpio_is_valid(lp->slp_tr)) {
+ at86rf230_slp_tr_rising_edge(lp);
+ } else {
+ buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
+ buf[1] = STATE_BUSY_TX;
+ ctx->msg.complete = NULL;
+ rc = spi_async(lp->spi, &ctx->msg);
+ if (rc)
+ at86rf230_async_error(lp, ctx, rc);
+ }
}
static void
@@ -993,12 +1006,21 @@ at86rf230_xmit_start(void *context)
* are in STATE_TX_ON. The pfad differs here, so we change
* the complete handler.
*/
- if (lp->tx_aret)
- at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
- at86rf230_xmit_tx_on, false);
- else
+ if (lp->tx_aret) {
+ if (lp->is_tx_from_off) {
+ lp->is_tx_from_off = false;
+ at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
+ at86rf230_xmit_tx_on,
+ false);
+ } else {
+ at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
+ at86rf230_xmit_tx_on,
+ false);
+ }
+ } else {
at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
at86rf230_write_frame, false);
+ }
}
static int
@@ -1017,11 +1039,13 @@ at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
* to TX_ON, the lp->cal_timeout should be reinit by state_delay
* function then to start in the next 5 minutes.
*/
- if (time_is_before_jiffies(lp->cal_timeout))
+ if (time_is_before_jiffies(lp->cal_timeout)) {
+ lp->is_tx_from_off = true;
at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
at86rf230_xmit_start, false);
- else
+ } else {
at86rf230_xmit_start(ctx);
+ }
return 0;
}
@@ -1037,9 +1061,6 @@ at86rf230_ed(struct ieee802154_hw *hw, u8 *level)
static int
at86rf230_start(struct ieee802154_hw *hw)
{
- struct at86rf230_local *lp = hw->priv;
-
- lp->cal_timeout = jiffies + AT86RF2XX_CAL_LOOP_TIMEOUT;
return at86rf230_sync_state_change(hw->priv, STATE_RX_AACK_ON);
}
@@ -1673,6 +1694,7 @@ static int at86rf230_probe(struct spi_device *spi)
lp = hw->priv;
lp->hw = hw;
lp->spi = spi;
+ lp->slp_tr = slp_tr;
hw->parent = &spi->dev;
hw->vif_data_size = sizeof(*lp);
ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index b227a13f6473..9f59f17dc317 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -599,10 +599,18 @@ static int macvlan_open(struct net_device *dev)
goto del_unicast;
}
+ if (dev->flags & IFF_PROMISC) {
+ err = dev_set_promiscuity(lowerdev, 1);
+ if (err < 0)
+ goto clear_multi;
+ }
+
hash_add:
macvlan_hash_add(vlan);
return 0;
+clear_multi:
+ dev_set_allmulti(lowerdev, -1);
del_unicast:
dev_uc_del(lowerdev, dev->dev_addr);
out:
@@ -638,6 +646,9 @@ static int macvlan_stop(struct net_device *dev)
if (dev->flags & IFF_ALLMULTI)
dev_set_allmulti(lowerdev, -1);
+ if (dev->flags & IFF_PROMISC)
+ dev_set_promiscuity(lowerdev, -1);
+
dev_uc_del(lowerdev, dev->dev_addr);
hash_del:
@@ -696,6 +707,10 @@ static void macvlan_change_rx_flags(struct net_device *dev, int change)
if (dev->flags & IFF_UP) {
if (change & IFF_ALLMULTI)
dev_set_allmulti(lowerdev, dev->flags & IFF_ALLMULTI ? 1 : -1);
+ if (change & IFF_PROMISC)
+ dev_set_promiscuity(lowerdev,
+ dev->flags & IFF_PROMISC ? 1 : -1);
+
}
}
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 8fadaa14b9f0..70641d2c0429 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -27,6 +27,7 @@ config AMD_PHY
config AMD_XGBE_PHY
tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs"
depends on (OF || ACPI) && HAS_IOMEM
+ depends on ARM64 || COMPILE_TEST
---help---
Currently supports the AMD 10GbE PHY
diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c
index fb276f64cd64..34a75cba3b73 100644
--- a/drivers/net/phy/amd-xgbe-phy.c
+++ b/drivers/net/phy/amd-xgbe-phy.c
@@ -755,6 +755,45 @@ static int amd_xgbe_phy_set_mode(struct phy_device *phydev,
return ret;
}
+static bool amd_xgbe_phy_use_xgmii_mode(struct phy_device *phydev)
+{
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+ if (phydev->advertising & ADVERTISED_10000baseKR_Full)
+ return true;
+ } else {
+ if (phydev->speed == SPEED_10000)
+ return true;
+ }
+
+ return false;
+}
+
+static bool amd_xgbe_phy_use_gmii_2500_mode(struct phy_device *phydev)
+{
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+ if (phydev->advertising & ADVERTISED_2500baseX_Full)
+ return true;
+ } else {
+ if (phydev->speed == SPEED_2500)
+ return true;
+ }
+
+ return false;
+}
+
+static bool amd_xgbe_phy_use_gmii_mode(struct phy_device *phydev)
+{
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+ if (phydev->advertising & ADVERTISED_1000baseKX_Full)
+ return true;
+ } else {
+ if (phydev->speed == SPEED_1000)
+ return true;
+ }
+
+ return false;
+}
+
static int amd_xgbe_phy_set_an(struct phy_device *phydev, bool enable,
bool restart)
{
@@ -1235,11 +1274,11 @@ static int amd_xgbe_phy_config_init(struct phy_device *phydev)
/* Set initial mode - call the mode setting routines
* directly to insure we are properly configured
*/
- if (phydev->advertising & SUPPORTED_10000baseKR_Full)
+ if (amd_xgbe_phy_use_xgmii_mode(phydev))
ret = amd_xgbe_phy_xgmii_mode(phydev);
- else if (phydev->advertising & SUPPORTED_1000baseKX_Full)
+ else if (amd_xgbe_phy_use_gmii_mode(phydev))
ret = amd_xgbe_phy_gmii_mode(phydev);
- else if (phydev->advertising & SUPPORTED_2500baseX_Full)
+ else if (amd_xgbe_phy_use_gmii_2500_mode(phydev))
ret = amd_xgbe_phy_gmii_2500_mode(phydev);
else
ret = -EINVAL;
diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 64c74c6a4828..b5dc59de094e 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -404,7 +404,7 @@ static struct phy_driver bcm7xxx_driver[] = {
.name = "Broadcom BCM7425",
.features = PHY_GBIT_FEATURES |
SUPPORTED_Pause | SUPPORTED_Asym_Pause,
- .flags = 0,
+ .flags = PHY_IS_INTERNAL,
.config_init = bcm7xxx_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 496e02f961d3..00cb41e71312 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -47,7 +47,7 @@
#define PSF_TX 0x1000
#define EXT_EVENT 1
#define CAL_EVENT 7
-#define CAL_TRIGGER 7
+#define CAL_TRIGGER 1
#define DP83640_N_PINS 12
#define MII_DP83640_MICR 0x11
@@ -496,7 +496,9 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
else
evnt |= EVNT_RISE;
}
+ mutex_lock(&clock->extreg_lock);
ext_write(0, phydev, PAGE5, PTP_EVNT, evnt);
+ mutex_unlock(&clock->extreg_lock);
return 0;
case PTP_CLK_REQ_PEROUT:
@@ -532,6 +534,8 @@ static u8 status_frame_src[6] = { 0x08, 0x00, 0x17, 0x0B, 0x6B, 0x0F };
static void enable_status_frames(struct phy_device *phydev, bool on)
{
+ struct dp83640_private *dp83640 = phydev->priv;
+ struct dp83640_clock *clock = dp83640->clock;
u16 cfg0 = 0, ver;
if (on)
@@ -539,9 +543,13 @@ static void enable_status_frames(struct phy_device *phydev, bool on)
ver = (PSF_PTPVER & VERSIONPTP_MASK) << VERSIONPTP_SHIFT;
+ mutex_lock(&clock->extreg_lock);
+
ext_write(0, phydev, PAGE5, PSF_CFG0, cfg0);
ext_write(0, phydev, PAGE6, PSF_CFG1, ver);
+ mutex_unlock(&clock->extreg_lock);
+
if (!phydev->attached_dev) {
pr_warn("expected to find an attached netdevice\n");
return;
@@ -838,7 +846,7 @@ static void decode_rxts(struct dp83640_private *dp83640,
list_del_init(&rxts->list);
phy2rxts(phy_rxts, rxts);
- spin_lock_irqsave(&dp83640->rx_queue.lock, flags);
+ spin_lock(&dp83640->rx_queue.lock);
skb_queue_walk(&dp83640->rx_queue, skb) {
struct dp83640_skb_info *skb_info;
@@ -853,7 +861,7 @@ static void decode_rxts(struct dp83640_private *dp83640,
break;
}
}
- spin_unlock_irqrestore(&dp83640->rx_queue.lock, flags);
+ spin_unlock(&dp83640->rx_queue.lock);
if (!shhwtstamps)
list_add_tail(&rxts->list, &dp83640->rxts);
@@ -1173,11 +1181,18 @@ static int dp83640_config_init(struct phy_device *phydev)
if (clock->chosen && !list_empty(&clock->phylist))
recalibrate(clock);
- else
+ else {
+ mutex_lock(&clock->extreg_lock);
enable_broadcast(phydev, clock->page, 1);
+ mutex_unlock(&clock->extreg_lock);
+ }
enable_status_frames(phydev, true);
+
+ mutex_lock(&clock->extreg_lock);
ext_write(0, phydev, PAGE4, PTP_CTL, PTP_ENABLE);
+ mutex_unlock(&clock->extreg_lock);
+
return 0;
}
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index c9cb486c753d..53d18150f4e2 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -168,7 +168,10 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
if (!new_bus->irq[i])
new_bus->irq[i] = PHY_POLL;
- snprintf(new_bus->id, MII_BUS_ID_SIZE, "gpio-%x", bus_id);
+ if (bus_id != -1)
+ snprintf(new_bus->id, MII_BUS_ID_SIZE, "gpio-%x", bus_id);
+ else
+ strncpy(new_bus->id, "gpio", MII_BUS_ID_SIZE);
if (devm_gpio_request(dev, bitbang->mdc, "mdc"))
goto out_free_bus;
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index 66edd99bc302..7ddb1ab70891 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -35,7 +35,8 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
for (n = 0; n < s->gpios->ndescs; n++)
values[n] = (desired_child >> n) & 1;
- gpiod_set_array_cansleep(s->gpios->ndescs, s->gpios->desc, values);
+ gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
+ values);
return 0;
}
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 1190fd8f0088..ebdc357c5131 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -548,7 +548,8 @@ static int kszphy_probe(struct phy_device *phydev)
}
clk = devm_clk_get(&phydev->dev, "rmii-ref");
- if (!IS_ERR(clk)) {
+ /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */
+ if (!IS_ERR_OR_NULL(clk)) {
unsigned long rate = clk_get_rate(clk);
bool rmii_ref_clk_sel_25_mhz;
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 52cd8db2c57d..47cd578052fc 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -742,6 +742,9 @@ EXPORT_SYMBOL(phy_stop);
*/
void phy_start(struct phy_device *phydev)
{
+ bool do_resume = false;
+ int err = 0;
+
mutex_lock(&phydev->lock);
switch (phydev->state) {
@@ -752,11 +755,22 @@ void phy_start(struct phy_device *phydev)
phydev->state = PHY_UP;
break;
case PHY_HALTED:
+ /* make sure interrupts are re-enabled for the PHY */
+ err = phy_enable_interrupts(phydev);
+ if (err < 0)
+ break;
+
phydev->state = PHY_RESUMING;
+ do_resume = true;
+ break;
default:
break;
}
mutex_unlock(&phydev->lock);
+
+ /* if phy was suspended, bring the physical link up again */
+ if (do_resume)
+ phy_resume(phydev);
}
EXPORT_SYMBOL(phy_start);
@@ -769,7 +783,7 @@ void phy_state_machine(struct work_struct *work)
struct delayed_work *dwork = to_delayed_work(work);
struct phy_device *phydev =
container_of(dwork, struct phy_device, state_queue);
- bool needs_aneg = false, do_suspend = false, do_resume = false;
+ bool needs_aneg = false, do_suspend = false;
int err = 0;
mutex_lock(&phydev->lock);
@@ -888,14 +902,6 @@ void phy_state_machine(struct work_struct *work)
}
break;
case PHY_RESUMING:
- err = phy_clear_interrupt(phydev);
- if (err)
- break;
-
- err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
- if (err)
- break;
-
if (AUTONEG_ENABLE == phydev->autoneg) {
err = phy_aneg_done(phydev);
if (err < 0)
@@ -933,7 +939,6 @@ void phy_state_machine(struct work_struct *work)
}
phydev->adjust_link(phydev->attached_dev);
}
- do_resume = true;
break;
}
@@ -943,8 +948,6 @@ void phy_state_machine(struct work_struct *work)
err = phy_start_aneg(phydev);
else if (do_suspend)
phy_suspend(phydev);
- else if (do_resume)
- phy_resume(phydev);
if (err < 0)
phy_error(phydev);
@@ -1053,13 +1056,14 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
{
/* According to 802.3az,the EEE is supported only in full duplex-mode.
* Also EEE feature is active when core is operating with MII, GMII
- * or RGMII. Internal PHYs are also allowed to proceed and should
- * return an error if they do not support EEE.
+ * or RGMII (all kinds). Internal PHYs are also allowed to proceed and
+ * should return an error if they do not support EEE.
*/
if ((phydev->duplex == DUPLEX_FULL) &&
((phydev->interface == PHY_INTERFACE_MODE_MII) ||
(phydev->interface == PHY_INTERFACE_MODE_GMII) ||
- (phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
+ (phydev->interface >= PHY_INTERFACE_MODE_RGMII &&
+ phydev->interface <= PHY_INTERFACE_MODE_RGMII_TXID) ||
phy_is_internal(phydev))) {
int eee_lp, eee_cap, eee_adv;
u32 lp, cap, adv;
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index aa1dd926623a..b62a5e3a1c65 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -465,6 +465,10 @@ static void pppoe_unbind_sock_work(struct work_struct *work)
struct sock *sk = sk_pppox(po);
lock_sock(sk);
+ if (po->pppoe_dev) {
+ dev_put(po->pppoe_dev);
+ po->pppoe_dev = NULL;
+ }
pppox_unbind_sock(sk);
release_sock(sk);
sock_put(sk);
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index c3e4da9e79ca..8067b8fbb0ee 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1182,7 +1182,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
* payload data instead.
*/
usbnet_set_skb_tx_stats(skb_out, n,
- ctx->tx_curr_frame_payload - skb_out->len);
+ (long)ctx->tx_curr_frame_payload - skb_out->len);
return skb_out;
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index ac4d03b328b1..aafa1a1898e4 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -4116,6 +4116,7 @@ static struct usb_device_id rtl8152_table[] = {
{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)},
{REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)},
{REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)},
+ {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x304f)},
{}
};
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 733f4feb2ef3..3c86b107275a 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1285,7 +1285,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
- int length;
+ unsigned int length;
struct urb *urb = NULL;
struct skb_data *entry;
struct driver_info *info = dev->driver_info;
@@ -1413,7 +1413,7 @@ not_drop:
}
} else
netif_dbg(dev, tx_queued, dev->net,
- "> tx, len %d, type 0x%x\n", length, skb->protocol);
+ "> tx, len %u, type 0x%x\n", length, skb->protocol);
#ifdef CONFIG_PM
deferred:
#endif
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 27a5f954f8e9..21a0fbf1ed94 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -2961,7 +2961,7 @@ static void __net_exit vxlan_exit_net(struct net *net)
* to the list by the previous loop.
*/
if (!net_eq(dev_net(vxlan->dev), net))
- unregister_netdevice_queue(dev, &list);
+ unregister_netdevice_queue(vxlan->dev, &list);
}
unregister_netdevice_many(&list);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 0acd079ba96b..3ad79bb4f2c2 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1103,28 +1103,14 @@ static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
struct sk_buff *skb;
struct ath_frame_info *fi;
struct ieee80211_tx_info *info;
- struct ieee80211_vif *vif;
struct ath_hw *ah = sc->sc_ah;
if (sc->tx99_state || !ah->tpc_enabled)
return MAX_RATE_POWER;
skb = bf->bf_mpdu;
- info = IEEE80211_SKB_CB(skb);
- vif = info->control.vif;
-
- if (!vif) {
- max_power = sc->cur_chan->cur_txpower;
- goto out;
- }
-
- if (vif->bss_conf.txpower_type != NL80211_TX_POWER_LIMITED) {
- max_power = min_t(u8, sc->cur_chan->cur_txpower,
- 2 * vif->bss_conf.txpower);
- goto out;
- }
-
fi = get_frame_info(skb);
+ info = IEEE80211_SKB_CB(skb);
if (!AR_SREV_9300_20_OR_LATER(ah)) {
int txpower = fi->tx_power;
@@ -1161,25 +1147,26 @@ static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
txpower -= 2;
txpower = max(txpower, 0);
- max_power = min_t(u8, ah->tx_power[rateidx],
- 2 * vif->bss_conf.txpower);
- max_power = min_t(u8, max_power, txpower);
+ max_power = min_t(u8, ah->tx_power[rateidx], txpower);
+
+ /* XXX: clamp minimum TX power at 1 for AR9160 since if
+ * max_power is set to 0, frames are transmitted at max
+ * TX power
+ */
+ if (!max_power && !AR_SREV_9280_20_OR_LATER(ah))
+ max_power = 1;
} else if (!bf->bf_state.bfs_paprd) {
if (rateidx < 8 && (info->flags & IEEE80211_TX_CTL_STBC))
max_power = min_t(u8, ah->tx_power_stbc[rateidx],
- 2 * vif->bss_conf.txpower);
+ fi->tx_power);
else
max_power = min_t(u8, ah->tx_power[rateidx],
- 2 * vif->bss_conf.txpower);
- max_power = min(max_power, fi->tx_power);
+ fi->tx_power);
} else {
max_power = ah->paprd_training_power;
}
-out:
- /* XXX: clamp minimum TX power at 1 for AR9160 since if max_power
- * is set to 0, frames are transmitted at max TX power
- */
- return (!max_power && !AR_SREV_9280_20_OR_LATER(ah)) ? 1 : max_power;
+
+ return max_power;
}
static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
@@ -2129,6 +2116,7 @@ static void setup_frame_info(struct ieee80211_hw *hw,
struct ath_node *an = NULL;
enum ath9k_key_type keytype;
bool short_preamble = false;
+ u8 txpower;
/*
* We check if Short Preamble is needed for the CTS rate by
@@ -2145,6 +2133,16 @@ static void setup_frame_info(struct ieee80211_hw *hw,
if (sta)
an = (struct ath_node *) sta->drv_priv;
+ if (tx_info->control.vif) {
+ struct ieee80211_vif *vif = tx_info->control.vif;
+
+ txpower = 2 * vif->bss_conf.txpower;
+ } else {
+ struct ath_softc *sc = hw->priv;
+
+ txpower = sc->cur_chan->cur_txpower;
+ }
+
memset(fi, 0, sizeof(*fi));
fi->txq = -1;
if (hw_key)
@@ -2155,7 +2153,7 @@ static void setup_frame_info(struct ieee80211_hw *hw,
fi->keyix = ATH9K_TXKEYIX_INVALID;
fi->keytype = keytype;
fi->framelen = framelen;
- fi->tx_power = MAX_RATE_POWER;
+ fi->tx_power = txpower;
if (!rate)
return;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
index 4ec9811f49c8..65efb1468988 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
@@ -511,11 +511,9 @@ static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx,
msgbuf->rx_pktids,
msgbuf->ioctl_resp_pktid);
if (msgbuf->ioctl_resp_ret_len != 0) {
- if (!skb) {
- brcmf_err("Invalid packet id idx recv'd %d\n",
- msgbuf->ioctl_resp_pktid);
+ if (!skb)
return -EBADF;
- }
+
memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ?
len : msgbuf->ioctl_resp_ret_len);
}
@@ -874,10 +872,8 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf)
flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS;
skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
msgbuf->tx_pktids, idx);
- if (!skb) {
- brcmf_err("Invalid packet id idx recv'd %d\n", idx);
+ if (!skb)
return;
- }
set_bit(flowid, msgbuf->txstatus_done_map);
commonring = msgbuf->flowrings[flowid];
@@ -1156,6 +1152,8 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf)
skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev,
msgbuf->rx_pktids, idx);
+ if (!skb)
+ return;
if (data_offset)
skb_pull(skb, data_offset);
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index ab019b45551b..f89f446e5c8a 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -21,6 +21,7 @@ config IWLWIFI
Intel 7260 Wi-Fi Adapter
Intel 3160 Wi-Fi Adapter
Intel 7265 Wi-Fi Adapter
+ Intel 3165 Wi-Fi Adapter
This driver uses the kernel's mac80211 subsystem.
diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c
index 36e786f0387b..74ad278116be 100644
--- a/drivers/net/wireless/iwlwifi/iwl-7000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-7000.c
@@ -70,15 +70,14 @@
/* Highest firmware API version supported */
#define IWL7260_UCODE_API_MAX 13
-#define IWL3160_UCODE_API_MAX 13
/* Oldest version we won't warn about */
#define IWL7260_UCODE_API_OK 12
-#define IWL3160_UCODE_API_OK 12
+#define IWL3165_UCODE_API_OK 13
/* Lowest firmware API version supported */
#define IWL7260_UCODE_API_MIN 10
-#define IWL3160_UCODE_API_MIN 10
+#define IWL3165_UCODE_API_MIN 13
/* NVM versions */
#define IWL7260_NVM_VERSION 0x0a1d
@@ -104,9 +103,6 @@
#define IWL3160_FW_PRE "iwlwifi-3160-"
#define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode"
-#define IWL3165_FW_PRE "iwlwifi-3165-"
-#define IWL3165_MODULE_FIRMWARE(api) IWL3165_FW_PRE __stringify(api) ".ucode"
-
#define IWL7265_FW_PRE "iwlwifi-7265-"
#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
@@ -248,8 +244,13 @@ static const struct iwl_ht_params iwl7265_ht_params = {
const struct iwl_cfg iwl3165_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 3165",
- .fw_name_pre = IWL3165_FW_PRE,
+ .fw_name_pre = IWL7265D_FW_PRE,
IWL_DEVICE_7000,
+ /* sparse doens't like the re-assignment but it is safe */
+#ifndef __CHECKER__
+ .ucode_api_ok = IWL3165_UCODE_API_OK,
+ .ucode_api_min = IWL3165_UCODE_API_MIN,
+#endif
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL3165_NVM_VERSION,
.nvm_calib_ver = IWL3165_TX_POWER_VERSION,
@@ -325,6 +326,5 @@ const struct iwl_cfg iwl7265d_n_cfg = {
MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
-MODULE_FIRMWARE(IWL3165_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
index 41ff85de7334..21302b6f2bfd 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
@@ -6,6 +6,7 @@
* GPL LICENSE SUMMARY
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
*
* 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
@@ -31,6 +32,7 @@
* BSD LICENSE
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -748,6 +750,9 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
return;
}
+ if (data->sku_cap_mimo_disabled)
+ rx_chains = 1;
+
ht_info->ht_supported = true;
ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40;
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
index 5234a0bf11e4..750c8c9ee70d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
@@ -6,6 +6,7 @@
* GPL LICENSE SUMMARY
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
*
* 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
@@ -31,6 +32,7 @@
* BSD LICENSE
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Mobile Communications GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -84,6 +86,7 @@ struct iwl_nvm_data {
bool sku_cap_11ac_enable;
bool sku_cap_amt_enable;
bool sku_cap_ipan_enable;
+ bool sku_cap_mimo_disabled;
u16 radio_cfg_type;
u8 radio_cfg_step;
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
index bfdf3faa6c47..62db2e5e45eb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
@@ -244,6 +244,7 @@ enum iwl_ucode_tlv_flag {
* longer than the passive one, which is essential for fragmented scan.
* @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source.
* IWL_UCODE_TLV_API_HDC_PHASE_0: ucode supports finer configuration of LTR
+ * @IWL_UCODE_TLV_API_TX_POWER_DEV: new API for tx power.
* @IWL_UCODE_TLV_API_BASIC_DWELL: use only basic dwell time in scan command,
* regardless of the band or the number of the probes. FW will calculate
* the actual dwell time.
@@ -260,6 +261,7 @@ enum iwl_ucode_tlv_api {
IWL_UCODE_TLV_API_FRAGMENTED_SCAN = BIT(8),
IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = BIT(9),
IWL_UCODE_TLV_API_HDC_PHASE_0 = BIT(10),
+ IWL_UCODE_TLV_API_TX_POWER_DEV = BIT(11),
IWL_UCODE_TLV_API_BASIC_DWELL = BIT(13),
IWL_UCODE_TLV_API_SCD_CFG = BIT(15),
IWL_UCODE_TLV_API_SINGLE_SCAN_EBS = BIT(16),
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
index 83903a5025c2..8e604a3931ca 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
@@ -6,7 +6,7 @@
* GPL LICENSE SUMMARY
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
*
* 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
@@ -32,7 +32,7 @@
* BSD LICENSE
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -116,10 +116,11 @@ enum family_8000_nvm_offsets {
/* 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),
- NVM_SKU_CAP_11AC_ENABLE = BIT(3),
+ NVM_SKU_CAP_BAND_24GHZ = BIT(0),
+ NVM_SKU_CAP_BAND_52GHZ = BIT(1),
+ NVM_SKU_CAP_11N_ENABLE = BIT(2),
+ NVM_SKU_CAP_11AC_ENABLE = BIT(3),
+ NVM_SKU_CAP_MIMO_DISABLE = BIT(5),
};
/*
@@ -368,6 +369,11 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
if (cfg->ht_params->ldpc)
vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
+ if (data->sku_cap_mimo_disabled) {
+ num_rx_ants = 1;
+ num_tx_ants = 1;
+ }
+
if (num_tx_ants > 1)
vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
else
@@ -465,7 +471,7 @@ static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
return le16_to_cpup(nvm_sw + RADIO_CFG);
- return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000));
+ return le32_to_cpup((__le32 *)(phy_sku + RADIO_CFG_FAMILY_8000));
}
@@ -527,6 +533,10 @@ static void iwl_set_hw_address_family_8000(struct device *dev,
const u8 *hw_addr;
if (mac_override) {
+ static const u8 reserved_mac[] = {
+ 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00
+ };
+
hw_addr = (const u8 *)(mac_override +
MAC_ADDRESS_OVERRIDE_FAMILY_8000);
@@ -538,7 +548,12 @@ static void iwl_set_hw_address_family_8000(struct device *dev,
data->hw_addr[4] = hw_addr[5];
data->hw_addr[5] = hw_addr[4];
- if (is_valid_ether_addr(data->hw_addr))
+ /*
+ * Force the use of the OTP MAC address in case of reserved MAC
+ * address in the NVM, or if address is given but invalid.
+ */
+ if (is_valid_ether_addr(data->hw_addr) &&
+ memcmp(reserved_mac, hw_addr, ETH_ALEN) != 0)
return;
IWL_ERR_DEV(dev,
@@ -610,6 +625,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
data->sku_cap_11n_enable = false;
data->sku_cap_11ac_enable = data->sku_cap_11n_enable &&
(sku & NVM_SKU_CAP_11AC_ENABLE);
+ data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE;
data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 6dfed1259260..56254a837214 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -6,7 +6,7 @@
* GPL LICENSE SUMMARY
*
* Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
*
* 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
@@ -32,7 +32,7 @@
* BSD LICENSE
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -421,8 +421,9 @@ struct iwl_trans_txq_scd_cfg {
*
* All the handlers MUST be implemented
*
- * @start_hw: starts the HW- from that point on, the HW can send interrupts
- * May sleep
+ * @start_hw: starts the HW. If low_power is true, the NIC needs to be taken
+ * out of a low power state. From that point on, the HW can send
+ * interrupts. May sleep.
* @op_mode_leave: Turn off the HW RF kill indication if on
* May sleep
* @start_fw: allocates and inits all the resources for the transport
@@ -432,10 +433,11 @@ struct iwl_trans_txq_scd_cfg {
* the SCD base address in SRAM, then provide it here, or 0 otherwise.
* May sleep
* @stop_device: stops the whole device (embedded CPU put to reset) and stops
- * the HW. From that point on, the HW will be in low power but will still
- * issue interrupt if the HW RF kill is triggered. This callback must do
- * the right thing and not crash even if start_hw() was called but not
- * start_fw(). May sleep
+ * the HW. If low_power is true, the NIC will be put in low power state.
+ * From that point on, the HW will be stopped but will still issue an
+ * interrupt if the HW RF kill switch is triggered.
+ * This callback must do the right thing and not crash even if %start_hw()
+ * was called but not &start_fw(). May sleep.
* @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.
@@ -491,14 +493,14 @@ struct iwl_trans_txq_scd_cfg {
*/
struct iwl_trans_ops {
- int (*start_hw)(struct iwl_trans *iwl_trans);
+ int (*start_hw)(struct iwl_trans *iwl_trans, bool low_power);
void (*op_mode_leave)(struct iwl_trans *iwl_trans);
int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw,
bool run_in_rfkill);
int (*update_sf)(struct iwl_trans *trans,
struct iwl_sf_region *st_fwrd_space);
void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
- void (*stop_device)(struct iwl_trans *trans);
+ void (*stop_device)(struct iwl_trans *trans, bool low_power);
void (*d3_suspend)(struct iwl_trans *trans, bool test);
int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status,
@@ -652,11 +654,16 @@ static inline void iwl_trans_configure(struct iwl_trans *trans,
trans->ops->configure(trans, trans_cfg);
}
-static inline int iwl_trans_start_hw(struct iwl_trans *trans)
+static inline int _iwl_trans_start_hw(struct iwl_trans *trans, bool low_power)
{
might_sleep();
- return trans->ops->start_hw(trans);
+ return trans->ops->start_hw(trans, low_power);
+}
+
+static inline int iwl_trans_start_hw(struct iwl_trans *trans)
+{
+ return trans->ops->start_hw(trans, true);
}
static inline void iwl_trans_op_mode_leave(struct iwl_trans *trans)
@@ -703,15 +710,21 @@ static inline int iwl_trans_update_sf(struct iwl_trans *trans,
return 0;
}
-static inline void iwl_trans_stop_device(struct iwl_trans *trans)
+static inline void _iwl_trans_stop_device(struct iwl_trans *trans,
+ bool low_power)
{
might_sleep();
- trans->ops->stop_device(trans);
+ trans->ops->stop_device(trans, low_power);
trans->state = IWL_TRANS_NO_FW;
}
+static inline void iwl_trans_stop_device(struct iwl_trans *trans)
+{
+ _iwl_trans_stop_device(trans, true);
+}
+
static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test)
{
might_sleep();
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
index d954591e0be5..6ac6de2af977 100644
--- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
+++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
@@ -776,7 +776,7 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
struct iwl_host_cmd cmd = {
.id = BT_CONFIG,
.len = { sizeof(*bt_cmd), },
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ .dataflags = { IWL_HCMD_DFL_DUP, },
.flags = CMD_ASYNC,
};
struct iwl_mvm_sta *mvmsta;
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
index a6c48c7b1e16..4310cf102d78 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -1726,7 +1726,7 @@ iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm,
results->matched_profiles = le32_to_cpu(query->matched_profiles);
memcpy(results->matches, query->matches, sizeof(results->matches));
-#ifdef CPTCFG_IWLWIFI_DEBUGFS
+#ifdef CONFIG_IWLWIFI_DEBUGFS
mvm->last_netdetect_scans = le32_to_cpu(query->n_scans_done);
#endif
@@ -1750,8 +1750,10 @@ static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm,
int i, j, n_matches, ret;
fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
- if (!IS_ERR_OR_NULL(fw_status))
+ if (!IS_ERR_OR_NULL(fw_status)) {
reasons = le32_to_cpu(fw_status->wakeup_reasons);
+ kfree(fw_status);
+ }
if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
wakeup.rfkill_release = true;
@@ -1868,15 +1870,15 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
/* get the BSS vif pointer again */
vif = iwl_mvm_get_bss_vif(mvm);
if (IS_ERR_OR_NULL(vif))
- goto out_unlock;
+ goto err;
ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test);
if (ret)
- goto out_unlock;
+ goto err;
if (d3_status != IWL_D3_STATUS_ALIVE) {
IWL_INFO(mvm, "Device was reset during suspend\n");
- goto out_unlock;
+ goto err;
}
/* query SRAM first in case we want event logging */
@@ -1902,7 +1904,8 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
goto out_iterate;
}
- out_unlock:
+err:
+ iwl_mvm_free_nd(mvm);
mutex_unlock(&mvm->mutex);
out_iterate:
@@ -1915,6 +1918,14 @@ out:
/* return 1 to reconfigure the device */
set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status);
+
+ /* We always return 1, which causes mac80211 to do a reconfig
+ * with IEEE80211_RECONFIG_TYPE_RESTART. This type of
+ * reconfig calls iwl_mvm_restart_complete(), where we unref
+ * the IWL_MVM_REF_UCODE_DOWN, so we need to take the
+ * reference here.
+ */
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
return 1;
}
@@ -2021,7 +2032,6 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
__iwl_mvm_resume(mvm, true);
rtnl_unlock();
iwl_abort_notification_waits(&mvm->notif_wait);
- iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
ieee80211_restart_hw(mvm->hw);
/* wait for restart and disconnect all interfaces */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
index 4fc0938b3fb6..b1baa33cc19b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -298,6 +298,40 @@ struct iwl_uapsd_misbehaving_ap_notif {
} __packed;
/**
+ * struct iwl_reduce_tx_power_cmd - TX power reduction command
+ * REDUCE_TX_POWER_CMD = 0x9f
+ * @flags: (reserved for future implementation)
+ * @mac_context_id: id of the mac ctx for which we are reducing TX power.
+ * @pwr_restriction: TX power restriction in dBms.
+ */
+struct iwl_reduce_tx_power_cmd {
+ u8 flags;
+ u8 mac_context_id;
+ __le16 pwr_restriction;
+} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */
+
+/**
+ * struct iwl_dev_tx_power_cmd - TX power reduction command
+ * REDUCE_TX_POWER_CMD = 0x9f
+ * @set_mode: 0 - MAC tx power, 1 - device tx power
+ * @mac_context_id: id of the mac ctx for which we are reducing TX power.
+ * @pwr_restriction: TX power restriction in 1/8 dBms.
+ * @dev_24: device TX power restriction in 1/8 dBms
+ * @dev_52_low: device TX power restriction upper band - low
+ * @dev_52_high: device TX power restriction upper band - high
+ */
+struct iwl_dev_tx_power_cmd {
+ __le32 set_mode;
+ __le32 mac_context_id;
+ __le16 pwr_restriction;
+ __le16 dev_24;
+ __le16 dev_52_low;
+ __le16 dev_52_high;
+} __packed; /* TX_REDUCED_POWER_API_S_VER_2 */
+
+#define IWL_DEV_MAX_TX_POWER 0x7FFF
+
+/**
* struct iwl_beacon_filter_cmd
* REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
* @id_and_color: MAC contex identifier
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
index 4f81dcf57a73..d6cced47d561 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
@@ -122,46 +122,6 @@ enum iwl_scan_complete_status {
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: array of 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[];
-} __packed; /* SCAN_COMPLETE_NTF_API_S_VER_2 */
-
/* scan offload */
#define IWL_SCAN_MAX_BLACKLIST_LEN 64
#define IWL_SCAN_SHORT_BLACKLIST_LEN 16
@@ -554,7 +514,7 @@ struct iwl_scan_req_unified_lmac {
} __packed;
/**
- * struct iwl_lmac_scan_results_notif - scan results for one channel -
+ * struct iwl_scan_results_notif - scan results for one channel -
* SCAN_RESULT_NTF_API_S_VER_3
* @channel: which channel the results are from
* @band: 0 for 5.2 GHz, 1 for 2.4 GHz
@@ -562,7 +522,7 @@ struct iwl_scan_req_unified_lmac {
* @num_probe_not_sent: # of request that weren't sent due to not enough time
* @duration: duration spent in channel, in usecs
*/
-struct iwl_lmac_scan_results_notif {
+struct iwl_scan_results_notif {
u8 channel;
u8 band;
u8 probe_status;
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index aab68cbae754..01b1da6ad359 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -281,19 +281,6 @@ struct iwl_tx_ant_cfg_cmd {
__le32 valid;
} __packed;
-/**
- * struct iwl_reduce_tx_power_cmd - TX power reduction command
- * REDUCE_TX_POWER_CMD = 0x9f
- * @flags: (reserved for future implementation)
- * @mac_context_id: id of the mac ctx for which we are reducing TX power.
- * @pwr_restriction: TX power restriction in dBms.
- */
-struct iwl_reduce_tx_power_cmd {
- u8 flags;
- u8 mac_context_id;
- __le16 pwr_restriction;
-} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */
-
/*
* Calibration control struct.
* Sent as part of the phy configuration command.
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index bc5eac4960e1..df869633f4dd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -6,7 +6,7 @@
* GPL LICENSE SUMMARY
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
*
* 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
@@ -32,7 +32,7 @@
* BSD LICENSE
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -322,7 +322,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
lockdep_assert_held(&mvm->mutex);
- if (WARN_ON_ONCE(mvm->init_ucode_complete || mvm->calibrating))
+ if (WARN_ON_ONCE(mvm->calibrating))
return 0;
iwl_init_notification_wait(&mvm->notif_wait,
@@ -396,8 +396,6 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
*/
ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait,
MVM_UCODE_CALIB_TIMEOUT);
- if (!ret)
- mvm->init_ucode_complete = true;
if (ret && iwl_mvm_is_radio_killed(mvm)) {
IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n");
@@ -494,15 +492,6 @@ int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
mvm->fw_dump_desc = desc;
- /* stop recording */
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
- iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
- } else {
- iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0);
- /* wait before we collect the data till the DBGC stop */
- udelay(100);
- }
-
queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
return 0;
@@ -658,25 +647,24 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
* module loading, load init ucode now
* (for example, if we were in RFKILL)
*/
- if (!mvm->init_ucode_complete) {
- 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);
- /* this can't happen */
- if (WARN_ON(ret > 0))
- ret = -ERFKILL;
- goto error;
- }
- if (!iwlmvm_mod_params.init_dbg) {
- /*
- * should stop and start HW since that INIT
- * image just loaded
- */
- iwl_trans_stop_device(mvm->trans);
- ret = iwl_trans_start_hw(mvm->trans);
- if (ret)
- return ret;
- }
+ 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);
+ /* this can't happen */
+ if (WARN_ON(ret > 0))
+ ret = -ERFKILL;
+ goto error;
+ }
+ if (!iwlmvm_mod_params.init_dbg) {
+ /*
+ * Stop and start the transport without entering low power
+ * mode. This will save the state of other components on the
+ * device that are triggered by the INIT firwmare (MFUART).
+ */
+ _iwl_trans_stop_device(mvm->trans, false);
+ _iwl_trans_start_hw(mvm->trans, false);
+ if (ret)
+ return ret;
}
if (iwlmvm_mod_params.init_dbg)
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 84555170b6f7..dda9f7b5f342 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -1322,7 +1322,7 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
iwl_mvm_d0i3_enable_tx(mvm, NULL);
- ret = iwl_mvm_update_quotas(mvm, false, NULL);
+ ret = iwl_mvm_update_quotas(mvm, true, NULL);
if (ret)
IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
ret);
@@ -1471,8 +1471,8 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
return NULL;
}
-static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- s8 tx_power)
+static int iwl_mvm_set_tx_power_old(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif, s8 tx_power)
{
/* FW is in charge of regulatory enforcement */
struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
@@ -1485,6 +1485,26 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
&reduce_txpwr_cmd);
}
+static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ s16 tx_power)
+{
+ struct iwl_dev_tx_power_cmd cmd = {
+ .set_mode = 0,
+ .mac_context_id =
+ cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id),
+ .pwr_restriction = cpu_to_le16(8 * tx_power),
+ };
+
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_TX_POWER_DEV))
+ return iwl_mvm_set_tx_power_old(mvm, vif, tx_power);
+
+ if (tx_power == IWL_DEFAULT_MAX_TX_POWER)
+ cmd.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER);
+
+ return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0,
+ sizeof(cmd), &cmd);
+}
+
static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
@@ -3975,9 +3995,6 @@ static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
return;
- if (event->u.mlme.status == MLME_SUCCESS)
- return;
-
trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
trig_mlme = (void *)trig->data;
if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index d5522a161242..cf70f681d1ac 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -603,7 +603,6 @@ struct iwl_mvm {
enum iwl_ucode_type cur_ucode;
bool ucode_loaded;
- bool init_ucode_complete;
bool calibrating;
u32 error_event_table;
u32 log_event_table;
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index a08b03d58d4b..2ea01238754e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -865,6 +865,16 @@ static void iwl_mvm_fw_error_dump_wk(struct work_struct *work)
return;
mutex_lock(&mvm->mutex);
+
+ /* stop recording */
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+ iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
+ } else {
+ iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0);
+ /* wait before we collect the data till the DBGC stop */
+ udelay(100);
+ }
+
iwl_mvm_fw_error_dump(mvm);
/* start recording again if the firmware is not crashed */
@@ -1253,11 +1263,13 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
ieee80211_iterate_active_interfaces(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_d0i3_disconnect_iter, mvm);
-
- iwl_free_resp(&get_status_cmd);
out:
iwl_mvm_d0i3_enable_tx(mvm, qos_seq);
+ /* qos_seq might point inside resp_pkt, so free it only now */
+ if (get_status_cmd.resp_pkt)
+ iwl_free_resp(&get_status_cmd);
+
/* the FW might have updated the regdomain */
iwl_mvm_update_changed_regdom(mvm);
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
index f9928f2c125f..33cd68ae7bf9 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.c
@@ -180,6 +180,9 @@ static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
if (iwl_mvm_vif_low_latency(mvmvif) && mvmsta->vif->p2p)
return false;
+ if (mvm->nvm_data->sku_cap_mimo_disabled)
+ return false;
+
return true;
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
index 78ec7db64ba5..d6314ddf57b5 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -478,6 +478,11 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
if (vif->type != NL80211_IFTYPE_STATION)
return;
+ if (sig == 0) {
+ IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n");
+ return;
+ }
+
mvmvif->bf_data.ave_beacon_signal = sig;
/* BT Coex */
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
index 74e1c86289dc..1075a213bd6a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/mvm/scan.c
@@ -319,7 +319,7 @@ int iwl_mvm_rx_scan_offload_iter_complete_notif(struct iwl_mvm *mvm,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_scan_complete_notif *notif = (void *)pkt->data;
+ struct iwl_lmac_scan_complete_notif *notif = (void *)pkt->data;
IWL_DEBUG_SCAN(mvm,
"Scan offload iteration complete: status=0x%x scanned channels=%d\n",
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 01996c9d98a7..376b84e54ad7 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -1,7 +1,7 @@
/******************************************************************************
*
- * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
*
* Portions of this file are derived from the ipw3945 project, as well
* as portions of the ieee80211 subsystem header files.
@@ -320,7 +320,7 @@ struct iwl_trans_pcie {
/*protect hw register */
spinlock_t reg_lock;
- bool cmd_in_flight;
+ bool cmd_hold_nic_awake;
bool ref_cmd_in_flight;
/* protect ref counter */
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 2de8fbfe4edf..dc179094e6a0 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -5,8 +5,8 @@
*
* GPL LICENSE SUMMARY
*
- * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
*
* 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
@@ -31,8 +31,8 @@
*
* BSD LICENSE
*
- * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -104,7 +104,7 @@ static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)
static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct page *page;
+ struct page *page = NULL;
dma_addr_t phys;
u32 size;
u8 power;
@@ -131,6 +131,7 @@ static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans)
DMA_FROM_DEVICE);
if (dma_mapping_error(trans->dev, phys)) {
__free_pages(page, order);
+ page = NULL;
continue;
}
IWL_INFO(trans,
@@ -1020,7 +1021,7 @@ static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
iwl_pcie_tx_start(trans, scd_addr);
}
-static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
+static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
bool hw_rfkill, was_hw_rfkill;
@@ -1048,9 +1049,11 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
iwl_pcie_rx_stop(trans);
/* Power-down device's busmaster DMA clocks */
- iwl_write_prph(trans, APMG_CLK_DIS_REG,
- APMG_CLK_VAL_DMA_CLK_RQT);
- udelay(5);
+ if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ iwl_write_prph(trans, APMG_CLK_DIS_REG,
+ APMG_CLK_VAL_DMA_CLK_RQT);
+ udelay(5);
+ }
}
/* Make sure (redundant) we've released our request to stay awake */
@@ -1115,7 +1118,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
{
if (iwl_op_mode_hw_rf_kill(trans->op_mode, state))
- iwl_trans_pcie_stop_device(trans);
+ iwl_trans_pcie_stop_device(trans, true);
}
static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
@@ -1200,7 +1203,7 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
return 0;
}
-static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
+static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
{
bool hw_rfkill;
int err;
@@ -1369,7 +1372,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
spin_lock_irqsave(&trans_pcie->reg_lock, *flags);
- if (trans_pcie->cmd_in_flight)
+ if (trans_pcie->cmd_hold_nic_awake)
goto out;
/* this bit wakes up the NIC */
@@ -1435,7 +1438,7 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
*/
__acquire(&trans_pcie->reg_lock);
- if (trans_pcie->cmd_in_flight)
+ if (trans_pcie->cmd_hold_nic_awake)
goto out;
__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index 06952aadfd7b..5ef8044c2ea3 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -1039,18 +1039,14 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
iwl_trans_pcie_ref(trans);
}
- if (trans_pcie->cmd_in_flight)
- return 0;
-
- trans_pcie->cmd_in_flight = true;
-
/*
* wake up the NIC to make sure that the firmware will see the host
* command - we will let the NIC sleep once all the host commands
* returned. This needs to be done only on NICs that have
* apmg_wake_up_wa set.
*/
- if (trans->cfg->base_params->apmg_wake_up_wa) {
+ if (trans->cfg->base_params->apmg_wake_up_wa &&
+ !trans_pcie->cmd_hold_nic_awake) {
__iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
@@ -1064,10 +1060,10 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
if (ret < 0) {
__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- trans_pcie->cmd_in_flight = false;
IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
return -EIO;
}
+ trans_pcie->cmd_hold_nic_awake = true;
}
return 0;
@@ -1085,15 +1081,14 @@ static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
iwl_trans_pcie_unref(trans);
}
- if (WARN_ON(!trans_pcie->cmd_in_flight))
- return 0;
-
- trans_pcie->cmd_in_flight = false;
+ if (trans->cfg->base_params->apmg_wake_up_wa) {
+ if (WARN_ON(!trans_pcie->cmd_hold_nic_awake))
+ return 0;
- if (trans->cfg->base_params->apmg_wake_up_wa)
+ trans_pcie->cmd_hold_nic_awake = false;
__iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ }
return 0;
}
diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c
index f0188c83c79f..2721cf89fb16 100644
--- a/drivers/net/wireless/rtlwifi/usb.c
+++ b/drivers/net/wireless/rtlwifi/usb.c
@@ -126,7 +126,7 @@ static int _usbctrl_vendorreq_sync_read(struct usb_device *udev, u8 request,
do {
status = usb_control_msg(udev, pipe, request, reqtype, value,
- index, pdata, len, 0); /*max. timeout*/
+ index, pdata, len, 1000);
if (status < 0) {
/* firmware download is checksumed, don't retry */
if ((value >= FW_8192C_START_ADDRESS &&
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 4de46aa61d95..0d2594395ffb 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -1250,7 +1250,7 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue,
netdev_err(queue->vif->dev,
"txreq.offset: %x, size: %u, end: %lu\n",
txreq.offset, txreq.size,
- (txreq.offset&~PAGE_MASK) + txreq.size);
+ (unsigned long)(txreq.offset&~PAGE_MASK) + txreq.size);
xenvif_fatal_tx_err(queue->vif);
break;
}
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index 3d8dbf5f2d39..968787abf78d 100644
--- a/drivers/net/xen-netback/xenbus.c
+++ b/drivers/net/xen-netback/xenbus.c
@@ -34,6 +34,8 @@ struct backend_info {
enum xenbus_state frontend_state;
struct xenbus_watch hotplug_status_watch;
u8 have_hotplug_status_watch:1;
+
+ const char *hotplug_script;
};
static int connect_rings(struct backend_info *be, struct xenvif_queue *queue);
@@ -238,6 +240,7 @@ static int netback_remove(struct xenbus_device *dev)
xenvif_free(be->vif);
be->vif = NULL;
}
+ kfree(be->hotplug_script);
kfree(be);
dev_set_drvdata(&dev->dev, NULL);
return 0;
@@ -255,6 +258,7 @@ static int netback_probe(struct xenbus_device *dev,
struct xenbus_transaction xbt;
int err;
int sg;
+ const char *script;
struct backend_info *be = kzalloc(sizeof(struct backend_info),
GFP_KERNEL);
if (!be) {
@@ -347,6 +351,15 @@ static int netback_probe(struct xenbus_device *dev,
if (err)
pr_debug("Error writing multi-queue-max-queues\n");
+ script = xenbus_read(XBT_NIL, dev->nodename, "script", NULL);
+ if (IS_ERR(script)) {
+ err = PTR_ERR(script);
+ xenbus_dev_fatal(dev, err, "reading script");
+ goto fail;
+ }
+
+ be->hotplug_script = script;
+
err = xenbus_switch_state(dev, XenbusStateInitWait);
if (err)
goto fail;
@@ -379,22 +392,14 @@ static int netback_uevent(struct xenbus_device *xdev,
struct kobj_uevent_env *env)
{
struct backend_info *be = dev_get_drvdata(&xdev->dev);
- char *val;
- val = xenbus_read(XBT_NIL, xdev->nodename, "script", NULL);
- if (IS_ERR(val)) {
- int err = PTR_ERR(val);
- xenbus_dev_fatal(xdev, err, "reading script");
- return err;
- } else {
- if (add_uevent_var(env, "script=%s", val)) {
- kfree(val);
- return -ENOMEM;
- }
- kfree(val);
- }
+ if (!be)
+ return 0;
- if (!be || !be->vif)
+ if (add_uevent_var(env, "script=%s", be->hotplug_script))
+ return -ENOMEM;
+
+ if (!be->vif)
return 0;
return add_uevent_var(env, "vif=%s", be->vif->dev->name);
@@ -793,6 +798,7 @@ static void connect(struct backend_info *be)
goto err;
}
+ queue->credit_bytes = credit_bytes;
queue->remaining_credit = credit_bytes;
queue->credit_usec = credit_usec;
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 3f45afd4382e..e031c943286e 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -1698,6 +1698,7 @@ static void xennet_destroy_queues(struct netfront_info *info)
if (netif_running(info->netdev))
napi_disable(&queue->napi);
+ del_timer_sync(&queue->rx_refill_timer);
netif_napi_del(&queue->napi);
}
@@ -2102,9 +2103,6 @@ static const struct attribute_group xennet_dev_group = {
static int xennet_remove(struct xenbus_device *dev)
{
struct netfront_info *info = dev_get_drvdata(&dev->dev);
- unsigned int num_queues = info->netdev->real_num_tx_queues;
- struct netfront_queue *queue = NULL;
- unsigned int i = 0;
dev_dbg(&dev->dev, "%s\n", dev->nodename);
@@ -2112,16 +2110,7 @@ static int xennet_remove(struct xenbus_device *dev)
unregister_netdev(info->netdev);
- for (i = 0; i < num_queues; ++i) {
- queue = &info->queues[i];
- del_timer_sync(&queue->rx_refill_timer);
- }
-
- if (num_queues) {
- kfree(info->queues);
- info->queues = NULL;
- }
-
+ xennet_destroy_queues(info);
xennet_free_netdev(info->netdev);
return 0;
diff --git a/drivers/ntb/ntb_hw.c b/drivers/ntb/ntb_hw.c
index cd29b1038c5e..3f6738612f45 100644
--- a/drivers/ntb/ntb_hw.c
+++ b/drivers/ntb/ntb_hw.c
@@ -1313,8 +1313,6 @@ static int ntb_setup_intx(struct ntb_device *ndev)
struct pci_dev *pdev = ndev->pdev;
int rc;
- pci_msi_off(pdev);
-
/* Verify intx is enabled */
pci_intx(pdev, 1);
@@ -1660,6 +1658,7 @@ static int ntb_atom_detect(struct ntb_device *ndev)
u32 ppd;
ndev->hw_type = BWD_HW;
+ ndev->limits.max_mw = BWD_MAX_MW;
rc = pci_read_config_dword(ndev->pdev, NTB_PPD_OFFSET, &ppd);
if (rc)
@@ -1778,7 +1777,7 @@ static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_warn(&pdev->dev, "Cannot remap BAR %d\n",
MW_TO_BAR(i));
rc = -EIO;
- goto err3;
+ goto err4;
}
}
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 78a7dcbec7d8..6906a3f61bd8 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -765,7 +765,7 @@ unsigned long __weak pci_address_to_pio(phys_addr_t address)
spin_lock(&io_range_lock);
list_for_each_entry(res, &io_range_list, list) {
if (address >= res->start && address < res->start + res->size) {
- addr = res->start - address + offset;
+ addr = address - res->start + offset;
break;
}
offset += res->size;
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 99764db0875a..f0650265febf 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -189,7 +189,7 @@ int __of_attach_node_sysfs(struct device_node *np)
return 0;
}
-static int __init of_init(void)
+void __init of_core_init(void)
{
struct device_node *np;
@@ -198,7 +198,8 @@ static int __init of_init(void)
of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
if (!of_kset) {
mutex_unlock(&of_mutex);
- return -ENOMEM;
+ pr_err("devicetree: failed to register existing nodes\n");
+ return;
}
for_each_of_allnodes(np)
__of_attach_node_sysfs(np);
@@ -207,10 +208,7 @@ static int __init of_init(void)
/* Symlink in /proc as required by userspace ABI */
if (of_root)
proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
-
- return 0;
}
-core_initcall(of_init);
static struct property *__of_find_property(const struct device_node *np,
const char *name, int *lenp)
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 3351ef408125..53826b84e0ec 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -225,7 +225,7 @@ void __of_attach_node(struct device_node *np)
phandle = __of_get_property(np, "phandle", &sz);
if (!phandle)
phandle = __of_get_property(np, "linux,phandle", &sz);
- if (IS_ENABLED(PPC_PSERIES) && !phandle)
+ if (IS_ENABLED(CONFIG_PPC_PSERIES) && !phandle)
phandle = __of_get_property(np, "ibm,phandle", &sz);
np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0;
diff --git a/drivers/parisc/superio.c b/drivers/parisc/superio.c
index 8be2096c8423..deeaed544222 100644
--- a/drivers/parisc/superio.c
+++ b/drivers/parisc/superio.c
@@ -348,7 +348,7 @@ int superio_fixup_irq(struct pci_dev *pcidev)
BUG();
return -1;
}
- printk("superio_fixup_irq(%s) ven 0x%x dev 0x%x from %pf\n",
+ printk(KERN_DEBUG "superio_fixup_irq(%s) ven 0x%x dev 0x%x from %ps\n",
pci_name(pcidev),
pcidev->vendor, pcidev->device,
__builtin_return_address(0));
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 7a8f1c5e65af..73de4efcbe6e 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -1,6 +1,10 @@
#
# PCI configuration
#
+config PCI_BUS_ADDR_T_64BIT
+ def_bool y if (ARCH_DMA_ADDR_T_64BIT || 64BIT)
+ depends on PCI
+
config PCI_MSI
bool "Message Signaled Interrupts (MSI and MSI-X)"
depends on PCI
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 90fa3a78fb7c..6fbd3f2b5992 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -92,11 +92,11 @@ void pci_bus_remove_resources(struct pci_bus *bus)
}
static struct pci_bus_region pci_32_bit = {0, 0xffffffffULL};
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+#ifdef CONFIG_PCI_BUS_ADDR_T_64BIT
static struct pci_bus_region pci_64_bit = {0,
- (dma_addr_t) 0xffffffffffffffffULL};
-static struct pci_bus_region pci_high = {(dma_addr_t) 0x100000000ULL,
- (dma_addr_t) 0xffffffffffffffffULL};
+ (pci_bus_addr_t) 0xffffffffffffffffULL};
+static struct pci_bus_region pci_high = {(pci_bus_addr_t) 0x100000000ULL,
+ (pci_bus_addr_t) 0xffffffffffffffffULL};
#endif
/*
@@ -200,7 +200,7 @@ int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
resource_size_t),
void *alignf_data)
{
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+#ifdef CONFIG_PCI_BUS_ADDR_T_64BIT
int rc;
if (res->flags & IORESOURCE_MEM_64) {
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 1dfb567b3522..c132bddc03f3 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -89,11 +89,20 @@ config PCI_XGENE
depends on ARCH_XGENE
depends on OF
select PCIEPORTBUS
+ select PCI_MSI_IRQ_DOMAIN if PCI_MSI
help
Say Y here if you want internal PCI support on APM X-Gene SoC.
There are 5 internal PCIe ports available. Each port is GEN3 capable
and have varied lanes from x1 to x8.
+config PCI_XGENE_MSI
+ bool "X-Gene v1 PCIe MSI feature"
+ depends on PCI_XGENE && PCI_MSI
+ default y
+ help
+ Say Y here if you want PCIe MSI support for the APM X-Gene v1 SoC.
+ This MSI driver supports 5 PCIe ports on the APM X-Gene v1 SoC.
+
config PCI_LAYERSCAPE
bool "Freescale Layerscape PCIe controller"
depends on OF && ARM
@@ -125,4 +134,15 @@ config PCIE_IPROC_PLATFORM
Say Y here if you want to use the Broadcom iProc PCIe controller
through the generic platform bus interface
+config PCIE_IPROC_BCMA
+ bool "Broadcom iProc PCIe BCMA bus driver"
+ depends on ARCH_BCM_IPROC || (ARM && COMPILE_TEST)
+ select PCIE_IPROC
+ select BCMA
+ select PCI_DOMAINS
+ default ARCH_BCM_5301X
+ help
+ Say Y here if you want to use the Broadcom iProc PCIe controller
+ through the BCMA bus interface
+
endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index f733b4e27642..140d66f796e4 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -11,7 +11,9 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
+obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c
index 2d57e19a2cd4..80db09e47800 100644
--- a/drivers/pci/host/pci-dra7xx.c
+++ b/drivers/pci/host/pci-dra7xx.c
@@ -93,9 +93,9 @@ static int dra7xx_pcie_link_up(struct pcie_port *pp)
static int dra7xx_pcie_establish_link(struct pcie_port *pp)
{
- u32 reg;
- unsigned int retries = 1000;
struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
+ u32 reg;
+ unsigned int retries;
if (dw_pcie_link_up(pp)) {
dev_err(pp->dev, "link is already up\n");
@@ -106,19 +106,14 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
reg |= LTSSM_EN;
dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
- while (retries--) {
- reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS);
- if (reg & LINK_UP)
- break;
+ for (retries = 0; retries < 1000; retries++) {
+ if (dw_pcie_link_up(pp))
+ return 0;
usleep_range(10, 20);
}
- if (retries == 0) {
- dev_err(pp->dev, "link is not up\n");
- return -ETIMEDOUT;
- }
-
- return 0;
+ dev_err(pp->dev, "link is not up\n");
+ return -EINVAL;
}
static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp)
diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
index c139237e0e52..f9f468d9a819 100644
--- a/drivers/pci/host/pci-exynos.c
+++ b/drivers/pci/host/pci-exynos.c
@@ -316,9 +316,9 @@ static void exynos_pcie_assert_reset(struct pcie_port *pp)
static int exynos_pcie_establish_link(struct pcie_port *pp)
{
- u32 val;
- int count = 0;
struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+ u32 val;
+ unsigned int retries;
if (dw_pcie_link_up(pp)) {
dev_err(pp->dev, "Link already up\n");
@@ -357,27 +357,23 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
PCIE_APP_LTSSM_ENABLE);
/* check if the link is up or not */
- while (!dw_pcie_link_up(pp)) {
- mdelay(100);
- count++;
- if (count == 10) {
- while (exynos_phy_readl(exynos_pcie,
- PCIE_PHY_PLL_LOCKED) == 0) {
- val = exynos_blk_readl(exynos_pcie,
- PCIE_PHY_PLL_LOCKED);
- dev_info(pp->dev, "PLL Locked: 0x%x\n", val);
- }
- /* power off phy */
- exynos_pcie_power_off_phy(pp);
-
- dev_err(pp->dev, "PCIe Link Fail\n");
- return -EINVAL;
+ for (retries = 0; retries < 10; retries++) {
+ if (dw_pcie_link_up(pp)) {
+ dev_info(pp->dev, "Link up\n");
+ return 0;
}
+ mdelay(100);
}
- dev_info(pp->dev, "Link up\n");
+ while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) {
+ val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED);
+ dev_info(pp->dev, "PLL Locked: 0x%x\n", val);
+ }
+ /* power off phy */
+ exynos_pcie_power_off_phy(pp);
- return 0;
+ dev_err(pp->dev, "PCIe Link Fail\n");
+ return -EINVAL;
}
static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp)
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
index fdb95367721e..233a196c6e66 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -47,6 +47,8 @@ struct imx6_pcie {
#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2
#define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK 0xf
+#define PCIE_RC_LCSR 0x80
+
/* PCIe Port Logic registers (memory-mapped) */
#define PL_OFFSET 0x700
#define PCIE_PL_PFLR (PL_OFFSET + 0x08)
@@ -335,21 +337,36 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
static int imx6_pcie_wait_for_link(struct pcie_port *pp)
{
- int count = 200;
+ unsigned int retries;
- while (!dw_pcie_link_up(pp)) {
+ for (retries = 0; retries < 200; retries++) {
+ if (dw_pcie_link_up(pp))
+ return 0;
usleep_range(100, 1000);
- if (--count)
- continue;
-
- dev_err(pp->dev, "phy link never came up\n");
- dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
- readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
- readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
- return -EINVAL;
}
- return 0;
+ dev_err(pp->dev, "phy link never came up\n");
+ dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
+ readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
+ readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
+ return -EINVAL;
+}
+
+static int imx6_pcie_wait_for_speed_change(struct pcie_port *pp)
+{
+ u32 tmp;
+ unsigned int retries;
+
+ for (retries = 0; retries < 200; retries++) {
+ tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+ /* Test if the speed change finished. */
+ if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
+ return 0;
+ usleep_range(100, 1000);
+ }
+
+ dev_err(pp->dev, "Speed change timeout\n");
+ return -EINVAL;
}
static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
@@ -359,11 +376,11 @@ static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
return dw_handle_msi_irq(pp);
}
-static int imx6_pcie_start_link(struct pcie_port *pp)
+static int imx6_pcie_establish_link(struct pcie_port *pp)
{
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
- uint32_t tmp;
- int ret, count;
+ u32 tmp;
+ int ret;
/*
* Force Gen1 operation when starting the link. In case the link is
@@ -397,29 +414,22 @@ static int imx6_pcie_start_link(struct pcie_port *pp)
tmp |= PORT_LOGIC_SPEED_CHANGE;
writel(tmp, pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
- count = 200;
- while (count--) {
- tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
- /* Test if the speed change finished. */
- if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
- break;
- usleep_range(100, 1000);
+ ret = imx6_pcie_wait_for_speed_change(pp);
+ if (ret) {
+ dev_err(pp->dev, "Failed to bring link up!\n");
+ return ret;
}
/* Make sure link training is finished as well! */
- if (count)
- ret = imx6_pcie_wait_for_link(pp);
- else
- ret = -EINVAL;
-
+ ret = imx6_pcie_wait_for_link(pp);
if (ret) {
dev_err(pp->dev, "Failed to bring link up!\n");
- } else {
- tmp = readl(pp->dbi_base + 0x80);
- dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
+ return ret;
}
- return ret;
+ tmp = readl(pp->dbi_base + PCIE_RC_LCSR);
+ dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
+ return 0;
}
static void imx6_pcie_host_init(struct pcie_port *pp)
@@ -432,7 +442,7 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
dw_pcie_setup_rc(pp);
- imx6_pcie_start_link(pp);
+ imx6_pcie_establish_link(pp);
if (IS_ENABLED(CONFIG_PCI_MSI))
dw_pcie_msi_init(pp);
@@ -440,19 +450,19 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
static void imx6_pcie_reset_phy(struct pcie_port *pp)
{
- uint32_t temp;
+ u32 tmp;
- pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
- temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
- PHY_RX_OVRD_IN_LO_RX_PLL_EN);
- pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp);
+ pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp);
+ tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
+ PHY_RX_OVRD_IN_LO_RX_PLL_EN);
+ pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
usleep_range(2000, 3000);
- pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
- temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
+ pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp);
+ tmp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
PHY_RX_OVRD_IN_LO_RX_PLL_EN);
- pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp);
+ pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
}
static int imx6_pcie_link_up(struct pcie_port *pp)
diff --git a/drivers/pci/host/pci-keystone.c b/drivers/pci/host/pci-keystone.c
index 75333b0c4f0a..b75d684aefcd 100644
--- a/drivers/pci/host/pci-keystone.c
+++ b/drivers/pci/host/pci-keystone.c
@@ -88,7 +88,7 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_mrrs);
static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
{
struct pcie_port *pp = &ks_pcie->pp;
- int count = 200;
+ unsigned int retries;
dw_pcie_setup_rc(pp);
@@ -99,17 +99,15 @@ static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
ks_dw_pcie_initiate_link_train(ks_pcie);
/* check if the link is up or not */
- while (!dw_pcie_link_up(pp)) {
+ for (retries = 0; retries < 200; retries++) {
+ if (dw_pcie_link_up(pp))
+ return 0;
usleep_range(100, 1000);
- if (--count) {
- ks_dw_pcie_initiate_link_train(ks_pcie);
- continue;
- }
- dev_err(pp->dev, "phy link never came up\n");
- return -EINVAL;
+ ks_dw_pcie_initiate_link_train(ks_pcie);
}
- return 0;
+ dev_err(pp->dev, "phy link never came up\n");
+ return -EINVAL;
}
static void ks_pcie_msi_irq_handler(unsigned int irq, struct irq_desc *desc)
diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c
index 4a6e62f67579..b2328ea13dcf 100644
--- a/drivers/pci/host/pci-layerscape.c
+++ b/drivers/pci/host/pci-layerscape.c
@@ -62,22 +62,27 @@ static int ls_pcie_link_up(struct pcie_port *pp)
return 1;
}
+static int ls_pcie_establish_link(struct pcie_port *pp)
+{
+ unsigned int retries;
+
+ for (retries = 0; retries < 200; retries++) {
+ if (dw_pcie_link_up(pp))
+ return 0;
+ usleep_range(100, 1000);
+ }
+
+ dev_err(pp->dev, "phy link never came up\n");
+ return -EINVAL;
+}
+
static void ls_pcie_host_init(struct pcie_port *pp)
{
struct ls_pcie *pcie = to_ls_pcie(pp);
- int count = 0;
u32 val;
dw_pcie_setup_rc(pp);
-
- while (!ls_pcie_link_up(pp)) {
- usleep_range(100, 1000);
- count++;
- if (count >= 200) {
- dev_err(pp->dev, "phy link never came up\n");
- return;
- }
- }
+ ls_pcie_establish_link(pp);
/*
* LS1021A Workaround for internal TKT228622
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
index 1ab863551920..70aa09556ec5 100644
--- a/drivers/pci/host/pci-mvebu.c
+++ b/drivers/pci/host/pci-mvebu.c
@@ -751,21 +751,6 @@ static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys)
return 1;
}
-static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys)
-{
- struct mvebu_pcie *pcie = sys_to_pcie(sys);
- struct pci_bus *bus;
-
- bus = pci_create_root_bus(&pcie->pdev->dev, sys->busnr,
- &mvebu_pcie_ops, sys, &sys->resources);
- if (!bus)
- return NULL;
-
- pci_scan_child_bus(bus);
-
- return bus;
-}
-
static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
const struct resource *res,
resource_size_t start,
@@ -809,12 +794,11 @@ static void mvebu_pcie_enable(struct mvebu_pcie *pcie)
hw.nr_controllers = 1;
hw.private_data = (void **)&pcie;
hw.setup = mvebu_pcie_setup;
- hw.scan = mvebu_pcie_scan_bus;
hw.map_irq = of_irq_parse_and_map_pci;
hw.ops = &mvebu_pcie_ops;
hw.align_resource = mvebu_pcie_align_resource;
- pci_common_init(&hw);
+ pci_common_init_dev(&pcie->pdev->dev, &hw);
}
/*
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 00e92720d7f7..10c05718dbfd 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -630,21 +630,6 @@ static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
return irq;
}
-static struct pci_bus *tegra_pcie_scan_bus(int nr, struct pci_sys_data *sys)
-{
- struct tegra_pcie *pcie = sys_to_pcie(sys);
- struct pci_bus *bus;
-
- bus = pci_create_root_bus(pcie->dev, sys->busnr, &tegra_pcie_ops, sys,
- &sys->resources);
- if (!bus)
- return NULL;
-
- pci_scan_child_bus(bus);
-
- return bus;
-}
-
static irqreturn_t tegra_pcie_isr(int irq, void *arg)
{
const char *err_msg[] = {
@@ -1831,7 +1816,6 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
hw.private_data = (void **)&pcie;
hw.setup = tegra_pcie_setup;
hw.map_irq = tegra_pcie_map_irq;
- hw.scan = tegra_pcie_scan_bus;
hw.ops = &tegra_pcie_ops;
pci_common_init_dev(pcie->dev, &hw);
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644
index 000000000000..2d31d4d6fd08
--- /dev/null
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -0,0 +1,596 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ * Duc Dang <dhdang@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0 0x000000
+#define MSI_INT0 0x800000
+#define IDX_PER_GROUP 8
+#define IRQS_PER_IDX 16
+#define NR_HW_IRQS 16
+#define NR_MSI_VEC (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+ struct xgene_msi *msi;
+ int gic_irq;
+ u32 msi_grp;
+};
+
+struct xgene_msi {
+ struct device_node *node;
+ struct msi_controller mchip;
+ struct irq_domain *domain;
+ u64 msi_addr;
+ void __iomem *msi_regs;
+ unsigned long *bitmap;
+ struct mutex bitmap_lock;
+ struct xgene_msi_group *msi_groups;
+ int num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+ .name = "X-Gene1 MSI",
+ .irq_enable = pci_msi_unmask_irq,
+ .irq_disable = pci_msi_mask_irq,
+ .irq_mask = pci_msi_mask_irq,
+ .irq_unmask = pci_msi_unmask_irq,
+};
+
+static struct msi_domain_info xgene_msi_domain_info = {
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_PCI_MSIX),
+ .chip = &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The register layout is as follows:
+ * MSI0IR0 base_addr
+ * MSI0IR1 base_addr + 0x10000
+ * ... ...
+ * MSI0IR6 base_addr + 0x60000
+ * MSI0IR7 base_addr + 0x70000
+ * MSI1IR0 base_addr + 0x80000
+ * MSI1IR1 base_addr + 0x90000
+ * ... ...
+ * MSI1IR7 base_addr + 0xF0000
+ * MSI2IR0 base_addr + 0x100000
+ * ... ...
+ * MSIFIR0 base_addr + 0x780000
+ * MSIFIR1 base_addr + 0x790000
+ * ... ...
+ * MSIFIR7 base_addr + 0x7F0000
+ * MSIINT0 base_addr + 0x800000
+ * MSIINT1 base_addr + 0x810000
+ * ... ...
+ * MSIINTF base_addr + 0x8F0000
+ *
+ * Each index register supports 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
+ * the MSI pending status caused by 1 of its 8 index registers.
+ */
+
+/* MSInIRx read helper */
+static u32 xgene_msi_ir_read(struct xgene_msi *msi,
+ u32 msi_grp, u32 msir_idx)
+{
+ return readl_relaxed(msi->msi_regs + MSI_IR0 +
+ (msi_grp << 19) + (msir_idx << 16));
+}
+
+/* MSIINTn read helper */
+static u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
+{
+ return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+}
+
+/*
+ * With 2048 MSI vectors supported, the MSI message can be constructed using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ * Group 0: 0-255
+ * Group 1: 256-511
+ * Group 2: 512-767
+ * ...
+ * Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ * As an example: 16 16-vector groups for 256-vector group 0-255 is
+ * Group 0: 0-15
+ * Group 1: 16-32
+ * ...
+ * Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ * group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static u32 hwirq_to_reg_set(unsigned long hwirq)
+{
+ return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
+}
+
+static u32 hwirq_to_group(unsigned long hwirq)
+{
+ return (hwirq % NR_HW_IRQS);
+}
+
+static u32 hwirq_to_msi_data(unsigned long hwirq)
+{
+ return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
+}
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+ struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+ u32 reg_set = hwirq_to_reg_set(data->hwirq);
+ u32 group = hwirq_to_group(data->hwirq);
+ u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
+
+ msg->address_hi = upper_32_bits(target_addr);
+ msg->address_lo = lower_32_bits(target_addr);
+ msg->data = hwirq_to_msi_data(data->hwirq);
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors. To maintain
+ * the expected behaviour of .set_affinity for each MSI interrupt, the 16
+ * MSI GIC IRQs are statically allocated to 8 X-Gene v1 cores (2 GIC IRQs
+ * for each core). The MSI vector is moved fom 1 MSI GIC IRQ to another
+ * MSI GIC IRQ to steer its MSI interrupt to correct X-Gene v1 core. As a
+ * consequence, the total MSI vectors that X-Gene v1 supports will be
+ * reduced to 256 (2048/8) vectors.
+ */
+static int hwirq_to_cpu(unsigned long hwirq)
+{
+ return (hwirq % xgene_msi_ctrl.num_cpus);
+}
+
+static unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
+{
+ return (hwirq - hwirq_to_cpu(hwirq));
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irqdata,
+ const struct cpumask *mask, bool force)
+{
+ int target_cpu = cpumask_first(mask);
+ int curr_cpu;
+
+ curr_cpu = hwirq_to_cpu(irqdata->hwirq);
+ if (curr_cpu == target_cpu)
+ return IRQ_SET_MASK_OK_DONE;
+
+ /* Update MSI number to target the new CPU */
+ irqdata->hwirq = hwirq_to_canonical_hwirq(irqdata->hwirq) + target_cpu;
+
+ return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+ .name = "MSI",
+ .irq_set_affinity = xgene_msi_set_affinity,
+ .irq_compose_msi_msg = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *args)
+{
+ struct xgene_msi *msi = domain->host_data;
+ int msi_irq;
+
+ mutex_lock(&msi->bitmap_lock);
+
+ msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+ msi->num_cpus, 0);
+ if (msi_irq < NR_MSI_VEC)
+ bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+ else
+ msi_irq = -ENOSPC;
+
+ mutex_unlock(&msi->bitmap_lock);
+
+ if (msi_irq < 0)
+ return msi_irq;
+
+ irq_domain_set_info(domain, virq, msi_irq,
+ &xgene_msi_bottom_irq_chip, domain->host_data,
+ handle_simple_irq, NULL, NULL);
+ set_irq_flags(virq, IRQF_VALID);
+
+ return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+ struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+ u32 hwirq;
+
+ mutex_lock(&msi->bitmap_lock);
+
+ hwirq = hwirq_to_canonical_hwirq(d->hwirq);
+ bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+ mutex_unlock(&msi->bitmap_lock);
+
+ irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+ .alloc = xgene_irq_domain_alloc,
+ .free = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+ msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+ &msi_domain_ops, msi);
+ if (!msi->domain)
+ return -ENOMEM;
+
+ msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+ &xgene_msi_domain_info,
+ msi->domain);
+
+ if (!msi->mchip.domain) {
+ irq_domain_remove(msi->domain);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+ if (msi->mchip.domain)
+ irq_domain_remove(msi->mchip.domain);
+ if (msi->domain)
+ irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+ int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+ xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+ if (!xgene_msi->bitmap)
+ return -ENOMEM;
+
+ mutex_init(&xgene_msi->bitmap_lock);
+
+ xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+ sizeof(struct xgene_msi_group),
+ GFP_KERNEL);
+ if (!xgene_msi->msi_groups)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct xgene_msi_group *msi_groups;
+ struct xgene_msi *xgene_msi;
+ unsigned int virq;
+ int msir_index, msir_val, hw_irq;
+ u32 intr_index, grp_select, msi_grp;
+
+ chained_irq_enter(chip, desc);
+
+ msi_groups = irq_desc_get_handler_data(desc);
+ xgene_msi = msi_groups->msi;
+ msi_grp = msi_groups->msi_grp;
+
+ /*
+ * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+ * If bit x of this register is set (x is 0..7), one or more interupts
+ * corresponding to MSInIRx is set.
+ */
+ grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+ while (grp_select) {
+ msir_index = ffs(grp_select) - 1;
+ /*
+ * Calculate MSInIRx address to read to check for interrupts
+ * (refer to termination address and data assignment
+ * described in xgene_compose_msi_msg() )
+ */
+ msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
+ while (msir_val) {
+ intr_index = ffs(msir_val) - 1;
+ /*
+ * Calculate MSI vector number (refer to the termination
+ * address and data assignment described in
+ * xgene_compose_msi_msg function)
+ */
+ hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+ NR_HW_IRQS) + msi_grp;
+ /*
+ * As we have multiple hw_irq that maps to single MSI,
+ * always look up the virq using the hw_irq as seen from
+ * CPU0
+ */
+ hw_irq = hwirq_to_canonical_hwirq(hw_irq);
+ virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+ WARN_ON(!virq);
+ if (virq != 0)
+ generic_handle_irq(virq);
+ msir_val &= ~(1 << intr_index);
+ }
+ grp_select &= ~(1 << msir_index);
+
+ if (!grp_select) {
+ /*
+ * We handled all interrupts happened in this group,
+ * resample this group MSI_INTx register in case
+ * something else has been made pending in the meantime
+ */
+ grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+ int virq, i;
+ struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+ for (i = 0; i < NR_HW_IRQS; i++) {
+ virq = msi->msi_groups[i].gic_irq;
+ if (virq != 0) {
+ irq_set_chained_handler(virq, NULL);
+ irq_set_handler_data(virq, NULL);
+ }
+ }
+ kfree(msi->msi_groups);
+
+ kfree(msi->bitmap);
+ msi->bitmap = NULL;
+
+ xgene_free_domains(msi);
+
+ return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+ struct xgene_msi *msi = &xgene_msi_ctrl;
+ struct xgene_msi_group *msi_group;
+ cpumask_var_t mask;
+ int i;
+ int err;
+
+ for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+ msi_group = &msi->msi_groups[i];
+ if (!msi_group->gic_irq)
+ continue;
+
+ irq_set_chained_handler(msi_group->gic_irq,
+ xgene_msi_isr);
+ err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+ if (err) {
+ pr_err("failed to register GIC IRQ handler\n");
+ return -EINVAL;
+ }
+ /*
+ * Statically allocate MSI GIC IRQs to each CPU core.
+ * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+ * to each core.
+ */
+ if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+ cpumask_clear(mask);
+ cpumask_set_cpu(cpu, mask);
+ err = irq_set_affinity(msi_group->gic_irq, mask);
+ if (err)
+ pr_err("failed to set affinity for GIC IRQ");
+ free_cpumask_var(mask);
+ } else {
+ pr_err("failed to alloc CPU mask for affinity\n");
+ err = -EINVAL;
+ }
+
+ if (err) {
+ irq_set_chained_handler(msi_group->gic_irq, NULL);
+ irq_set_handler_data(msi_group->gic_irq, NULL);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+ struct xgene_msi *msi = &xgene_msi_ctrl;
+ struct xgene_msi_group *msi_group;
+ int i;
+
+ for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+ msi_group = &msi->msi_groups[i];
+ if (!msi_group->gic_irq)
+ continue;
+
+ irq_set_chained_handler(msi_group->gic_irq, NULL);
+ irq_set_handler_data(msi_group->gic_irq, NULL);
+ }
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ unsigned cpu = (unsigned long)hcpu;
+
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ xgene_msi_hwirq_alloc(cpu);
+ break;
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ xgene_msi_hwirq_free(cpu);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+ .notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+ {.compatible = "apm,xgene1-msi"},
+ {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int rc, irq_index;
+ struct xgene_msi *xgene_msi;
+ unsigned int cpu;
+ int virt_msir;
+ u32 msi_val, msi_idx;
+
+ xgene_msi = &xgene_msi_ctrl;
+
+ platform_set_drvdata(pdev, xgene_msi);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(xgene_msi->msi_regs)) {
+ dev_err(&pdev->dev, "no reg space\n");
+ rc = -EINVAL;
+ goto error;
+ }
+ xgene_msi->msi_addr = res->start;
+
+ xgene_msi->num_cpus = num_possible_cpus();
+
+ rc = xgene_msi_init_allocator(xgene_msi);
+ if (rc) {
+ dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+ goto error;
+ }
+
+ rc = xgene_allocate_domains(xgene_msi);
+ if (rc) {
+ dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+ goto error;
+ }
+
+ for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+ virt_msir = platform_get_irq(pdev, irq_index);
+ if (virt_msir < 0) {
+ dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+ irq_index);
+ rc = -EINVAL;
+ goto error;
+ }
+ xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+ xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+ xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+ }
+
+ /*
+ * MSInIRx registers are read-to-clear; before registering
+ * interrupt handlers, read all of them to clear spurious
+ * interrupts that may occur before the driver is probed.
+ */
+ for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+ for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
+ msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
+ msi_idx);
+ /* Read MSIINTn to confirm */
+ msi_val = xgene_msi_int_read(xgene_msi, irq_index);
+ if (msi_val) {
+ dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+ rc = -EINVAL;
+ goto error;
+ }
+ }
+
+ cpu_notifier_register_begin();
+
+ for_each_online_cpu(cpu)
+ if (xgene_msi_hwirq_alloc(cpu)) {
+ dev_err(&pdev->dev, "failed to register MSI handlers\n");
+ cpu_notifier_register_done();
+ goto error;
+ }
+
+ rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+ cpu_notifier_register_done();
+ goto error;
+ }
+
+ cpu_notifier_register_done();
+
+ xgene_msi->mchip.of_node = pdev->dev.of_node;
+ rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+ goto error_notifier;
+ }
+
+ dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+ return 0;
+
+error_notifier:
+ unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+ xgene_msi_remove(pdev);
+ return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+ .driver = {
+ .name = "xgene-msi",
+ .owner = THIS_MODULE,
+ .of_match_table = xgene_msi_match_table,
+ },
+ .probe = xgene_msi_probe,
+ .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+ return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index ee082c0366ec..a9dfb70d623a 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -59,6 +59,12 @@
#define SZ_1T (SZ_1G*1024ULL)
#define PIPE_PHY_RATE_RD(src) ((0xc000 & (u32)(src)) >> 0xe)
+#define ROOT_CAP_AND_CTRL 0x5C
+
+/* PCIe IP version */
+#define XGENE_PCIE_IP_VER_UNKN 0
+#define XGENE_PCIE_IP_VER_1 1
+
struct xgene_pcie_port {
struct device_node *node;
struct device *dev;
@@ -67,6 +73,7 @@ struct xgene_pcie_port {
void __iomem *cfg_base;
unsigned long cfg_addr;
bool link_up;
+ u32 version;
};
static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
@@ -130,9 +137,7 @@ static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
int offset)
{
- struct xgene_pcie_port *port = bus->sysdata;
-
- if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up ||
+ if ((pci_is_root_bus(bus) && devfn != 0) ||
xgene_pcie_hide_rc_bars(bus, offset))
return NULL;
@@ -140,9 +145,37 @@ static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
return xgene_pcie_get_cfg_base(bus) + offset;
}
+static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ struct xgene_pcie_port *port = bus->sysdata;
+
+ if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
+ PCIBIOS_SUCCESSFUL)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /*
+ * The v1 controller has a bug in its Configuration Request
+ * Retry Status (CRS) logic: when CRS is enabled and we read the
+ * Vendor and Device ID of a non-existent device, the controller
+ * fabricates return data of 0xFFFF0001 ("device exists but is not
+ * ready") instead of 0xFFFFFFFF ("device does not exist"). This
+ * causes the PCI core to retry the read until it times out.
+ * Avoid this by not claiming to support CRS.
+ */
+ if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) &&
+ ((where & ~0x3) == ROOT_CAP_AND_CTRL))
+ *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
+
+ if (size <= 2)
+ *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
static struct pci_ops xgene_pcie_ops = {
.map_bus = xgene_pcie_map_bus,
- .read = pci_generic_config_read32,
+ .read = xgene_pcie_config_read32,
.write = pci_generic_config_write32,
};
@@ -468,6 +501,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
return 0;
}
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+ struct device_node *msi_node;
+
+ msi_node = of_parse_phandle(bus->dev.of_node,
+ "msi-parent", 0);
+ if (!msi_node)
+ return -ENODEV;
+
+ bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+ if (!bus->msi)
+ return -ENODEV;
+
+ bus->msi->dev = &bus->dev;
+ return 0;
+}
+
static int xgene_pcie_probe_bridge(struct platform_device *pdev)
{
struct device_node *dn = pdev->dev.of_node;
@@ -483,6 +533,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
port->node = of_node_get(pdev->dev.of_node);
port->dev = &pdev->dev;
+ port->version = XGENE_PCIE_IP_VER_UNKN;
+ if (of_device_is_compatible(port->node, "apm,xgene-pcie"))
+ port->version = XGENE_PCIE_IP_VER_1;
+
ret = xgene_pcie_map_reg(port, pdev);
if (ret)
return ret;
@@ -504,6 +558,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
if (!bus)
return -ENOMEM;
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ if (xgene_pcie_msi_enable(bus))
+ dev_info(port->dev, "failed to enable MSI\n");
+
pci_scan_child_bus(bus);
pci_assign_unassigned_bus_resources(bus);
pci_bus_add_devices(bus);
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 2e9f84fdd9ce..69486be7181e 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -31,6 +31,7 @@
#define PORT_LINK_MODE_1_LANES (0x1 << 16)
#define PORT_LINK_MODE_2_LANES (0x3 << 16)
#define PORT_LINK_MODE_4_LANES (0x7 << 16)
+#define PORT_LINK_MODE_8_LANES (0xf << 16)
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
@@ -38,6 +39,7 @@
#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8)
+#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8)
#define PCIE_MSI_ADDR_LO 0x820
#define PCIE_MSI_ADDR_HI 0x824
@@ -150,6 +152,21 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
return ret;
}
+static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
+ int type, u64 cpu_addr, u64 pci_addr, u32 size)
+{
+ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
+ PCIE_ATU_VIEWPORT);
+ dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE);
+ dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), PCIE_ATU_UPPER_BASE);
+ dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
+ PCIE_ATU_LIMIT);
+ dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), PCIE_ATU_LOWER_TARGET);
+ dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET);
+ dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
+ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+}
+
static struct irq_chip dw_msi_irq_chip = {
.name = "PCI-MSI",
.irq_enable = pci_msi_unmask_irq,
@@ -493,6 +510,11 @@ int dw_pcie_host_init(struct pcie_port *pp)
if (pp->ops->host_init)
pp->ops->host_init(pp);
+ if (!pp->ops->rd_other_conf)
+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
+ PCIE_ATU_TYPE_MEM, pp->mem_mod_base,
+ pp->mem_bus_addr, pp->mem_size);
+
dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
/* program correct class for RC */
@@ -515,115 +537,73 @@ int dw_pcie_host_init(struct pcie_port *pp)
return 0;
}
-static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev)
-{
- /* Program viewport 0 : OUTBOUND : CFG0 */
- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0,
- PCIE_ATU_VIEWPORT);
- dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE);
- dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE);
- dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1,
- PCIE_ATU_LIMIT);
- dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
- dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, PCIE_ATU_CR1);
- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
-}
-
-static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev)
-{
- /* Program viewport 1 : OUTBOUND : CFG1 */
- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1,
- PCIE_ATU_VIEWPORT);
- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1);
- dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE);
- dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE);
- dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1,
- PCIE_ATU_LIMIT);
- dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
- dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
-}
-
-static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp)
-{
- /* Program viewport 0 : OUTBOUND : MEM */
- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0,
- PCIE_ATU_VIEWPORT);
- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1);
- dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE);
- dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE);
- dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1,
- PCIE_ATU_LIMIT);
- dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET);
- dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr),
- PCIE_ATU_UPPER_TARGET);
- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
-}
-
-static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp)
-{
- /* Program viewport 1 : OUTBOUND : IO */
- dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1,
- PCIE_ATU_VIEWPORT);
- dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1);
- dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE);
- dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE);
- dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1,
- PCIE_ATU_LIMIT);
- dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET);
- dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr),
- PCIE_ATU_UPPER_TARGET);
- dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
-}
-
static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
u32 devfn, int where, int size, u32 *val)
{
- int ret = PCIBIOS_SUCCESSFUL;
- u32 address, busdev;
+ int ret, type;
+ u32 address, busdev, cfg_size;
+ u64 cpu_addr;
+ void __iomem *va_cfg_base;
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
PCIE_ATU_FUNC(PCI_FUNC(devfn));
address = where & ~0x3;
if (bus->parent->number == pp->root_bus_nr) {
- dw_pcie_prog_viewport_cfg0(pp, busdev);
- ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size,
- val);
- dw_pcie_prog_viewport_mem_outbound(pp);
+ type = PCIE_ATU_TYPE_CFG0;
+ cpu_addr = pp->cfg0_mod_base;
+ cfg_size = pp->cfg0_size;
+ va_cfg_base = pp->va_cfg0_base;
} else {
- dw_pcie_prog_viewport_cfg1(pp, busdev);
- ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size,
- val);
- dw_pcie_prog_viewport_io_outbound(pp);
+ type = PCIE_ATU_TYPE_CFG1;
+ cpu_addr = pp->cfg1_mod_base;
+ cfg_size = pp->cfg1_size;
+ va_cfg_base = pp->va_cfg1_base;
}
+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
+ type, cpu_addr,
+ busdev, cfg_size);
+ ret = dw_pcie_cfg_read(va_cfg_base + address, where, size, val);
+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
+ PCIE_ATU_TYPE_IO, pp->io_mod_base,
+ pp->io_bus_addr, pp->io_size);
+
return ret;
}
static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
u32 devfn, int where, int size, u32 val)
{
- int ret = PCIBIOS_SUCCESSFUL;
- u32 address, busdev;
+ int ret, type;
+ u32 address, busdev, cfg_size;
+ u64 cpu_addr;
+ void __iomem *va_cfg_base;
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
PCIE_ATU_FUNC(PCI_FUNC(devfn));
address = where & ~0x3;
if (bus->parent->number == pp->root_bus_nr) {
- dw_pcie_prog_viewport_cfg0(pp, busdev);
- ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size,
- val);
- dw_pcie_prog_viewport_mem_outbound(pp);
+ type = PCIE_ATU_TYPE_CFG0;
+ cpu_addr = pp->cfg0_mod_base;
+ cfg_size = pp->cfg0_size;
+ va_cfg_base = pp->va_cfg0_base;
} else {
- dw_pcie_prog_viewport_cfg1(pp, busdev);
- ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size,
- val);
- dw_pcie_prog_viewport_io_outbound(pp);
+ type = PCIE_ATU_TYPE_CFG1;
+ cpu_addr = pp->cfg1_mod_base;
+ cfg_size = pp->cfg1_size;
+ va_cfg_base = pp->va_cfg1_base;
}
+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
+ type, cpu_addr,
+ busdev, cfg_size);
+ ret = dw_pcie_cfg_write(va_cfg_base + address, where, size, val);
+ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
+ PCIE_ATU_TYPE_IO, pp->io_mod_base,
+ pp->io_bus_addr, pp->io_size);
+
return ret;
}
@@ -728,13 +708,11 @@ static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys)
struct pcie_port *pp = sys_to_pcie(sys);
pp->root_bus_nr = sys->busnr;
- bus = pci_create_root_bus(pp->dev, sys->busnr,
+ bus = pci_scan_root_bus(pp->dev, sys->busnr,
&dw_pcie_ops, sys, &sys->resources);
if (!bus)
return NULL;
- pci_scan_child_bus(bus);
-
if (bus && pp->ops->scan_bus)
pp->ops->scan_bus(pp);
@@ -778,6 +756,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
case 4:
val |= PORT_LINK_MODE_4_LANES;
break;
+ case 8:
+ val |= PORT_LINK_MODE_8_LANES;
+ break;
}
dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
@@ -794,6 +775,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
case 4:
val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
break;
+ case 8:
+ val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
+ break;
}
dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL);
diff --git a/drivers/pci/host/pcie-iproc-bcma.c b/drivers/pci/host/pcie-iproc-bcma.c
new file mode 100644
index 000000000000..96a7d999fd5e
--- /dev/null
+++ b/drivers/pci/host/pcie-iproc-bcma.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ * Copyright (C) 2015 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * 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/pci.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/phy/phy.h>
+#include <linux/bcma/bcma.h>
+#include <linux/ioport.h>
+
+#include "pcie-iproc.h"
+
+
+/* NS: CLASS field is R/O, and set to wrong 0x200 value */
+static void bcma_pcie2_fixup_class(struct pci_dev *dev)
+{
+ dev->class = PCI_CLASS_BRIDGE_PCI << 8;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8011, bcma_pcie2_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class);
+
+static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct pci_sys_data *sys = dev->sysdata;
+ struct iproc_pcie *pcie = sys->private_data;
+ struct bcma_device *bdev = container_of(pcie->dev, struct bcma_device, dev);
+
+ return bcma_core_irq(bdev, 5);
+}
+
+static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
+{
+ struct iproc_pcie *pcie;
+ LIST_HEAD(res);
+ struct resource res_mem;
+ int ret;
+
+ pcie = devm_kzalloc(&bdev->dev, sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ pcie->dev = &bdev->dev;
+ bcma_set_drvdata(bdev, pcie);
+
+ pcie->base = bdev->io_addr;
+
+ res_mem.start = bdev->addr_s[0];
+ res_mem.end = bdev->addr_s[0] + SZ_128M - 1;
+ res_mem.name = "PCIe MEM space";
+ res_mem.flags = IORESOURCE_MEM;
+ pci_add_resource(&res, &res_mem);
+
+ pcie->map_irq = iproc_pcie_bcma_map_irq;
+
+ ret = iproc_pcie_setup(pcie, &res);
+ if (ret)
+ dev_err(pcie->dev, "PCIe controller setup failed\n");
+
+ pci_free_resource_list(&res);
+
+ return ret;
+}
+
+static void iproc_pcie_bcma_remove(struct bcma_device *bdev)
+{
+ struct iproc_pcie *pcie = bcma_get_drvdata(bdev);
+
+ iproc_pcie_remove(pcie);
+}
+
+static const struct bcma_device_id iproc_pcie_bcma_table[] = {
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_PCIEG2, BCMA_ANY_REV, BCMA_ANY_CLASS),
+ {},
+};
+MODULE_DEVICE_TABLE(bcma, iproc_pcie_bcma_table);
+
+static struct bcma_driver iproc_pcie_bcma_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = iproc_pcie_bcma_table,
+ .probe = iproc_pcie_bcma_probe,
+ .remove = iproc_pcie_bcma_remove,
+};
+
+static int __init iproc_pcie_bcma_init(void)
+{
+ return bcma_driver_register(&iproc_pcie_bcma_driver);
+}
+module_init(iproc_pcie_bcma_init);
+
+static void __exit iproc_pcie_bcma_exit(void)
+{
+ bcma_driver_unregister(&iproc_pcie_bcma_driver);
+}
+module_exit(iproc_pcie_bcma_exit);
+
+MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_DESCRIPTION("Broadcom iProc PCIe BCMA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c
index afad6c21fcfa..9aedc8eb2c6e 100644
--- a/drivers/pci/host/pcie-iproc-platform.c
+++ b/drivers/pci/host/pcie-iproc-platform.c
@@ -69,15 +69,15 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
return ret;
}
- pcie->resources = &res;
+ pcie->map_irq = of_irq_parse_and_map_pci;
- ret = iproc_pcie_setup(pcie);
- if (ret) {
+ ret = iproc_pcie_setup(pcie, &res);
+ if (ret)
dev_err(pcie->dev, "PCIe controller setup failed\n");
- return ret;
- }
- return 0;
+ pci_free_resource_list(&res);
+
+ return ret;
}
static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index 329e1b54528b..d77481ea553e 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -183,7 +183,7 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie)
writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
}
-int iproc_pcie_setup(struct iproc_pcie *pcie)
+int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
{
int ret;
struct pci_bus *bus;
@@ -211,7 +211,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie)
pcie->sysdata.private_data = pcie;
bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops,
- &pcie->sysdata, pcie->resources);
+ &pcie->sysdata, res);
if (!bus) {
dev_err(pcie->dev, "unable to create PCI root bus\n");
ret = -ENOMEM;
@@ -229,7 +229,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie)
pci_scan_child_bus(bus);
pci_assign_unassigned_bus_resources(bus);
- pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+ pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
pci_bus_add_devices(bus);
return 0;
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index e28075ed1856..ba0a108309cc 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -29,14 +29,14 @@
struct iproc_pcie {
struct device *dev;
void __iomem *base;
- struct list_head *resources;
struct pci_sys_data sysdata;
struct pci_bus *root_bus;
struct phy *phy;
int irqs[IPROC_PCIE_MAX_NUM_IRQS];
+ int (*map_irq)(const struct pci_dev *, u8, u8);
};
-int iproc_pcie_setup(struct iproc_pcie *pcie);
+int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
int iproc_pcie_remove(struct iproc_pcie *pcie);
#endif /* _PCIE_IPROC_H */
diff --git a/drivers/pci/host/pcie-spear13xx.c b/drivers/pci/host/pcie-spear13xx.c
index 020d78890719..dfec4281bd50 100644
--- a/drivers/pci/host/pcie-spear13xx.c
+++ b/drivers/pci/host/pcie-spear13xx.c
@@ -146,10 +146,10 @@ struct pcie_app_reg {
static int spear13xx_pcie_establish_link(struct pcie_port *pp)
{
u32 val;
- int count = 0;
struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
u32 exp_cap_off = EXP_CAP_ID_OFFSET;
+ unsigned int retries;
if (dw_pcie_link_up(pp)) {
dev_err(pp->dev, "link already up\n");
@@ -201,17 +201,16 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
&app_reg->app_ctrl_0);
/* check if the link is up or not */
- while (!dw_pcie_link_up(pp)) {
- mdelay(100);
- count++;
- if (count == 10) {
- dev_err(pp->dev, "link Fail\n");
- return -EINVAL;
+ for (retries = 0; retries < 10; retries++) {
+ if (dw_pcie_link_up(pp)) {
+ dev_info(pp->dev, "link up\n");
+ return 0;
}
+ mdelay(100);
}
- dev_info(pp->dev, "link up\n");
- return 0;
+ dev_err(pp->dev, "link Fail\n");
+ return -EINVAL;
}
static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg)
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index 4a9aa08b08f1..b616e7588ff4 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -61,9 +61,6 @@ pciehp-objs := pciehp_core.o \
pciehp_ctrl.o \
pciehp_pci.o \
pciehp_hpc.o
-ifdef CONFIG_ACPI
-pciehp-objs += pciehp_acpi.o
-endif
shpchp-objs := shpchp_core.o \
shpchp_ctrl.o \
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index bcb90e4888dd..ff538568a617 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -632,15 +632,14 @@ static void trim_stale_devices(struct pci_dev *dev)
{
struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
struct pci_bus *bus = dev->subordinate;
- bool alive = false;
+ bool alive = dev->ignore_hotplug;
if (adev) {
acpi_status status;
unsigned long long sta;
status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
- alive = (ACPI_SUCCESS(status) && device_status_valid(sta))
- || dev->ignore_hotplug;
+ alive = alive || (ACPI_SUCCESS(status) && device_status_valid(sta));
}
if (!alive)
alive = pci_device_is_present(dev);
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index b11521953485..57cd1327346f 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -132,11 +132,7 @@ struct controller {
int pciehp_sysfs_enable_slot(struct slot *slot);
int pciehp_sysfs_disable_slot(struct slot *slot);
-u8 pciehp_handle_attention_button(struct slot *p_slot);
-u8 pciehp_handle_switch_change(struct slot *p_slot);
-u8 pciehp_handle_presence_change(struct slot *p_slot);
-u8 pciehp_handle_power_fault(struct slot *p_slot);
-void pciehp_handle_linkstate_change(struct slot *p_slot);
+void pciehp_queue_interrupt_event(struct slot *slot, u32 event_type);
int pciehp_configure_device(struct slot *p_slot);
int pciehp_unconfigure_device(struct slot *p_slot);
void pciehp_queue_pushbutton_work(struct work_struct *work);
@@ -167,21 +163,4 @@ static inline const char *slot_name(struct slot *slot)
return hotplug_slot_name(slot->hotplug_slot);
}
-#ifdef CONFIG_ACPI
-#include <linux/pci-acpi.h>
-
-void __init pciehp_acpi_slot_detection_init(void);
-int pciehp_acpi_slot_detection_check(struct pci_dev *dev);
-
-static inline void pciehp_firmware_init(void)
-{
- pciehp_acpi_slot_detection_init();
-}
-#else
-#define pciehp_firmware_init() do {} while (0)
-static inline int pciehp_acpi_slot_detection_check(struct pci_dev *dev)
-{
- return 0;
-}
-#endif /* CONFIG_ACPI */
#endif /* _PCIEHP_H */
diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c
deleted file mode 100644
index 93cc9266e8cb..000000000000
--- a/drivers/pci/hotplug/pciehp_acpi.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * ACPI related functions for PCI Express Hot Plug driver.
- *
- * Copyright (C) 2008 Kenji Kaneshige
- * Copyright (C) 2008 Fujitsu Limited.
- *
- * 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 as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/acpi.h>
-#include <linux/pci.h>
-#include <linux/pci_hotplug.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include "pciehp.h"
-
-#define PCIEHP_DETECT_PCIE (0)
-#define PCIEHP_DETECT_ACPI (1)
-#define PCIEHP_DETECT_AUTO (2)
-#define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_AUTO
-
-struct dummy_slot {
- u32 number;
- struct list_head list;
-};
-
-static int slot_detection_mode;
-static char *pciehp_detect_mode;
-module_param(pciehp_detect_mode, charp, 0444);
-MODULE_PARM_DESC(pciehp_detect_mode,
- "Slot detection mode: pcie, acpi, auto\n"
- " pcie - Use PCIe based slot detection\n"
- " acpi - Use ACPI for slot detection\n"
- " auto(default) - Auto select mode. Use acpi option if duplicate\n"
- " slot ids are found. Otherwise, use pcie option\n");
-
-int pciehp_acpi_slot_detection_check(struct pci_dev *dev)
-{
- if (slot_detection_mode != PCIEHP_DETECT_ACPI)
- return 0;
- if (acpi_pci_detect_ejectable(ACPI_HANDLE(&dev->dev)))
- return 0;
- return -ENODEV;
-}
-
-static int __init parse_detect_mode(void)
-{
- if (!pciehp_detect_mode)
- return PCIEHP_DETECT_DEFAULT;
- if (!strcmp(pciehp_detect_mode, "pcie"))
- return PCIEHP_DETECT_PCIE;
- if (!strcmp(pciehp_detect_mode, "acpi"))
- return PCIEHP_DETECT_ACPI;
- if (!strcmp(pciehp_detect_mode, "auto"))
- return PCIEHP_DETECT_AUTO;
- warn("bad specifier '%s' for pciehp_detect_mode. Use default\n",
- pciehp_detect_mode);
- return PCIEHP_DETECT_DEFAULT;
-}
-
-static int __initdata dup_slot_id;
-static int __initdata acpi_slot_detected;
-static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots);
-
-/* Dummy driver for duplicate name detection */
-static int __init dummy_probe(struct pcie_device *dev)
-{
- u32 slot_cap;
- acpi_handle handle;
- struct dummy_slot *slot, *tmp;
- struct pci_dev *pdev = dev->port;
-
- pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
- slot = kzalloc(sizeof(*slot), GFP_KERNEL);
- if (!slot)
- return -ENOMEM;
- slot->number = (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19;
- list_for_each_entry(tmp, &dummy_slots, list) {
- if (tmp->number == slot->number)
- dup_slot_id++;
- }
- list_add_tail(&slot->list, &dummy_slots);
- handle = ACPI_HANDLE(&pdev->dev);
- if (!acpi_slot_detected && acpi_pci_detect_ejectable(handle))
- acpi_slot_detected = 1;
- return -ENODEV; /* dummy driver always returns error */
-}
-
-static struct pcie_port_service_driver __initdata dummy_driver = {
- .name = "pciehp_dummy",
- .port_type = PCIE_ANY_PORT,
- .service = PCIE_PORT_SERVICE_HP,
- .probe = dummy_probe,
-};
-
-static int __init select_detection_mode(void)
-{
- struct dummy_slot *slot, *tmp;
-
- if (pcie_port_service_register(&dummy_driver))
- return PCIEHP_DETECT_ACPI;
- pcie_port_service_unregister(&dummy_driver);
- list_for_each_entry_safe(slot, tmp, &dummy_slots, list) {
- list_del(&slot->list);
- kfree(slot);
- }
- if (acpi_slot_detected && dup_slot_id)
- return PCIEHP_DETECT_ACPI;
- return PCIEHP_DETECT_PCIE;
-}
-
-void __init pciehp_acpi_slot_detection_init(void)
-{
- slot_detection_mode = parse_detect_mode();
- if (slot_detection_mode != PCIEHP_DETECT_AUTO)
- goto out;
- slot_detection_mode = select_detection_mode();
-out:
- if (slot_detection_mode == PCIEHP_DETECT_ACPI)
- info("Using ACPI for slot detection.\n");
-}
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 07aa722bb12c..612b21a14df5 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -77,11 +77,6 @@ static int reset_slot (struct hotplug_slot *slot, int probe);
*/
static void release_slot(struct hotplug_slot *hotplug_slot)
{
- struct slot *slot = hotplug_slot->private;
-
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, hotplug_slot_name(hotplug_slot));
-
kfree(hotplug_slot->ops);
kfree(hotplug_slot->info);
kfree(hotplug_slot);
@@ -129,14 +124,10 @@ static int init_slot(struct controller *ctrl)
slot->hotplug_slot = hotplug;
snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
- ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:00 sun=%x\n",
- pci_domain_nr(ctrl->pcie->port->subordinate),
- ctrl->pcie->port->subordinate->number, PSN(ctrl));
retval = pci_hp_register(hotplug,
ctrl->pcie->port->subordinate, 0, name);
if (retval)
- ctrl_err(ctrl,
- "pci_hp_register failed with error %d\n", retval);
+ ctrl_err(ctrl, "pci_hp_register failed: error %d\n", retval);
out:
if (retval) {
kfree(ops);
@@ -158,9 +149,6 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
{
struct slot *slot = hotplug_slot->private;
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
pciehp_set_attention_status(slot, status);
return 0;
}
@@ -170,9 +158,6 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
return pciehp_sysfs_enable_slot(slot);
}
@@ -181,9 +166,6 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
return pciehp_sysfs_disable_slot(slot);
}
@@ -191,9 +173,6 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
pciehp_get_power_status(slot, value);
return 0;
}
@@ -202,9 +181,6 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
pciehp_get_attention_status(slot, value);
return 0;
}
@@ -213,9 +189,6 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
pciehp_get_latch_status(slot, value);
return 0;
}
@@ -224,9 +197,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
pciehp_get_adapter_status(slot, value);
return 0;
}
@@ -235,9 +205,6 @@ static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
{
struct slot *slot = hotplug_slot->private;
- ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
- __func__, slot_name(slot));
-
return pciehp_reset_slot(slot, probe);
}
@@ -248,24 +215,21 @@ static int pciehp_probe(struct pcie_device *dev)
struct slot *slot;
u8 occupied, poweron;
- if (pciehp_force)
- dev_info(&dev->device,
- "Bypassing BIOS check for pciehp use on %s\n",
- pci_name(dev->port));
- else if (pciehp_acpi_slot_detection_check(dev->port))
- goto err_out_none;
+ /* If this is not a "hotplug" service, we have no business here. */
+ if (dev->service != PCIE_PORT_SERVICE_HP)
+ return -ENODEV;
if (!dev->port->subordinate) {
/* Can happen if we run out of bus numbers during probe */
dev_err(&dev->device,
"Hotplug bridge without secondary bus, ignoring\n");
- goto err_out_none;
+ return -ENODEV;
}
ctrl = pcie_init(dev);
if (!ctrl) {
dev_err(&dev->device, "Controller initialization failed\n");
- goto err_out_none;
+ return -ENODEV;
}
set_service_data(dev, ctrl);
@@ -275,14 +239,14 @@ static int pciehp_probe(struct pcie_device *dev)
if (rc == -EBUSY)
ctrl_warn(ctrl, "Slot already registered by another hotplug driver\n");
else
- ctrl_err(ctrl, "Slot initialization failed\n");
+ ctrl_err(ctrl, "Slot initialization failed (%d)\n", rc);
goto err_out_release_ctlr;
}
/* Enable events after we have setup the data structures */
rc = pcie_init_notification(ctrl);
if (rc) {
- ctrl_err(ctrl, "Notification initialization failed\n");
+ ctrl_err(ctrl, "Notification initialization failed (%d)\n", rc);
goto err_out_free_ctrl_slot;
}
@@ -305,7 +269,6 @@ err_out_free_ctrl_slot:
cleanup_slot(ctrl);
err_out_release_ctlr:
pciehp_release_ctrl(ctrl);
-err_out_none:
return -ENODEV;
}
@@ -366,7 +329,6 @@ static int __init pcied_init(void)
{
int retval = 0;
- pciehp_firmware_init();
retval = pcie_port_service_register(&hpdriver_portdrv);
dbg("pcie_port_service_register = %d\n", retval);
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index f052e951b23e..f3796124ad7c 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -37,138 +37,20 @@
static void interrupt_event_handler(struct work_struct *work);
-static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
+void pciehp_queue_interrupt_event(struct slot *p_slot, u32 event_type)
{
struct event_info *info;
info = kmalloc(sizeof(*info), GFP_ATOMIC);
- if (!info)
- return -ENOMEM;
+ if (!info) {
+ ctrl_err(p_slot->ctrl, "dropped event %d (ENOMEM)\n", event_type);
+ return;
+ }
+ INIT_WORK(&info->work, interrupt_event_handler);
info->event_type = event_type;
info->p_slot = p_slot;
- INIT_WORK(&info->work, interrupt_event_handler);
-
queue_work(p_slot->wq, &info->work);
-
- return 0;
-}
-
-u8 pciehp_handle_attention_button(struct slot *p_slot)
-{
- u32 event_type;
- struct controller *ctrl = p_slot->ctrl;
-
- /* Attention Button Change */
- ctrl_dbg(ctrl, "Attention button interrupt received\n");
-
- /*
- * Button pressed - See if need to TAKE ACTION!!!
- */
- ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot));
- event_type = INT_BUTTON_PRESS;
-
- queue_interrupt_event(p_slot, event_type);
-
- return 0;
-}
-
-u8 pciehp_handle_switch_change(struct slot *p_slot)
-{
- u8 getstatus;
- u32 event_type;
- struct controller *ctrl = p_slot->ctrl;
-
- /* Switch Change */
- ctrl_dbg(ctrl, "Switch interrupt received\n");
-
- pciehp_get_latch_status(p_slot, &getstatus);
- if (getstatus) {
- /*
- * Switch opened
- */
- ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot));
- event_type = INT_SWITCH_OPEN;
- } else {
- /*
- * Switch closed
- */
- ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot));
- event_type = INT_SWITCH_CLOSE;
- }
-
- queue_interrupt_event(p_slot, event_type);
-
- return 1;
-}
-
-u8 pciehp_handle_presence_change(struct slot *p_slot)
-{
- u32 event_type;
- u8 presence_save;
- struct controller *ctrl = p_slot->ctrl;
-
- /* Presence Change */
- ctrl_dbg(ctrl, "Presence/Notify input change\n");
-
- /* Switch is open, assume a presence change
- * Save the presence state
- */
- pciehp_get_adapter_status(p_slot, &presence_save);
- if (presence_save) {
- /*
- * Card Present
- */
- ctrl_info(ctrl, "Card present on Slot(%s)\n", slot_name(p_slot));
- event_type = INT_PRESENCE_ON;
- } else {
- /*
- * Not Present
- */
- ctrl_info(ctrl, "Card not present on Slot(%s)\n",
- slot_name(p_slot));
- event_type = INT_PRESENCE_OFF;
- }
-
- queue_interrupt_event(p_slot, event_type);
-
- return 1;
-}
-
-u8 pciehp_handle_power_fault(struct slot *p_slot)
-{
- u32 event_type;
- struct controller *ctrl = p_slot->ctrl;
-
- /* power fault */
- ctrl_dbg(ctrl, "Power fault interrupt received\n");
- ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
- event_type = INT_POWER_FAULT;
- ctrl_info(ctrl, "Power fault bit %x set\n", 0);
- queue_interrupt_event(p_slot, event_type);
-
- return 1;
-}
-
-void pciehp_handle_linkstate_change(struct slot *p_slot)
-{
- u32 event_type;
- struct controller *ctrl = p_slot->ctrl;
-
- /* Link Status Change */
- ctrl_dbg(ctrl, "Data Link Layer State change\n");
-
- if (pciehp_check_link_active(ctrl)) {
- ctrl_info(ctrl, "slot(%s): Link Up event\n",
- slot_name(p_slot));
- event_type = INT_LINK_UP;
- } else {
- ctrl_info(ctrl, "slot(%s): Link Down event\n",
- slot_name(p_slot));
- event_type = INT_LINK_DOWN;
- }
-
- queue_interrupt_event(p_slot, event_type);
}
/* The following routines constitute the bulk of the
@@ -298,10 +180,6 @@ static void pciehp_power_thread(struct work_struct *work)
switch (info->req) {
case DISABLE_REQ:
- ctrl_dbg(p_slot->ctrl,
- "Disabling domain:bus:device=%04x:%02x:00\n",
- pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
- p_slot->ctrl->pcie->port->subordinate->number);
mutex_lock(&p_slot->hotplug_lock);
pciehp_disable_slot(p_slot);
mutex_unlock(&p_slot->hotplug_lock);
@@ -310,10 +188,6 @@ static void pciehp_power_thread(struct work_struct *work)
mutex_unlock(&p_slot->lock);
break;
case ENABLE_REQ:
- ctrl_dbg(p_slot->ctrl,
- "Enabling domain:bus:device=%04x:%02x:00\n",
- pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
- p_slot->ctrl->pcie->port->subordinate->number);
mutex_lock(&p_slot->hotplug_lock);
ret = pciehp_enable_slot(p_slot);
mutex_unlock(&p_slot->hotplug_lock);
@@ -416,7 +290,7 @@ static void handle_button_press_event(struct slot *p_slot)
ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
break;
default:
- ctrl_warn(ctrl, "Not a valid state\n");
+ ctrl_warn(ctrl, "ignoring invalid state %#x\n", p_slot->state);
break;
}
}
@@ -507,8 +381,8 @@ static void handle_link_event(struct slot *p_slot, u32 event)
}
break;
default:
- ctrl_err(ctrl, "Not a valid state on slot(%s)\n",
- slot_name(p_slot));
+ ctrl_err(ctrl, "ignoring invalid state %#x on slot(%s)\n",
+ p_slot->state, slot_name(p_slot));
kfree(info);
break;
}
@@ -532,7 +406,6 @@ static void interrupt_event_handler(struct work_struct *work)
pciehp_green_led_off(p_slot);
break;
case INT_PRESENCE_ON:
- ctrl_dbg(ctrl, "Surprise Insertion\n");
handle_surprise_event(p_slot);
break;
case INT_PRESENCE_OFF:
@@ -540,7 +413,6 @@ static void interrupt_event_handler(struct work_struct *work)
* Regardless of surprise capability, we need to
* definitely remove a card that has been pulled out!
*/
- ctrl_dbg(ctrl, "Surprise Removal\n");
handle_surprise_event(p_slot);
break;
case INT_LINK_UP:
@@ -647,8 +519,8 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
slot_name(p_slot));
break;
default:
- ctrl_err(ctrl, "Not a valid state on slot %s\n",
- slot_name(p_slot));
+ ctrl_err(ctrl, "invalid state %#x on slot %s\n",
+ p_slot->state, slot_name(p_slot));
break;
}
mutex_unlock(&p_slot->lock);
@@ -682,8 +554,8 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
slot_name(p_slot));
break;
default:
- ctrl_err(ctrl, "Not a valid state on slot %s\n",
- slot_name(p_slot));
+ ctrl_err(ctrl, "invalid state %#x on slot %s\n",
+ p_slot->state, slot_name(p_slot));
break;
}
mutex_unlock(&p_slot->lock);
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 0ebf754fc177..2913f7e68a10 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -176,20 +176,17 @@ static void pcie_wait_cmd(struct controller *ctrl)
jiffies_to_msecs(jiffies - ctrl->cmd_started));
}
-/**
- * pcie_write_cmd - Issue controller command
- * @ctrl: controller to which the command is issued
- * @cmd: command value written to slot control register
- * @mask: bitmask of slot control register to be modified
- */
-static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
+static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd,
+ u16 mask, bool wait)
{
struct pci_dev *pdev = ctrl_dev(ctrl);
u16 slot_ctrl;
mutex_lock(&ctrl->ctrl_lock);
- /* Wait for any previous command that might still be in progress */
+ /*
+ * Always wait for any previous command that might still be in progress
+ */
pcie_wait_cmd(ctrl);
pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
@@ -201,9 +198,33 @@ static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
ctrl->cmd_started = jiffies;
ctrl->slot_ctrl = slot_ctrl;
+ /*
+ * Optionally wait for the hardware to be ready for a new command,
+ * indicating completion of the above issued command.
+ */
+ if (wait)
+ pcie_wait_cmd(ctrl);
+
mutex_unlock(&ctrl->ctrl_lock);
}
+/**
+ * pcie_write_cmd - Issue controller command
+ * @ctrl: controller to which the command is issued
+ * @cmd: command value written to slot control register
+ * @mask: bitmask of slot control register to be modified
+ */
+static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
+{
+ pcie_do_write_cmd(ctrl, cmd, mask, true);
+}
+
+/* Same as above without waiting for the hardware to latch */
+static void pcie_write_cmd_nowait(struct controller *ctrl, u16 cmd, u16 mask)
+{
+ pcie_do_write_cmd(ctrl, cmd, mask, false);
+}
+
bool pciehp_check_link_active(struct controller *ctrl)
{
struct pci_dev *pdev = ctrl_dev(ctrl);
@@ -291,7 +312,8 @@ int pciehp_check_link_status(struct controller *ctrl)
ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
if ((lnk_status & PCI_EXP_LNKSTA_LT) ||
!(lnk_status & PCI_EXP_LNKSTA_NLW)) {
- ctrl_err(ctrl, "Link Training Error occurs\n");
+ ctrl_err(ctrl, "link training error: status %#06x\n",
+ lnk_status);
return -1;
}
@@ -422,7 +444,7 @@ void pciehp_set_attention_status(struct slot *slot, u8 value)
default:
return;
}
- pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
+ pcie_write_cmd_nowait(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
}
@@ -434,7 +456,8 @@ void pciehp_green_led_on(struct slot *slot)
if (!PWR_LED(ctrl))
return;
- pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PIC);
+ pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
+ PCI_EXP_SLTCTL_PIC);
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_PWR_IND_ON);
@@ -447,7 +470,8 @@ void pciehp_green_led_off(struct slot *slot)
if (!PWR_LED(ctrl))
return;
- pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PIC);
+ pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
+ PCI_EXP_SLTCTL_PIC);
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_PWR_IND_OFF);
@@ -460,7 +484,8 @@ void pciehp_green_led_blink(struct slot *slot)
if (!PWR_LED(ctrl))
return;
- pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PIC);
+ pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
+ PCI_EXP_SLTCTL_PIC);
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_PWR_IND_BLINK);
@@ -510,6 +535,8 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
struct pci_dev *dev;
struct slot *slot = ctrl->slot;
u16 detected, intr_loc;
+ u8 open, present;
+ bool link;
/*
* In order to guarantee that all interrupt events are
@@ -532,7 +559,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
intr_loc);
} while (detected);
- ctrl_dbg(ctrl, "%s: intr_loc %x\n", __func__, intr_loc);
+ ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", intr_loc);
/* Check Command Complete Interrupt Pending */
if (intr_loc & PCI_EXP_SLTSTA_CC) {
@@ -555,25 +582,44 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
return IRQ_HANDLED;
/* Check MRL Sensor Changed */
- if (intr_loc & PCI_EXP_SLTSTA_MRLSC)
- pciehp_handle_switch_change(slot);
+ if (intr_loc & PCI_EXP_SLTSTA_MRLSC) {
+ pciehp_get_latch_status(slot, &open);
+ ctrl_info(ctrl, "Latch %s on Slot(%s)\n",
+ open ? "open" : "close", slot_name(slot));
+ pciehp_queue_interrupt_event(slot, open ? INT_SWITCH_OPEN :
+ INT_SWITCH_CLOSE);
+ }
/* Check Attention Button Pressed */
- if (intr_loc & PCI_EXP_SLTSTA_ABP)
- pciehp_handle_attention_button(slot);
+ if (intr_loc & PCI_EXP_SLTSTA_ABP) {
+ ctrl_info(ctrl, "Button pressed on Slot(%s)\n",
+ slot_name(slot));
+ pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS);
+ }
/* Check Presence Detect Changed */
- if (intr_loc & PCI_EXP_SLTSTA_PDC)
- pciehp_handle_presence_change(slot);
+ if (intr_loc & PCI_EXP_SLTSTA_PDC) {
+ pciehp_get_adapter_status(slot, &present);
+ ctrl_info(ctrl, "Card %spresent on Slot(%s)\n",
+ present ? "" : "not ", slot_name(slot));
+ pciehp_queue_interrupt_event(slot, present ? INT_PRESENCE_ON :
+ INT_PRESENCE_OFF);
+ }
/* Check Power Fault Detected */
if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
ctrl->power_fault_detected = 1;
- pciehp_handle_power_fault(slot);
+ ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(slot));
+ pciehp_queue_interrupt_event(slot, INT_POWER_FAULT);
}
- if (intr_loc & PCI_EXP_SLTSTA_DLLSC)
- pciehp_handle_linkstate_change(slot);
+ if (intr_loc & PCI_EXP_SLTSTA_DLLSC) {
+ link = pciehp_check_link_active(ctrl);
+ ctrl_info(ctrl, "slot(%s): Link %s event\n",
+ slot_name(slot), link ? "Up" : "Down");
+ pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP :
+ INT_LINK_DOWN);
+ }
return IRQ_HANDLED;
}
@@ -613,7 +659,7 @@ void pcie_enable_notification(struct controller *ctrl)
PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
PCI_EXP_SLTCTL_DLLSCE);
- pcie_write_cmd(ctrl, cmd, mask);
+ pcie_write_cmd_nowait(ctrl, cmd, mask);
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
}
@@ -664,7 +710,7 @@ int pciehp_reset_slot(struct slot *slot, int probe)
pci_reset_bridge_secondary_bus(ctrl->pcie->port);
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
- pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask);
+ pcie_write_cmd_nowait(ctrl, ctrl_mask, ctrl_mask);
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask);
if (pciehp_poll_mode)
@@ -724,48 +770,13 @@ static void pcie_cleanup_slot(struct controller *ctrl)
static inline void dbg_ctrl(struct controller *ctrl)
{
- int i;
- u16 reg16;
struct pci_dev *pdev = ctrl->pcie->port;
+ u16 reg16;
if (!pciehp_debug)
return;
- ctrl_info(ctrl, "Hotplug Controller:\n");
- ctrl_info(ctrl, " Seg/Bus/Dev/Func/IRQ : %s IRQ %d\n",
- pci_name(pdev), pdev->irq);
- ctrl_info(ctrl, " Vendor ID : 0x%04x\n", pdev->vendor);
- ctrl_info(ctrl, " Device ID : 0x%04x\n", pdev->device);
- ctrl_info(ctrl, " Subsystem ID : 0x%04x\n",
- pdev->subsystem_device);
- ctrl_info(ctrl, " Subsystem Vendor ID : 0x%04x\n",
- pdev->subsystem_vendor);
- ctrl_info(ctrl, " PCIe Cap offset : 0x%02x\n",
- pci_pcie_cap(pdev));
- for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
- if (!pci_resource_len(pdev, i))
- continue;
- ctrl_info(ctrl, " PCI resource [%d] : %pR\n",
- i, &pdev->resource[i]);
- }
ctrl_info(ctrl, "Slot Capabilities : 0x%08x\n", ctrl->slot_cap);
- ctrl_info(ctrl, " Physical Slot Number : %d\n", PSN(ctrl));
- ctrl_info(ctrl, " Attention Button : %3s\n",
- ATTN_BUTTN(ctrl) ? "yes" : "no");
- ctrl_info(ctrl, " Power Controller : %3s\n",
- POWER_CTRL(ctrl) ? "yes" : "no");
- ctrl_info(ctrl, " MRL Sensor : %3s\n",
- MRL_SENS(ctrl) ? "yes" : "no");
- ctrl_info(ctrl, " Attention Indicator : %3s\n",
- ATTN_LED(ctrl) ? "yes" : "no");
- ctrl_info(ctrl, " Power Indicator : %3s\n",
- PWR_LED(ctrl) ? "yes" : "no");
- ctrl_info(ctrl, " Hot-Plug Surprise : %3s\n",
- HP_SUPR_RM(ctrl) ? "yes" : "no");
- ctrl_info(ctrl, " EMI Present : %3s\n",
- EMI(ctrl) ? "yes" : "no");
- ctrl_info(ctrl, " Command Completed : %3s\n",
- NO_CMD_CMPL(ctrl) ? "no" : "yes");
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &reg16);
ctrl_info(ctrl, "Slot Status : 0x%04x\n", reg16);
pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &reg16);
@@ -794,10 +805,8 @@ struct controller *pcie_init(struct pcie_device *dev)
/* Check if Data Link Layer Link Active Reporting is implemented */
pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
- if (link_cap & PCI_EXP_LNKCAP_DLLLARC) {
- ctrl_dbg(ctrl, "Link Active Reporting supported\n");
+ if (link_cap & PCI_EXP_LNKCAP_DLLLARC)
ctrl->link_active_reporting = 1;
- }
/* Clear all remaining event bits in Slot Status register */
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
@@ -805,13 +814,15 @@ struct controller *pcie_init(struct pcie_device *dev)
PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
- ctrl_info(ctrl, "Slot #%d AttnBtn%c AttnInd%c PwrInd%c PwrCtrl%c MRL%c Interlock%c NoCompl%c LLActRep%c\n",
+ ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c LLActRep%c\n",
(slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
FLAG(slot_cap, PCI_EXP_SLTCAP_ABP),
- FLAG(slot_cap, PCI_EXP_SLTCAP_AIP),
- FLAG(slot_cap, PCI_EXP_SLTCAP_PIP),
FLAG(slot_cap, PCI_EXP_SLTCAP_PCP),
FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP),
+ FLAG(slot_cap, PCI_EXP_SLTCAP_AIP),
+ FLAG(slot_cap, PCI_EXP_SLTCAP_PIP),
+ FLAG(slot_cap, PCI_EXP_SLTCAP_HPC),
+ FLAG(slot_cap, PCI_EXP_SLTCAP_HPS),
FLAG(slot_cap, PCI_EXP_SLTCAP_EIP),
FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS),
FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC));
diff --git a/drivers/pci/htirq.c b/drivers/pci/htirq.c
index a94dd2c4183a..7eb4109a3df4 100644
--- a/drivers/pci/htirq.c
+++ b/drivers/pci/htirq.c
@@ -23,20 +23,11 @@
*/
static DEFINE_SPINLOCK(ht_irq_lock);
-struct ht_irq_cfg {
- struct pci_dev *dev;
- /* Update callback used to cope with buggy hardware */
- ht_irq_update_t *update;
- unsigned pos;
- unsigned idx;
- struct ht_irq_msg msg;
-};
-
-
void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
{
struct ht_irq_cfg *cfg = irq_get_handler_data(irq);
unsigned long flags;
+
spin_lock_irqsave(&ht_irq_lock, flags);
if (cfg->msg.address_lo != msg->address_lo) {
pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx);
@@ -55,6 +46,7 @@ void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
void fetch_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
{
struct ht_irq_cfg *cfg = irq_get_handler_data(irq);
+
*msg = cfg->msg;
}
@@ -86,7 +78,6 @@ void unmask_ht_irq(struct irq_data *data)
*/
int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
{
- struct ht_irq_cfg *cfg;
int max_irq, pos, irq;
unsigned long flags;
u32 data;
@@ -105,29 +96,9 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
if (idx > max_irq)
return -EINVAL;
- cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
- if (!cfg)
- return -ENOMEM;
-
- cfg->dev = dev;
- cfg->update = update;
- cfg->pos = pos;
- cfg->idx = 0x10 + (idx * 2);
- /* Initialize msg to a value that will never match the first write. */
- cfg->msg.address_lo = 0xffffffff;
- cfg->msg.address_hi = 0xffffffff;
-
- irq = irq_alloc_hwirq(dev_to_node(&dev->dev));
- if (!irq) {
- kfree(cfg);
- return -EBUSY;
- }
- irq_set_handler_data(irq, cfg);
-
- if (arch_setup_ht_irq(irq, dev) < 0) {
- ht_destroy_irq(irq);
- return -EBUSY;
- }
+ irq = arch_setup_ht_irq(idx, pos, dev, update);
+ if (irq > 0)
+ dev_dbg(&dev->dev, "irq %d for HT\n", irq);
return irq;
}
@@ -158,13 +129,6 @@ EXPORT_SYMBOL(ht_create_irq);
*/
void ht_destroy_irq(unsigned int irq)
{
- struct ht_irq_cfg *cfg;
-
- cfg = irq_get_handler_data(irq);
- irq_set_chip(irq, NULL);
- irq_set_handler_data(irq, NULL);
- irq_free_hwirq(irq);
-
- kfree(cfg);
+ arch_teardown_ht_irq(irq);
}
EXPORT_SYMBOL(ht_destroy_irq);
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index c3e7dfcf9ff5..f66be868ad21 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -185,27 +185,6 @@ void __weak arch_restore_msi_irqs(struct pci_dev *dev)
return default_restore_msi_irqs(dev);
}
-static void msi_set_enable(struct pci_dev *dev, int enable)
-{
- u16 control;
-
- pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
- control &= ~PCI_MSI_FLAGS_ENABLE;
- if (enable)
- control |= PCI_MSI_FLAGS_ENABLE;
- pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
-}
-
-static void msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u16 set)
-{
- u16 ctrl;
-
- pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &ctrl);
- ctrl &= ~clear;
- ctrl |= set;
- pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl);
-}
-
static inline __attribute_const__ u32 msi_mask(unsigned x)
{
/* Don't shift by >= width of type */
@@ -452,7 +431,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev)
entry = irq_get_msi_desc(dev->irq);
pci_intx_for_msi(dev, 0);
- msi_set_enable(dev, 0);
+ pci_msi_set_enable(dev, 0);
arch_restore_msi_irqs(dev);
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
@@ -473,14 +452,14 @@ static void __pci_restore_msix_state(struct pci_dev *dev)
/* route the table */
pci_intx_for_msi(dev, 0);
- msix_clear_and_set_ctrl(dev, 0,
+ pci_msix_clear_and_set_ctrl(dev, 0,
PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL);
arch_restore_msi_irqs(dev);
list_for_each_entry(entry, &dev->msi_list, list)
msix_mask_irq(entry, entry->masked);
- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
}
void pci_restore_msi_state(struct pci_dev *dev)
@@ -647,7 +626,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
int ret;
unsigned mask;
- msi_set_enable(dev, 0); /* Disable MSI during set up */
+ pci_msi_set_enable(dev, 0); /* Disable MSI during set up */
entry = msi_setup_entry(dev, nvec);
if (!entry)
@@ -683,7 +662,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
/* Set MSI enabled bits */
pci_intx_for_msi(dev, 0);
- msi_set_enable(dev, 1);
+ pci_msi_set_enable(dev, 1);
dev->msi_enabled = 1;
dev->irq = entry->irq;
@@ -775,7 +754,7 @@ static int msix_capability_init(struct pci_dev *dev,
void __iomem *base;
/* Ensure MSI-X is disabled while it is set up */
- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control);
/* Request & Map MSI-X table region */
@@ -801,7 +780,7 @@ static int msix_capability_init(struct pci_dev *dev,
* MSI-X registers. We need to mask all the vectors to prevent
* interrupts coming in before they're fully set up.
*/
- msix_clear_and_set_ctrl(dev, 0,
+ pci_msix_clear_and_set_ctrl(dev, 0,
PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE);
msix_program_entries(dev, entries);
@@ -814,7 +793,7 @@ static int msix_capability_init(struct pci_dev *dev,
pci_intx_for_msi(dev, 0);
dev->msix_enabled = 1;
- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
return 0;
@@ -919,7 +898,7 @@ void pci_msi_shutdown(struct pci_dev *dev)
BUG_ON(list_empty(&dev->msi_list));
desc = list_first_entry(&dev->msi_list, struct msi_desc, list);
- msi_set_enable(dev, 0);
+ pci_msi_set_enable(dev, 0);
pci_intx_for_msi(dev, 1);
dev->msi_enabled = 0;
@@ -1027,7 +1006,7 @@ void pci_msix_shutdown(struct pci_dev *dev)
__pci_msix_desc_mask_irq(entry, 1);
}
- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
pci_intx_for_msi(dev, 1);
dev->msix_enabled = 0;
}
@@ -1062,18 +1041,6 @@ EXPORT_SYMBOL(pci_msi_enabled);
void pci_msi_init_pci_dev(struct pci_dev *dev)
{
INIT_LIST_HEAD(&dev->msi_list);
-
- /* Disable the msi hardware to avoid screaming interrupts
- * during boot. This is the power on reset default so
- * usually this should be a noop.
- */
- dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI);
- if (dev->msi_cap)
- msi_set_enable(dev, 0);
-
- dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX);
- if (dev->msix_cap)
- msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
}
/**
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 6f6f175f51f7..314a625b78d6 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -420,7 +420,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
[PCI_D0] = ACPI_STATE_D0,
[PCI_D1] = ACPI_STATE_D1,
[PCI_D2] = ACPI_STATE_D2,
- [PCI_D3hot] = ACPI_STATE_D3_COLD,
+ [PCI_D3hot] = ACPI_STATE_D3_HOT,
[PCI_D3cold] = ACPI_STATE_D3_COLD,
};
int error = -EINVAL;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index acc4b6ef78c4..0008c950452c 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3101,39 +3101,6 @@ bool pci_check_and_unmask_intx(struct pci_dev *dev)
}
EXPORT_SYMBOL_GPL(pci_check_and_unmask_intx);
-/**
- * pci_msi_off - disables any MSI or MSI-X capabilities
- * @dev: the PCI device to operate on
- *
- * If you want to use MSI, see pci_enable_msi() and friends.
- * This is a lower-level primitive that allows us to disable
- * MSI operation at the device level.
- */
-void pci_msi_off(struct pci_dev *dev)
-{
- int pos;
- u16 control;
-
- /*
- * This looks like it could go in msi.c, but we need it even when
- * CONFIG_PCI_MSI=n. For the same reason, we can't use
- * dev->msi_cap or dev->msix_cap here.
- */
- pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
- if (pos) {
- pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
- control &= ~PCI_MSI_FLAGS_ENABLE;
- pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
- }
- pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
- if (pos) {
- pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
- control &= ~PCI_MSIX_FLAGS_ENABLE;
- pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
- }
-}
-EXPORT_SYMBOL_GPL(pci_msi_off);
-
int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size)
{
return dma_set_max_seg_size(&dev->dev, size);
@@ -4324,6 +4291,17 @@ bool pci_device_is_present(struct pci_dev *pdev)
}
EXPORT_SYMBOL_GPL(pci_device_is_present);
+void pci_ignore_hotplug(struct pci_dev *dev)
+{
+ struct pci_dev *bridge = dev->bus->self;
+
+ dev->ignore_hotplug = 1;
+ /* Propagate the "ignore hotplug" setting to the parent bridge. */
+ if (bridge)
+ bridge->ignore_hotplug = 1;
+}
+EXPORT_SYMBOL_GPL(pci_ignore_hotplug);
+
#define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE
static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0};
static DEFINE_SPINLOCK(resource_alignment_lock);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 9bd762c237ab..4ff0ff1c4088 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -146,6 +146,27 @@ static inline void pci_no_msi(void) { }
static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { }
#endif
+static inline void pci_msi_set_enable(struct pci_dev *dev, int enable)
+{
+ u16 control;
+
+ pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
+ control &= ~PCI_MSI_FLAGS_ENABLE;
+ if (enable)
+ control |= PCI_MSI_FLAGS_ENABLE;
+ pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
+}
+
+static inline void pci_msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u16 set)
+{
+ u16 ctrl;
+
+ pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &ctrl);
+ ctrl &= ~clear;
+ ctrl |= set;
+ pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl);
+}
+
void pci_realloc_get_opt(char *);
static inline int pci_no_d1d2(struct pci_dev *dev)
@@ -216,17 +237,6 @@ void __pci_bus_assign_resources(const struct pci_bus *bus,
struct list_head *fail_head);
bool pci_bus_clip_resource(struct pci_dev *dev, int idx);
-/**
- * pci_ari_enabled - query ARI forwarding status
- * @bus: the PCI bus
- *
- * Returns 1 if ARI forwarding is enabled, or 0 if not enabled;
- */
-static inline int pci_ari_enabled(struct pci_bus *bus)
-{
- return bus->self && bus->self->ari_enabled;
-}
-
void pci_reassigndev_resource_alignment(struct pci_dev *dev);
void pci_disable_bridge_window(struct pci_dev *dev);
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index 5653ea94547f..9803e3d039fe 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -425,8 +425,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev)
if (driver && driver->reset_link) {
status = driver->reset_link(udev);
- } else if (pci_pcie_type(udev) == PCI_EXP_TYPE_DOWNSTREAM ||
- pci_pcie_type(udev) == PCI_EXP_TYPE_ROOT_PORT) {
+ } else if (udev->has_secondary_link) {
status = default_reset_link(udev);
} else {
dev_printk(KERN_DEBUG, &dev->dev,
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 7d4fcdc512aa..317e3558a35e 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -127,15 +127,12 @@ static void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable)
{
struct pci_dev *child;
struct pci_bus *linkbus = link->pdev->subordinate;
+ u32 val = enable ? PCI_EXP_LNKCTL_CLKREQ_EN : 0;
- list_for_each_entry(child, &linkbus->devices, bus_list) {
- if (enable)
- pcie_capability_set_word(child, PCI_EXP_LNKCTL,
- PCI_EXP_LNKCTL_CLKREQ_EN);
- else
- pcie_capability_clear_word(child, PCI_EXP_LNKCTL,
- PCI_EXP_LNKCTL_CLKREQ_EN);
- }
+ list_for_each_entry(child, &linkbus->devices, bus_list)
+ pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN,
+ val);
link->clkpm_enabled = !!enable;
}
@@ -525,7 +522,7 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
INIT_LIST_HEAD(&link->children);
INIT_LIST_HEAD(&link->link);
link->pdev = pdev;
- if (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) {
struct pcie_link_state *parent;
parent = pdev->bus->parent->self->link_state;
if (!parent) {
@@ -559,10 +556,15 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
if (!aspm_support_enabled)
return;
- if (!pci_is_pcie(pdev) || pdev->link_state)
+ if (pdev->link_state)
return;
- if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
- pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM)
+
+ /*
+ * We allocate pcie_link_state for the component on the upstream
+ * end of a Link, so there's nothing to do unless this device has a
+ * Link on its secondary side.
+ */
+ if (!pdev->has_secondary_link)
return;
/* VIA has a strange chipset, root port is under a bridge */
@@ -675,10 +677,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev)
{
struct pcie_link_state *link = pdev->link_state;
- if (aspm_disabled || !pci_is_pcie(pdev) || !link)
- return;
- if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
- (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
+ if (aspm_disabled || !link)
return;
/*
* Devices changed PM state, we should recheck if latency
@@ -696,16 +695,12 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
{
struct pcie_link_state *link = pdev->link_state;
- if (aspm_disabled || !pci_is_pcie(pdev) || !link)
+ if (aspm_disabled || !link)
return;
if (aspm_policy != POLICY_POWERSAVE)
return;
- if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
- (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
- return;
-
down_read(&pci_bus_sem);
mutex_lock(&aspm_lock);
pcie_config_aspm_path(link);
@@ -714,8 +709,7 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
up_read(&pci_bus_sem);
}
-static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
- bool force)
+static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem)
{
struct pci_dev *parent = pdev->bus->self;
struct pcie_link_state *link;
@@ -723,8 +717,7 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
if (!pci_is_pcie(pdev))
return;
- if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
- pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM)
+ if (pdev->has_secondary_link)
parent = pdev;
if (!parent || !parent->link_state)
return;
@@ -737,7 +730,7 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
* a similar mechanism using "PciASPMOptOut", which is also
* ignored in this situation.
*/
- if (aspm_disabled && !force) {
+ if (aspm_disabled) {
dev_warn(&pdev->dev, "can't disable ASPM; OS doesn't have ASPM control\n");
return;
}
@@ -763,7 +756,7 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
void pci_disable_link_state_locked(struct pci_dev *pdev, int state)
{
- __pci_disable_link_state(pdev, state, false, false);
+ __pci_disable_link_state(pdev, state, false);
}
EXPORT_SYMBOL(pci_disable_link_state_locked);
@@ -778,7 +771,7 @@ EXPORT_SYMBOL(pci_disable_link_state_locked);
*/
void pci_disable_link_state(struct pci_dev *pdev, int state)
{
- __pci_disable_link_state(pdev, state, true, false);
+ __pci_disable_link_state(pdev, state, true);
}
EXPORT_SYMBOL(pci_disable_link_state);
@@ -907,9 +900,7 @@ void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev)
{
struct pcie_link_state *link_state = pdev->link_state;
- if (!pci_is_pcie(pdev) ||
- (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
- pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+ if (!link_state)
return;
if (link_state->aspm_support)
@@ -924,9 +915,7 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
{
struct pcie_link_state *link_state = pdev->link_state;
- if (!pci_is_pcie(pdev) ||
- (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
- pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+ if (!link_state)
return;
if (link_state->aspm_support)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 6675a7a1b9fc..cefd636681b6 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -254,8 +254,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
}
if (res->flags & IORESOURCE_MEM_64) {
- if ((sizeof(dma_addr_t) < 8 || sizeof(resource_size_t) < 8) &&
- sz64 > 0x100000000ULL) {
+ if ((sizeof(pci_bus_addr_t) < 8 || sizeof(resource_size_t) < 8)
+ && sz64 > 0x100000000ULL) {
res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
res->start = 0;
res->end = 0;
@@ -264,7 +264,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
goto out;
}
- if ((sizeof(dma_addr_t) < 8) && l) {
+ if ((sizeof(pci_bus_addr_t) < 8) && l) {
/* Above 32-bit boundary; try to reallocate */
res->flags |= IORESOURCE_UNSET;
res->start = 0;
@@ -399,7 +399,7 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
struct pci_dev *dev = child->self;
u16 mem_base_lo, mem_limit_lo;
u64 base64, limit64;
- dma_addr_t base, limit;
+ pci_bus_addr_t base, limit;
struct pci_bus_region region;
struct resource *res;
@@ -426,8 +426,8 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
}
}
- base = (dma_addr_t) base64;
- limit = (dma_addr_t) limit64;
+ base = (pci_bus_addr_t) base64;
+ limit = (pci_bus_addr_t) limit64;
if (base != base64) {
dev_err(&dev->dev, "can't handle bridge window above 4GB (bus address %#010llx)\n",
@@ -973,6 +973,8 @@ void set_pcie_port_type(struct pci_dev *pdev)
{
int pos;
u16 reg16;
+ int type;
+ struct pci_dev *parent;
pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
if (!pos)
@@ -982,6 +984,22 @@ void set_pcie_port_type(struct pci_dev *pdev)
pdev->pcie_flags_reg = reg16;
pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
+
+ /*
+ * A Root Port is always the upstream end of a Link. No PCIe
+ * component has two Links. Two Links are connected by a Switch
+ * that has a Port on each Link and internal logic to connect the
+ * two Ports.
+ */
+ type = pci_pcie_type(pdev);
+ if (type == PCI_EXP_TYPE_ROOT_PORT)
+ pdev->has_secondary_link = 1;
+ else if (type == PCI_EXP_TYPE_UPSTREAM ||
+ type == PCI_EXP_TYPE_DOWNSTREAM) {
+ parent = pci_upstream_bridge(pdev);
+ if (!parent->has_secondary_link)
+ pdev->has_secondary_link = 1;
+ }
}
void set_pcie_hotplug_bridge(struct pci_dev *pdev)
@@ -1085,6 +1103,22 @@ int pci_cfg_space_size(struct pci_dev *dev)
#define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
+static void pci_msi_setup_pci_dev(struct pci_dev *dev)
+{
+ /*
+ * Disable the MSI hardware to avoid screaming interrupts
+ * during boot. This is the power on reset default so
+ * usually this should be a noop.
+ */
+ dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI);
+ if (dev->msi_cap)
+ pci_msi_set_enable(dev, 0);
+
+ dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX);
+ if (dev->msix_cap)
+ pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
+}
+
/**
* pci_setup_device - fill in class and map information of a device
* @dev: the device structure to fill
@@ -1140,6 +1174,8 @@ int pci_setup_device(struct pci_dev *dev)
/* "Unknown power state" */
dev->current_state = PCI_UNKNOWN;
+ pci_msi_setup_pci_dev(dev);
+
/* Early fixups, before probing the BARs */
pci_fixup_device(pci_fixup_early, dev);
/* device class may be changed after fixup */
@@ -1611,7 +1647,7 @@ static int only_one_child(struct pci_bus *bus)
return 0;
if (pci_pcie_type(parent) == PCI_EXP_TYPE_ROOT_PORT)
return 1;
- if (pci_pcie_type(parent) == PCI_EXP_TYPE_DOWNSTREAM &&
+ if (parent->has_secondary_link &&
!pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS))
return 1;
return 0;
@@ -2094,25 +2130,6 @@ struct pci_bus *pci_scan_root_bus(struct device *parent, int bus,
}
EXPORT_SYMBOL(pci_scan_root_bus);
-/* Deprecated; use pci_scan_root_bus() instead */
-struct pci_bus *pci_scan_bus_parented(struct device *parent,
- int bus, struct pci_ops *ops, void *sysdata)
-{
- LIST_HEAD(resources);
- struct pci_bus *b;
-
- pci_add_resource(&resources, &ioport_resource);
- pci_add_resource(&resources, &iomem_resource);
- pci_add_resource(&resources, &busn_resource);
- b = pci_create_root_bus(parent, bus, ops, sysdata, &resources);
- if (b)
- pci_scan_child_bus(b);
- else
- pci_free_resource_list(&resources);
- return b;
-}
-EXPORT_SYMBOL(pci_scan_bus_parented);
-
struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops,
void *sysdata)
{
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index c6dc1dfd25d5..e9fd0e90fa3b 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -819,13 +819,6 @@ static void quirk_amd_ioapic(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, quirk_amd_ioapic);
-
-static void quirk_ioapic_rmw(struct pci_dev *dev)
-{
- if (dev->devfn == 0 && dev->bus->number == 0)
- sis_apic_bug = 1;
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_ANY_ID, quirk_ioapic_rmw);
#endif /* CONFIG_X86_IO_APIC */
/*
@@ -1600,7 +1593,6 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EESSC, quirk_a
static void quirk_pcie_mch(struct pci_dev *pdev)
{
- pci_msi_off(pdev);
pdev->no_msi = 1;
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, quirk_pcie_mch);
@@ -1614,7 +1606,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7525_MCH, quir
*/
static void quirk_pcie_pxh(struct pci_dev *dev)
{
- pci_msi_off(dev);
dev->no_msi = 1;
dev_warn(&dev->dev, "PXH quirk detected; SHPC device MSI disabled\n");
}
@@ -3572,6 +3563,8 @@ static void quirk_dma_func1_alias(struct pci_dev *dev)
* SKUs this function is not present, making this a ghost requester.
* https://bugzilla.kernel.org/show_bug.cgi?id=42679
*/
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9120,
+ quirk_dma_func1_alias);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9123,
quirk_dma_func1_alias);
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c14 */
@@ -3740,6 +3733,8 @@ static const u16 pci_quirk_intel_pch_acs_ids[] = {
/* Wellsburg (X99) PCH */
0x8d10, 0x8d11, 0x8d12, 0x8d13, 0x8d14, 0x8d15, 0x8d16, 0x8d17,
0x8d18, 0x8d19, 0x8d1a, 0x8d1b, 0x8d1c, 0x8d1d, 0x8d1e,
+ /* Lynx Point (9 series) PCH */
+ 0x8c90, 0x8c92, 0x8c94, 0x8c96, 0x8c98, 0x8c9a, 0x8c9c, 0x8c9e,
};
static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev)
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 4fd0cacf7ca0..508cc56130e3 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -428,16 +428,19 @@ static void __assign_resources_sorted(struct list_head *head,
* consistent.
*/
if (add_align > dev_res->res->start) {
+ resource_size_t r_size = resource_size(dev_res->res);
+
dev_res->res->start = add_align;
- dev_res->res->end = add_align +
- resource_size(dev_res->res);
+ dev_res->res->end = add_align + r_size - 1;
list_for_each_entry(dev_res2, head, list) {
align = pci_resource_alignment(dev_res2->dev,
dev_res2->res);
- if (add_align > align)
+ if (add_align > align) {
list_move_tail(&dev_res->list,
&dev_res2->list);
+ break;
+ }
}
}
diff --git a/drivers/pci/vc.c b/drivers/pci/vc.c
index 7e1304d2e389..dfbab61a1b47 100644
--- a/drivers/pci/vc.c
+++ b/drivers/pci/vc.c
@@ -108,8 +108,7 @@ static void pci_vc_enable(struct pci_dev *dev, int pos, int res)
struct pci_dev *link = NULL;
/* Enable VCs from the downstream device */
- if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
- pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)
+ if (!dev->has_secondary_link)
return;
ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF);
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index 7cfd2db02deb..240f38872085 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -446,9 +446,15 @@ static int pcifront_scan_root(struct pcifront_device *pdev,
unsigned int domain, unsigned int bus)
{
struct pci_bus *b;
+ LIST_HEAD(resources);
struct pcifront_sd *sd = NULL;
struct pci_bus_entry *bus_entry = NULL;
int err = 0;
+ static struct resource busn_res = {
+ .start = 0,
+ .end = 255,
+ .flags = IORESOURCE_BUS,
+ };
#ifndef CONFIG_PCI_DOMAINS
if (domain != 0) {
@@ -470,17 +476,21 @@ static int pcifront_scan_root(struct pcifront_device *pdev,
err = -ENOMEM;
goto err_out;
}
+ pci_add_resource(&resources, &ioport_resource);
+ pci_add_resource(&resources, &iomem_resource);
+ pci_add_resource(&resources, &busn_res);
pcifront_init_sd(sd, domain, bus, pdev);
pci_lock_rescan_remove();
- b = pci_scan_bus_parented(&pdev->xdev->dev, bus,
- &pcifront_bus_ops, sd);
+ b = pci_scan_root_bus(&pdev->xdev->dev, bus,
+ &pcifront_bus_ops, sd, &resources);
if (!b) {
dev_err(&pdev->xdev->dev,
"Error creating PCI Frontend Bus!\n");
err = -ENOMEM;
pci_unlock_rescan_remove();
+ pci_free_resource_list(&resources);
goto err_out;
}
@@ -488,7 +498,7 @@ static int pcifront_scan_root(struct pcifront_device *pdev,
list_add(&bus_entry->list, &pdev->root_buses);
- /* pci_scan_bus_parented skips devices which do not have a have
+ /* pci_scan_root_bus skips devices which do not have a
* devfn==0. The pcifront_scan_bus enumerates all devfn. */
err = pcifront_scan_bus(pdev, domain, bus, b);
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
index 64d0515b76bd..55ef7d1fd8da 100644
--- a/drivers/pcmcia/cistpl.c
+++ b/drivers/pcmcia/cistpl.c
@@ -94,8 +94,7 @@ static void __iomem *set_cis_map(struct pcmcia_socket *s,
mem->res = pcmcia_find_mem_region(0, s->map_size,
s->map_size, 0, s);
if (mem->res == NULL) {
- dev_printk(KERN_NOTICE, &s->dev,
- "cs: unable to map card memory!\n");
+ dev_notice(&s->dev, "cs: unable to map card memory!\n");
return NULL;
}
s->cis_virt = NULL;
@@ -381,8 +380,7 @@ int verify_cis_cache(struct pcmcia_socket *s)
buf = kmalloc(256, GFP_KERNEL);
if (buf == NULL) {
- dev_printk(KERN_WARNING, &s->dev,
- "no memory for verifying CIS\n");
+ dev_warn(&s->dev, "no memory for verifying CIS\n");
return -ENOMEM;
}
mutex_lock(&s->ops_mutex);
@@ -414,14 +412,14 @@ int pcmcia_replace_cis(struct pcmcia_socket *s,
const u8 *data, const size_t len)
{
if (len > CISTPL_MAX_CIS_SIZE) {
- dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n");
+ dev_warn(&s->dev, "replacement CIS too big\n");
return -EINVAL;
}
mutex_lock(&s->ops_mutex);
kfree(s->fake_cis);
s->fake_cis = kmalloc(len, GFP_KERNEL);
if (s->fake_cis == NULL) {
- dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n");
+ dev_warn(&s->dev, "no memory to replace CIS\n");
mutex_unlock(&s->ops_mutex);
return -ENOMEM;
}
@@ -434,17 +432,17 @@ int pcmcia_replace_cis(struct pcmcia_socket *s,
/* The high-level CIS tuple services */
-typedef struct tuple_flags {
+struct tuple_flags {
u_int link_space:4;
u_int has_link:1;
u_int mfc_fn:3;
u_int space:4;
-} tuple_flags;
+};
-#define LINK_SPACE(f) (((tuple_flags *)(&(f)))->link_space)
-#define HAS_LINK(f) (((tuple_flags *)(&(f)))->has_link)
-#define MFC_FN(f) (((tuple_flags *)(&(f)))->mfc_fn)
-#define SPACE(f) (((tuple_flags *)(&(f)))->space)
+#define LINK_SPACE(f) (((struct tuple_flags *)(&(f)))->link_space)
+#define HAS_LINK(f) (((struct tuple_flags *)(&(f)))->has_link)
+#define MFC_FN(f) (((struct tuple_flags *)(&(f)))->mfc_fn)
+#define SPACE(f) (((struct tuple_flags *)(&(f)))->space)
int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function,
tuple_t *tuple)
@@ -1451,26 +1449,16 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
done:
/* invalidate CIS cache on failure */
if (!dev_ok || !ident_ok || !count) {
-#if defined(CONFIG_MTD_PCMCIA_ANONYMOUS)
- /* Set up as an anonymous card. If we don't have anonymous
- memory support then just error the card as there is no
- point trying to second guess.
-
- Note: some cards have just a device entry, it may be
- worth extending support to cover these in future */
- if (!dev_ok || !ident_ok) {
- dev_info(&s->dev, "no CIS, assuming an anonymous memory card.\n");
- pcmcia_replace_cis(s, "\xFF", 1);
- count = 1;
- ret = 0;
- } else
-#endif
- {
- mutex_lock(&s->ops_mutex);
- destroy_cis_cache(s);
- mutex_unlock(&s->ops_mutex);
+ mutex_lock(&s->ops_mutex);
+ destroy_cis_cache(s);
+ mutex_unlock(&s->ops_mutex);
+ /* We differentiate between dev_ok, ident_ok and count
+ failures to allow for an override for anonymous cards
+ in ds.c */
+ if (!dev_ok || !ident_ok)
ret = -EIO;
- }
+ else
+ ret = -EFAULT;
}
if (info)
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index 5292db69c426..8007bfda720a 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -177,8 +177,8 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
wait_for_completion(&socket->thread_done);
if (!socket->thread) {
- dev_printk(KERN_WARNING, &socket->dev,
- "PCMCIA: warning: socket thread did not start\n");
+ dev_warn(&socket->dev,
+ "PCMCIA: warning: socket thread did not start\n");
return -EIO;
}
@@ -275,7 +275,7 @@ static int socket_reset(struct pcmcia_socket *skt)
msleep(unreset_check * 10);
}
- dev_printk(KERN_ERR, &skt->dev, "time out after reset.\n");
+ dev_err(&skt->dev, "time out after reset\n");
return -ETIMEDOUT;
}
@@ -325,8 +325,8 @@ static void socket_shutdown(struct pcmcia_socket *s)
s->ops->get_status(s, &status);
if (status & SS_POWERON) {
- dev_printk(KERN_ERR, &s->dev,
- "*** DANGER *** unable to remove socket power\n");
+ dev_err(&s->dev,
+ "*** DANGER *** unable to remove socket power\n");
}
s->state &= ~SOCKET_INUSE;
@@ -356,15 +356,13 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
}
if (status & SS_PENDING) {
- dev_printk(KERN_ERR, &skt->dev,
- "voltage interrogation timed out.\n");
+ dev_err(&skt->dev, "voltage interrogation timed out\n");
return -ETIMEDOUT;
}
if (status & SS_CARDBUS) {
if (!(skt->features & SS_CAP_CARDBUS)) {
- dev_printk(KERN_ERR, &skt->dev,
- "cardbus cards are not supported.\n");
+ dev_err(&skt->dev, "cardbus cards are not supported\n");
return -EINVAL;
}
skt->state |= SOCKET_CARDBUS;
@@ -379,7 +377,7 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
else if (!(status & SS_XVCARD))
skt->socket.Vcc = skt->socket.Vpp = 50;
else {
- dev_printk(KERN_ERR, &skt->dev, "unsupported voltage key.\n");
+ dev_err(&skt->dev, "unsupported voltage key\n");
return -EIO;
}
@@ -396,7 +394,7 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
skt->ops->get_status(skt, &status);
if (!(status & SS_POWERON)) {
- dev_printk(KERN_ERR, &skt->dev, "unable to apply power.\n");
+ dev_err(&skt->dev, "unable to apply power\n");
return -EIO;
}
@@ -429,8 +427,7 @@ static int socket_insert(struct pcmcia_socket *skt)
if (ret == 0) {
skt->state |= SOCKET_PRESENT;
- dev_printk(KERN_NOTICE, &skt->dev,
- "pccard: %s card inserted into slot %d\n",
+ dev_notice(&skt->dev, "pccard: %s card inserted into slot %d\n",
(skt->state & SOCKET_CARDBUS) ? "CardBus" : "PCMCIA",
skt->sock);
@@ -558,8 +555,7 @@ static int socket_resume(struct pcmcia_socket *skt)
static void socket_remove(struct pcmcia_socket *skt)
{
- dev_printk(KERN_NOTICE, &skt->dev,
- "pccard: card ejected from slot %d\n", skt->sock);
+ dev_notice(&skt->dev, "pccard: card ejected from slot %d\n", skt->sock);
socket_shutdown(skt);
}
@@ -605,8 +601,7 @@ static int pccardd(void *__skt)
/* register with the device core */
ret = device_register(&skt->dev);
if (ret) {
- dev_printk(KERN_WARNING, &skt->dev,
- "PCMCIA: unable to register socket\n");
+ dev_warn(&skt->dev, "PCMCIA: unable to register socket\n");
skt->thread = NULL;
complete(&skt->thread_done);
return 0;
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index d3baf0bfca9f..0decee6c556e 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -81,8 +81,8 @@ static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
struct pcmcia_dynid {
- struct list_head node;
- struct pcmcia_device_id id;
+ struct list_head node;
+ struct pcmcia_device_id id;
};
/**
@@ -284,8 +284,8 @@ static int pcmcia_device_probe(struct device *dev)
dev_dbg(dev, "base %x, regs %x", p_dev->config_base,
p_dev->config_regs);
} else {
- dev_printk(KERN_INFO, dev,
- "pcmcia: could not parse base and rmask0 of CIS\n");
+ dev_info(dev,
+ "pcmcia: could not parse base and rmask0 of CIS\n");
p_dev->config_base = 0;
p_dev->config_regs = 0;
}
@@ -382,15 +382,15 @@ static int pcmcia_device_remove(struct device *dev)
/* check for proper unloading */
if (p_dev->_irq || p_dev->_io || p_dev->_locked)
- dev_printk(KERN_INFO, dev,
- "pcmcia: driver %s did not release config properly\n",
- p_drv->name);
+ dev_info(dev,
+ "pcmcia: driver %s did not release config properly\n",
+ p_drv->name);
for (i = 0; i < MAX_WIN; i++)
if (p_dev->_win & CLIENT_WIN_REQ(i))
- dev_printk(KERN_INFO, dev,
- "pcmcia: driver %s did not release window properly\n",
- p_drv->name);
+ dev_info(dev,
+ "pcmcia: driver %s did not release window properly\n",
+ p_drv->name);
/* references from pcmcia_probe_device */
pcmcia_put_dev(p_dev);
@@ -566,7 +566,7 @@ static struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s,
c->io[i].name = p_dev->devname;
c->io[i].flags = IORESOURCE_IO;
}
- for (i = 0; i< MAX_WIN; i++) {
+ for (i = 0; i < MAX_WIN; i++) {
c->mem[i].name = p_dev->devname;
c->mem[i].flags = IORESOURCE_MEM;
}
@@ -578,8 +578,7 @@ static struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s,
mutex_unlock(&s->ops_mutex);
- dev_printk(KERN_NOTICE, &p_dev->dev,
- "pcmcia: registering new device %s (IRQ: %d)\n",
+ dev_notice(&p_dev->dev, "pcmcia: registering new device %s (IRQ: %d)\n",
p_dev->devname, p_dev->irq);
pcmcia_device_query(p_dev);
@@ -634,8 +633,24 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
ret = pccard_validate_cis(s, &no_chains);
if (ret || !no_chains) {
- dev_dbg(&s->dev, "invalid CIS or invalid resources\n");
- return -ENODEV;
+#if defined(CONFIG_MTD_PCMCIA_ANONYMOUS)
+ /* Set up as an anonymous card. If we don't have anonymous
+ memory support then just error the card as there is no
+ point trying to second guess.
+
+ Note: some cards have just a device entry, it may be
+ worth extending support to cover these in future */
+ if (ret == -EIO) {
+ dev_info(&s->dev, "no CIS, assuming an anonymous memory card.\n");
+ pcmcia_replace_cis(s, "\xFF", 1);
+ no_chains = 1;
+ ret = 0;
+ } else
+#endif
+ {
+ dev_dbg(&s->dev, "invalid CIS or invalid resources\n");
+ return -ENODEV;
+ }
}
if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc))
@@ -651,7 +666,7 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
}
-static int pcmcia_requery_callback(struct device *dev, void * _data)
+static int pcmcia_requery_callback(struct device *dev, void *_data)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
if (!p_dev->dev.driver) {
@@ -729,7 +744,7 @@ static void pcmcia_requery(struct pcmcia_socket *s)
* the one provided by the card is broken. The firmware files reside in
* /lib/firmware/ in userspace.
*/
-static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+static int pcmcia_load_firmware(struct pcmcia_device *dev, char *filename)
{
struct pcmcia_socket *s = dev->socket;
const struct firmware *fw;
@@ -745,16 +760,14 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
if (request_firmware(&fw, filename, &dev->dev) == 0) {
if (fw->size >= CISTPL_MAX_CIS_SIZE) {
ret = -EINVAL;
- dev_printk(KERN_ERR, &dev->dev,
- "pcmcia: CIS override is too big\n");
+ dev_err(&dev->dev, "pcmcia: CIS override is too big\n");
goto release;
}
if (!pcmcia_replace_cis(s, fw->data, fw->size))
ret = 0;
else {
- dev_printk(KERN_ERR, &dev->dev,
- "pcmcia: CIS override failed\n");
+ dev_err(&dev->dev, "pcmcia: CIS override failed\n");
goto release;
}
@@ -781,7 +794,8 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
#else /* !CONFIG_PCMCIA_LOAD_CIS */
-static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+static inline int pcmcia_load_firmware(struct pcmcia_device *dev,
+ char *filename)
{
return -ENODEV;
}
@@ -1148,10 +1162,9 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state)
if (p_drv->suspend) {
ret = p_drv->suspend(p_dev);
if (ret) {
- dev_printk(KERN_ERR, dev,
- "pcmcia: device %s (driver %s) did "
- "not want to go to sleep (%d)\n",
- p_dev->devname, p_drv->name, ret);
+ dev_err(dev,
+ "pcmcia: device %s (driver %s) did not want to go to sleep (%d)\n",
+ p_dev->devname, p_drv->name, ret);
mutex_lock(&p_dev->socket->ops_mutex);
p_dev->suspended = 0;
mutex_unlock(&p_dev->socket->ops_mutex);
@@ -1206,7 +1219,7 @@ static int pcmcia_dev_resume(struct device *dev)
}
-static int pcmcia_bus_suspend_callback(struct device *dev, void * _data)
+static int pcmcia_bus_suspend_callback(struct device *dev, void *_data)
{
struct pcmcia_socket *skt = _data;
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
@@ -1217,7 +1230,7 @@ static int pcmcia_bus_suspend_callback(struct device *dev, void * _data)
return runtime_suspend(dev);
}
-static int pcmcia_bus_resume_callback(struct device *dev, void * _data)
+static int pcmcia_bus_resume_callback(struct device *dev, void *_data)
{
struct pcmcia_socket *skt = _data;
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
@@ -1342,14 +1355,13 @@ static int pcmcia_bus_add_socket(struct device *dev,
socket = pcmcia_get_socket(socket);
if (!socket) {
- dev_printk(KERN_ERR, dev,
- "PCMCIA obtaining reference to socket failed\n");
+ dev_err(dev, "PCMCIA obtaining reference to socket failed\n");
return -ENODEV;
}
ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr);
if (ret) {
- dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n");
+ dev_err(dev, "PCMCIA registration failed\n");
pcmcia_put_socket(socket);
return ret;
}
@@ -1361,7 +1373,7 @@ static int pcmcia_bus_add_socket(struct device *dev,
ret = pccard_register_pcmcia(socket, &pcmcia_bus_callback);
if (ret) {
- dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n");
+ dev_err(dev, "PCMCIA registration failed\n");
pcmcia_put_socket(socket);
return ret;
}
diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c
index 7f9950d324df..61cf61ac621e 100644
--- a/drivers/pcmcia/electra_cf.c
+++ b/drivers/pcmcia/electra_cf.c
@@ -48,14 +48,14 @@ struct electra_cf_socket {
struct platform_device *ofdev;
unsigned long mem_phys;
- void __iomem * mem_base;
+ void __iomem *mem_base;
unsigned long mem_size;
- void __iomem * io_virt;
+ void __iomem *io_virt;
unsigned int io_base;
unsigned int io_size;
u_int irq;
struct resource iomem;
- void __iomem * gpio_base;
+ void __iomem *gpio_base;
int gpio_detect;
int gpio_vsense;
int gpio_3v;
@@ -202,7 +202,7 @@ static int electra_cf_probe(struct platform_device *ofdev)
if (err)
return -EINVAL;
- cf = kzalloc(sizeof *cf, GFP_KERNEL);
+ cf = kzalloc(sizeof(*cf), GFP_KERNEL);
if (!cf)
return -ENOMEM;
@@ -216,8 +216,10 @@ static int electra_cf_probe(struct platform_device *ofdev)
cf->io_size = PAGE_ALIGN(resource_size(&io));
area = __get_vm_area(cf->io_size, 0, PHB_IO_BASE, PHB_IO_END);
- if (area == NULL)
- return -ENOMEM;
+ if (area == NULL) {
+ status = -ENOMEM;
+ goto fail1;
+ }
cf->io_virt = (void __iomem *)(area->addr);
@@ -320,7 +322,8 @@ fail1:
iounmap(cf->mem_base);
if (cf->gpio_base)
iounmap(cf->gpio_base);
- device_init_wakeup(&ofdev->dev, 0);
+ if (area)
+ device_init_wakeup(&ofdev->dev, 0);
kfree(cf);
return status;
@@ -369,5 +372,5 @@ static struct platform_driver electra_cf_driver = {
module_platform_driver(electra_cf_driver);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
+MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
MODULE_DESCRIPTION("PA Semi Electra CF driver");
diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
index a2c138719bac..eb0d80a429e4 100644
--- a/drivers/pcmcia/i82365.c
+++ b/drivers/pcmcia/i82365.c
@@ -132,14 +132,14 @@ module_param(recov_time, int, 0444);
/*====================================================================*/
-typedef struct cirrus_state_t {
+struct cirrus_state {
u_char misc1, misc2;
u_char timer[6];
-} cirrus_state_t;
+};
-typedef struct vg46x_state_t {
+struct vg46x_state {
u_char ctl, ema;
-} vg46x_state_t;
+};
struct i82365_socket {
u_short type, flags;
@@ -149,8 +149,8 @@ struct i82365_socket {
u_short psock;
u_char cs_irq, intr;
union {
- cirrus_state_t cirrus;
- vg46x_state_t vg46x;
+ struct cirrus_state cirrus;
+ struct vg46x_state vg46x;
} state;
};
@@ -173,11 +173,11 @@ static struct timer_list poll_timer;
/*====================================================================*/
/* These definitions must match the pcic table! */
-typedef enum pcic_id {
+enum pcic_id {
IS_I82365A, IS_I82365B, IS_I82365DF,
IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
IS_PD6710, IS_PD672X, IS_VT83C469,
-} pcic_id;
+};
/* Flags for classifying groups of controllers */
#define IS_VADEM 0x0001
@@ -189,12 +189,12 @@ typedef enum pcic_id {
#define IS_REGISTERED 0x2000
#define IS_ALIVE 0x8000
-typedef struct pcic_t {
+struct pcic {
char *name;
u_short flags;
-} pcic_t;
+};
-static pcic_t pcic[] = {
+static struct pcic pcic[] = {
{ "Intel i82365sl A step", 0 },
{ "Intel i82365sl B step", 0 },
{ "Intel i82365sl DF", IS_DF_PWR },
@@ -208,7 +208,7 @@ static pcic_t pcic[] = {
{ "VIA VT83C469", IS_CIRRUS|IS_VIA },
};
-#define PCIC_COUNT (sizeof(pcic)/sizeof(pcic_t))
+#define PCIC_COUNT ARRAY_SIZE(pcic)
/*====================================================================*/
@@ -294,7 +294,7 @@ static void i365_set_pair(u_short sock, u_short reg, u_short data)
static void cirrus_get_state(u_short s)
{
int i;
- cirrus_state_t *p = &socket[s].state.cirrus;
+ struct cirrus_state *p = &socket[s].state.cirrus;
p->misc1 = i365_get(s, PD67_MISC_CTL_1);
p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
p->misc2 = i365_get(s, PD67_MISC_CTL_2);
@@ -306,7 +306,7 @@ static void cirrus_set_state(u_short s)
{
int i;
u_char misc;
- cirrus_state_t *p = &socket[s].state.cirrus;
+ struct cirrus_state *p = &socket[s].state.cirrus;
misc = i365_get(s, PD67_MISC_CTL_2);
i365_set(s, PD67_MISC_CTL_2, p->misc2);
@@ -321,7 +321,7 @@ static void cirrus_set_state(u_short s)
static u_int __init cirrus_set_opts(u_short s, char *buf)
{
struct i82365_socket *t = &socket[s];
- cirrus_state_t *p = &socket[s].state.cirrus;
+ struct cirrus_state *p = &socket[s].state.cirrus;
u_int mask = 0xffff;
if (has_ring == -1) has_ring = 1;
@@ -377,7 +377,7 @@ static u_int __init cirrus_set_opts(u_short s, char *buf)
static void vg46x_get_state(u_short s)
{
- vg46x_state_t *p = &socket[s].state.vg46x;
+ struct vg46x_state *p = &socket[s].state.vg46x;
p->ctl = i365_get(s, VG468_CTL);
if (socket[s].type == IS_VG469)
p->ema = i365_get(s, VG469_EXT_MODE);
@@ -385,7 +385,7 @@ static void vg46x_get_state(u_short s)
static void vg46x_set_state(u_short s)
{
- vg46x_state_t *p = &socket[s].state.vg46x;
+ struct vg46x_state *p = &socket[s].state.vg46x;
i365_set(s, VG468_CTL, p->ctl);
if (socket[s].type == IS_VG469)
i365_set(s, VG469_EXT_MODE, p->ema);
@@ -393,7 +393,7 @@ static void vg46x_set_state(u_short s)
static u_int __init vg46x_set_opts(u_short s, char *buf)
{
- vg46x_state_t *p = &socket[s].state.vg46x;
+ struct vg46x_state *p = &socket[s].state.vg46x;
flip(p->ctl, VG468_CTL_ASYNC, async_clock);
flip(p->ema, VG469_MODE_CABLE, cable_mode);
@@ -1285,13 +1285,6 @@ static int __init init_i82365(void)
ret = pcmcia_register_socket(&socket[i].socket);
if (!ret)
socket[i].flags |= IS_REGISTERED;
-
-#if 0 /* driver model ordering issue */
- class_device_create_file(&socket[i].socket.dev,
- &class_device_attr_info);
- class_device_create_file(&socket[i].socket.dev,
- &class_device_attr_exca);
-#endif
}
/* Finally, schedule a polling interrupt */
diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c
index 0075bd7162ed..70b089430fcc 100644
--- a/drivers/pcmcia/m32r_cfc.c
+++ b/drivers/pcmcia/m32r_cfc.c
@@ -754,13 +754,6 @@ static int __init init_m32r_pcc(void)
ret = pcmcia_register_socket(&socket[i].socket);
if (!ret)
socket[i].flags |= IS_REGISTERED;
-
-#if 0 /* driver model ordering issue */
- class_device_create_file(&socket[i].socket.dev,
- &class_device_attr_info);
- class_device_create_file(&socket[i].socket.dev,
- &class_device_attr_exca);
-#endif
}
/* Finally, schedule a polling interrupt */
diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c
index a77e571b08b8..eb126b98ed8a 100644
--- a/drivers/pcmcia/m32r_pcc.c
+++ b/drivers/pcmcia/m32r_pcc.c
@@ -716,13 +716,6 @@ static int __init init_m32r_pcc(void)
ret = pcmcia_register_socket(&socket[i].socket);
if (!ret)
socket[i].flags |= IS_REGISTERED;
-
-#if 0 /* driver model ordering issue */
- class_device_create_file(&socket[i].socket.dev,
- &class_device_attr_info);
- class_device_create_file(&socket[i].socket.dev,
- &class_device_attr_exca);
-#endif
}
/* Finally, schedule a polling interrupt */
diff --git a/drivers/pcmcia/pcmcia_cis.c b/drivers/pcmcia/pcmcia_cis.c
index e2c92415b892..1c05d74e850d 100644
--- a/drivers/pcmcia/pcmcia_cis.c
+++ b/drivers/pcmcia/pcmcia_cis.c
@@ -44,7 +44,7 @@ int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function,
buf = kmalloc(256, GFP_KERNEL);
if (buf == NULL) {
- dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
+ dev_warn(&s->dev, "no memory to read tuple\n");
return -ENOMEM;
}
tuple.DesiredTuple = code;
@@ -94,7 +94,7 @@ int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
buf = kzalloc(256, GFP_KERNEL);
if (buf == NULL) {
- dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
+ dev_warn(&s->dev, "no memory to read tuple\n");
return -ENOMEM;
}
diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c
index e8c19def1b0f..34aad895a239 100644
--- a/drivers/pcmcia/pcmcia_resource.c
+++ b/drivers/pcmcia/pcmcia_resource.c
@@ -508,8 +508,7 @@ int pcmcia_enable_device(struct pcmcia_device *p_dev)
s->socket.Vpp = p_dev->vpp;
if (s->ops->set_socket(s, &s->socket)) {
mutex_unlock(&s->ops_mutex);
- dev_printk(KERN_WARNING, &p_dev->dev,
- "Unable to set socket state\n");
+ dev_warn(&p_dev->dev, "Unable to set socket state\n");
return -EINVAL;
}
@@ -736,13 +735,11 @@ __pcmcia_request_exclusive_irq(struct pcmcia_device *p_dev,
ret = request_irq(p_dev->irq, handler, 0, p_dev->devname, p_dev->priv);
if (ret) {
ret = pcmcia_request_irq(p_dev, handler);
- dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: "
- "request for exclusive IRQ could not be fulfilled.\n");
- dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver "
- "needs updating to supported shared IRQ lines.\n");
+ dev_warn(&p_dev->dev, "pcmcia: request for exclusive IRQ could not be fulfilled\n");
+ dev_warn(&p_dev->dev, "pcmcia: the driver needs updating to supported shared IRQ lines\n");
}
if (ret)
- dev_printk(KERN_INFO, &p_dev->dev, "request_irq() failed\n");
+ dev_info(&p_dev->dev, "request_irq() failed\n");
else
p_dev->_irq = 1;
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
index 065704c605d5..5ef7b46a2578 100644
--- a/drivers/pcmcia/rsrc_nonstatic.c
+++ b/drivers/pcmcia/rsrc_nonstatic.c
@@ -191,15 +191,13 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
int any;
u_char *b, hole, most;
- dev_printk(KERN_INFO, &s->dev, "cs: IO port probe %#x-%#x:",
- base, base+num-1);
+ dev_info(&s->dev, "cs: IO port probe %#x-%#x:", base, base+num-1);
/* First, what does a floating port look like? */
b = kzalloc(256, GFP_KERNEL);
if (!b) {
- printk("\n");
- dev_printk(KERN_ERR, &s->dev,
- "do_io_probe: unable to kmalloc 256 bytes");
+ pr_cont("\n");
+ dev_err(&s->dev, "do_io_probe: unable to kmalloc 256 bytes\n");
return;
}
for (i = base, most = 0; i < base+num; i += 8) {
@@ -223,7 +221,7 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
if (!res) {
if (!any)
- printk(" excluding");
+ pr_cont(" excluding");
if (!bad)
bad = any = i;
continue;
@@ -234,13 +232,13 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
free_region(res);
if (j < 8) {
if (!any)
- printk(" excluding");
+ pr_cont(" excluding");
if (!bad)
bad = any = i;
} else {
if (bad) {
sub_interval(&s_data->io_db, bad, i-bad);
- printk(" %#x-%#x", bad, i-1);
+ pr_cont(" %#x-%#x", bad, i-1);
bad = 0;
}
}
@@ -248,15 +246,15 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
if (bad) {
if ((num > 16) && (bad == base) && (i == base+num)) {
sub_interval(&s_data->io_db, bad, i-bad);
- printk(" nothing: probe failed.\n");
+ pr_cont(" nothing: probe failed.\n");
return;
} else {
sub_interval(&s_data->io_db, bad, i-bad);
- printk(" %#x-%#x", bad, i-1);
+ pr_cont(" %#x-%#x", bad, i-1);
}
}
- printk(any ? "\n" : " clean.\n");
+ pr_cont("%s\n", !any ? " clean" : "");
}
#endif
@@ -413,8 +411,8 @@ static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
struct socket_data *s_data = s->resource_data;
u_long i, j, bad, fail, step;
- dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
- base, base+num-1);
+ dev_info(&s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
+ base, base+num-1);
bad = fail = 0;
step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
/* don't allow too large steps */
@@ -438,13 +436,13 @@ static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
}
if (i != j) {
if (!bad)
- printk(" excluding");
- printk(" %#05lx-%#05lx", i, j-1);
+ pr_cont(" excluding");
+ pr_cont(" %#05lx-%#05lx", i, j-1);
sub_interval(&s_data->mem_db, i, j-i);
bad += j-i;
}
}
- printk(bad ? "\n" : " clean.\n");
+ pr_cont("%s\n", !bad ? " clean" : "");
return num - bad;
}
@@ -495,7 +493,7 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
return 0;
if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
return 0;
- dev_printk(KERN_NOTICE, &s->dev,
+ dev_notice(&s->dev,
"cs: warning: no high memory space available!\n");
return -ENODEV;
}
@@ -975,9 +973,9 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
if (res == &ioport_resource)
continue;
- dev_printk(KERN_INFO, &s->cb_dev->dev,
- "pcmcia: parent PCI bridge window: %pR\n",
- res);
+ dev_info(&s->cb_dev->dev,
+ "pcmcia: parent PCI bridge window: %pR\n",
+ res);
if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
done |= IORESOURCE_IO;
@@ -990,9 +988,9 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
if (res == &iomem_resource)
continue;
- dev_printk(KERN_INFO, &s->cb_dev->dev,
- "pcmcia: parent PCI bridge window: %pR\n",
- res);
+ dev_info(&s->cb_dev->dev,
+ "pcmcia: parent PCI bridge window: %pR\n",
+ res);
if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
done |= IORESOURCE_MEM;
}
diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h
index a71789486cdf..5cb670e037a0 100644
--- a/drivers/pcmcia/ti113x.h
+++ b/drivers/pcmcia/ti113x.h
@@ -372,8 +372,8 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: mfunc 0x%08x, devctl 0x%02x\n", mfunc, devctl);
+ dev_info(&socket->dev->dev, "TI: mfunc 0x%08x, devctl 0x%02x\n",
+ mfunc, devctl);
/* make sure PCI interrupts are enabled before probing */
ti_init(socket);
@@ -387,8 +387,8 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
* We're here which means PCI interrupts are _not_ delivered. try to
* find the right setting (all serial or parallel)
*/
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: probing PCI interrupt failed, trying to fix\n");
+ dev_info(&socket->dev->dev,
+ "TI: probing PCI interrupt failed, trying to fix\n");
/* for serial PCI make sure MFUNC3 is set to IRQSER */
if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
@@ -412,8 +412,8 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: all-serial interrupts ok\n");
+ dev_info(&socket->dev->dev,
+ "TI: all-serial interrupts ok\n");
mfunc_old = mfunc;
goto out;
}
@@ -428,8 +428,8 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
}
/* serial PCI interrupts not working fall back to parallel */
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: falling back to parallel PCI interrupts\n");
+ dev_info(&socket->dev->dev,
+ "TI: falling back to parallel PCI interrupts\n");
devctl &= ~TI113X_DCR_IMODE_MASK;
devctl |= TI113X_DCR_IMODE_SERIAL; /* serial ISA could be right */
config_writeb(socket, TI113X_DEVICE_CONTROL, devctl);
@@ -460,8 +460,7 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
mfunc_old = mfunc;
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: parallel PCI interrupts ok\n");
+ dev_info(&socket->dev->dev, "TI: parallel PCI interrupts ok\n");
} else {
/* not working, back to old value */
mfunc = mfunc_old;
@@ -473,9 +472,8 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
out:
if (pci_irq_status < 1) {
socket->cb_irq = 0;
- dev_printk(KERN_INFO, &socket->dev->dev,
- "Yenta TI: no PCI interrupts. Fish. "
- "Please report.\n");
+ dev_info(&socket->dev->dev,
+ "Yenta TI: no PCI interrupts. Fish. Please report.\n");
}
}
@@ -547,9 +545,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: mfunc 0x%08x, devctl 0x%02x\n",
- mfunc, devctl);
+ dev_info(&socket->dev->dev, "TI: mfunc 0x%08x, devctl 0x%02x\n",
+ mfunc, devctl);
/* if IRQs are configured as tied, align irq of func1 with func0 */
sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
@@ -568,8 +565,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
* We're here which means PCI interrupts are _not_ delivered. try to
* find the right setting
*/
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: probing PCI interrupt failed, trying to fix\n");
+ dev_info(&socket->dev->dev,
+ "TI: probing PCI interrupt failed, trying to fix\n");
/* if all serial: set INTRTIE, probe again */
if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
@@ -578,8 +575,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
if (ti12xx_tie_interrupts(socket, &old_irq)) {
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: all-serial interrupts, tied ok\n");
+ dev_info(&socket->dev->dev,
+ "TI: all-serial interrupts, tied ok\n");
goto out;
}
@@ -616,8 +613,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: parallel PCI interrupts ok\n");
+ dev_info(&socket->dev->dev,
+ "TI: parallel PCI interrupts ok\n");
goto out;
}
@@ -632,8 +629,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
if (ti12xx_tie_interrupts(socket, &old_irq)) {
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: parallel PCI interrupts, tied ok\n");
+ dev_info(&socket->dev->dev,
+ "TI: parallel PCI interrupts, tied ok\n");
goto out;
}
@@ -644,8 +641,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
out:
if (pci_irq_status < 1) {
socket->cb_irq = 0;
- dev_printk(KERN_INFO, &socket->dev->dev,
- "TI: no PCI interrupts. Fish. Please report.\n");
+ dev_info(&socket->dev->dev,
+ "TI: no PCI interrupts. Fish. Please report.\n");
}
}
@@ -849,13 +846,12 @@ static int ti12xx_override(struct yenta_socket *socket)
/* make sure that memory burst is active */
val_orig = val = config_readl(socket, TI113X_SYSTEM_CONTROL);
if (disable_clkrun && PCI_FUNC(socket->dev->devfn) == 0) {
- dev_printk(KERN_INFO, &socket->dev->dev,
- "Disabling CLKRUN feature\n");
+ dev_info(&socket->dev->dev, "Disabling CLKRUN feature\n");
val |= TI113X_SCR_KEEPCLK;
}
if (!(val & TI122X_SCR_MRBURSTUP)) {
- dev_printk(KERN_INFO, &socket->dev->dev,
- "Enabling burst memory read transactions\n");
+ dev_info(&socket->dev->dev,
+ "Enabling burst memory read transactions\n");
val |= TI122X_SCR_MRBURSTUP;
}
if (val_orig != val)
@@ -866,12 +862,10 @@ static int ti12xx_override(struct yenta_socket *socket)
* CSC interrupts to PCI rather than INTVAL.
*/
val = config_readb(socket, TI1250_DIAGNOSTIC);
- dev_printk(KERN_INFO, &socket->dev->dev,
- "Using %s to route CSC interrupts to PCI\n",
- (val & TI1250_DIAG_PCI_CSC) ? "CSCINT" : "INTVAL");
- dev_printk(KERN_INFO, &socket->dev->dev,
- "Routing CardBus interrupts to %s\n",
- (val & TI1250_DIAG_PCI_IREQ) ? "PCI" : "ISA");
+ dev_info(&socket->dev->dev, "Using %s to route CSC interrupts to PCI\n",
+ (val & TI1250_DIAG_PCI_CSC) ? "CSCINT" : "INTVAL");
+ dev_info(&socket->dev->dev, "Routing CardBus interrupts to %s\n",
+ (val & TI1250_DIAG_PCI_IREQ) ? "PCI" : "ISA");
/* do irqrouting, depending on function */
if (PCI_FUNC(socket->dev->devfn) == 0)
@@ -896,9 +890,9 @@ static int ti1250_override(struct yenta_socket *socket)
diag |= TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ;
if (diag != old) {
- dev_printk(KERN_INFO, &socket->dev->dev,
- "adjusting diagnostic: %02x -> %02x\n",
- old, diag);
+ dev_info(&socket->dev->dev,
+ "adjusting diagnostic: %02x -> %02x\n",
+ old, diag);
config_writeb(socket, TI1250_DIAGNOSTIC, diag);
}
@@ -963,9 +957,9 @@ static void ene_tune_bridge(struct pcmcia_socket *sock, struct pci_bus *bus)
/* default to clear TLTEnable bit, old behaviour */
test_c9 &= ~ENE_TEST_C9_TLTENABLE;
- dev_printk(KERN_INFO, &socket->dev->dev,
- "EnE: chaning testregister 0xC9, %02x -> %02x\n",
- old_c9, test_c9);
+ dev_info(&socket->dev->dev,
+ "EnE: changing testregister 0xC9, %02x -> %02x\n",
+ old_c9, test_c9);
config_writeb(socket, ENE_TEST_C9, test_c9);
}
diff --git a/drivers/pcmcia/topic.h b/drivers/pcmcia/topic.h
index 615a45a8fe86..582688fe7505 100644
--- a/drivers/pcmcia/topic.h
+++ b/drivers/pcmcia/topic.h
@@ -104,6 +104,9 @@
#define TOPIC_EXCA_IF_CONTROL 0x3e /* 8 bit */
#define TOPIC_EXCA_IFC_33V_ENA 0x01
+#define TOPIC_PCI_CFG_PPBCN 0x3e /* 16-bit */
+#define TOPIC_PCI_CFG_PPBCN_WBEN 0x0400
+
static void topic97_zoom_video(struct pcmcia_socket *sock, int onoff)
{
struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
@@ -138,6 +141,7 @@ static int topic97_override(struct yenta_socket *socket)
static int topic95_override(struct yenta_socket *socket)
{
u8 fctrl;
+ u16 ppbcn;
/* enable 3.3V support for 16bit cards */
fctrl = exca_readb(socket, TOPIC_EXCA_IF_CONTROL);
@@ -146,6 +150,18 @@ static int topic95_override(struct yenta_socket *socket)
/* tell yenta to use exca registers to power 16bit cards */
socket->flags |= YENTA_16BIT_POWER_EXCA | YENTA_16BIT_POWER_DF;
+ /* Disable write buffers to prevent lockups under load with numerous
+ Cardbus cards, observed on Tecra 500CDT and reported elsewhere on the
+ net. This is not a power-on default according to the datasheet
+ but some BIOSes seem to set it. */
+ if (pci_read_config_word(socket->dev, TOPIC_PCI_CFG_PPBCN, &ppbcn) == 0
+ && socket->dev->revision <= 7
+ && (ppbcn & TOPIC_PCI_CFG_PPBCN_WBEN)) {
+ ppbcn &= ~TOPIC_PCI_CFG_PPBCN_WBEN;
+ pci_write_config_word(socket->dev, TOPIC_PCI_CFG_PPBCN, ppbcn);
+ dev_info(&socket->dev->dev, "Disabled ToPIC95 Cardbus write buffers.\n");
+ }
+
return 0;
}
diff --git a/drivers/pcmcia/vrc4171_card.c b/drivers/pcmcia/vrc4171_card.c
index 21973d55a055..1e5fa211fb70 100644
--- a/drivers/pcmcia/vrc4171_card.c
+++ b/drivers/pcmcia/vrc4171_card.c
@@ -84,32 +84,32 @@ MODULE_LICENSE("GPL");
#define IO_MAX_MAPS 2
#define MEM_MAX_MAPS 5
-typedef enum {
+enum vrc4171_slot {
SLOT_PROBE = 0,
SLOT_NOPROBE_IO,
SLOT_NOPROBE_MEM,
SLOT_NOPROBE_ALL,
SLOT_INITIALIZED,
-} vrc4171_slot_t;
+};
-typedef enum {
+enum vrc4171_slotb {
SLOTB_IS_NONE,
SLOTB_IS_PCCARD,
SLOTB_IS_CF,
SLOTB_IS_FLASHROM,
-} vrc4171_slotb_t;
+};
-typedef struct vrc4171_socket {
- vrc4171_slot_t slot;
+struct vrc4171_socket {
+ enum vrc4171_slot slot;
struct pcmcia_socket pcmcia_socket;
char name[24];
int csc_irq;
int io_irq;
spinlock_t lock;
-} vrc4171_socket_t;
+};
-static vrc4171_socket_t vrc4171_sockets[CARD_MAX_SLOTS];
-static vrc4171_slotb_t vrc4171_slotb = SLOTB_IS_NONE;
+static struct vrc4171_socket vrc4171_sockets[CARD_MAX_SLOTS];
+static enum vrc4171_slotb vrc4171_slotb = SLOTB_IS_NONE;
static char vrc4171_card_name[] = "NEC VRC4171 Card Controller";
static unsigned int vrc4171_irq;
static uint16_t vrc4171_irq_mask = 0xdeb8;
@@ -141,7 +141,7 @@ static inline uint16_t vrc4171_get_irq_status(void)
return inw(INTERRUPT_STATUS);
}
-static inline void vrc4171_set_multifunction_pin(vrc4171_slotb_t config)
+static inline void vrc4171_set_multifunction_pin(enum vrc4171_slotb config)
{
uint16_t config1;
@@ -234,7 +234,7 @@ static inline int search_nonuse_irq(void)
static int pccard_init(struct pcmcia_socket *sock)
{
- vrc4171_socket_t *socket;
+ struct vrc4171_socket *socket;
unsigned int slot;
sock->features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
@@ -317,7 +317,7 @@ static inline uint8_t set_Vcc_value(u_char Vcc)
static int pccard_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
{
- vrc4171_socket_t *socket;
+ struct vrc4171_socket *socket;
unsigned int slot;
uint8_t voltage, power, control, cscint;
@@ -517,7 +517,7 @@ static inline unsigned int get_events(int slot)
static irqreturn_t pccard_interrupt(int irq, void *dev_id)
{
- vrc4171_socket_t *socket;
+ struct vrc4171_socket *socket;
unsigned int events;
irqreturn_t retval = IRQ_NONE;
uint16_t status;
@@ -567,7 +567,7 @@ static inline void reserve_using_irq(int slot)
static int vrc4171_add_sockets(void)
{
- vrc4171_socket_t *socket;
+ struct vrc4171_socket *socket;
int slot, retval;
for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
@@ -617,7 +617,7 @@ static int vrc4171_add_sockets(void)
static void vrc4171_remove_sockets(void)
{
- vrc4171_socket_t *socket;
+ struct vrc4171_socket *socket;
int slot;
for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c
index 965bd8491233..5d6d9b1549bc 100644
--- a/drivers/pcmcia/yenta_socket.c
+++ b/drivers/pcmcia/yenta_socket.c
@@ -712,10 +712,9 @@ static int yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned type
pcibios_bus_to_resource(dev->bus, res, &region);
if (pci_claim_resource(dev, PCI_BRIDGE_RESOURCES + nr) == 0)
return 0;
- dev_printk(KERN_INFO, &dev->dev,
- "Preassigned resource %d busy or not available, "
- "reconfiguring...\n",
- nr);
+ dev_info(&dev->dev,
+ "Preassigned resource %d busy or not available, reconfiguring...\n",
+ nr);
}
if (type & IORESOURCE_IO) {
@@ -738,9 +737,9 @@ static int yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned type
return 1;
}
- dev_printk(KERN_INFO, &dev->dev,
- "no resource of type %x available, trying to continue...\n",
- type);
+ dev_info(&dev->dev,
+ "no resource of type %x available, trying to continue...\n",
+ type);
res->start = res->end = res->flags = 0;
return 0;
}
@@ -802,13 +801,13 @@ static void yenta_close(struct pci_dev *dev)
else
del_timer_sync(&sock->poll_timer);
- if (sock->base)
- iounmap(sock->base);
+ iounmap(sock->base);
yenta_free_resources(sock);
pci_release_regions(dev);
pci_disable_device(dev);
pci_set_drvdata(dev, NULL);
+ kfree(sock);
}
@@ -979,8 +978,8 @@ static int yenta_probe_cb_irq(struct yenta_socket *socket)
socket->probe_status = 0;
if (request_irq(socket->cb_irq, yenta_probe_handler, IRQF_SHARED, "yenta", socket)) {
- dev_printk(KERN_WARNING, &socket->dev->dev,
- "request_irq() in yenta_probe_cb_irq() failed!\n");
+ dev_warn(&socket->dev->dev,
+ "request_irq() in yenta_probe_cb_irq() failed!\n");
return -1;
}
@@ -1019,9 +1018,8 @@ static void yenta_get_socket_capabilities(struct yenta_socket *socket, u32 isa_i
else
socket->socket.irq_mask = 0;
- dev_printk(KERN_INFO, &socket->dev->dev,
- "ISA IRQ mask 0x%04x, PCI irq %d\n",
- socket->socket.irq_mask, socket->cb_irq);
+ dev_info(&socket->dev->dev, "ISA IRQ mask 0x%04x, PCI irq %d\n",
+ socket->socket.irq_mask, socket->cb_irq);
}
/*
@@ -1111,9 +1109,9 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge)
/* Show that the wanted subordinate number is not possible: */
if (cardbus_bridge->busn_res.end > upper_limit)
- dev_printk(KERN_WARNING, &cardbus_bridge->dev,
- "Upper limit for fixing this "
- "bridge's parent bridge: #%02x\n", upper_limit);
+ dev_warn(&cardbus_bridge->dev,
+ "Upper limit for fixing this bridge's parent bridge: #%02x\n",
+ upper_limit);
/* If we have room to increase the bridge's subordinate number, */
if (bridge_to_fix->busn_res.end < upper_limit) {
@@ -1122,11 +1120,11 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge)
unsigned char subordinate_to_assign =
min_t(int, cardbus_bridge->busn_res.end, upper_limit);
- dev_printk(KERN_INFO, &bridge_to_fix->dev,
- "Raising subordinate bus# of parent "
- "bus (#%02x) from #%02x to #%02x\n",
- bridge_to_fix->number,
- (int)bridge_to_fix->busn_res.end, subordinate_to_assign);
+ dev_info(&bridge_to_fix->dev,
+ "Raising subordinate bus# of parent bus (#%02x) from #%02x to #%02x\n",
+ bridge_to_fix->number,
+ (int)bridge_to_fix->busn_res.end,
+ subordinate_to_assign);
/* Save the new subordinate in the bus struct of the bridge */
bridge_to_fix->busn_res.end = subordinate_to_assign;
@@ -1153,8 +1151,7 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
* Bail out if so.
*/
if (!dev->subordinate) {
- dev_printk(KERN_ERR, &dev->dev, "no bus associated! "
- "(try 'pci=assign-busses')\n");
+ dev_err(&dev->dev, "no bus associated! (try 'pci=assign-busses')\n");
return -ENODEV;
}
@@ -1189,7 +1186,7 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto disable;
if (!pci_resource_start(dev, 0)) {
- dev_printk(KERN_ERR, &dev->dev, "No cardbus resource!\n");
+ dev_err(&dev->dev, "No cardbus resource!\n");
ret = -ENODEV;
goto release;
}
@@ -1208,8 +1205,8 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
* report the subsystem vendor and device for help debugging
* the irq stuff...
*/
- dev_printk(KERN_INFO, &dev->dev, "CardBus bridge found [%04x:%04x]\n",
- dev->subsystem_vendor, dev->subsystem_device);
+ dev_info(&dev->dev, "CardBus bridge found [%04x:%04x]\n",
+ dev->subsystem_vendor, dev->subsystem_device);
yenta_config_init(socket);
@@ -1239,12 +1236,10 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
setup_timer(&socket->poll_timer, yenta_interrupt_wrapper,
(unsigned long)socket);
mod_timer(&socket->poll_timer, jiffies + HZ);
- dev_printk(KERN_INFO, &dev->dev,
- "no PCI IRQ, CardBus support disabled for this "
- "socket.\n");
- dev_printk(KERN_INFO, &dev->dev,
- "check your BIOS CardBus, BIOS IRQ or ACPI "
- "settings.\n");
+ dev_info(&dev->dev,
+ "no PCI IRQ, CardBus support disabled for this socket.\n");
+ dev_info(&dev->dev,
+ "check your BIOS CardBus, BIOS IRQ or ACPI settings.\n");
} else {
socket->socket.features |= SS_CAP_CARDBUS;
}
@@ -1252,32 +1247,41 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
/* Figure out what the dang thing can do for the PCMCIA layer... */
yenta_interrogate(socket);
yenta_get_socket_capabilities(socket, isa_interrupts);
- dev_printk(KERN_INFO, &dev->dev,
- "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE));
+ dev_info(&dev->dev, "Socket status: %08x\n",
+ cb_readl(socket, CB_SOCKET_STATE));
yenta_fixup_parent_bridge(dev->subordinate);
/* Register it with the pcmcia layer.. */
ret = pcmcia_register_socket(&socket->socket);
- if (ret == 0) {
- /* Add the yenta register attributes */
- ret = device_create_file(&dev->dev, &dev_attr_yenta_registers);
- if (ret == 0)
- goto out;
-
- /* error path... */
- pcmcia_unregister_socket(&socket->socket);
- }
+ if (ret)
+ goto free_irq;
+
+ /* Add the yenta register attributes */
+ ret = device_create_file(&dev->dev, &dev_attr_yenta_registers);
+ if (ret)
+ goto unregister_socket;
+ return ret;
+
+ /* error path... */
+ unregister_socket:
+ pcmcia_unregister_socket(&socket->socket);
+ free_irq:
+ if (socket->cb_irq)
+ free_irq(socket->cb_irq, socket);
+ else
+ del_timer_sync(&socket->poll_timer);
unmap:
iounmap(socket->base);
+ yenta_free_resources(socket);
release:
pci_release_regions(dev);
disable:
pci_disable_device(dev);
free:
+ pci_set_drvdata(dev, NULL);
kfree(socket);
- out:
return ret;
}
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index a53bd5b52df9..fc9b9f0ea91e 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -38,7 +38,9 @@ config ARMADA375_USBCLUSTER_PHY
config PHY_DM816X_USB
tristate "TI dm816x USB PHY driver"
depends on ARCH_OMAP2PLUS
+ depends on USB_SUPPORT
select GENERIC_PHY
+ select USB_PHY
help
Enable this for dm816x USB to work.
@@ -97,8 +99,9 @@ config OMAP_CONTROL_PHY
config OMAP_USB2
tristate "OMAP USB2 PHY Driver"
depends on ARCH_OMAP2PLUS
- depends on USB_PHY
+ depends on USB_SUPPORT
select GENERIC_PHY
+ select USB_PHY
select OMAP_CONTROL_PHY
depends on OMAP_OCP2SCP
help
@@ -122,8 +125,9 @@ config TI_PIPE3
config TWL4030_USB
tristate "TWL4030 USB Transceiver Driver"
depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
- depends on USB_PHY
+ depends on USB_SUPPORT
select GENERIC_PHY
+ select USB_PHY
help
Enable this to support the USB OTG transceiver on TWL4030
family chips (including the TWL5030 and TPS659x0 devices).
@@ -304,7 +308,7 @@ config PHY_STIH41X_USB
config PHY_QCOM_UFS
tristate "Qualcomm UFS PHY driver"
- depends on OF && ARCH_MSM
+ depends on OF && ARCH_QCOM
select GENERIC_PHY
help
Support for UFS PHY on QCOM chipsets.
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 3791838f4bd4..63bc12d7a73e 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -530,7 +530,7 @@ struct phy *phy_optional_get(struct device *dev, const char *string)
{
struct phy *phy = phy_get(dev, string);
- if (PTR_ERR(phy) == -ENODEV)
+ if (IS_ERR(phy) && (PTR_ERR(phy) == -ENODEV))
phy = NULL;
return phy;
@@ -584,7 +584,7 @@ struct phy *devm_phy_optional_get(struct device *dev, const char *string)
{
struct phy *phy = devm_phy_get(dev, string);
- if (PTR_ERR(phy) == -ENODEV)
+ if (IS_ERR(phy) && (PTR_ERR(phy) == -ENODEV))
phy = NULL;
return phy;
diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c
index 183ef4368101..c1a468686bdc 100644
--- a/drivers/phy/phy-omap-usb2.c
+++ b/drivers/phy/phy-omap-usb2.c
@@ -275,6 +275,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
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");
+ pm_runtime_disable(phy->dev);
return PTR_ERR(phy->wkupclk);
} else {
dev_warn(&pdev->dev,
diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c
index 778276aba3aa..97d45f47d1ad 100644
--- a/drivers/phy/phy-rcar-gen2.c
+++ b/drivers/phy/phy-rcar-gen2.c
@@ -23,7 +23,7 @@
#define USBHS_LPSTS 0x02
#define USBHS_UGCTRL 0x80
#define USBHS_UGCTRL2 0x84
-#define USBHS_UGSTS 0x88 /* The manuals have 0x90 */
+#define USBHS_UGSTS 0x88 /* From technical update */
/* Low Power Status register (LPSTS) */
#define USBHS_LPSTS_SUSPM 0x4000
@@ -41,7 +41,7 @@
#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030
/* USB General status register (UGSTS) */
-#define USBHS_UGSTS_LOCK 0x00000300 /* The manuals have 0x3 */
+#define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */
#define PHYS_PER_CHANNEL 2
diff --git a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c b/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
index 4ad5c1a996e3..e406e3d8c1c7 100644
--- a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
+++ b/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
@@ -643,7 +643,9 @@ static const struct cygnus_gpio_pin_range cygnus_gpio_pintable[] = {
CYGNUS_PINRANGE(87, 104, 12),
CYGNUS_PINRANGE(99, 102, 2),
CYGNUS_PINRANGE(101, 90, 4),
- CYGNUS_PINRANGE(105, 116, 10),
+ CYGNUS_PINRANGE(105, 116, 6),
+ CYGNUS_PINRANGE(111, 100, 2),
+ CYGNUS_PINRANGE(113, 122, 4),
CYGNUS_PINRANGE(123, 11, 1),
CYGNUS_PINRANGE(124, 38, 4),
CYGNUS_PINRANGE(128, 43, 1),
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 89dca77ca038..18ee2089df4a 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -1110,7 +1110,7 @@ void devm_pinctrl_put(struct pinctrl *p)
EXPORT_SYMBOL_GPL(devm_pinctrl_put);
int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
- bool dup, bool locked)
+ bool dup)
{
int i, ret;
struct pinctrl_maps *maps_node;
@@ -1178,11 +1178,9 @@ int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
maps_node->maps = maps;
}
- if (!locked)
- mutex_lock(&pinctrl_maps_mutex);
+ mutex_lock(&pinctrl_maps_mutex);
list_add_tail(&maps_node->node, &pinctrl_maps);
- if (!locked)
- mutex_unlock(&pinctrl_maps_mutex);
+ mutex_unlock(&pinctrl_maps_mutex);
return 0;
}
@@ -1197,7 +1195,7 @@ int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
int pinctrl_register_mappings(struct pinctrl_map const *maps,
unsigned num_maps)
{
- return pinctrl_register_map(maps, num_maps, true, false);
+ return pinctrl_register_map(maps, num_maps, true);
}
void pinctrl_unregister_map(struct pinctrl_map const *map)
diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h
index 75476b3d87da..b24ea846c867 100644
--- a/drivers/pinctrl/core.h
+++ b/drivers/pinctrl/core.h
@@ -183,7 +183,7 @@ static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev,
}
int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
- bool dup, bool locked);
+ bool dup);
void pinctrl_unregister_map(struct pinctrl_map const *map);
extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev);
diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
index eda13de2e7c0..0bbf7d71b281 100644
--- a/drivers/pinctrl/devicetree.c
+++ b/drivers/pinctrl/devicetree.c
@@ -92,7 +92,7 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
dt_map->num_maps = num_maps;
list_add_tail(&dt_map->node, &p->dt_maps);
- return pinctrl_register_map(map, num_maps, false, true);
+ return pinctrl_register_map(map, num_maps, false);
}
struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c
index 82f691eeeec4..732ff757a95f 100644
--- a/drivers/pinctrl/intel/pinctrl-cherryview.c
+++ b/drivers/pinctrl/intel/pinctrl-cherryview.c
@@ -1292,6 +1292,49 @@ static void chv_gpio_irq_unmask(struct irq_data *d)
chv_gpio_irq_mask_unmask(d, false);
}
+static unsigned chv_gpio_irq_startup(struct irq_data *d)
+{
+ /*
+ * Check if the interrupt has been requested with 0 as triggering
+ * type. In that case it is assumed that the current values
+ * programmed to the hardware are used (e.g BIOS configured
+ * defaults).
+ *
+ * In that case ->irq_set_type() will never be called so we need to
+ * read back the values from hardware now, set correct flow handler
+ * and update mappings before the interrupt is being used.
+ */
+ if (irqd_get_trigger_type(d) == IRQ_TYPE_NONE) {
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct chv_pinctrl *pctrl = gpiochip_to_pinctrl(gc);
+ unsigned offset = irqd_to_hwirq(d);
+ int pin = chv_gpio_offset_to_pin(pctrl, offset);
+ irq_flow_handler_t handler;
+ unsigned long flags;
+ u32 intsel, value;
+
+ intsel = readl(chv_padreg(pctrl, pin, CHV_PADCTRL0));
+ intsel &= CHV_PADCTRL0_INTSEL_MASK;
+ intsel >>= CHV_PADCTRL0_INTSEL_SHIFT;
+
+ value = readl(chv_padreg(pctrl, pin, CHV_PADCTRL1));
+ if (value & CHV_PADCTRL1_INTWAKECFG_LEVEL)
+ handler = handle_level_irq;
+ else
+ handler = handle_edge_irq;
+
+ spin_lock_irqsave(&pctrl->lock, flags);
+ if (!pctrl->intr_lines[intsel]) {
+ __irq_set_handler_locked(d->irq, handler);
+ pctrl->intr_lines[intsel] = offset;
+ }
+ spin_unlock_irqrestore(&pctrl->lock, flags);
+ }
+
+ chv_gpio_irq_unmask(d);
+ return 0;
+}
+
static int chv_gpio_irq_type(struct irq_data *d, unsigned type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
@@ -1357,6 +1400,7 @@ static int chv_gpio_irq_type(struct irq_data *d, unsigned type)
static struct irq_chip chv_gpio_irqchip = {
.name = "chv-gpio",
+ .irq_startup = chv_gpio_irq_startup,
.irq_ack = chv_gpio_irq_ack,
.irq_mask = chv_gpio_irq_mask,
.irq_unmask = chv_gpio_irq_unmask,
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
index 493294c0ebe6..474812e2b0cb 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
@@ -881,6 +881,8 @@ static int mtk_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
if (!mtk_eint_get_mask(pctl, eint_num)) {
mtk_eint_mask(d);
unmask = 1;
+ } else {
+ unmask = 0;
}
clr_bit = 0xff << eint_offset;
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index edcd140e0899..a70a5fe79d44 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -569,7 +569,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc)
domain->chip.direction_output = meson_gpio_direction_output;
domain->chip.get = meson_gpio_get;
domain->chip.set = meson_gpio_set;
- domain->chip.base = -1;
+ domain->chip.base = domain->data->pin_base;
domain->chip.ngpio = domain->data->num_pins;
domain->chip.can_sleep = false;
domain->chip.of_node = domain->of_node;
diff --git a/drivers/pinctrl/meson/pinctrl-meson8b.c b/drivers/pinctrl/meson/pinctrl-meson8b.c
index 2f7ea6229880..9677807db364 100644
--- a/drivers/pinctrl/meson/pinctrl-meson8b.c
+++ b/drivers/pinctrl/meson/pinctrl-meson8b.c
@@ -876,13 +876,13 @@ static struct meson_domain_data meson8b_domain_data[] = {
.banks = meson8b_banks,
.num_banks = ARRAY_SIZE(meson8b_banks),
.pin_base = 0,
- .num_pins = 83,
+ .num_pins = 130,
},
{
.name = "ao-bank",
.banks = meson8b_ao_banks,
.num_banks = ARRAY_SIZE(meson8b_ao_banks),
- .pin_base = 83,
+ .pin_base = 130,
.num_pins = 16,
},
};
diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-370.c b/drivers/pinctrl/mvebu/pinctrl-armada-370.c
index 42f930f70de3..03aa58c4cb85 100644
--- a/drivers/pinctrl/mvebu/pinctrl-armada-370.c
+++ b/drivers/pinctrl/mvebu/pinctrl-armada-370.c
@@ -364,7 +364,7 @@ static struct mvebu_mpp_mode mv88f6710_mpp_modes[] = {
MPP_FUNCTION(0x5, "audio", "mclk"),
MPP_FUNCTION(0x6, "uart0", "cts")),
MPP_MODE(63,
- MPP_FUNCTION(0x0, "gpo", NULL),
+ MPP_FUNCTION(0x0, "gpio", NULL),
MPP_FUNCTION(0x1, "spi0", "sck"),
MPP_FUNCTION(0x2, "tclk", NULL)),
MPP_MODE(64,
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index b2d22218a258..ae4115e4b4ef 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -260,6 +260,7 @@ static int pmic_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned function,
val = 1;
}
+ val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
@@ -417,7 +418,7 @@ static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
return ret;
val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT;
- val = pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
+ val |= pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val);
if (ret < 0)
@@ -466,12 +467,13 @@ static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
seq_puts(s, " ---");
} else {
- if (!pad->input_enabled) {
+ if (pad->input_enabled) {
ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
- if (!ret) {
- ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
- pad->out_value = ret;
- }
+ if (ret < 0)
+ return;
+
+ ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
+ pad->out_value = ret;
}
seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in");
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
index 8f36c5f91949..211b942ad6d5 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
@@ -370,6 +370,7 @@ static int pmic_mpp_set_mux(struct pinctrl_dev *pctldev, unsigned function,
}
}
+ val = val << PMIC_MPP_REG_MODE_DIR_SHIFT;
val |= pad->function << PMIC_MPP_REG_MODE_FUNCTION_SHIFT;
val |= pad->out_value & PMIC_MPP_REG_MODE_VALUE_MASK;
@@ -576,10 +577,11 @@ static void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev,
if (pad->input_enabled) {
ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
- if (!ret) {
- ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
- pad->out_value = ret;
- }
+ if (ret < 0)
+ return;
+
+ ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
+ pad->out_value = ret;
}
seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in");
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 2a6531a5fde8..cb1329919527 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -40,7 +40,7 @@ config CHROMEOS_PSTORE
config CROS_EC_CHARDEV
tristate "Chrome OS Embedded Controller userspace device interface"
- depends on MFD_CROS_EC
+ depends on CROS_EC_PROTO
---help---
This driver adds support to talk with the ChromeOS EC from userspace.
@@ -49,7 +49,7 @@ config CROS_EC_CHARDEV
config CROS_EC_LPC
tristate "ChromeOS Embedded Controller (LPC)"
- depends on MFD_CROS_EC && (X86 || COMPILE_TEST)
+ depends on MFD_CROS_EC && CROS_EC_PROTO && (X86 || COMPILE_TEST)
help
If you say Y here, you get support for talking to the ChromeOS EC
over an LPC bus. This uses a simple byte-level protocol with a
@@ -59,4 +59,9 @@ config CROS_EC_LPC
To compile this driver as a module, choose M here: the
module will be called cros_ec_lpc.
+config CROS_EC_PROTO
+ bool
+ help
+ ChromeOS EC communication protocol helpers.
+
endif # CHROMEOS_PLATFORMS
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index bd8d8601e875..4a11b010f5d8 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o
cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o cros_ec_lightbar.o
obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o
obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o
+obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
index 6090d0b2826f..e8fcdc237029 100644
--- a/drivers/platform/chrome/cros_ec_dev.c
+++ b/drivers/platform/chrome/cros_ec_dev.c
@@ -20,44 +20,59 @@
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <linux/uaccess.h>
#include "cros_ec_dev.h"
/* Device variables */
#define CROS_MAX_DEV 128
-static struct class *cros_class;
static int ec_major;
+static const struct attribute_group *cros_ec_groups[] = {
+ &cros_ec_attr_group,
+ &cros_ec_lightbar_attr_group,
+ NULL,
+};
+
+static struct class cros_class = {
+ .owner = THIS_MODULE,
+ .name = "chromeos",
+ .dev_groups = cros_ec_groups,
+};
+
/* Basic communication */
-static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
+static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
{
struct ec_response_get_version *resp;
static const char * const current_image_name[] = {
"unknown", "read-only", "read-write", "invalid",
};
- struct cros_ec_command msg = {
- .version = 0,
- .command = EC_CMD_GET_VERSION,
- .outdata = { 0 },
- .outsize = 0,
- .indata = { 0 },
- .insize = sizeof(*resp),
- };
+ struct cros_ec_command *msg;
int ret;
- ret = cros_ec_cmd_xfer(ec, &msg);
+ msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
+ msg->insize = sizeof(*resp);
+ msg->outsize = 0;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
+ goto exit;
- if (msg.result != EC_RES_SUCCESS) {
+ if (msg->result != EC_RES_SUCCESS) {
snprintf(str, maxlen,
"%s\nUnknown EC version: EC returned %d\n",
- CROS_EC_DEV_VERSION, msg.result);
- return 0;
+ CROS_EC_DEV_VERSION, msg->result);
+ ret = -EINVAL;
+ goto exit;
}
- resp = (struct ec_response_get_version *)msg.indata;
+ resp = (struct ec_response_get_version *)msg->data;
if (resp->current_image >= ARRAY_SIZE(current_image_name))
resp->current_image = 3; /* invalid */
@@ -65,14 +80,19 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
resp->version_string_ro, resp->version_string_rw,
current_image_name[resp->current_image]);
- return 0;
+ ret = 0;
+exit:
+ kfree(msg);
+ return ret;
}
/* Device file ops */
static int ec_device_open(struct inode *inode, struct file *filp)
{
- filp->private_data = container_of(inode->i_cdev,
- struct cros_ec_device, cdev);
+ struct cros_ec_dev *ec = container_of(inode->i_cdev,
+ struct cros_ec_dev, cdev);
+ filp->private_data = ec;
+ nonseekable_open(inode, filp);
return 0;
}
@@ -84,7 +104,7 @@ static int ec_device_release(struct inode *inode, struct file *filp)
static ssize_t ec_device_read(struct file *filp, char __user *buffer,
size_t length, loff_t *offset)
{
- struct cros_ec_device *ec = filp->private_data;
+ struct cros_ec_dev *ec = filp->private_data;
char msg[sizeof(struct ec_response_get_version) +
sizeof(CROS_EC_DEV_VERSION)];
size_t count;
@@ -107,38 +127,53 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer,
}
/* Ioctls */
-static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg)
+static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
{
long ret;
- struct cros_ec_command s_cmd = { };
+ struct cros_ec_command u_cmd;
+ struct cros_ec_command *s_cmd;
- if (copy_from_user(&s_cmd, arg, sizeof(s_cmd)))
+ if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
return -EFAULT;
- ret = cros_ec_cmd_xfer(ec, &s_cmd);
+ s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
+ GFP_KERNEL);
+ if (!s_cmd)
+ return -ENOMEM;
+
+ if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ s_cmd->command += ec->cmd_offset;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
/* Only copy data to userland if data was received. */
if (ret < 0)
- return ret;
-
- if (copy_to_user(arg, &s_cmd, sizeof(s_cmd)))
- return -EFAULT;
+ goto exit;
- return 0;
+ if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + u_cmd.insize))
+ ret = -EFAULT;
+exit:
+ kfree(s_cmd);
+ return ret;
}
-static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
+static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg)
{
+ struct cros_ec_device *ec_dev = ec->ec_dev;
struct cros_ec_readmem s_mem = { };
long num;
/* Not every platform supports direct reads */
- if (!ec->cmd_readmem)
+ if (!ec_dev->cmd_readmem)
return -ENOTTY;
if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
return -EFAULT;
- num = ec->cmd_readmem(ec, s_mem.offset, s_mem.bytes, s_mem.buffer);
+ num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes,
+ s_mem.buffer);
if (num <= 0)
return num;
@@ -151,7 +186,7 @@ static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
static long ec_device_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
- struct cros_ec_device *ec = filp->private_data;
+ struct cros_ec_dev *ec = filp->private_data;
if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
return -ENOTTY;
@@ -174,45 +209,81 @@ static const struct file_operations fops = {
.unlocked_ioctl = ec_device_ioctl,
};
+static void __remove(struct device *dev)
+{
+ struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev,
+ class_dev);
+ kfree(ec);
+}
+
static int ec_device_probe(struct platform_device *pdev)
{
- struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
- int retval = -ENOTTY;
- dev_t devno = MKDEV(ec_major, 0);
+ int retval = -ENOMEM;
+ struct device *dev = &pdev->dev;
+ struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
+ dev_t devno = MKDEV(ec_major, pdev->id);
+ struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
+
+ if (!ec)
+ return retval;
- /* Instantiate it (and remember the EC) */
+ dev_set_drvdata(dev, ec);
+ ec->ec_dev = dev_get_drvdata(dev->parent);
+ ec->dev = dev;
+ ec->cmd_offset = ec_platform->cmd_offset;
+ device_initialize(&ec->class_dev);
cdev_init(&ec->cdev, &fops);
+ /*
+ * Add the character device
+ * Link cdev to the class device to be sure device is not used
+ * before unbinding it.
+ */
+ ec->cdev.kobj.parent = &ec->class_dev.kobj;
retval = cdev_add(&ec->cdev, devno, 1);
if (retval) {
- dev_err(&pdev->dev, ": failed to add character device\n");
- return retval;
+ dev_err(dev, ": failed to add character device\n");
+ goto cdev_add_failed;
}
- ec->vdev = device_create(cros_class, NULL, devno, ec,
- CROS_EC_DEV_NAME);
- if (IS_ERR(ec->vdev)) {
- retval = PTR_ERR(ec->vdev);
- dev_err(&pdev->dev, ": failed to create device\n");
- cdev_del(&ec->cdev);
- return retval;
+ /*
+ * Add the class device
+ * Link to the character device for creating the /dev entry
+ * in devtmpfs.
+ */
+ ec->class_dev.devt = ec->cdev.dev;
+ ec->class_dev.class = &cros_class;
+ ec->class_dev.parent = dev;
+ ec->class_dev.release = __remove;
+
+ retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
+ if (retval) {
+ dev_err(dev, "dev_set_name failed => %d\n", retval);
+ goto set_named_failed;
}
- /* Initialize extra interfaces */
- ec_dev_sysfs_init(ec);
- ec_dev_lightbar_init(ec);
+ retval = device_add(&ec->class_dev);
+ if (retval) {
+ dev_err(dev, "device_register failed => %d\n", retval);
+ goto dev_reg_failed;
+ }
return 0;
+
+dev_reg_failed:
+set_named_failed:
+ dev_set_drvdata(dev, NULL);
+ cdev_del(&ec->cdev);
+cdev_add_failed:
+ kfree(ec);
+ return retval;
}
static int ec_device_remove(struct platform_device *pdev)
{
- struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
-
- ec_dev_lightbar_remove(ec);
- ec_dev_sysfs_remove(ec);
- device_destroy(cros_class, MKDEV(ec_major, 0));
+ struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
cdev_del(&ec->cdev);
+ device_unregister(&ec->class_dev);
return 0;
}
@@ -229,10 +300,10 @@ static int __init cros_ec_dev_init(void)
int ret;
dev_t dev = 0;
- cros_class = class_create(THIS_MODULE, "chromeos");
- if (IS_ERR(cros_class)) {
+ ret = class_register(&cros_class);
+ if (ret) {
pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
- return PTR_ERR(cros_class);
+ return ret;
}
/* Get a range of minor numbers (starting with 0) to work with */
@@ -254,7 +325,7 @@ static int __init cros_ec_dev_init(void)
failed_devreg:
unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
failed_chrdevreg:
- class_destroy(cros_class);
+ class_unregister(&cros_class);
return ret;
}
@@ -262,7 +333,7 @@ static void __exit cros_ec_dev_exit(void)
{
platform_driver_unregister(&cros_ec_dev_driver);
unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
- class_destroy(cros_class);
+ class_unregister(&cros_class);
}
module_init(cros_ec_dev_init);
diff --git a/drivers/platform/chrome/cros_ec_dev.h b/drivers/platform/chrome/cros_ec_dev.h
index 45d67f7e518c..bfd2c84c3571 100644
--- a/drivers/platform/chrome/cros_ec_dev.h
+++ b/drivers/platform/chrome/cros_ec_dev.h
@@ -24,7 +24,6 @@
#include <linux/types.h>
#include <linux/mfd/cros_ec.h>
-#define CROS_EC_DEV_NAME "cros_ec"
#define CROS_EC_DEV_VERSION "1.0.0"
/*
@@ -44,10 +43,4 @@ struct cros_ec_readmem {
#define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command)
#define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem)
-void ec_dev_sysfs_init(struct cros_ec_device *);
-void ec_dev_sysfs_remove(struct cros_ec_device *);
-
-void ec_dev_lightbar_init(struct cros_ec_device *);
-void ec_dev_lightbar_remove(struct cros_ec_device *);
-
#endif /* _CROS_EC_DEV_H_ */
diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c
index b4ff47a9069a..144e09df9b84 100644
--- a/drivers/platform/chrome/cros_ec_lightbar.c
+++ b/drivers/platform/chrome/cros_ec_lightbar.c
@@ -31,6 +31,7 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/uaccess.h>
+#include <linux/slab.h>
#include "cros_ec_dev.h"
@@ -91,55 +92,81 @@ out:
return ret;
}
-#define INIT_MSG(P, R) { \
- .command = EC_CMD_LIGHTBAR_CMD, \
- .outsize = sizeof(*P), \
- .insize = sizeof(*R), \
- }
+static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec)
+{
+ struct cros_ec_command *msg;
+ int len;
+
+ len = max(sizeof(struct ec_params_lightbar),
+ sizeof(struct ec_response_lightbar));
+
+ msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
+ if (!msg)
+ return NULL;
+
+ msg->version = 0;
+ msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset;
+ msg->outsize = sizeof(struct ec_params_lightbar);
+ msg->insize = sizeof(struct ec_response_lightbar);
+
+ return msg;
+}
-static int get_lightbar_version(struct cros_ec_device *ec,
+static int get_lightbar_version(struct cros_ec_dev *ec,
uint32_t *ver_ptr, uint32_t *flg_ptr)
{
struct ec_params_lightbar *param;
struct ec_response_lightbar *resp;
- struct cros_ec_command msg = INIT_MSG(param, resp);
+ struct cros_ec_command *msg;
int ret;
- param = (struct ec_params_lightbar *)msg.outdata;
- param->cmd = LIGHTBAR_CMD_VERSION;
- ret = cros_ec_cmd_xfer(ec, &msg);
- if (ret < 0)
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
return 0;
- switch (msg.result) {
+ param = (struct ec_params_lightbar *)msg->data;
+ param->cmd = LIGHTBAR_CMD_VERSION;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0) {
+ ret = 0;
+ goto exit;
+ }
+
+ switch (msg->result) {
case EC_RES_INVALID_PARAM:
/* Pixel had no version command. */
if (ver_ptr)
*ver_ptr = 0;
if (flg_ptr)
*flg_ptr = 0;
- return 1;
+ ret = 1;
+ goto exit;
case EC_RES_SUCCESS:
- resp = (struct ec_response_lightbar *)msg.indata;
+ resp = (struct ec_response_lightbar *)msg->data;
/* Future devices w/lightbars should implement this command */
if (ver_ptr)
*ver_ptr = resp->version.num;
if (flg_ptr)
*flg_ptr = resp->version.flags;
- return 1;
+ ret = 1;
+ goto exit;
}
/* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
- return 0;
+ ret = 0;
+exit:
+ kfree(msg);
+ return ret;
}
static ssize_t version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- uint32_t version, flags;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ uint32_t version = 0, flags = 0;
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
int ret;
ret = lb_throttle();
@@ -158,30 +185,39 @@ static ssize_t brightness_store(struct device *dev,
const char *buf, size_t count)
{
struct ec_params_lightbar *param;
- struct ec_response_lightbar *resp;
- struct cros_ec_command msg = INIT_MSG(param, resp);
+ struct cros_ec_command *msg;
int ret;
unsigned int val;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
if (kstrtouint(buf, 0, &val))
return -EINVAL;
- param = (struct ec_params_lightbar *)msg.outdata;
- param->cmd = LIGHTBAR_CMD_BRIGHTNESS;
- param->brightness.num = val;
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_lightbar *)msg->data;
+ param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS;
+ param->set_brightness.num = val;
ret = lb_throttle();
if (ret)
- return ret;
+ goto exit;
- ret = cros_ec_cmd_xfer(ec, &msg);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
+ goto exit;
- if (msg.result != EC_RES_SUCCESS)
- return -EINVAL;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = -EINVAL;
+ goto exit;
+ }
- return count;
+ ret = count;
+exit:
+ kfree(msg);
+ return ret;
}
@@ -196,12 +232,16 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ec_params_lightbar *param;
- struct ec_response_lightbar *resp;
- struct cros_ec_command msg = INIT_MSG(param, resp);
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_command *msg;
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
unsigned int val[4];
int ret, i = 0, j = 0, ok = 0;
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
do {
/* Skip any whitespace */
while (*buf && isspace(*buf))
@@ -215,12 +255,12 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
return -EINVAL;
if (i == 4) {
- param = (struct ec_params_lightbar *)msg.outdata;
- param->cmd = LIGHTBAR_CMD_RGB;
- param->rgb.led = val[0];
- param->rgb.red = val[1];
- param->rgb.green = val[2];
- param->rgb.blue = val[3];
+ param = (struct ec_params_lightbar *)msg->data;
+ param->cmd = LIGHTBAR_CMD_SET_RGB;
+ param->set_rgb.led = val[0];
+ param->set_rgb.red = val[1];
+ param->set_rgb.green = val[2];
+ param->set_rgb.blue = val[3];
/*
* Throttle only the first of every four transactions,
* so that the user can update all four LEDs at once.
@@ -231,12 +271,14 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
return ret;
}
- ret = cros_ec_cmd_xfer(ec, &msg);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
+ goto exit;
- if (msg.result != EC_RES_SUCCESS)
- return -EINVAL;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = -EINVAL;
+ goto exit;
+ }
i = 0;
ok = 1;
@@ -248,6 +290,8 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
} while (*buf);
+exit:
+ kfree(msg);
return (ok && i == 0) ? count : -EINVAL;
}
@@ -261,41 +305,56 @@ static ssize_t sequence_show(struct device *dev,
{
struct ec_params_lightbar *param;
struct ec_response_lightbar *resp;
- struct cros_ec_command msg = INIT_MSG(param, resp);
+ struct cros_ec_command *msg;
int ret;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
+
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
- param = (struct ec_params_lightbar *)msg.outdata;
+ param = (struct ec_params_lightbar *)msg->data;
param->cmd = LIGHTBAR_CMD_GET_SEQ;
ret = lb_throttle();
if (ret)
- return ret;
+ goto exit;
- ret = cros_ec_cmd_xfer(ec, &msg);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
+ goto exit;
- if (msg.result != EC_RES_SUCCESS)
- return scnprintf(buf, PAGE_SIZE,
- "ERROR: EC returned %d\n", msg.result);
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = scnprintf(buf, PAGE_SIZE,
+ "ERROR: EC returned %d\n", msg->result);
+ goto exit;
+ }
- resp = (struct ec_response_lightbar *)msg.indata;
+ resp = (struct ec_response_lightbar *)msg->data;
if (resp->get_seq.num >= ARRAY_SIZE(seqname))
- return scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
else
- return scnprintf(buf, PAGE_SIZE, "%s\n",
- seqname[resp->get_seq.num]);
+ ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+ seqname[resp->get_seq.num]);
+
+exit:
+ kfree(msg);
+ return ret;
}
static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ec_params_lightbar *param;
- struct ec_response_lightbar *resp;
- struct cros_ec_command msg = INIT_MSG(param, resp);
+ struct cros_ec_command *msg;
unsigned int num;
int ret, len;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
+
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
for (len = 0; len < count; len++)
if (!isalnum(buf[len]))
@@ -311,18 +370,18 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
return ret;
}
- param = (struct ec_params_lightbar *)msg.outdata;
+ param = (struct ec_params_lightbar *)msg->data;
param->cmd = LIGHTBAR_CMD_SEQ;
param->seq.num = num;
ret = lb_throttle();
if (ret)
return ret;
- ret = cros_ec_cmd_xfer(ec, &msg);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
return ret;
- if (msg.result != EC_RES_SUCCESS)
+ if (msg->result != EC_RES_SUCCESS)
return -EINVAL;
return count;
@@ -343,25 +402,27 @@ static struct attribute *__lb_cmds_attrs[] = {
&dev_attr_sequence.attr,
NULL,
};
-static struct attribute_group lb_cmds_attr_group = {
- .name = "lightbar",
- .attrs = __lb_cmds_attrs,
-};
-void ec_dev_lightbar_init(struct cros_ec_device *ec)
+static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
{
- int ret = 0;
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
+ struct platform_device *pdev = container_of(ec->dev,
+ struct platform_device, dev);
+ if (pdev->id != 0)
+ return 0;
/* Only instantiate this stuff if the EC has a lightbar */
- if (!get_lightbar_version(ec, NULL, NULL))
- return;
-
- ret = sysfs_create_group(&ec->vdev->kobj, &lb_cmds_attr_group);
- if (ret)
- pr_warn("sysfs_create_group() failed: %d\n", ret);
+ if (get_lightbar_version(ec, NULL, NULL))
+ return a->mode;
+ else
+ return 0;
}
-void ec_dev_lightbar_remove(struct cros_ec_device *ec)
-{
- sysfs_remove_group(&ec->vdev->kobj, &lb_cmds_attr_group);
-}
+struct attribute_group cros_ec_lightbar_attr_group = {
+ .name = "lightbar",
+ .attrs = __lb_cmds_attrs,
+ .is_visible = cros_ec_lightbar_attrs_are_visible,
+};
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index 8f9ac4d7bbd0..bdd77ce45f05 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -46,6 +46,77 @@ static int ec_response_timed_out(void)
return 1;
}
+static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
+ struct cros_ec_command *msg)
+{
+ struct ec_host_request *request;
+ struct ec_host_response response;
+ u8 sum = 0;
+ int i;
+ int ret = 0;
+ u8 *dout;
+
+ ret = cros_ec_prepare_tx(ec, msg);
+
+ /* Write buffer */
+ for (i = 0; i < ret; i++)
+ outb(ec->dout[i], EC_LPC_ADDR_HOST_PACKET + i);
+
+ request = (struct ec_host_request *)ec->dout;
+
+ /* Here we go */
+ outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
+
+ if (ec_response_timed_out()) {
+ dev_warn(ec->dev, "EC responsed timed out\n");
+ ret = -EIO;
+ goto done;
+ }
+
+ /* Check result */
+ msg->result = inb(EC_LPC_ADDR_HOST_DATA);
+ ret = cros_ec_check_result(ec, msg);
+ if (ret)
+ goto done;
+
+ /* Read back response */
+ dout = (u8 *)&response;
+ for (i = 0; i < sizeof(response); i++) {
+ dout[i] = inb(EC_LPC_ADDR_HOST_PACKET + i);
+ sum += dout[i];
+ }
+
+ msg->result = response.result;
+
+ if (response.data_len > msg->insize) {
+ dev_err(ec->dev,
+ "packet too long (%d bytes, expected %d)",
+ response.data_len, msg->insize);
+ ret = -EMSGSIZE;
+ goto done;
+ }
+
+ /* Read response and process checksum */
+ for (i = 0; i < response.data_len; i++) {
+ msg->data[i] =
+ inb(EC_LPC_ADDR_HOST_PACKET + sizeof(response) + i);
+ sum += msg->data[i];
+ }
+
+ if (sum) {
+ dev_err(ec->dev,
+ "bad packet checksum %02x\n",
+ response.checksum);
+ ret = -EBADMSG;
+ goto done;
+ }
+
+ /* Return actual amount of data received */
+ ret = response.data_len;
+done:
+ return ret;
+}
+
static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
struct cros_ec_command *msg)
{
@@ -73,8 +144,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
/* Copy data and update checksum */
for (i = 0; i < msg->outsize; i++) {
- outb(msg->outdata[i], EC_LPC_ADDR_HOST_PARAM + i);
- csum += msg->outdata[i];
+ outb(msg->data[i], EC_LPC_ADDR_HOST_PARAM + i);
+ csum += msg->data[i];
}
/* Finalize checksum and write args */
@@ -129,8 +200,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
/* Read response and update checksum */
for (i = 0; i < args.data_size; i++) {
- msg->indata[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
- csum += msg->indata[i];
+ msg->data[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
+ csum += msg->data[i];
}
/* Verify checksum */
@@ -212,11 +283,13 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ec_dev);
ec_dev->dev = dev;
- ec_dev->ec_name = pdev->name;
ec_dev->phys_name = dev_name(dev);
- ec_dev->parent = dev;
ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc;
+ ec_dev->pkt_xfer = cros_ec_pkt_xfer_lpc;
ec_dev->cmd_readmem = cros_ec_lpc_readmem;
+ ec_dev->din_size = sizeof(struct ec_host_response) +
+ sizeof(struct ec_response_get_protocol_info);
+ ec_dev->dout_size = sizeof(struct ec_host_request);
ret = cros_ec_register(ec_dev);
if (ret) {
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
new file mode 100644
index 000000000000..990308ca384f
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -0,0 +1,382 @@
+/*
+ * ChromeOS EC communication protocol helper functions
+ *
+ * Copyright (C) 2015 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mfd/cros_ec.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#define EC_COMMAND_RETRIES 50
+
+static int prepare_packet(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ struct ec_host_request *request;
+ u8 *out;
+ int i;
+ u8 csum = 0;
+
+ BUG_ON(ec_dev->proto_version != EC_HOST_REQUEST_VERSION);
+ BUG_ON(msg->outsize + sizeof(*request) > ec_dev->dout_size);
+
+ out = ec_dev->dout;
+ request = (struct ec_host_request *)out;
+ request->struct_version = EC_HOST_REQUEST_VERSION;
+ request->checksum = 0;
+ request->command = msg->command;
+ request->command_version = msg->version;
+ request->reserved = 0;
+ request->data_len = msg->outsize;
+
+ for (i = 0; i < sizeof(*request); i++)
+ csum += out[i];
+
+ /* Copy data and update checksum */
+ memcpy(out + sizeof(*request), msg->data, msg->outsize);
+ for (i = 0; i < msg->outsize; i++)
+ csum += msg->data[i];
+
+ request->checksum = -csum;
+
+ return sizeof(*request) + msg->outsize;
+}
+
+static int send_command(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ int ret;
+
+ if (ec_dev->proto_version > 2)
+ ret = ec_dev->pkt_xfer(ec_dev, msg);
+ else
+ ret = ec_dev->cmd_xfer(ec_dev, msg);
+
+ if (msg->result == EC_RES_IN_PROGRESS) {
+ int i;
+ struct cros_ec_command *status_msg;
+ struct ec_response_get_comms_status *status;
+
+ status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status),
+ GFP_KERNEL);
+ if (!status_msg)
+ return -ENOMEM;
+
+ status_msg->version = 0;
+ status_msg->command = EC_CMD_GET_COMMS_STATUS;
+ status_msg->insize = sizeof(*status);
+ status_msg->outsize = 0;
+
+ /*
+ * Query the EC's status until it's no longer busy or
+ * we encounter an error.
+ */
+ for (i = 0; i < EC_COMMAND_RETRIES; i++) {
+ usleep_range(10000, 11000);
+
+ ret = ec_dev->cmd_xfer(ec_dev, status_msg);
+ if (ret < 0)
+ break;
+
+ msg->result = status_msg->result;
+ if (status_msg->result != EC_RES_SUCCESS)
+ break;
+
+ status = (struct ec_response_get_comms_status *)
+ status_msg->data;
+ if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
+ break;
+ }
+
+ kfree(status_msg);
+ }
+
+ return ret;
+}
+
+int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ u8 *out;
+ u8 csum;
+ int i;
+
+ if (ec_dev->proto_version > 2)
+ return prepare_packet(ec_dev, msg);
+
+ BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
+ out = ec_dev->dout;
+ out[0] = EC_CMD_VERSION0 + msg->version;
+ out[1] = msg->command;
+ out[2] = msg->outsize;
+ csum = out[0] + out[1] + out[2];
+ for (i = 0; i < msg->outsize; i++)
+ csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i];
+ out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = csum;
+
+ return EC_MSG_TX_PROTO_BYTES + msg->outsize;
+}
+EXPORT_SYMBOL(cros_ec_prepare_tx);
+
+int cros_ec_check_result(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ switch (msg->result) {
+ case EC_RES_SUCCESS:
+ return 0;
+ case EC_RES_IN_PROGRESS:
+ dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
+ msg->command);
+ return -EAGAIN;
+ default:
+ dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
+ msg->command, msg->result);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(cros_ec_check_result);
+
+static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev,
+ int devidx,
+ struct cros_ec_command *msg)
+{
+ /*
+ * Try using v3+ to query for supported protocols. If this
+ * command fails, fall back to v2. Returns the highest protocol
+ * supported by the EC.
+ * Also sets the max request/response/passthru size.
+ */
+ int ret;
+
+ if (!ec_dev->pkt_xfer)
+ return -EPROTONOSUPPORT;
+
+ memset(msg, 0, sizeof(*msg));
+ msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO;
+ msg->insize = sizeof(struct ec_response_get_protocol_info);
+
+ ret = send_command(ec_dev, msg);
+
+ if (ret < 0) {
+ dev_dbg(ec_dev->dev,
+ "failed to check for EC[%d] protocol version: %d\n",
+ devidx, ret);
+ return ret;
+ }
+
+ if (devidx > 0 && msg->result == EC_RES_INVALID_COMMAND)
+ return -ENODEV;
+ else if (msg->result != EC_RES_SUCCESS)
+ return msg->result;
+
+ return 0;
+}
+
+static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev)
+{
+ struct cros_ec_command *msg;
+ struct ec_params_hello *hello_params;
+ struct ec_response_hello *hello_response;
+ int ret;
+ int len = max(sizeof(*hello_params), sizeof(*hello_response));
+
+ msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = EC_CMD_HELLO;
+ hello_params = (struct ec_params_hello *)msg->data;
+ msg->outsize = sizeof(*hello_params);
+ hello_response = (struct ec_response_hello *)msg->data;
+ msg->insize = sizeof(*hello_response);
+
+ hello_params->in_data = 0xa0b0c0d0;
+
+ ret = send_command(ec_dev, msg);
+
+ if (ret < 0) {
+ dev_dbg(ec_dev->dev,
+ "EC failed to respond to v2 hello: %d\n",
+ ret);
+ goto exit;
+ } else if (msg->result != EC_RES_SUCCESS) {
+ dev_err(ec_dev->dev,
+ "EC responded to v2 hello with error: %d\n",
+ msg->result);
+ ret = msg->result;
+ goto exit;
+ } else if (hello_response->out_data != 0xa1b2c3d4) {
+ dev_err(ec_dev->dev,
+ "EC responded to v2 hello with bad result: %u\n",
+ hello_response->out_data);
+ ret = -EBADMSG;
+ goto exit;
+ }
+
+ ret = 0;
+
+ exit:
+ kfree(msg);
+ return ret;
+}
+
+int cros_ec_query_all(struct cros_ec_device *ec_dev)
+{
+ struct device *dev = ec_dev->dev;
+ struct cros_ec_command *proto_msg;
+ struct ec_response_get_protocol_info *proto_info;
+ int ret;
+
+ proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info),
+ GFP_KERNEL);
+ if (!proto_msg)
+ return -ENOMEM;
+
+ /* First try sending with proto v3. */
+ ec_dev->proto_version = 3;
+ ret = cros_ec_host_command_proto_query(ec_dev, 0, proto_msg);
+
+ if (ret == 0) {
+ proto_info = (struct ec_response_get_protocol_info *)
+ proto_msg->data;
+ ec_dev->max_request = proto_info->max_request_packet_size -
+ sizeof(struct ec_host_request);
+ ec_dev->max_response = proto_info->max_response_packet_size -
+ sizeof(struct ec_host_response);
+ ec_dev->proto_version =
+ min(EC_HOST_REQUEST_VERSION,
+ fls(proto_info->protocol_versions) - 1);
+ dev_dbg(ec_dev->dev,
+ "using proto v%u\n",
+ ec_dev->proto_version);
+
+ ec_dev->din_size = ec_dev->max_response +
+ sizeof(struct ec_host_response) +
+ EC_MAX_RESPONSE_OVERHEAD;
+ ec_dev->dout_size = ec_dev->max_request +
+ sizeof(struct ec_host_request) +
+ EC_MAX_REQUEST_OVERHEAD;
+
+ /*
+ * Check for PD
+ */
+ ret = cros_ec_host_command_proto_query(ec_dev, 1, proto_msg);
+
+ if (ret) {
+ dev_dbg(ec_dev->dev, "no PD chip found: %d\n", ret);
+ ec_dev->max_passthru = 0;
+ } else {
+ dev_dbg(ec_dev->dev, "found PD chip\n");
+ ec_dev->max_passthru =
+ proto_info->max_request_packet_size -
+ sizeof(struct ec_host_request);
+ }
+ } else {
+ /* Try querying with a v2 hello message. */
+ ec_dev->proto_version = 2;
+ ret = cros_ec_host_command_proto_query_v2(ec_dev);
+
+ if (ret == 0) {
+ /* V2 hello succeeded. */
+ dev_dbg(ec_dev->dev, "falling back to proto v2\n");
+
+ ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE;
+ ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE;
+ ec_dev->max_passthru = 0;
+ ec_dev->pkt_xfer = NULL;
+ ec_dev->din_size = EC_MSG_BYTES;
+ ec_dev->dout_size = EC_MSG_BYTES;
+ } else {
+ /*
+ * It's possible for a test to occur too early when
+ * the EC isn't listening. If this happens, we'll
+ * test later when the first command is run.
+ */
+ ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN;
+ dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret);
+ goto exit;
+ }
+ }
+
+ devm_kfree(dev, ec_dev->din);
+ devm_kfree(dev, ec_dev->dout);
+
+ ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
+ if (!ec_dev->din) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
+ if (!ec_dev->dout) {
+ devm_kfree(dev, ec_dev->din);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+exit:
+ kfree(proto_msg);
+ return ret;
+}
+EXPORT_SYMBOL(cros_ec_query_all);
+
+int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ int ret;
+
+ mutex_lock(&ec_dev->lock);
+ if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) {
+ ret = cros_ec_query_all(ec_dev);
+ if (ret) {
+ dev_err(ec_dev->dev,
+ "EC version unknown and query failed; aborting command\n");
+ mutex_unlock(&ec_dev->lock);
+ return ret;
+ }
+ }
+
+ if (msg->insize > ec_dev->max_response) {
+ dev_dbg(ec_dev->dev, "clamping message receive buffer\n");
+ msg->insize = ec_dev->max_response;
+ }
+
+ if (msg->command < EC_CMD_PASSTHRU_OFFSET(1)) {
+ if (msg->outsize > ec_dev->max_request) {
+ dev_err(ec_dev->dev,
+ "request of size %u is too big (max: %u)\n",
+ msg->outsize,
+ ec_dev->max_request);
+ mutex_unlock(&ec_dev->lock);
+ return -EMSGSIZE;
+ }
+ } else {
+ if (msg->outsize > ec_dev->max_passthru) {
+ dev_err(ec_dev->dev,
+ "passthru rq of size %u is too big (max: %u)\n",
+ msg->outsize,
+ ec_dev->max_passthru);
+ mutex_unlock(&ec_dev->lock);
+ return -EMSGSIZE;
+ }
+ }
+ ret = send_command(ec_dev, msg);
+ mutex_unlock(&ec_dev->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(cros_ec_cmd_xfer);
diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c
index fb62ab6cc659..f3baf9973989 100644
--- a/drivers/platform/chrome/cros_ec_sysfs.c
+++ b/drivers/platform/chrome/cros_ec_sysfs.c
@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
+#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/types.h>
#include <linux/uaccess.h>
@@ -66,13 +67,19 @@ static ssize_t store_ec_reboot(struct device *dev,
{"hibernate", EC_REBOOT_HIBERNATE, 0},
{"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN},
};
- struct cros_ec_command msg = { 0 };
- struct ec_params_reboot_ec *param =
- (struct ec_params_reboot_ec *)msg.outdata;
+ struct cros_ec_command *msg;
+ struct ec_params_reboot_ec *param;
int got_cmd = 0, offset = 0;
int i;
int ret;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
+
+ msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_reboot_ec *)msg->data;
param->flags = 0;
while (1) {
@@ -100,19 +107,26 @@ static ssize_t store_ec_reboot(struct device *dev,
offset++;
}
- if (!got_cmd)
- return -EINVAL;
-
- msg.command = EC_CMD_REBOOT_EC;
- msg.outsize = sizeof(param);
- ret = cros_ec_cmd_xfer(ec, &msg);
- if (ret < 0)
- return ret;
- if (msg.result != EC_RES_SUCCESS) {
- dev_dbg(ec->dev, "EC result %d\n", msg.result);
- return -EINVAL;
+ if (!got_cmd) {
+ count = -EINVAL;
+ goto exit;
}
+ msg->version = 0;
+ msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset;
+ msg->outsize = sizeof(*param);
+ msg->insize = 0;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0) {
+ count = ret;
+ goto exit;
+ }
+ if (msg->result != EC_RES_SUCCESS) {
+ dev_dbg(ec->dev, "EC result %d\n", msg->result);
+ count = -EINVAL;
+ }
+exit:
+ kfree(msg);
return count;
}
@@ -123,22 +137,33 @@ static ssize_t show_ec_version(struct device *dev,
struct ec_response_get_version *r_ver;
struct ec_response_get_chip_info *r_chip;
struct ec_response_board_version *r_board;
- struct cros_ec_command msg = { 0 };
+ struct cros_ec_command *msg;
int ret;
int count = 0;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
+
+ msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
/* Get versions. RW may change. */
- msg.command = EC_CMD_GET_VERSION;
- msg.insize = sizeof(*r_ver);
- ret = cros_ec_cmd_xfer(ec, &msg);
- if (ret < 0)
- return ret;
- if (msg.result != EC_RES_SUCCESS)
- return scnprintf(buf, PAGE_SIZE,
- "ERROR: EC returned %d\n", msg.result);
+ msg->version = 0;
+ msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
+ msg->insize = sizeof(*r_ver);
+ msg->outsize = 0;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0) {
+ count = ret;
+ goto exit;
+ }
+ if (msg->result != EC_RES_SUCCESS) {
+ count = scnprintf(buf, PAGE_SIZE,
+ "ERROR: EC returned %d\n", msg->result);
+ goto exit;
+ }
- r_ver = (struct ec_response_get_version *)msg.indata;
+ r_ver = (struct ec_response_get_version *)msg->data;
/* Strings should be null-terminated, but let's be sure. */
r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0';
r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0';
@@ -152,33 +177,33 @@ static ssize_t show_ec_version(struct device *dev,
image_names[r_ver->current_image] : "?"));
/* Get build info. */
- msg.command = EC_CMD_GET_BUILD_INFO;
- msg.insize = sizeof(msg.indata);
- ret = cros_ec_cmd_xfer(ec, &msg);
+ msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset;
+ msg->insize = EC_HOST_PARAM_SIZE;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: XFER ERROR %d\n", ret);
- else if (msg.result != EC_RES_SUCCESS)
+ else if (msg->result != EC_RES_SUCCESS)
count += scnprintf(buf + count, PAGE_SIZE - count,
- "Build info: EC error %d\n", msg.result);
+ "Build info: EC error %d\n", msg->result);
else {
- msg.indata[sizeof(msg.indata) - 1] = '\0';
+ msg->data[sizeof(msg->data) - 1] = '\0';
count += scnprintf(buf + count, PAGE_SIZE - count,
- "Build info: %s\n", msg.indata);
+ "Build info: %s\n", msg->data);
}
/* Get chip info. */
- msg.command = EC_CMD_GET_CHIP_INFO;
- msg.insize = sizeof(*r_chip);
- ret = cros_ec_cmd_xfer(ec, &msg);
+ msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset;
+ msg->insize = sizeof(*r_chip);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Chip info: XFER ERROR %d\n", ret);
- else if (msg.result != EC_RES_SUCCESS)
+ else if (msg->result != EC_RES_SUCCESS)
count += scnprintf(buf + count, PAGE_SIZE - count,
- "Chip info: EC error %d\n", msg.result);
+ "Chip info: EC error %d\n", msg->result);
else {
- r_chip = (struct ec_response_get_chip_info *)msg.indata;
+ r_chip = (struct ec_response_get_chip_info *)msg->data;
r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0';
r_chip->name[sizeof(r_chip->name) - 1] = '\0';
@@ -192,23 +217,25 @@ static ssize_t show_ec_version(struct device *dev,
}
/* Get board version */
- msg.command = EC_CMD_GET_BOARD_VERSION;
- msg.insize = sizeof(*r_board);
- ret = cros_ec_cmd_xfer(ec, &msg);
+ msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset;
+ msg->insize = sizeof(*r_board);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Board version: XFER ERROR %d\n", ret);
- else if (msg.result != EC_RES_SUCCESS)
+ else if (msg->result != EC_RES_SUCCESS)
count += scnprintf(buf + count, PAGE_SIZE - count,
- "Board version: EC error %d\n", msg.result);
+ "Board version: EC error %d\n", msg->result);
else {
- r_board = (struct ec_response_board_version *)msg.indata;
+ r_board = (struct ec_response_board_version *)msg->data;
count += scnprintf(buf + count, PAGE_SIZE - count,
"Board version: %d\n",
r_board->board_version);
}
+exit:
+ kfree(msg);
return count;
}
@@ -216,27 +243,39 @@ static ssize_t show_ec_flashinfo(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ec_response_flash_info *resp;
- struct cros_ec_command msg = { 0 };
+ struct cros_ec_command *msg;
int ret;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
+
+ msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
/* The flash info shouldn't ever change, but ask each time anyway. */
- msg.command = EC_CMD_FLASH_INFO;
- msg.insize = sizeof(*resp);
- ret = cros_ec_cmd_xfer(ec, &msg);
+ msg->version = 0;
+ msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset;
+ msg->insize = sizeof(*resp);
+ msg->outsize = 0;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
- if (msg.result != EC_RES_SUCCESS)
- return scnprintf(buf, PAGE_SIZE,
- "ERROR: EC returned %d\n", msg.result);
-
- resp = (struct ec_response_flash_info *)msg.indata;
-
- return scnprintf(buf, PAGE_SIZE,
- "FlashSize %d\nWriteSize %d\n"
- "EraseSize %d\nProtectSize %d\n",
- resp->flash_size, resp->write_block_size,
- resp->erase_block_size, resp->protect_block_size);
+ goto exit;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = scnprintf(buf, PAGE_SIZE,
+ "ERROR: EC returned %d\n", msg->result);
+ goto exit;
+ }
+
+ resp = (struct ec_response_flash_info *)msg->data;
+
+ ret = scnprintf(buf, PAGE_SIZE,
+ "FlashSize %d\nWriteSize %d\n"
+ "EraseSize %d\nProtectSize %d\n",
+ resp->flash_size, resp->write_block_size,
+ resp->erase_block_size, resp->protect_block_size);
+exit:
+ kfree(msg);
+ return ret;
}
/* Module initialization */
@@ -252,20 +291,7 @@ static struct attribute *__ec_attrs[] = {
NULL,
};
-static struct attribute_group ec_attr_group = {
+struct attribute_group cros_ec_attr_group = {
.attrs = __ec_attrs,
};
-void ec_dev_sysfs_init(struct cros_ec_device *ec)
-{
- int error;
-
- error = sysfs_create_group(&ec->vdev->kobj, &ec_attr_group);
- if (error)
- pr_warn("failed to create group: %d\n", error);
-}
-
-void ec_dev_sysfs_remove(struct cros_ec_device *ec)
-{
- sysfs_remove_group(&ec->vdev->kobj, &ec_attr_group);
-}
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index f9f205cb1f11..7137a077b9a6 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -71,9 +71,10 @@ config ASUS_LAPTOP
depends on ACPI
select LEDS_CLASS
select NEW_LEDS
- select BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_CLASS_DEVICE
depends on INPUT
depends on RFKILL || RFKILL = n
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
select INPUT_POLLDEV
---help---
@@ -95,6 +96,7 @@ config DELL_LAPTOP
depends on X86
depends on DCDBAS
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL || RFKILL = n
depends on SERIO_I8042
select POWER_SUPPLY
@@ -109,6 +111,7 @@ config DELL_WMI
tristate "Dell WMI extras"
depends on ACPI_WMI
depends on INPUT
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
---help---
Say Y here if you want to support WMI-based hotkeys on Dell laptops.
@@ -144,6 +147,7 @@ config FUJITSU_LAPTOP
depends on ACPI
depends on INPUT
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on LEDS_CLASS || LEDS_CLASS=n
---help---
This is a driver for laptops built by Fujitsu:
@@ -247,6 +251,7 @@ config MSI_LAPTOP
tristate "MSI Laptop Extras"
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL
depends on INPUT && SERIO_I8042
select INPUT_SPARSEKMAP
@@ -280,6 +285,7 @@ config COMPAL_LAPTOP
tristate "Compal (and others) Laptop Extras"
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL
depends on HWMON
depends on POWER_SUPPLY
@@ -296,7 +302,8 @@ config COMPAL_LAPTOP
config SONY_LAPTOP
tristate "Sony Laptop Extras"
depends on ACPI
- select BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
+ depends on BACKLIGHT_CLASS_DEVICE
depends on INPUT
depends on RFKILL
---help---
@@ -321,6 +328,7 @@ config IDEAPAD_LAPTOP
depends on RFKILL && INPUT
depends on SERIO_I8042
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
help
This is a driver for Lenovo IdeaPad netbooks contains drivers for
@@ -331,8 +339,8 @@ config THINKPAD_ACPI
depends on ACPI
depends on INPUT
depends on RFKILL || RFKILL = n
- select BACKLIGHT_LCD_SUPPORT
- select BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
+ depends on BACKLIGHT_CLASS_DEVICE
select HWMON
select NVRAM
select NEW_LEDS
@@ -500,8 +508,9 @@ config EEEPC_LAPTOP
depends on ACPI
depends on INPUT
depends on RFKILL || RFKILL = n
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on HOTPLUG_PCI
- select BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_CLASS_DEVICE
select HWMON
select LEDS_CLASS
select NEW_LEDS
@@ -587,6 +596,7 @@ config MSI_WMI
depends on ACPI_WMI
depends on INPUT
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
help
Say Y here if you want to support WMI-based hotkeys on MSI laptops.
@@ -660,7 +670,7 @@ config TOSHIBA_HAPS
depends on ACPI
---help---
This driver adds support for the built-in accelerometer
- found on recent Toshiba laptops equiped with HID TOS620A
+ found on recent Toshiba laptops equipped with HID TOS620A
device.
This driver receives ACPI notify events 0x80 when the sensor
@@ -669,7 +679,7 @@ config TOSHIBA_HAPS
been stabilized.
Also provides sysfs entries to get/set the desired protection
- level and reseting the HDD protection interface.
+ level and resetting the HDD protection interface.
If you have a recent Toshiba laptop with a built-in accelerometer
device, say Y.
@@ -824,6 +834,7 @@ config MXM_WMI
config INTEL_OAKTRAIL
tristate "Intel Oaktrail Platform Extras"
depends on ACPI
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL && BACKLIGHT_CLASS_DEVICE && ACPI
---help---
Intel Oaktrail platform need this driver to provide interfaces to
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 3ac29a1e8f92..f6b280dbfb33 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -2246,14 +2246,10 @@ static int __init acer_wmi_init(void)
set_quirks();
if (dmi_check_system(video_vendor_dmi_table))
- acpi_video_dmi_promote_vendor();
- if (acpi_video_backlight_support()) {
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+
+ if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
interface->capability &= ~ACER_CAP_BRIGHTNESS;
- pr_info("Brightness must be controlled by acpi video driver\n");
- } else {
- pr_info("Disabling ACPI video driver\n");
- acpi_video_unregister_backlight();
- }
if (wmi_has_guid(WMID_GUID3)) {
if (ec_raw_mode) {
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index 6808715003f6..0dec3f59917a 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -550,8 +550,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
* backlight control and supports more levels than other options.
* Disable the other backlight choices.
*/
- acpi_video_dmi_promote_vendor();
- acpi_video_unregister();
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
apple_bl_unregister();
gmux_data->power_state = VGA_SWITCHEROO_ON;
@@ -645,7 +644,6 @@ static void gmux_remove(struct pnp_dev *pnp)
apple_gmux_data = NULL;
kfree(gmux_data);
- acpi_video_dmi_demote_vendor();
acpi_video_register();
apple_bl_register();
}
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index 46b274693872..58d29c4f2840 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -54,6 +54,7 @@
#include <linux/slab.h>
#include <linux/dmi.h>
#include <linux/acpi.h>
+#include <acpi/video.h>
#define ASUS_LAPTOP_VERSION "0.42"
@@ -1884,12 +1885,11 @@ static int asus_acpi_add(struct acpi_device *device)
if (result)
goto fail_platform;
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
result = asus_backlight_init(asus);
if (result)
goto fail_backlight;
- } else
- pr_info("Backlight controlled by ACPI video driver\n");
+ }
result = asus_input_init(asus);
if (result)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 7543a56e0f45..6f8558f744a4 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1364,7 +1364,7 @@ static void asus_wmi_notify(u32 value, void *context)
code = ASUS_WMI_BRN_DOWN;
if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
asus_wmi_backlight_notify(asus, orig_code);
goto exit;
}
@@ -1772,17 +1772,16 @@ static int asus_wmi_add(struct platform_device *pdev)
stop this from showing up */
chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
if (chassis_type && !strcmp(chassis_type, "3"))
- acpi_video_dmi_promote_vendor();
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+
if (asus->driver->quirks->wmi_backlight_power)
- acpi_video_dmi_promote_vendor();
- if (!acpi_video_backlight_support()) {
- pr_info("Disabling ACPI video driver\n");
- acpi_video_unregister();
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
err = asus_wmi_backlight_init(asus);
if (err && err != -ENODEV)
goto fail_backlight;
- } else
- pr_info("Backlight controlled by ACPI video driver\n");
+ }
status = wmi_install_notify_handler(asus->driver->event_guid,
asus_wmi_notify, asus);
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
index b4e94471f3d5..f2706d27adff 100644
--- a/drivers/platform/x86/compal-laptop.c
+++ b/drivers/platform/x86/compal-laptop.c
@@ -82,7 +82,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/power_supply.h>
#include <linux/fb.h>
-
+#include <acpi/video.h>
/* ======= */
/* Defines */
@@ -959,7 +959,7 @@ static int __init compal_init(void)
return -ENODEV;
}
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index d688d806a8a5..01d081052b50 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -31,6 +31,7 @@
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <acpi/video.h>
#include "../../firmware/dcdbas.h"
#define BRIGHTNESS_TOKEN 0x7d
@@ -1920,13 +1921,8 @@ static int __init dell_init(void)
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
&dell_debugfs_fops);
-#ifdef CONFIG_ACPI
- /* In the event of an ACPI backlight being available, don't
- * register the platform controller.
- */
- if (acpi_video_backlight_support())
+ if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return 0;
-#endif
get_buffer();
buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 6512a06bc053..f2d77fe696ac 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -35,6 +35,7 @@
#include <linux/acpi.h>
#include <linux/string.h>
#include <linux/dmi.h>
+#include <acpi/video.h>
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
@@ -397,7 +398,7 @@ static int __init dell_wmi_init(void)
}
dmi_walk(find_hk_type, NULL);
- acpi_video = acpi_video_backlight_support();
+ acpi_video = acpi_video_get_backlight_type() != acpi_backlight_vendor;
err = dell_wmi_input_setup();
if (err)
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 844c2096bde9..8cdf315f9730 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -37,6 +37,7 @@
#include <linux/pci_hotplug.h>
#include <linux/leds.h>
#include <linux/dmi.h>
+#include <acpi/video.h>
#define EEEPC_LAPTOP_VERSION "0.1"
#define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver"
@@ -1433,12 +1434,10 @@ static int eeepc_acpi_add(struct acpi_device *device)
if (result)
goto fail_platform;
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
result = eeepc_backlight_init(eeepc);
if (result)
goto fail_backlight;
- } else {
- pr_info("Backlight controlled by ACPI video driver\n");
}
result = eeepc_input_init(eeepc);
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index 2a9afa261c61..1c62caff93fd 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -72,6 +72,7 @@
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
#include <linux/leds.h>
#endif
+#include <acpi/video.h>
#define FUJITSU_DRIVER_VERSION "0.6.0"
@@ -1099,7 +1100,7 @@ static int __init fujitsu_init(void)
/* Register backlight stuff */
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
@@ -1137,8 +1138,7 @@ static int __init fujitsu_init(void)
}
/* Sync backlight power status (needs FUJ02E3 device, hence deferred) */
-
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
fujitsu->bl_device->props.power = FB_BLANK_POWERDOWN;
else
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index b3d419a84723..bea022830944 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -38,6 +38,7 @@
#include <linux/i8042.h>
#include <linux/dmi.h>
#include <linux/device.h>
+#include <acpi/video.h>
#define IDEAPAD_RFKILL_DEV_NUM (3)
@@ -830,6 +831,13 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
*/
static const struct dmi_system_id no_hw_rfkill_list[] = {
{
+ .ident = "Lenovo G40-30",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G40-30"),
+ },
+ },
+ {
.ident = "Lenovo Yoga 2 11 / 13 / Pro",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
@@ -896,7 +904,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
ideapad_sync_rfk_state(priv);
ideapad_sync_touchpad_state(priv);
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
ret = ideapad_backlight_init(priv);
if (ret && ret != -ENODEV)
goto backlight_failed;
diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c
index 8037c8b46241..6aa33c4a809f 100644
--- a/drivers/platform/x86/intel_oaktrail.c
+++ b/drivers/platform/x86/intel_oaktrail.c
@@ -50,6 +50,7 @@
#include <linux/platform_device.h>
#include <linux/dmi.h>
#include <linux/rfkill.h>
+#include <acpi/video.h>
#define DRIVER_NAME "intel_oaktrail"
#define DRIVER_VERSION "0.4ac1"
@@ -343,13 +344,11 @@ static int __init oaktrail_init(void)
goto err_device_add;
}
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
ret = oaktrail_backlight_init();
if (ret)
goto err_backlight;
-
- } else
- pr_info("Backlight controlled by ACPI video driver\n");
+ }
ret = oaktrail_rfkill_init();
if (ret) {
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index 085987730aab..42317704629d 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -64,6 +64,7 @@
#include <linux/i8042.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <acpi/video.h>
#define MSI_DRIVER_VERSION "0.5"
@@ -1069,9 +1070,8 @@ static int __init msi_init(void)
/* Register backlight stuff */
- if (!quirks->old_ec_model || acpi_video_backlight_support()) {
- pr_info("Brightness ignored, must be controlled by ACPI video driver\n");
- } else {
+ if (quirks->old_ec_model ||
+ acpi_video_get_backlight_type() == acpi_backlight_vendor) {
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c
index 6d2bac0c463c..978e6d640572 100644
--- a/drivers/platform/x86/msi-wmi.c
+++ b/drivers/platform/x86/msi-wmi.c
@@ -29,6 +29,7 @@
#include <linux/backlight.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <acpi/video.h>
MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver");
@@ -320,7 +321,8 @@ static int __init msi_wmi_init(void)
break;
}
- if (wmi_has_guid(MSIWMI_BIOS_GUID) && !acpi_video_backlight_support()) {
+ if (wmi_has_guid(MSIWMI_BIOS_GUID) &&
+ acpi_video_get_backlight_type() == acpi_backlight_vendor) {
err = msi_wmi_backlight_setup();
if (err) {
pr_err("Unable to setup backlight device\n");
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index 9e701b2256f9..8c146e2b6727 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -1720,27 +1720,14 @@ static int __init samsung_init(void)
samsung->handle_backlight = true;
samsung->quirks = quirks;
-
#ifdef CONFIG_ACPI
if (samsung->quirks->broken_acpi_video)
- acpi_video_dmi_promote_vendor();
-
- /* Don't handle backlight here if the acpi video already handle it */
- if (acpi_video_backlight_support()) {
- samsung->handle_backlight = false;
- } else if (samsung->quirks->broken_acpi_video) {
- pr_info("Disabling ACPI video driver\n");
- acpi_video_unregister();
- }
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+ if (samsung->quirks->use_native_backlight)
+ acpi_video_set_dmi_backlight_type(acpi_backlight_native);
- if (samsung->quirks->use_native_backlight) {
- pr_info("Using native backlight driver\n");
- /* Tell acpi-video to not handle the backlight */
- acpi_video_dmi_promote_vendor();
- acpi_video_unregister();
- /* And also do not handle it ourselves */
+ if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
samsung->handle_backlight = false;
- }
#endif
ret = samsung_platform_init(samsung);
@@ -1751,12 +1738,6 @@ static int __init samsung_init(void)
if (ret)
goto error_sabi;
-#ifdef CONFIG_ACPI
- /* Only log that if we are really on a sabi platform */
- if (acpi_video_backlight_support())
- pr_info("Backlight controlled by ACPI video driver\n");
-#endif
-
ret = samsung_sysfs_init(samsung);
if (ret)
goto error_sysfs;
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index e51c1e753607..aeb80d1c2b07 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -69,6 +69,7 @@
#include <linux/miscdevice.h>
#endif
#include <asm/uaccess.h>
+#include <acpi/video.h>
#define dprintk(fmt, ...) \
do { \
@@ -3198,12 +3199,8 @@ static int sony_nc_add(struct acpi_device *device)
sony_nc_function_setup(device, sony_pf_device);
}
- /* setup input devices and helper fifo */
- if (acpi_video_backlight_support()) {
- pr_info("brightness ignored, must be controlled by ACPI video driver\n");
- } else {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor)
sony_nc_backlight_setup();
- }
/* create sony_pf sysfs attributes related to the SNC device */
for (item = sony_nc_values; item->name; ++item) {
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 7769575345d8..33e488cf5569 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -83,6 +83,7 @@
#include <sound/control.h>
#include <sound/initval.h>
#include <asm/uaccess.h>
+#include <acpi/video.h>
/* ThinkPad CMOS commands */
#define TP_CMOS_VOLUME_DOWN 0
@@ -2115,7 +2116,7 @@ static int hotkey_mask_get(void)
return 0;
}
-void static hotkey_mask_warn_incomplete_mask(void)
+static void hotkey_mask_warn_incomplete_mask(void)
{
/* log only what the user can fix... */
const u32 wantedmask = hotkey_driver_mask &
@@ -2897,7 +2898,7 @@ static ssize_t hotkey_wakeup_reason_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_wakeup_reason);
}
-static DEVICE_ATTR_RO(hotkey_wakeup_reason);
+static DEVICE_ATTR(wakeup_reason, S_IRUGO, hotkey_wakeup_reason_show, NULL);
static void hotkey_wakeup_reason_notify_change(void)
{
@@ -2913,7 +2914,8 @@ static ssize_t hotkey_wakeup_hotunplug_complete_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_autosleep_ack);
}
-static DEVICE_ATTR_RO(hotkey_wakeup_hotunplug_complete);
+static DEVICE_ATTR(wakeup_hotunplug_complete, S_IRUGO,
+ hotkey_wakeup_hotunplug_complete_show, NULL);
static void hotkey_wakeup_hotunplug_complete_notify_change(void)
{
@@ -2978,8 +2980,8 @@ static struct attribute *hotkey_attributes[] __initdata = {
&dev_attr_hotkey_enable.attr,
&dev_attr_hotkey_bios_enabled.attr,
&dev_attr_hotkey_bios_mask.attr,
- &dev_attr_hotkey_wakeup_reason.attr,
- &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
+ &dev_attr_wakeup_reason.attr,
+ &dev_attr_wakeup_hotunplug_complete.attr,
&dev_attr_hotkey_mask.attr,
&dev_attr_hotkey_all_mask.attr,
&dev_attr_hotkey_recommended_mask.attr,
@@ -3486,7 +3488,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
/* Do not issue duplicate brightness change events to
* userspace. tpacpi_detect_brightness_capabilities() must have
* been called before this point */
- if (acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() != acpi_backlight_vendor) {
pr_info("This ThinkPad has standard ACPI backlight "
"brightness control, supported by the ACPI "
"video driver\n");
@@ -4393,12 +4395,13 @@ static ssize_t wan_enable_store(struct device *dev,
attr, buf, count);
}
-static DEVICE_ATTR_RW(wan_enable);
+static DEVICE_ATTR(wwan_enable, S_IWUSR | S_IRUGO,
+ wan_enable_show, wan_enable_store);
/* --------------------------------------------------------------------- */
static struct attribute *wan_attributes[] = {
- &dev_attr_wan_enable.attr,
+ &dev_attr_wwan_enable.attr,
NULL
};
@@ -6489,7 +6492,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
return 1;
}
- if (acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() != acpi_backlight_vendor) {
if (brightness_enable > 1) {
pr_info("Standard ACPI backlight interface "
"available, not loading native one\n");
@@ -8138,7 +8141,8 @@ static ssize_t fan_pwm1_enable_store(struct device *dev,
return count;
}
-static DEVICE_ATTR_RW(fan_pwm1_enable);
+static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+ fan_pwm1_enable_show, fan_pwm1_enable_store);
/* sysfs fan pwm1 ------------------------------------------------------ */
static ssize_t fan_pwm1_show(struct device *dev,
@@ -8198,7 +8202,7 @@ static ssize_t fan_pwm1_store(struct device *dev,
return (rc) ? rc : count;
}
-static DEVICE_ATTR_RW(fan_pwm1);
+static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, fan_pwm1_show, fan_pwm1_store);
/* sysfs fan fan1_input ------------------------------------------------ */
static ssize_t fan_fan1_input_show(struct device *dev,
@@ -8215,7 +8219,7 @@ static ssize_t fan_fan1_input_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%u\n", speed);
}
-static DEVICE_ATTR_RO(fan_fan1_input);
+static DEVICE_ATTR(fan1_input, S_IRUGO, fan_fan1_input_show, NULL);
/* sysfs fan fan2_input ------------------------------------------------ */
static ssize_t fan_fan2_input_show(struct device *dev,
@@ -8232,7 +8236,7 @@ static ssize_t fan_fan2_input_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%u\n", speed);
}
-static DEVICE_ATTR_RO(fan_fan2_input);
+static DEVICE_ATTR(fan2_input, S_IRUGO, fan_fan2_input_show, NULL);
/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
@@ -8265,8 +8269,8 @@ static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
/* --------------------------------------------------------------------- */
static struct attribute *fan_attributes[] = {
- &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
- &dev_attr_fan_fan1_input.attr,
+ &dev_attr_pwm1_enable.attr, &dev_attr_pwm1.attr,
+ &dev_attr_fan1_input.attr,
NULL, /* for fan2_input */
NULL
};
@@ -8400,7 +8404,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
if (tp_features.second_fan) {
/* attach second fan tachometer */
fan_attributes[ARRAY_SIZE(fan_attributes)-2] =
- &dev_attr_fan_fan2_input.attr;
+ &dev_attr_fan2_input.attr;
}
rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
&fan_attr_group);
@@ -8848,7 +8852,7 @@ static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%s\n", TPACPI_NAME);
}
-static DEVICE_ATTR_RO(thinkpad_acpi_pdev_name);
+static DEVICE_ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL);
/* --------------------------------------------------------------------- */
@@ -9390,8 +9394,7 @@ static void thinkpad_acpi_module_exit(void)
hwmon_device_unregister(tpacpi_hwmon);
if (tp_features.sensors_pdev_attrs_registered)
- device_remove_file(&tpacpi_sensors_pdev->dev,
- &dev_attr_thinkpad_acpi_pdev_name);
+ device_remove_file(&tpacpi_sensors_pdev->dev, &dev_attr_name);
if (tpacpi_sensors_pdev)
platform_device_unregister(tpacpi_sensors_pdev);
if (tpacpi_pdev)
@@ -9512,8 +9515,7 @@ static int __init thinkpad_acpi_module_init(void)
thinkpad_acpi_module_exit();
return ret;
}
- ret = device_create_file(&tpacpi_sensors_pdev->dev,
- &dev_attr_thinkpad_acpi_pdev_name);
+ ret = device_create_file(&tpacpi_sensors_pdev->dev, &dev_attr_name);
if (ret) {
pr_err("unable to create sysfs hwmon device attributes\n");
thinkpad_acpi_module_exit();
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 9956b9902bb4..59bf27ed72d6 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -2640,14 +2640,11 @@ static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
*/
if (dev->tr_backlight_supported ||
dmi_check_system(toshiba_vendor_backlight_dmi))
- acpi_video_dmi_promote_vendor();
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
- if (acpi_video_backlight_support())
+ if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return 0;
- /* acpi-video may have loaded before we called dmi_promote_vendor() */
- acpi_video_unregister_backlight();
-
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c
index ff0356fb378f..05796495be0e 100644
--- a/drivers/pnp/pnpacpi/rsparser.c
+++ b/drivers/pnp/pnpacpi/rsparser.c
@@ -28,8 +28,8 @@
#include "../base.h"
#include "pnpacpi.h"
-static void decode_irq_flags(struct pnp_dev *dev, int flags, int *triggering,
- int *polarity, int *shareable)
+static void decode_irq_flags(struct pnp_dev *dev, int flags, u8 *triggering,
+ u8 *polarity, u8 *shareable)
{
switch (flags & (IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_HIGHLEVEL |
IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE)) {
@@ -654,7 +654,7 @@ static void pnpacpi_encode_irq(struct pnp_dev *dev,
struct resource *p)
{
struct acpi_resource_irq *irq = &resource->data.irq;
- int triggering, polarity, shareable;
+ u8 triggering, polarity, shareable;
if (!pnp_resource_enabled(p)) {
irq->interrupt_count = 0;
@@ -683,7 +683,7 @@ static void pnpacpi_encode_ext_irq(struct pnp_dev *dev,
struct resource *p)
{
struct acpi_resource_extended_irq *extended_irq = &resource->data.extended_irq;
- int triggering, polarity, shareable;
+ u8 triggering, polarity, shareable;
if (!pnp_resource_enabled(p)) {
extended_irq->interrupt_count = 0;
@@ -873,7 +873,7 @@ int pnpacpi_encode_resources(struct pnp_dev *dev, struct acpi_buffer *buffer)
/* pnpacpi_build_resource_template allocates extra mem */
int res_cnt = (buffer->length - 1) / sizeof(struct acpi_resource) - 1;
struct acpi_resource *resource = buffer->pointer;
- int port = 0, irq = 0, dma = 0, mem = 0;
+ unsigned int port = 0, irq = 0, dma = 0, mem = 0;
pnp_dbg(&dev->dev, "encode %d resources\n", res_cnt);
while (i < res_cnt) {
diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c
index 49c1720df59a..515f33882ab8 100644
--- a/drivers/pnp/system.c
+++ b/drivers/pnp/system.c
@@ -7,6 +7,7 @@
* Bjorn Helgaas <bjorn.helgaas@hp.com>
*/
+#include <linux/acpi.h>
#include <linux/pnp.h>
#include <linux/device.h>
#include <linux/init.h>
@@ -22,25 +23,41 @@ static const struct pnp_device_id pnp_dev_table[] = {
{"", 0}
};
+#ifdef CONFIG_ACPI
+static bool __reserve_range(u64 start, unsigned int length, bool io, char *desc)
+{
+ u8 space_id = io ? ACPI_ADR_SPACE_SYSTEM_IO : ACPI_ADR_SPACE_SYSTEM_MEMORY;
+ return !acpi_reserve_region(start, length, space_id, IORESOURCE_BUSY, desc);
+}
+#else
+static bool __reserve_range(u64 start, unsigned int length, bool io, char *desc)
+{
+ struct resource *res;
+
+ res = io ? request_region(start, length, desc) :
+ request_mem_region(start, length, desc);
+ if (res) {
+ res->flags &= ~IORESOURCE_BUSY;
+ return true;
+ }
+ return false;
+}
+#endif
+
static void reserve_range(struct pnp_dev *dev, struct resource *r, int port)
{
char *regionid;
const char *pnpid = dev_name(&dev->dev);
resource_size_t start = r->start, end = r->end;
- struct resource *res;
+ bool reserved;
regionid = kmalloc(16, GFP_KERNEL);
if (!regionid)
return;
snprintf(regionid, 16, "pnp %s", pnpid);
- if (port)
- res = request_region(start, end - start + 1, regionid);
- else
- res = request_mem_region(start, end - start + 1, regionid);
- if (res)
- res->flags &= ~IORESOURCE_BUSY;
- else
+ reserved = __reserve_range(start, end - start + 1, !!port, regionid);
+ if (!reserved)
kfree(regionid);
/*
@@ -49,7 +66,7 @@ static void reserve_range(struct pnp_dev *dev, struct resource *r, int port)
* have double reservations.
*/
dev_info(&dev->dev, "%pR %s reserved\n", r,
- res ? "has been" : "could not be");
+ reserved ? "has been" : "could not be");
}
static void reserve_resources_of_dev(struct pnp_dev *dev)
diff --git a/drivers/power/88pm860x_charger.c b/drivers/power/88pm860x_charger.c
index 0e448c68c02b..297e72dc70e6 100644
--- a/drivers/power/88pm860x_charger.c
+++ b/drivers/power/88pm860x_charger.c
@@ -742,7 +742,6 @@ static int pm860x_charger_remove(struct platform_device *pdev)
int i;
power_supply_unregister(info->usb);
- free_irq(info->irq[0], info);
for (i = 0; i < info->irq_nums; i++)
free_irq(info->irq[i], info);
return 0;
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 4091fb092d06..08beeed5485d 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -204,6 +204,13 @@ config CHARGER_DA9150
This driver can also be built as a module. If so, the module will be
called da9150-charger.
+config AXP288_CHARGER
+ tristate "X-Powers AXP288 Charger"
+ depends on MFD_AXP20X && EXTCON_AXP288
+ help
+ Say yes here to have support X-Power AXP288 power management IC (PMIC)
+ integrated charger.
+
config AXP288_FUEL_GAUGE
tristate "X-Powers AXP288 Fuel Gauge"
depends on MFD_AXP20X && IIO
@@ -388,12 +395,26 @@ config CHARGER_BQ24190
help
Say Y to enable support for the TI BQ24190 battery charger.
+config CHARGER_BQ24257
+ tristate "TI BQ24257 battery charger driver"
+ depends on I2C && GPIOLIB
+ depends on REGMAP_I2C
+ help
+ Say Y to enable support for the TI BQ24257 battery charger.
+
config CHARGER_BQ24735
tristate "TI BQ24735 battery charger support"
depends on I2C && GPIOLIB
help
Say Y to enable support for the TI BQ24735 battery charger.
+config CHARGER_BQ25890
+ tristate "TI BQ25890 battery charger driver"
+ depends on I2C && GPIOLIB
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the TI BQ25890 battery charger.
+
config CHARGER_SMB347
tristate "Summit Microelectronics SMB347 Battery Charger"
depends on I2C
@@ -439,6 +460,13 @@ config BATTERY_RT5033
The fuelgauge calculates and determines the battery state of charge
according to battery open circuit voltage.
+config CHARGER_RT9455
+ tristate "Richtek RT9455 battery charger driver"
+ depends on I2C && GPIOLIB
+ select REGMAP_I2C
+ help
+ Say Y to enable support for Richtek RT9455 battery charger.
+
source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b7b0181c95e5..5752ce818f51 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o
+obj-$(CONFIG_CHARGER_RT9455) += rt9455_charger.o
obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o
obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
@@ -58,9 +59,12 @@ obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
+obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o
obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o
+obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
obj-$(CONFIG_POWER_AVS) += avs/
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_POWER_RESET) += reset/
obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
+obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o
diff --git a/drivers/power/axp288_charger.c b/drivers/power/axp288_charger.c
new file mode 100644
index 000000000000..5680317f4823
--- /dev/null
+++ b/drivers/power/axp288_charger.c
@@ -0,0 +1,941 @@
+/*
+ * axp288_charger.c - X-power AXP288 PMIC Charger driver
+ *
+ * Copyright (C) 2014 Intel Corporation
+ * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.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.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/usb/otg.h>
+#include <linux/notifier.h>
+#include <linux/power_supply.h>
+#include <linux/notifier.h>
+#include <linux/property.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/extcon.h>
+
+#define PS_STAT_VBUS_TRIGGER (1 << 0)
+#define PS_STAT_BAT_CHRG_DIR (1 << 2)
+#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3)
+#define PS_STAT_VBUS_VALID (1 << 4)
+#define PS_STAT_VBUS_PRESENT (1 << 5)
+
+#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
+#define CHRG_STAT_BAT_VALID (1 << 4)
+#define CHRG_STAT_BAT_PRESENT (1 << 5)
+#define CHRG_STAT_CHARGING (1 << 6)
+#define CHRG_STAT_PMIC_OTP (1 << 7)
+
+#define VBUS_ISPOUT_CUR_LIM_MASK 0x03
+#define VBUS_ISPOUT_CUR_LIM_BIT_POS 0
+#define VBUS_ISPOUT_CUR_LIM_900MA 0x0 /* 900mA */
+#define VBUS_ISPOUT_CUR_LIM_1500MA 0x1 /* 1500mA */
+#define VBUS_ISPOUT_CUR_LIM_2000MA 0x2 /* 2000mA */
+#define VBUS_ISPOUT_CUR_NO_LIM 0x3 /* 2500mA */
+#define VBUS_ISPOUT_VHOLD_SET_MASK 0x31
+#define VBUS_ISPOUT_VHOLD_SET_BIT_POS 0x3
+#define VBUS_ISPOUT_VHOLD_SET_OFFSET 4000 /* 4000mV */
+#define VBUS_ISPOUT_VHOLD_SET_LSB_RES 100 /* 100mV */
+#define VBUS_ISPOUT_VHOLD_SET_4300MV 0x3 /* 4300mV */
+#define VBUS_ISPOUT_VBUS_PATH_DIS (1 << 7)
+
+#define CHRG_CCCV_CC_MASK 0xf /* 4 bits */
+#define CHRG_CCCV_CC_BIT_POS 0
+#define CHRG_CCCV_CC_OFFSET 200 /* 200mA */
+#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */
+#define CHRG_CCCV_ITERM_20P (1 << 4) /* 20% of CC */
+#define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */
+#define CHRG_CCCV_CV_BIT_POS 5
+#define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */
+#define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */
+#define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */
+#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */
+#define CHRG_CCCV_CHG_EN (1 << 7)
+
+#define CNTL2_CC_TIMEOUT_MASK 0x3 /* 2 bits */
+#define CNTL2_CC_TIMEOUT_OFFSET 6 /* 6 Hrs */
+#define CNTL2_CC_TIMEOUT_LSB_RES 2 /* 2 Hrs */
+#define CNTL2_CC_TIMEOUT_12HRS 0x3 /* 12 Hrs */
+#define CNTL2_CHGLED_TYPEB (1 << 4)
+#define CNTL2_CHG_OUT_TURNON (1 << 5)
+#define CNTL2_PC_TIMEOUT_MASK 0xC0
+#define CNTL2_PC_TIMEOUT_OFFSET 40 /* 40 mins */
+#define CNTL2_PC_TIMEOUT_LSB_RES 10 /* 10 mins */
+#define CNTL2_PC_TIMEOUT_70MINS 0x3
+
+#define CHRG_ILIM_TEMP_LOOP_EN (1 << 3)
+#define CHRG_VBUS_ILIM_MASK 0xf0
+#define CHRG_VBUS_ILIM_BIT_POS 4
+#define CHRG_VBUS_ILIM_100MA 0x0 /* 100mA */
+#define CHRG_VBUS_ILIM_500MA 0x1 /* 500mA */
+#define CHRG_VBUS_ILIM_900MA 0x2 /* 900mA */
+#define CHRG_VBUS_ILIM_1500MA 0x3 /* 1500mA */
+#define CHRG_VBUS_ILIM_2000MA 0x4 /* 2000mA */
+#define CHRG_VBUS_ILIM_2500MA 0x5 /* 2500mA */
+#define CHRG_VBUS_ILIM_3000MA 0x6 /* 3000mA */
+
+#define CHRG_VLTFC_0C 0xA5 /* 0 DegC */
+#define CHRG_VHTFC_45C 0x1F /* 45 DegC */
+
+#define BAT_IRQ_CFG_CHRG_DONE (1 << 2)
+#define BAT_IRQ_CFG_CHRG_START (1 << 3)
+#define BAT_IRQ_CFG_BAT_SAFE_EXIT (1 << 4)
+#define BAT_IRQ_CFG_BAT_SAFE_ENTER (1 << 5)
+#define BAT_IRQ_CFG_BAT_DISCON (1 << 6)
+#define BAT_IRQ_CFG_BAT_CONN (1 << 7)
+#define BAT_IRQ_CFG_BAT_MASK 0xFC
+
+#define TEMP_IRQ_CFG_QCBTU (1 << 4)
+#define TEMP_IRQ_CFG_CBTU (1 << 5)
+#define TEMP_IRQ_CFG_QCBTO (1 << 6)
+#define TEMP_IRQ_CFG_CBTO (1 << 7)
+#define TEMP_IRQ_CFG_MASK 0xF0
+
+#define FG_CNTL_OCV_ADJ_EN (1 << 3)
+
+#define CV_4100MV 4100 /* 4100mV */
+#define CV_4150MV 4150 /* 4150mV */
+#define CV_4200MV 4200 /* 4200mV */
+#define CV_4350MV 4350 /* 4350mV */
+
+#define CC_200MA 200 /* 200mA */
+#define CC_600MA 600 /* 600mA */
+#define CC_800MA 800 /* 800mA */
+#define CC_1000MA 1000 /* 1000mA */
+#define CC_1600MA 1600 /* 1600mA */
+#define CC_2000MA 2000 /* 2000mA */
+
+#define ILIM_100MA 100 /* 100mA */
+#define ILIM_500MA 500 /* 500mA */
+#define ILIM_900MA 900 /* 900mA */
+#define ILIM_1500MA 1500 /* 1500mA */
+#define ILIM_2000MA 2000 /* 2000mA */
+#define ILIM_2500MA 2500 /* 2500mA */
+#define ILIM_3000MA 3000 /* 3000mA */
+
+#define AXP288_EXTCON_DEV_NAME "axp288_extcon"
+
+#define AXP288_EXTCON_SLOW_CHARGER "SLOW-CHARGER"
+#define AXP288_EXTCON_DOWNSTREAM_CHARGER "CHARGE-DOWNSTREAM"
+#define AXP288_EXTCON_FAST_CHARGER "FAST-CHARGER"
+
+enum {
+ VBUS_OV_IRQ = 0,
+ CHARGE_DONE_IRQ,
+ CHARGE_CHARGING_IRQ,
+ BAT_SAFE_QUIT_IRQ,
+ BAT_SAFE_ENTER_IRQ,
+ QCBTU_IRQ,
+ CBTU_IRQ,
+ QCBTO_IRQ,
+ CBTO_IRQ,
+ CHRG_INTR_END,
+};
+
+struct axp288_chrg_info {
+ struct platform_device *pdev;
+ struct axp20x_chrg_pdata *pdata;
+ struct regmap *regmap;
+ struct regmap_irq_chip_data *regmap_irqc;
+ int irq[CHRG_INTR_END];
+ struct power_supply *psy_usb;
+ struct mutex lock;
+
+ /* OTG/Host mode */
+ struct {
+ struct work_struct work;
+ struct extcon_specific_cable_nb cable;
+ struct notifier_block id_nb;
+ bool id_short;
+ } otg;
+
+ /* SDP/CDP/DCP USB charging cable notifications */
+ struct {
+ struct extcon_dev *edev;
+ bool connected;
+ enum power_supply_type chg_type;
+ struct notifier_block nb;
+ struct work_struct work;
+ } cable;
+
+ int health;
+ int inlmt;
+ int cc;
+ int cv;
+ int max_cc;
+ int max_cv;
+ bool online;
+ bool present;
+ bool enable_charger;
+ bool is_charger_enabled;
+};
+
+static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
+{
+ u8 reg_val;
+ int ret;
+
+ if (cc < CHRG_CCCV_CC_OFFSET)
+ cc = CHRG_CCCV_CC_OFFSET;
+ else if (cc > info->max_cc)
+ cc = info->max_cc;
+
+ reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES;
+ cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
+ reg_val = reg_val << CHRG_CCCV_CC_BIT_POS;
+
+ ret = regmap_update_bits(info->regmap,
+ AXP20X_CHRG_CTRL1,
+ CHRG_CCCV_CC_MASK, reg_val);
+ if (ret >= 0)
+ info->cc = cc;
+
+ return ret;
+}
+
+static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
+{
+ u8 reg_val;
+ int ret;
+
+ if (cv <= CV_4100MV) {
+ reg_val = CHRG_CCCV_CV_4100MV;
+ cv = CV_4100MV;
+ } else if (cv <= CV_4150MV) {
+ reg_val = CHRG_CCCV_CV_4150MV;
+ cv = CV_4150MV;
+ } else if (cv <= CV_4200MV) {
+ reg_val = CHRG_CCCV_CV_4200MV;
+ cv = CV_4200MV;
+ } else {
+ reg_val = CHRG_CCCV_CV_4350MV;
+ cv = CV_4350MV;
+ }
+
+ reg_val = reg_val << CHRG_CCCV_CV_BIT_POS;
+
+ ret = regmap_update_bits(info->regmap,
+ AXP20X_CHRG_CTRL1,
+ CHRG_CCCV_CV_MASK, reg_val);
+
+ if (ret >= 0)
+ info->cv = cv;
+
+ return ret;
+}
+
+static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
+ int inlmt)
+{
+ int ret;
+ unsigned int val;
+ u8 reg_val;
+
+ /* Read in limit register */
+ ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val);
+ if (ret < 0)
+ goto set_inlmt_fail;
+
+ if (inlmt <= ILIM_100MA) {
+ reg_val = CHRG_VBUS_ILIM_100MA;
+ inlmt = ILIM_100MA;
+ } else if (inlmt <= ILIM_500MA) {
+ reg_val = CHRG_VBUS_ILIM_500MA;
+ inlmt = ILIM_500MA;
+ } else if (inlmt <= ILIM_900MA) {
+ reg_val = CHRG_VBUS_ILIM_900MA;
+ inlmt = ILIM_900MA;
+ } else if (inlmt <= ILIM_1500MA) {
+ reg_val = CHRG_VBUS_ILIM_1500MA;
+ inlmt = ILIM_1500MA;
+ } else if (inlmt <= ILIM_2000MA) {
+ reg_val = CHRG_VBUS_ILIM_2000MA;
+ inlmt = ILIM_2000MA;
+ } else if (inlmt <= ILIM_2500MA) {
+ reg_val = CHRG_VBUS_ILIM_2500MA;
+ inlmt = ILIM_2500MA;
+ } else {
+ reg_val = CHRG_VBUS_ILIM_3000MA;
+ inlmt = ILIM_3000MA;
+ }
+
+ reg_val = (val & ~CHRG_VBUS_ILIM_MASK)
+ | (reg_val << CHRG_VBUS_ILIM_BIT_POS);
+ ret = regmap_write(info->regmap, AXP20X_CHRG_BAK_CTRL, reg_val);
+ if (ret >= 0)
+ info->inlmt = inlmt;
+ else
+ dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
+
+
+set_inlmt_fail:
+ return ret;
+}
+
+static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
+ bool enable)
+{
+ int ret;
+
+ if (enable)
+ ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
+ VBUS_ISPOUT_VBUS_PATH_DIS, 0);
+ else
+ ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
+ VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS);
+
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
+
+
+ return ret;
+}
+
+static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
+ bool enable)
+{
+ int ret;
+
+ if (enable)
+ ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
+ CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
+ else
+ ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
+ CHRG_CCCV_CHG_EN, 0);
+ if (ret < 0)
+ dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
+ else
+ info->is_charger_enabled = enable;
+
+ return ret;
+}
+
+static int axp288_charger_is_present(struct axp288_chrg_info *info)
+{
+ int ret, present = 0;
+ unsigned int val;
+
+ ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & PS_STAT_VBUS_PRESENT)
+ present = 1;
+ return present;
+}
+
+static int axp288_charger_is_online(struct axp288_chrg_info *info)
+{
+ int ret, online = 0;
+ unsigned int val;
+
+ ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & PS_STAT_VBUS_VALID)
+ online = 1;
+ return online;
+}
+
+static int axp288_get_charger_health(struct axp288_chrg_info *info)
+{
+ int ret, pwr_stat, chrg_stat;
+ int health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ unsigned int val;
+
+ ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
+ if ((ret < 0) || !(val & PS_STAT_VBUS_PRESENT))
+ goto health_read_fail;
+ else
+ pwr_stat = val;
+
+ ret = regmap_read(info->regmap, AXP20X_PWR_OP_MODE, &val);
+ if (ret < 0)
+ goto health_read_fail;
+ else
+ chrg_stat = val;
+
+ if (!(pwr_stat & PS_STAT_VBUS_VALID))
+ health = POWER_SUPPLY_HEALTH_DEAD;
+ else if (chrg_stat & CHRG_STAT_PMIC_OTP)
+ health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (chrg_stat & CHRG_STAT_BAT_SAFE_MODE)
+ health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+ else
+ health = POWER_SUPPLY_HEALTH_GOOD;
+
+health_read_fail:
+ return health;
+}
+
+static int axp288_charger_usb_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
+ int ret = 0;
+ int scaled_val;
+
+ mutex_lock(&info->lock);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ scaled_val = min(val->intval, info->max_cc);
+ scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
+ ret = axp288_charger_set_cc(info, scaled_val);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev, "set charge current failed\n");
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ scaled_val = min(val->intval, info->max_cv);
+ scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
+ ret = axp288_charger_set_cv(info, scaled_val);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev, "set charge voltage failed\n");
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int axp288_charger_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
+ int ret = 0;
+
+ mutex_lock(&info->lock);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ /* Check for OTG case first */
+ if (info->otg.id_short) {
+ val->intval = 0;
+ break;
+ }
+ ret = axp288_charger_is_present(info);
+ if (ret < 0)
+ goto psy_get_prop_fail;
+ info->present = ret;
+ val->intval = info->present;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ /* Check for OTG case first */
+ if (info->otg.id_short) {
+ val->intval = 0;
+ break;
+ }
+ ret = axp288_charger_is_online(info);
+ if (ret < 0)
+ goto psy_get_prop_fail;
+ info->online = ret;
+ val->intval = info->online;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = axp288_get_charger_health(info);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ val->intval = info->cc * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = info->max_cc * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ val->intval = info->cv * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ val->intval = info->max_cv * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+ val->intval = info->inlmt * 1000;
+ break;
+ default:
+ ret = -EINVAL;
+ goto psy_get_prop_fail;
+ }
+
+psy_get_prop_fail:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int axp288_charger_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = 1;
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static enum power_supply_property axp288_usb_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
+};
+
+static const struct power_supply_desc axp288_charger_desc = {
+ .name = "axp288_charger",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = axp288_usb_props,
+ .num_properties = ARRAY_SIZE(axp288_usb_props),
+ .get_property = axp288_charger_usb_get_property,
+ .set_property = axp288_charger_usb_set_property,
+ .property_is_writeable = axp288_charger_property_is_writeable,
+};
+
+static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)
+{
+ struct axp288_chrg_info *info = dev;
+ int i;
+
+ for (i = 0; i < CHRG_INTR_END; i++) {
+ if (info->irq[i] == irq)
+ break;
+ }
+
+ if (i >= CHRG_INTR_END) {
+ dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
+ return IRQ_NONE;
+ }
+
+ switch (i) {
+ case VBUS_OV_IRQ:
+ dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n");
+ break;
+ case CHARGE_DONE_IRQ:
+ dev_dbg(&info->pdev->dev, "Charging Done INTR\n");
+ break;
+ case CHARGE_CHARGING_IRQ:
+ dev_dbg(&info->pdev->dev, "Start Charging IRQ\n");
+ break;
+ case BAT_SAFE_QUIT_IRQ:
+ dev_dbg(&info->pdev->dev,
+ "Quit Safe Mode(restart timer) Charging IRQ\n");
+ break;
+ case BAT_SAFE_ENTER_IRQ:
+ dev_dbg(&info->pdev->dev,
+ "Enter Safe Mode(timer expire) Charging IRQ\n");
+ break;
+ case QCBTU_IRQ:
+ dev_dbg(&info->pdev->dev,
+ "Quit Battery Under Temperature(CHRG) INTR\n");
+ break;
+ case CBTU_IRQ:
+ dev_dbg(&info->pdev->dev,
+ "Hit Battery Under Temperature(CHRG) INTR\n");
+ break;
+ case QCBTO_IRQ:
+ dev_dbg(&info->pdev->dev,
+ "Quit Battery Over Temperature(CHRG) INTR\n");
+ break;
+ case CBTO_IRQ:
+ dev_dbg(&info->pdev->dev,
+ "Hit Battery Over Temperature(CHRG) INTR\n");
+ break;
+ default:
+ dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
+ goto out;
+ }
+
+ power_supply_changed(info->psy_usb);
+out:
+ return IRQ_HANDLED;
+}
+
+static void axp288_charger_extcon_evt_worker(struct work_struct *work)
+{
+ struct axp288_chrg_info *info =
+ container_of(work, struct axp288_chrg_info, cable.work);
+ int ret, current_limit;
+ bool changed = false;
+ struct extcon_dev *edev = info->cable.edev;
+ bool old_connected = info->cable.connected;
+
+ /* Determine cable/charger type */
+ if (extcon_get_cable_state(edev, AXP288_EXTCON_SLOW_CHARGER) > 0) {
+ dev_dbg(&info->pdev->dev, "USB SDP charger is connected");
+ info->cable.connected = true;
+ info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
+ } else if (extcon_get_cable_state(edev,
+ AXP288_EXTCON_DOWNSTREAM_CHARGER) > 0) {
+ dev_dbg(&info->pdev->dev, "USB CDP charger is connected");
+ info->cable.connected = true;
+ info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP;
+ } else if (extcon_get_cable_state(edev,
+ AXP288_EXTCON_FAST_CHARGER) > 0) {
+ dev_dbg(&info->pdev->dev, "USB DCP charger is connected");
+ info->cable.connected = true;
+ info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP;
+ } else {
+ if (old_connected)
+ dev_dbg(&info->pdev->dev, "USB charger disconnected");
+ info->cable.connected = false;
+ info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
+ }
+
+ /* Cable status changed */
+ if (old_connected != info->cable.connected)
+ changed = true;
+
+ if (!changed)
+ return;
+
+ mutex_lock(&info->lock);
+
+ if (info->is_charger_enabled && !info->cable.connected) {
+ info->enable_charger = false;
+ ret = axp288_charger_enable_charger(info, info->enable_charger);
+ if (ret < 0)
+ dev_err(&info->pdev->dev,
+ "cannot disable charger (%d)", ret);
+
+ } else if (!info->is_charger_enabled && info->cable.connected) {
+ switch (info->cable.chg_type) {
+ case POWER_SUPPLY_TYPE_USB:
+ current_limit = ILIM_500MA;
+ break;
+ case POWER_SUPPLY_TYPE_USB_CDP:
+ current_limit = ILIM_1500MA;
+ break;
+ case POWER_SUPPLY_TYPE_USB_DCP:
+ current_limit = ILIM_2000MA;
+ break;
+ default:
+ /* Unknown */
+ current_limit = 0;
+ break;
+ }
+
+ /* Set vbus current limit first, then enable charger */
+ ret = axp288_charger_set_vbus_inlmt(info, current_limit);
+ if (ret < 0) {
+ dev_err(&info->pdev->dev,
+ "error setting current limit (%d)", ret);
+ } else {
+ info->enable_charger = (current_limit > 0);
+ ret = axp288_charger_enable_charger(info,
+ info->enable_charger);
+ if (ret < 0)
+ dev_err(&info->pdev->dev,
+ "cannot enable charger (%d)", ret);
+ }
+ }
+
+ if (changed)
+ info->health = axp288_get_charger_health(info);
+
+ mutex_unlock(&info->lock);
+
+ if (changed)
+ power_supply_changed(info->psy_usb);
+}
+
+static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
+ unsigned long event, void *param)
+{
+ struct axp288_chrg_info *info =
+ container_of(nb, struct axp288_chrg_info, cable.nb);
+
+ schedule_work(&info->cable.work);
+
+ return NOTIFY_OK;
+}
+
+static void axp288_charger_otg_evt_worker(struct work_struct *work)
+{
+ struct axp288_chrg_info *info =
+ container_of(work, struct axp288_chrg_info, otg.work);
+ int ret;
+
+ /* Disable VBUS path before enabling the 5V boost */
+ ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev, "vbus path disable failed\n");
+}
+
+static int axp288_charger_handle_otg_evt(struct notifier_block *nb,
+ unsigned long event, void *param)
+{
+ struct axp288_chrg_info *info =
+ container_of(nb, struct axp288_chrg_info, otg.id_nb);
+ struct extcon_dev *edev = param;
+ int usb_host = extcon_get_cable_state(edev, "USB-Host");
+
+ dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
+ usb_host ? "attached" : "detached");
+
+ /*
+ * Set usb_id_short flag to avoid running charger detection logic
+ * in case usb host.
+ */
+ info->otg.id_short = usb_host;
+ schedule_work(&info->otg.work);
+
+ return NOTIFY_OK;
+}
+
+static void charger_init_hw_regs(struct axp288_chrg_info *info)
+{
+ int ret, cc, cv;
+ unsigned int val;
+
+ /* Program temperature thresholds */
+ ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ AXP20X_V_LTF_CHRG, ret);
+
+ ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ AXP20X_V_HTF_CHRG, ret);
+
+ /* Do not turn-off charger o/p after charge cycle ends */
+ ret = regmap_update_bits(info->regmap,
+ AXP20X_CHRG_CTRL2,
+ CNTL2_CHG_OUT_TURNON, 1);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ AXP20X_CHRG_CTRL2, ret);
+
+ /* Enable interrupts */
+ ret = regmap_update_bits(info->regmap,
+ AXP20X_IRQ2_EN,
+ BAT_IRQ_CFG_BAT_MASK, 1);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ AXP20X_IRQ2_EN, ret);
+
+ ret = regmap_update_bits(info->regmap, AXP20X_IRQ3_EN,
+ TEMP_IRQ_CFG_MASK, 1);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ AXP20X_IRQ3_EN, ret);
+
+ /* Setup ending condition for charging to be 10% of I(chrg) */
+ ret = regmap_update_bits(info->regmap,
+ AXP20X_CHRG_CTRL1,
+ CHRG_CCCV_ITERM_20P, 0);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ AXP20X_CHRG_CTRL1, ret);
+
+ /* Disable OCV-SOC curve calibration */
+ ret = regmap_update_bits(info->regmap,
+ AXP20X_CC_CTRL,
+ FG_CNTL_OCV_ADJ_EN, 0);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+ AXP20X_CC_CTRL, ret);
+
+ /* Init charging current and voltage */
+ info->max_cc = info->pdata->max_cc;
+ info->max_cv = info->pdata->max_cv;
+
+ /* Read current charge voltage and current limit */
+ ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
+ if (ret < 0) {
+ /* Assume default if cannot read */
+ info->cc = info->pdata->def_cc;
+ info->cv = info->pdata->def_cv;
+ } else {
+ /* Determine charge voltage */
+ cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
+ switch (cv) {
+ case CHRG_CCCV_CV_4100MV:
+ info->cv = CV_4100MV;
+ break;
+ case CHRG_CCCV_CV_4150MV:
+ info->cv = CV_4150MV;
+ break;
+ case CHRG_CCCV_CV_4200MV:
+ info->cv = CV_4200MV;
+ break;
+ case CHRG_CCCV_CV_4350MV:
+ info->cv = CV_4350MV;
+ break;
+ default:
+ info->cv = INT_MAX;
+ break;
+ }
+
+ /* Determine charge current limit */
+ cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
+ cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
+ info->cc = cc;
+
+ /* Program default charging voltage and current */
+ cc = min(info->pdata->def_cc, info->max_cc);
+ cv = min(info->pdata->def_cv, info->max_cv);
+
+ ret = axp288_charger_set_cc(info, cc);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev,
+ "error(%d) in setting CC\n", ret);
+
+ ret = axp288_charger_set_cv(info, cv);
+ if (ret < 0)
+ dev_warn(&info->pdev->dev,
+ "error(%d) in setting CV\n", ret);
+ }
+}
+
+static int axp288_charger_probe(struct platform_device *pdev)
+{
+ int ret, i, pirq;
+ struct axp288_chrg_info *info;
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ struct power_supply_config charger_cfg = {};
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->pdev = pdev;
+ info->regmap = axp20x->regmap;
+ info->regmap_irqc = axp20x->regmap_irqc;
+ info->pdata = pdev->dev.platform_data;
+
+ if (!info->pdata) {
+ /* Try ACPI provided pdata via device properties */
+ if (!device_property_present(&pdev->dev,
+ "axp288_charger_data\n"))
+ dev_err(&pdev->dev, "failed to get platform data\n");
+ return -ENODEV;
+ }
+
+ info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
+ if (info->cable.edev == NULL) {
+ dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n",
+ AXP288_EXTCON_DEV_NAME);
+ return -EPROBE_DEFER;
+ }
+
+ /* Register for extcon notification */
+ INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
+ info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
+ ret = extcon_register_notifier(info->cable.edev, &info->cable.nb);
+ if (ret) {
+ dev_err(&info->pdev->dev,
+ "failed to register extcon notifier %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, info);
+ mutex_init(&info->lock);
+
+ /* Register with power supply class */
+ charger_cfg.drv_data = info;
+ info->psy_usb = power_supply_register(&pdev->dev, &axp288_charger_desc,
+ &charger_cfg);
+ if (IS_ERR(info->psy_usb)) {
+ dev_err(&pdev->dev, "failed to register power supply charger\n");
+ ret = PTR_ERR(info->psy_usb);
+ goto psy_reg_failed;
+ }
+
+ /* Register for OTG notification */
+ INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
+ info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
+ ret = extcon_register_interest(&info->otg.cable, NULL, "USB-Host",
+ &info->otg.id_nb);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to register otg notifier\n");
+
+ if (info->otg.cable.edev)
+ info->otg.id_short = extcon_get_cable_state(
+ info->otg.cable.edev, "USB-Host");
+
+ /* Register charger interrupts */
+ for (i = 0; i < CHRG_INTR_END; i++) {
+ pirq = platform_get_irq(info->pdev, i);
+ info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
+ if (info->irq[i] < 0) {
+ dev_warn(&info->pdev->dev,
+ "failed to get virtual interrupt=%d\n", pirq);
+ ret = info->irq[i];
+ goto intr_reg_failed;
+ }
+ ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
+ NULL, axp288_charger_irq_thread_handler,
+ IRQF_ONESHOT, info->pdev->name, info);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request interrupt=%d\n",
+ info->irq[i]);
+ goto intr_reg_failed;
+ }
+ }
+
+ charger_init_hw_regs(info);
+
+ return 0;
+
+intr_reg_failed:
+ if (info->otg.cable.edev)
+ extcon_unregister_interest(&info->otg.cable);
+ power_supply_unregister(info->psy_usb);
+psy_reg_failed:
+ extcon_unregister_notifier(info->cable.edev, &info->cable.nb);
+ return ret;
+}
+
+static int axp288_charger_remove(struct platform_device *pdev)
+{
+ struct axp288_chrg_info *info = dev_get_drvdata(&pdev->dev);
+
+ if (info->otg.cable.edev)
+ extcon_unregister_interest(&info->otg.cable);
+
+ extcon_unregister_notifier(info->cable.edev, &info->cable.nb);
+ power_supply_unregister(info->psy_usb);
+
+ return 0;
+}
+
+static struct platform_driver axp288_charger_driver = {
+ .probe = axp288_charger_probe,
+ .remove = axp288_charger_remove,
+ .driver = {
+ .name = "axp288_charger",
+ },
+};
+
+module_platform_driver(axp288_charger_driver);
+
+MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
+MODULE_DESCRIPTION("X-power AXP288 Charger Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/axp288_fuel_gauge.c b/drivers/power/axp288_fuel_gauge.c
index ca1cc5a47eb1..50c0110d6b58 100644
--- a/drivers/power/axp288_fuel_gauge.c
+++ b/drivers/power/axp288_fuel_gauge.c
@@ -1117,7 +1117,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
return ret;
}
-static struct platform_device_id axp288_fg_id_table[] = {
+static const struct platform_device_id axp288_fg_id_table[] = {
{ .name = DEV_NAME },
{},
};
@@ -1149,6 +1149,7 @@ static struct platform_driver axp288_fuel_gauge_driver = {
module_platform_driver(axp288_fuel_gauge_driver);
+MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>");
MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c
index 6c534dcbc19c..e98dcb661cc9 100644
--- a/drivers/power/bq2415x_charger.c
+++ b/drivers/power/bq2415x_charger.c
@@ -35,6 +35,7 @@
#include <linux/idr.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/acpi.h>
#include <linux/power/bq2415x_charger.h>
@@ -631,7 +632,7 @@ static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA)
int val;
if (bq->init_data.resistor_sense <= 0)
- return -ENOSYS;
+ return -EINVAL;
val = (mA * bq->init_data.resistor_sense - 37400) / 6800;
if (val < 0)
@@ -650,7 +651,7 @@ static int bq2415x_get_charge_current(struct bq2415x_device *bq)
int ret;
if (bq->init_data.resistor_sense <= 0)
- return -ENOSYS;
+ return -EINVAL;
ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG);
@@ -665,7 +666,7 @@ static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA)
int val;
if (bq->init_data.resistor_sense <= 0)
- return -ENOSYS;
+ return -EINVAL;
val = (mA * bq->init_data.resistor_sense - 3400) / 3400;
if (val < 0)
@@ -684,7 +685,7 @@ static int bq2415x_get_termination_current(struct bq2415x_device *bq)
int ret;
if (bq->init_data.resistor_sense <= 0)
- return -ENOSYS;
+ return -EINVAL;
ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM);
@@ -1166,7 +1167,7 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
if (strncmp(buf, "auto", 4) == 0) {
if (bq->automode < 0)
- return -ENOSYS;
+ return -EINVAL;
bq->automode = 1;
mode = bq->reported_mode;
} else if (strncmp(buf, "off", 3) == 0) {
@@ -1530,13 +1531,14 @@ static int bq2415x_probe(struct i2c_client *client,
{
int ret;
int num;
- char *name;
+ char *name = NULL;
struct bq2415x_device *bq;
struct device_node *np = client->dev.of_node;
struct bq2415x_platform_data *pdata = client->dev.platform_data;
+ const struct acpi_device_id *acpi_id = NULL;
- if (!np && !pdata) {
- dev_err(&client->dev, "platform data missing\n");
+ if (!np && !pdata && !ACPI_HANDLE(&client->dev)) {
+ dev_err(&client->dev, "Neither devicetree, nor platform data, nor ACPI support\n");
return -ENODEV;
}
@@ -1547,7 +1549,14 @@ static int bq2415x_probe(struct i2c_client *client,
if (num < 0)
return num;
- name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
+ if (id) {
+ name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
+ } else if (ACPI_HANDLE(&client->dev)) {
+ acpi_id =
+ acpi_match_device(client->dev.driver->acpi_match_table,
+ &client->dev);
+ name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);
+ }
if (!name) {
dev_err(&client->dev, "failed to allocate device name\n");
ret = -ENOMEM;
@@ -1556,63 +1565,72 @@ static int bq2415x_probe(struct i2c_client *client,
bq = devm_kzalloc(&client->dev, sizeof(*bq), GFP_KERNEL);
if (!bq) {
- dev_err(&client->dev, "failed to allocate device data\n");
ret = -ENOMEM;
goto error_2;
}
if (np) {
- bq->notify_psy = power_supply_get_by_phandle(np, "ti,usb-charger-detection");
+ bq->notify_psy = power_supply_get_by_phandle(np,
+ "ti,usb-charger-detection");
if (IS_ERR(bq->notify_psy)) {
dev_info(&client->dev,
- "no 'ti,usb-charger-detection' property (err=%ld)\n",
+ "no 'ti,usb-charger-detection' property (err=%ld)\n",
PTR_ERR(bq->notify_psy));
bq->notify_psy = NULL;
} else if (!bq->notify_psy) {
ret = -EPROBE_DEFER;
goto error_2;
}
- }
- else if (pdata->notify_device)
+ } else if (pdata && pdata->notify_device) {
bq->notify_psy = power_supply_get_by_name(pdata->notify_device);
- else
+ } else {
bq->notify_psy = NULL;
+ }
i2c_set_clientdata(client, bq);
bq->id = num;
bq->dev = &client->dev;
- bq->chip = id->driver_data;
+ if (id)
+ bq->chip = id->driver_data;
+ else if (ACPI_HANDLE(bq->dev))
+ bq->chip = acpi_id->driver_data;
bq->name = name;
bq->mode = BQ2415X_MODE_OFF;
bq->reported_mode = BQ2415X_MODE_OFF;
bq->autotimer = 0;
bq->automode = 0;
- if (np) {
- ret = of_property_read_u32(np, "ti,current-limit",
- &bq->init_data.current_limit);
+ if (np || ACPI_HANDLE(bq->dev)) {
+ ret = device_property_read_u32(bq->dev,
+ "ti,current-limit",
+ &bq->init_data.current_limit);
if (ret)
goto error_3;
- ret = of_property_read_u32(np, "ti,weak-battery-voltage",
- &bq->init_data.weak_battery_voltage);
+ ret = device_property_read_u32(bq->dev,
+ "ti,weak-battery-voltage",
+ &bq->init_data.weak_battery_voltage);
if (ret)
goto error_3;
- ret = of_property_read_u32(np, "ti,battery-regulation-voltage",
+ ret = device_property_read_u32(bq->dev,
+ "ti,battery-regulation-voltage",
&bq->init_data.battery_regulation_voltage);
if (ret)
goto error_3;
- ret = of_property_read_u32(np, "ti,charge-current",
- &bq->init_data.charge_current);
+ ret = device_property_read_u32(bq->dev,
+ "ti,charge-current",
+ &bq->init_data.charge_current);
if (ret)
goto error_3;
- ret = of_property_read_u32(np, "ti,termination-current",
+ ret = device_property_read_u32(bq->dev,
+ "ti,termination-current",
&bq->init_data.termination_current);
if (ret)
goto error_3;
- ret = of_property_read_u32(np, "ti,resistor-sense",
- &bq->init_data.resistor_sense);
+ ret = device_property_read_u32(bq->dev,
+ "ti,resistor-sense",
+ &bq->init_data.resistor_sense);
if (ret)
goto error_3;
} else {
@@ -1648,7 +1666,8 @@ static int bq2415x_probe(struct i2c_client *client,
}
/* Query for initial reported_mode and set it */
- bq2415x_notifier_call(&bq->nb, PSY_EVENT_PROP_CHANGED, bq->notify_psy);
+ bq2415x_notifier_call(&bq->nb, PSY_EVENT_PROP_CHANGED,
+ bq->notify_psy);
bq2415x_set_mode(bq, bq->reported_mode);
bq->automode = 1;
@@ -1727,9 +1746,28 @@ static const struct i2c_device_id bq2415x_i2c_id_table[] = {
};
MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table);
+static const struct acpi_device_id bq2415x_i2c_acpi_match[] = {
+ { "BQ2415X", BQUNKNOWN },
+ { "BQ241500", BQ24150 },
+ { "BQA24150", BQ24150A },
+ { "BQ241510", BQ24151 },
+ { "BQA24151", BQ24151A },
+ { "BQ241520", BQ24152 },
+ { "BQ241530", BQ24153 },
+ { "BQA24153", BQ24153A },
+ { "BQ241550", BQ24155 },
+ { "BQ241560", BQ24156 },
+ { "BQA24156", BQ24156A },
+ { "BQS24157", BQ24157S },
+ { "BQ241580", BQ24158 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, bq2415x_i2c_acpi_match);
+
static struct i2c_driver bq2415x_driver = {
.driver = {
.name = "bq2415x-charger",
+ .acpi_match_table = ACPI_PTR(bq2415x_i2c_acpi_match),
},
.probe = bq2415x_probe,
.remove = bq2415x_remove,
diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c
index 407c4af83891..052db78c3736 100644
--- a/drivers/power/bq24190_charger.c
+++ b/drivers/power/bq24190_charger.c
@@ -1258,10 +1258,13 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
* register reset so we should ignore that one (the very first
* interrupt received).
*/
- if (alert_userspace && !bdi->first_time) {
- power_supply_changed(bdi->charger);
- power_supply_changed(bdi->battery);
- bdi->first_time = false;
+ if (alert_userspace) {
+ if (!bdi->first_time) {
+ power_supply_changed(bdi->charger);
+ power_supply_changed(bdi->battery);
+ } else {
+ bdi->first_time = false;
+ }
}
out:
diff --git a/drivers/power/bq24257_charger.c b/drivers/power/bq24257_charger.c
new file mode 100644
index 000000000000..5859bc7c1616
--- /dev/null
+++ b/drivers/power/bq24257_charger.c
@@ -0,0 +1,858 @@
+/*
+ * TI BQ24257 charger driver
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/acpi.h>
+#include <linux/of.h>
+
+#define BQ24257_REG_1 0x00
+#define BQ24257_REG_2 0x01
+#define BQ24257_REG_3 0x02
+#define BQ24257_REG_4 0x03
+#define BQ24257_REG_5 0x04
+#define BQ24257_REG_6 0x05
+#define BQ24257_REG_7 0x06
+
+#define BQ24257_MANUFACTURER "Texas Instruments"
+#define BQ24257_STAT_IRQ "stat"
+#define BQ24257_PG_GPIO "pg"
+
+#define BQ24257_ILIM_SET_DELAY 1000 /* msec */
+
+enum bq24257_fields {
+ F_WD_FAULT, F_WD_EN, F_STAT, F_FAULT, /* REG 1 */
+ F_RESET, F_IILIMIT, F_EN_STAT, F_EN_TERM, F_CE, F_HZ_MODE, /* REG 2 */
+ F_VBAT, F_USB_DET, /* REG 3 */
+ F_ICHG, F_ITERM, /* REG 4 */
+ F_LOOP_STATUS, F_LOW_CHG, F_DPDM_EN, F_CE_STATUS, F_VINDPM, /* REG 5 */
+ F_X2_TMR_EN, F_TMR, F_SYSOFF, F_TS_STAT, /* REG 6 */
+ F_VOVP, F_CLR_VDP, F_FORCE_BATDET, F_FORCE_PTM, /* REG 7 */
+
+ F_MAX_FIELDS
+};
+
+/* initial field values, converted from uV/uA */
+struct bq24257_init_data {
+ u8 ichg; /* charge current */
+ u8 vbat; /* regulation voltage */
+ u8 iterm; /* termination current */
+};
+
+struct bq24257_state {
+ u8 status;
+ u8 fault;
+ bool power_good;
+};
+
+struct bq24257_device {
+ struct i2c_client *client;
+ struct device *dev;
+ struct power_supply *charger;
+
+ struct regmap *rmap;
+ struct regmap_field *rmap_fields[F_MAX_FIELDS];
+
+ struct gpio_desc *pg;
+
+ struct delayed_work iilimit_setup_work;
+
+ struct bq24257_init_data init_data;
+ struct bq24257_state state;
+
+ struct mutex lock; /* protect state data */
+};
+
+static bool bq24257_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BQ24257_REG_2:
+ case BQ24257_REG_4:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config bq24257_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BQ24257_REG_7,
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = bq24257_is_volatile_reg,
+};
+
+static const struct reg_field bq24257_reg_fields[] = {
+ /* REG 1 */
+ [F_WD_FAULT] = REG_FIELD(BQ24257_REG_1, 7, 7),
+ [F_WD_EN] = REG_FIELD(BQ24257_REG_1, 6, 6),
+ [F_STAT] = REG_FIELD(BQ24257_REG_1, 4, 5),
+ [F_FAULT] = REG_FIELD(BQ24257_REG_1, 0, 3),
+ /* REG 2 */
+ [F_RESET] = REG_FIELD(BQ24257_REG_2, 7, 7),
+ [F_IILIMIT] = REG_FIELD(BQ24257_REG_2, 4, 6),
+ [F_EN_STAT] = REG_FIELD(BQ24257_REG_2, 3, 3),
+ [F_EN_TERM] = REG_FIELD(BQ24257_REG_2, 2, 2),
+ [F_CE] = REG_FIELD(BQ24257_REG_2, 1, 1),
+ [F_HZ_MODE] = REG_FIELD(BQ24257_REG_2, 0, 0),
+ /* REG 3 */
+ [F_VBAT] = REG_FIELD(BQ24257_REG_3, 2, 7),
+ [F_USB_DET] = REG_FIELD(BQ24257_REG_3, 0, 1),
+ /* REG 4 */
+ [F_ICHG] = REG_FIELD(BQ24257_REG_4, 3, 7),
+ [F_ITERM] = REG_FIELD(BQ24257_REG_4, 0, 2),
+ /* REG 5 */
+ [F_LOOP_STATUS] = REG_FIELD(BQ24257_REG_5, 6, 7),
+ [F_LOW_CHG] = REG_FIELD(BQ24257_REG_5, 5, 5),
+ [F_DPDM_EN] = REG_FIELD(BQ24257_REG_5, 4, 4),
+ [F_CE_STATUS] = REG_FIELD(BQ24257_REG_5, 3, 3),
+ [F_VINDPM] = REG_FIELD(BQ24257_REG_5, 0, 2),
+ /* REG 6 */
+ [F_X2_TMR_EN] = REG_FIELD(BQ24257_REG_6, 7, 7),
+ [F_TMR] = REG_FIELD(BQ24257_REG_6, 5, 6),
+ [F_SYSOFF] = REG_FIELD(BQ24257_REG_6, 4, 4),
+ [F_TS_STAT] = REG_FIELD(BQ24257_REG_6, 0, 2),
+ /* REG 7 */
+ [F_VOVP] = REG_FIELD(BQ24257_REG_7, 5, 7),
+ [F_CLR_VDP] = REG_FIELD(BQ24257_REG_7, 4, 4),
+ [F_FORCE_BATDET] = REG_FIELD(BQ24257_REG_7, 3, 3),
+ [F_FORCE_PTM] = REG_FIELD(BQ24257_REG_7, 2, 2)
+};
+
+static const u32 bq24257_vbat_map[] = {
+ 3500000, 3520000, 3540000, 3560000, 3580000, 3600000, 3620000, 3640000,
+ 3660000, 3680000, 3700000, 3720000, 3740000, 3760000, 3780000, 3800000,
+ 3820000, 3840000, 3860000, 3880000, 3900000, 3920000, 3940000, 3960000,
+ 3980000, 4000000, 4020000, 4040000, 4060000, 4080000, 4100000, 4120000,
+ 4140000, 4160000, 4180000, 4200000, 4220000, 4240000, 4260000, 4280000,
+ 4300000, 4320000, 4340000, 4360000, 4380000, 4400000, 4420000, 4440000
+};
+
+#define BQ24257_VBAT_MAP_SIZE ARRAY_SIZE(bq24257_vbat_map)
+
+static const u32 bq24257_ichg_map[] = {
+ 500000, 550000, 600000, 650000, 700000, 750000, 800000, 850000, 900000,
+ 950000, 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, 1300000,
+ 1350000, 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000,
+ 1750000, 1800000, 1850000, 1900000, 1950000, 2000000
+};
+
+#define BQ24257_ICHG_MAP_SIZE ARRAY_SIZE(bq24257_ichg_map)
+
+static const u32 bq24257_iterm_map[] = {
+ 50000, 75000, 100000, 125000, 150000, 175000, 200000, 225000
+};
+
+#define BQ24257_ITERM_MAP_SIZE ARRAY_SIZE(bq24257_iterm_map)
+
+static int bq24257_field_read(struct bq24257_device *bq,
+ enum bq24257_fields field_id)
+{
+ int ret;
+ int val;
+
+ ret = regmap_field_read(bq->rmap_fields[field_id], &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int bq24257_field_write(struct bq24257_device *bq,
+ enum bq24257_fields field_id, u8 val)
+{
+ return regmap_field_write(bq->rmap_fields[field_id], val);
+}
+
+static u8 bq24257_find_idx(u32 value, const u32 *map, u8 map_size)
+{
+ u8 idx;
+
+ for (idx = 1; idx < map_size; idx++)
+ if (value < map[idx])
+ break;
+
+ return idx - 1;
+}
+
+enum bq24257_status {
+ STATUS_READY,
+ STATUS_CHARGE_IN_PROGRESS,
+ STATUS_CHARGE_DONE,
+ STATUS_FAULT,
+};
+
+enum bq24257_fault {
+ FAULT_NORMAL,
+ FAULT_INPUT_OVP,
+ FAULT_INPUT_UVLO,
+ FAULT_SLEEP,
+ FAULT_BAT_TS,
+ FAULT_BAT_OVP,
+ FAULT_TS,
+ FAULT_TIMER,
+ FAULT_NO_BAT,
+ FAULT_ISET,
+ FAULT_INPUT_LDO_LOW,
+};
+
+static int bq24257_power_supply_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct bq24257_device *bq = power_supply_get_drvdata(psy);
+ struct bq24257_state state;
+
+ mutex_lock(&bq->lock);
+ state = bq->state;
+ mutex_unlock(&bq->lock);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (!state.power_good)
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (state.status == STATUS_READY)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else if (state.status == STATUS_CHARGE_IN_PROGRESS)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (state.status == STATUS_CHARGE_DONE)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = BQ24257_MANUFACTURER;
+ break;
+
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = state.power_good;
+ break;
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ switch (state.fault) {
+ case FAULT_NORMAL:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+
+ case FAULT_INPUT_OVP:
+ case FAULT_BAT_OVP:
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ break;
+
+ case FAULT_TS:
+ case FAULT_BAT_TS:
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ break;
+
+ case FAULT_TIMER:
+ val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+ break;
+
+ default:
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ break;
+ }
+
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ val->intval = bq24257_ichg_map[bq->init_data.ichg];
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = bq24257_ichg_map[BQ24257_ICHG_MAP_SIZE - 1];
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ val->intval = bq24257_vbat_map[bq->init_data.vbat];
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ val->intval = bq24257_vbat_map[BQ24257_VBAT_MAP_SIZE - 1];
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+ val->intval = bq24257_iterm_map[bq->init_data.iterm];
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bq24257_get_chip_state(struct bq24257_device *bq,
+ struct bq24257_state *state)
+{
+ int ret;
+
+ ret = bq24257_field_read(bq, F_STAT);
+ if (ret < 0)
+ return ret;
+
+ state->status = ret;
+
+ ret = bq24257_field_read(bq, F_FAULT);
+ if (ret < 0)
+ return ret;
+
+ state->fault = ret;
+
+ state->power_good = !gpiod_get_value_cansleep(bq->pg);
+
+ return 0;
+}
+
+static bool bq24257_state_changed(struct bq24257_device *bq,
+ struct bq24257_state *new_state)
+{
+ int ret;
+
+ mutex_lock(&bq->lock);
+ ret = (bq->state.status != new_state->status ||
+ bq->state.fault != new_state->fault ||
+ bq->state.power_good != new_state->power_good);
+ mutex_unlock(&bq->lock);
+
+ return ret;
+}
+
+enum bq24257_loop_status {
+ LOOP_STATUS_NONE,
+ LOOP_STATUS_IN_DPM,
+ LOOP_STATUS_IN_CURRENT_LIMIT,
+ LOOP_STATUS_THERMAL,
+};
+
+enum bq24257_in_ilimit {
+ IILIMIT_100,
+ IILIMIT_150,
+ IILIMIT_500,
+ IILIMIT_900,
+ IILIMIT_1500,
+ IILIMIT_2000,
+ IILIMIT_EXT,
+ IILIMIT_NONE,
+};
+
+enum bq24257_port_type {
+ PORT_TYPE_DCP, /* Dedicated Charging Port */
+ PORT_TYPE_CDP, /* Charging Downstream Port */
+ PORT_TYPE_SDP, /* Standard Downstream Port */
+ PORT_TYPE_NON_STANDARD,
+};
+
+enum bq24257_safety_timer {
+ SAFETY_TIMER_45,
+ SAFETY_TIMER_360,
+ SAFETY_TIMER_540,
+ SAFETY_TIMER_NONE,
+};
+
+static int bq24257_iilimit_autoset(struct bq24257_device *bq)
+{
+ int loop_status;
+ int iilimit;
+ int port_type;
+ int ret;
+ const u8 new_iilimit[] = {
+ [PORT_TYPE_DCP] = IILIMIT_2000,
+ [PORT_TYPE_CDP] = IILIMIT_2000,
+ [PORT_TYPE_SDP] = IILIMIT_500,
+ [PORT_TYPE_NON_STANDARD] = IILIMIT_500
+ };
+
+ ret = bq24257_field_read(bq, F_LOOP_STATUS);
+ if (ret < 0)
+ goto error;
+
+ loop_status = ret;
+
+ ret = bq24257_field_read(bq, F_IILIMIT);
+ if (ret < 0)
+ goto error;
+
+ iilimit = ret;
+
+ /*
+ * All USB ports should be able to handle 500mA. If not, DPM will lower
+ * the charging current to accommodate the power source. No need to set
+ * a lower IILIMIT value.
+ */
+ if (loop_status == LOOP_STATUS_IN_DPM && iilimit == IILIMIT_500)
+ return 0;
+
+ ret = bq24257_field_read(bq, F_USB_DET);
+ if (ret < 0)
+ goto error;
+
+ port_type = ret;
+
+ ret = bq24257_field_write(bq, F_IILIMIT, new_iilimit[port_type]);
+ if (ret < 0)
+ goto error;
+
+ ret = bq24257_field_write(bq, F_TMR, SAFETY_TIMER_360);
+ if (ret < 0)
+ goto error;
+
+ ret = bq24257_field_write(bq, F_CLR_VDP, 1);
+ if (ret < 0)
+ goto error;
+
+ dev_dbg(bq->dev, "port/loop = %d/%d -> iilimit = %d\n",
+ port_type, loop_status, new_iilimit[port_type]);
+
+ return 0;
+
+error:
+ dev_err(bq->dev, "%s: Error communicating with the chip.\n", __func__);
+ return ret;
+}
+
+static void bq24257_iilimit_setup_work(struct work_struct *work)
+{
+ struct bq24257_device *bq = container_of(work, struct bq24257_device,
+ iilimit_setup_work.work);
+
+ bq24257_iilimit_autoset(bq);
+}
+
+static void bq24257_handle_state_change(struct bq24257_device *bq,
+ struct bq24257_state *new_state)
+{
+ int ret;
+ struct bq24257_state old_state;
+ bool reset_iilimit = false;
+ bool config_iilimit = false;
+
+ mutex_lock(&bq->lock);
+ old_state = bq->state;
+ mutex_unlock(&bq->lock);
+
+ if (!new_state->power_good) { /* power removed */
+ cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+ /* activate D+/D- port detection algorithm */
+ ret = bq24257_field_write(bq, F_DPDM_EN, 1);
+ if (ret < 0)
+ goto error;
+
+ reset_iilimit = true;
+ } else if (!old_state.power_good) { /* power inserted */
+ config_iilimit = true;
+ } else if (new_state->fault == FAULT_NO_BAT) { /* battery removed */
+ cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+ reset_iilimit = true;
+ } else if (old_state.fault == FAULT_NO_BAT) { /* battery connected */
+ config_iilimit = true;
+ } else if (new_state->fault == FAULT_TIMER) { /* safety timer expired */
+ dev_err(bq->dev, "Safety timer expired! Battery dead?\n");
+ }
+
+ if (reset_iilimit) {
+ ret = bq24257_field_write(bq, F_IILIMIT, IILIMIT_500);
+ if (ret < 0)
+ goto error;
+ } else if (config_iilimit) {
+ schedule_delayed_work(&bq->iilimit_setup_work,
+ msecs_to_jiffies(BQ24257_ILIM_SET_DELAY));
+ }
+
+ return;
+
+error:
+ dev_err(bq->dev, "%s: Error communicating with the chip.\n", __func__);
+}
+
+static irqreturn_t bq24257_irq_handler_thread(int irq, void *private)
+{
+ int ret;
+ struct bq24257_device *bq = private;
+ struct bq24257_state state;
+
+ ret = bq24257_get_chip_state(bq, &state);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ if (!bq24257_state_changed(bq, &state))
+ return IRQ_HANDLED;
+
+ dev_dbg(bq->dev, "irq(state changed): status/fault/pg = %d/%d/%d\n",
+ state.status, state.fault, state.power_good);
+
+ bq24257_handle_state_change(bq, &state);
+
+ mutex_lock(&bq->lock);
+ bq->state = state;
+ mutex_unlock(&bq->lock);
+
+ power_supply_changed(bq->charger);
+
+ return IRQ_HANDLED;
+}
+
+static int bq24257_hw_init(struct bq24257_device *bq)
+{
+ int ret;
+ int i;
+ struct bq24257_state state;
+
+ const struct {
+ int field;
+ u32 value;
+ } init_data[] = {
+ {F_ICHG, bq->init_data.ichg},
+ {F_VBAT, bq->init_data.vbat},
+ {F_ITERM, bq->init_data.iterm}
+ };
+
+ /*
+ * Disable the watchdog timer to prevent the IC from going back to
+ * default settings after 50 seconds of I2C inactivity.
+ */
+ ret = bq24257_field_write(bq, F_WD_EN, 0);
+ if (ret < 0)
+ return ret;
+
+ /* configure the charge currents and voltages */
+ for (i = 0; i < ARRAY_SIZE(init_data); i++) {
+ ret = bq24257_field_write(bq, init_data[i].field,
+ init_data[i].value);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = bq24257_get_chip_state(bq, &state);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&bq->lock);
+ bq->state = state;
+ mutex_unlock(&bq->lock);
+
+ if (!state.power_good)
+ /* activate D+/D- detection algorithm */
+ ret = bq24257_field_write(bq, F_DPDM_EN, 1);
+ else if (state.fault != FAULT_NO_BAT)
+ ret = bq24257_iilimit_autoset(bq);
+
+ return ret;
+}
+
+static enum power_supply_property bq24257_power_supply_props[] = {
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+};
+
+static char *bq24257_charger_supplied_to[] = {
+ "main-battery",
+};
+
+static const struct power_supply_desc bq24257_power_supply_desc = {
+ .name = "bq24257-charger",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = bq24257_power_supply_props,
+ .num_properties = ARRAY_SIZE(bq24257_power_supply_props),
+ .get_property = bq24257_power_supply_get_property,
+};
+
+static int bq24257_power_supply_init(struct bq24257_device *bq)
+{
+ struct power_supply_config psy_cfg = { .drv_data = bq, };
+
+ psy_cfg.supplied_to = bq24257_charger_supplied_to;
+ psy_cfg.num_supplicants = ARRAY_SIZE(bq24257_charger_supplied_to);
+
+ bq->charger = power_supply_register(bq->dev, &bq24257_power_supply_desc,
+ &psy_cfg);
+ if (IS_ERR(bq->charger))
+ return PTR_ERR(bq->charger);
+
+ return 0;
+}
+
+static int bq24257_irq_probe(struct bq24257_device *bq)
+{
+ struct gpio_desc *stat_irq;
+
+ stat_irq = devm_gpiod_get_index(bq->dev, BQ24257_STAT_IRQ, 0, GPIOD_IN);
+ if (IS_ERR(stat_irq)) {
+ dev_err(bq->dev, "could not probe stat_irq pin\n");
+ return PTR_ERR(stat_irq);
+ }
+
+ return gpiod_to_irq(stat_irq);
+}
+
+static int bq24257_pg_gpio_probe(struct bq24257_device *bq)
+{
+ bq->pg = devm_gpiod_get_index(bq->dev, BQ24257_PG_GPIO, 0, GPIOD_IN);
+ if (IS_ERR(bq->pg)) {
+ dev_err(bq->dev, "could not probe PG pin\n");
+ return PTR_ERR(bq->pg);
+ }
+
+ return 0;
+}
+
+static int bq24257_fw_probe(struct bq24257_device *bq)
+{
+ int ret;
+ u32 property;
+
+ ret = device_property_read_u32(bq->dev, "ti,charge-current", &property);
+ if (ret < 0)
+ return ret;
+
+ bq->init_data.ichg = bq24257_find_idx(property, bq24257_ichg_map,
+ BQ24257_ICHG_MAP_SIZE);
+
+ ret = device_property_read_u32(bq->dev, "ti,battery-regulation-voltage",
+ &property);
+ if (ret < 0)
+ return ret;
+
+ bq->init_data.vbat = bq24257_find_idx(property, bq24257_vbat_map,
+ BQ24257_VBAT_MAP_SIZE);
+
+ ret = device_property_read_u32(bq->dev, "ti,termination-current",
+ &property);
+ if (ret < 0)
+ return ret;
+
+ bq->init_data.iterm = bq24257_find_idx(property, bq24257_iterm_map,
+ BQ24257_ITERM_MAP_SIZE);
+
+ return 0;
+}
+
+static int bq24257_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct device *dev = &client->dev;
+ struct bq24257_device *bq;
+ int ret;
+ int i;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+ return -ENODEV;
+ }
+
+ bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
+ if (!bq)
+ return -ENOMEM;
+
+ bq->client = client;
+ bq->dev = dev;
+
+ mutex_init(&bq->lock);
+
+ bq->rmap = devm_regmap_init_i2c(client, &bq24257_regmap_config);
+ if (IS_ERR(bq->rmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(bq->rmap);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bq24257_reg_fields); i++) {
+ const struct reg_field *reg_fields = bq24257_reg_fields;
+
+ bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap,
+ reg_fields[i]);
+ if (IS_ERR(bq->rmap_fields[i])) {
+ dev_err(dev, "cannot allocate regmap field\n");
+ return PTR_ERR(bq->rmap_fields[i]);
+ }
+ }
+
+ i2c_set_clientdata(client, bq);
+
+ INIT_DELAYED_WORK(&bq->iilimit_setup_work, bq24257_iilimit_setup_work);
+
+ if (!dev->platform_data) {
+ ret = bq24257_fw_probe(bq);
+ if (ret < 0) {
+ dev_err(dev, "Cannot read device properties.\n");
+ return ret;
+ }
+ } else {
+ return -ENODEV;
+ }
+
+ /* we can only check Power Good status by probing the PG pin */
+ ret = bq24257_pg_gpio_probe(bq);
+ if (ret < 0)
+ return ret;
+
+ /* reset all registers to defaults */
+ ret = bq24257_field_write(bq, F_RESET, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Put the RESET bit back to 0, in cache. For some reason the HW always
+ * returns 1 on this bit, so this is the only way to avoid resetting the
+ * chip every time we update another field in this register.
+ */
+ ret = bq24257_field_write(bq, F_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = bq24257_hw_init(bq);
+ if (ret < 0) {
+ dev_err(dev, "Cannot initialize the chip.\n");
+ return ret;
+ }
+
+ if (client->irq <= 0)
+ client->irq = bq24257_irq_probe(bq);
+
+ if (client->irq < 0) {
+ dev_err(dev, "no irq resource found\n");
+ return client->irq;
+ }
+
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ bq24257_irq_handler_thread,
+ IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ BQ24257_STAT_IRQ, bq);
+ if (ret)
+ return ret;
+
+ ret = bq24257_power_supply_init(bq);
+ if (ret < 0)
+ dev_err(dev, "Failed to register power supply\n");
+
+ return ret;
+}
+
+static int bq24257_remove(struct i2c_client *client)
+{
+ struct bq24257_device *bq = i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+ power_supply_unregister(bq->charger);
+
+ bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bq24257_suspend(struct device *dev)
+{
+ struct bq24257_device *bq = dev_get_drvdata(dev);
+ int ret = 0;
+
+ cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+ /* reset all registers to default (and activate standalone mode) */
+ ret = bq24257_field_write(bq, F_RESET, 1);
+ if (ret < 0)
+ dev_err(bq->dev, "Cannot reset chip to standalone mode.\n");
+
+ return ret;
+}
+
+static int bq24257_resume(struct device *dev)
+{
+ int ret;
+ struct bq24257_device *bq = dev_get_drvdata(dev);
+
+ ret = regcache_drop_region(bq->rmap, BQ24257_REG_1, BQ24257_REG_7);
+ if (ret < 0)
+ return ret;
+
+ ret = bq24257_field_write(bq, F_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = bq24257_hw_init(bq);
+ if (ret < 0) {
+ dev_err(bq->dev, "Cannot init chip after resume.\n");
+ return ret;
+ }
+
+ /* signal userspace, maybe state changed while suspended */
+ power_supply_changed(bq->charger);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops bq24257_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(bq24257_suspend, bq24257_resume)
+};
+
+static const struct i2c_device_id bq24257_i2c_ids[] = {
+ { "bq24257", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, bq24257_i2c_ids);
+
+static const struct of_device_id bq24257_of_match[] = {
+ { .compatible = "ti,bq24257", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bq24257_of_match);
+
+static const struct acpi_device_id bq24257_acpi_match[] = {
+ {"BQ242570", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, bq24257_acpi_match);
+
+static struct i2c_driver bq24257_driver = {
+ .driver = {
+ .name = "bq24257-charger",
+ .of_match_table = of_match_ptr(bq24257_of_match),
+ .acpi_match_table = ACPI_PTR(bq24257_acpi_match),
+ .pm = &bq24257_pm,
+ },
+ .probe = bq24257_probe,
+ .remove = bq24257_remove,
+ .id_table = bq24257_i2c_ids,
+};
+module_i2c_driver(bq24257_driver);
+
+MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
+MODULE_DESCRIPTION("bq24257 charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq25890_charger.c b/drivers/power/bq25890_charger.c
new file mode 100644
index 000000000000..f993a55cde20
--- /dev/null
+++ b/drivers/power/bq25890_charger.c
@@ -0,0 +1,994 @@
+/*
+ * TI BQ25890 charger driver
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/usb/phy.h>
+
+#include <linux/acpi.h>
+#include <linux/of.h>
+
+#define BQ25890_MANUFACTURER "Texas Instruments"
+#define BQ25890_IRQ_PIN "bq25890_irq"
+
+#define BQ25890_ID 3
+
+enum bq25890_fields {
+ F_EN_HIZ, F_EN_ILIM, F_IILIM, /* Reg00 */
+ F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */
+ F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN,
+ F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN, /* Reg02 */
+ F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN, /* Reg03 */
+ F_PUMPX_EN, F_ICHG, /* Reg04 */
+ F_IPRECHG, F_ITERM, /* Reg05 */
+ F_VREG, F_BATLOWV, F_VRECHG, /* Reg06 */
+ F_TERM_EN, F_STAT_DIS, F_WD, F_TMR_EN, F_CHG_TMR,
+ F_JEITA_ISET, /* Reg07 */
+ F_BATCMP, F_VCLAMP, F_TREG, /* Reg08 */
+ F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET,
+ F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN, /* Reg09 */
+ F_BOOSTV, F_BOOSTI, /* Reg0A */
+ F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_VSYS_STAT, /* Reg0B */
+ F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT,
+ F_NTC_FAULT, /* Reg0C */
+ F_FORCE_VINDPM, F_VINDPM, /* Reg0D */
+ F_THERM_STAT, F_BATV, /* Reg0E */
+ F_SYSV, /* Reg0F */
+ F_TSPCT, /* Reg10 */
+ F_VBUS_GD, F_VBUSV, /* Reg11 */
+ F_ICHGR, /* Reg12 */
+ F_VDPM_STAT, F_IDPM_STAT, F_IDPM_LIM, /* Reg13 */
+ F_REG_RST, F_ICO_OPTIMIZED, F_PN, F_TS_PROFILE, F_DEV_REV, /* Reg14 */
+
+ F_MAX_FIELDS
+};
+
+/* initial field values, converted to register values */
+struct bq25890_init_data {
+ u8 ichg; /* charge current */
+ u8 vreg; /* regulation voltage */
+ u8 iterm; /* termination current */
+ u8 iprechg; /* precharge current */
+ u8 sysvmin; /* minimum system voltage limit */
+ u8 boostv; /* boost regulation voltage */
+ u8 boosti; /* boost current limit */
+ u8 boostf; /* boost frequency */
+ u8 ilim_en; /* enable ILIM pin */
+ u8 treg; /* thermal regulation threshold */
+};
+
+struct bq25890_state {
+ u8 online;
+ u8 chrg_status;
+ u8 chrg_fault;
+ u8 vsys_status;
+ u8 boost_fault;
+ u8 bat_fault;
+};
+
+struct bq25890_device {
+ struct i2c_client *client;
+ struct device *dev;
+ struct power_supply *charger;
+
+ struct usb_phy *usb_phy;
+ struct notifier_block usb_nb;
+ struct work_struct usb_work;
+ unsigned long usb_event;
+
+ struct regmap *rmap;
+ struct regmap_field *rmap_fields[F_MAX_FIELDS];
+
+ int chip_id;
+ struct bq25890_init_data init_data;
+ struct bq25890_state state;
+
+ struct mutex lock; /* protect state data */
+};
+
+static const struct regmap_range bq25890_readonly_reg_ranges[] = {
+ regmap_reg_range(0x0b, 0x0c),
+ regmap_reg_range(0x0e, 0x13),
+};
+
+static const struct regmap_access_table bq25890_writeable_regs = {
+ .no_ranges = bq25890_readonly_reg_ranges,
+ .n_no_ranges = ARRAY_SIZE(bq25890_readonly_reg_ranges),
+};
+
+static const struct regmap_range bq25890_volatile_reg_ranges[] = {
+ regmap_reg_range(0x00, 0x00),
+ regmap_reg_range(0x09, 0x09),
+ regmap_reg_range(0x0b, 0x0c),
+ regmap_reg_range(0x0e, 0x14),
+};
+
+static const struct regmap_access_table bq25890_volatile_regs = {
+ .yes_ranges = bq25890_volatile_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(bq25890_volatile_reg_ranges),
+};
+
+static const struct regmap_config bq25890_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0x14,
+ .cache_type = REGCACHE_RBTREE,
+
+ .wr_table = &bq25890_writeable_regs,
+ .volatile_table = &bq25890_volatile_regs,
+};
+
+static const struct reg_field bq25890_reg_fields[] = {
+ /* REG00 */
+ [F_EN_HIZ] = REG_FIELD(0x00, 7, 7),
+ [F_EN_ILIM] = REG_FIELD(0x00, 6, 6),
+ [F_IILIM] = REG_FIELD(0x00, 0, 5),
+ /* REG01 */
+ [F_BHOT] = REG_FIELD(0x01, 6, 7),
+ [F_BCOLD] = REG_FIELD(0x01, 5, 5),
+ [F_VINDPM_OFS] = REG_FIELD(0x01, 0, 4),
+ /* REG02 */
+ [F_CONV_START] = REG_FIELD(0x02, 7, 7),
+ [F_CONV_RATE] = REG_FIELD(0x02, 6, 6),
+ [F_BOOSTF] = REG_FIELD(0x02, 5, 5),
+ [F_ICO_EN] = REG_FIELD(0x02, 4, 4),
+ [F_HVDCP_EN] = REG_FIELD(0x02, 3, 3),
+ [F_MAXC_EN] = REG_FIELD(0x02, 2, 2),
+ [F_FORCE_DPM] = REG_FIELD(0x02, 1, 1),
+ [F_AUTO_DPDM_EN] = REG_FIELD(0x02, 0, 0),
+ /* REG03 */
+ [F_BAT_LOAD_EN] = REG_FIELD(0x03, 7, 7),
+ [F_WD_RST] = REG_FIELD(0x03, 6, 6),
+ [F_OTG_CFG] = REG_FIELD(0x03, 5, 5),
+ [F_CHG_CFG] = REG_FIELD(0x03, 4, 4),
+ [F_SYSVMIN] = REG_FIELD(0x03, 1, 3),
+ /* REG04 */
+ [F_PUMPX_EN] = REG_FIELD(0x04, 7, 7),
+ [F_ICHG] = REG_FIELD(0x04, 0, 6),
+ /* REG05 */
+ [F_IPRECHG] = REG_FIELD(0x05, 4, 7),
+ [F_ITERM] = REG_FIELD(0x05, 0, 3),
+ /* REG06 */
+ [F_VREG] = REG_FIELD(0x06, 2, 7),
+ [F_BATLOWV] = REG_FIELD(0x06, 1, 1),
+ [F_VRECHG] = REG_FIELD(0x06, 0, 0),
+ /* REG07 */
+ [F_TERM_EN] = REG_FIELD(0x07, 7, 7),
+ [F_STAT_DIS] = REG_FIELD(0x07, 6, 6),
+ [F_WD] = REG_FIELD(0x07, 4, 5),
+ [F_TMR_EN] = REG_FIELD(0x07, 3, 3),
+ [F_CHG_TMR] = REG_FIELD(0x07, 1, 2),
+ [F_JEITA_ISET] = REG_FIELD(0x07, 0, 0),
+ /* REG08 */
+ [F_BATCMP] = REG_FIELD(0x08, 6, 7),
+ [F_VCLAMP] = REG_FIELD(0x08, 2, 4),
+ [F_TREG] = REG_FIELD(0x08, 0, 1),
+ /* REG09 */
+ [F_FORCE_ICO] = REG_FIELD(0x09, 7, 7),
+ [F_TMR2X_EN] = REG_FIELD(0x09, 6, 6),
+ [F_BATFET_DIS] = REG_FIELD(0x09, 5, 5),
+ [F_JEITA_VSET] = REG_FIELD(0x09, 4, 4),
+ [F_BATFET_DLY] = REG_FIELD(0x09, 3, 3),
+ [F_BATFET_RST_EN] = REG_FIELD(0x09, 2, 2),
+ [F_PUMPX_UP] = REG_FIELD(0x09, 1, 1),
+ [F_PUMPX_DN] = REG_FIELD(0x09, 0, 0),
+ /* REG0A */
+ [F_BOOSTV] = REG_FIELD(0x0A, 4, 7),
+ [F_BOOSTI] = REG_FIELD(0x0A, 0, 2),
+ /* REG0B */
+ [F_VBUS_STAT] = REG_FIELD(0x0B, 5, 7),
+ [F_CHG_STAT] = REG_FIELD(0x0B, 3, 4),
+ [F_PG_STAT] = REG_FIELD(0x0B, 2, 2),
+ [F_SDP_STAT] = REG_FIELD(0x0B, 1, 1),
+ [F_VSYS_STAT] = REG_FIELD(0x0B, 0, 0),
+ /* REG0C */
+ [F_WD_FAULT] = REG_FIELD(0x0C, 7, 7),
+ [F_BOOST_FAULT] = REG_FIELD(0x0C, 6, 6),
+ [F_CHG_FAULT] = REG_FIELD(0x0C, 4, 5),
+ [F_BAT_FAULT] = REG_FIELD(0x0C, 3, 3),
+ [F_NTC_FAULT] = REG_FIELD(0x0C, 0, 2),
+ /* REG0D */
+ [F_FORCE_VINDPM] = REG_FIELD(0x0D, 7, 7),
+ [F_VINDPM] = REG_FIELD(0x0D, 0, 6),
+ /* REG0E */
+ [F_THERM_STAT] = REG_FIELD(0x0E, 7, 7),
+ [F_BATV] = REG_FIELD(0x0E, 0, 6),
+ /* REG0F */
+ [F_SYSV] = REG_FIELD(0x0F, 0, 6),
+ /* REG10 */
+ [F_TSPCT] = REG_FIELD(0x10, 0, 6),
+ /* REG11 */
+ [F_VBUS_GD] = REG_FIELD(0x11, 7, 7),
+ [F_VBUSV] = REG_FIELD(0x11, 0, 6),
+ /* REG12 */
+ [F_ICHGR] = REG_FIELD(0x12, 0, 6),
+ /* REG13 */
+ [F_VDPM_STAT] = REG_FIELD(0x13, 7, 7),
+ [F_IDPM_STAT] = REG_FIELD(0x13, 6, 6),
+ [F_IDPM_LIM] = REG_FIELD(0x13, 0, 5),
+ /* REG14 */
+ [F_REG_RST] = REG_FIELD(0x14, 7, 7),
+ [F_ICO_OPTIMIZED] = REG_FIELD(0x14, 6, 6),
+ [F_PN] = REG_FIELD(0x14, 3, 5),
+ [F_TS_PROFILE] = REG_FIELD(0x14, 2, 2),
+ [F_DEV_REV] = REG_FIELD(0x14, 0, 1)
+};
+
+/*
+ * Most of the val -> idx conversions can be computed, given the minimum,
+ * maximum and the step between values. For the rest of conversions, we use
+ * lookup tables.
+ */
+enum bq25890_table_ids {
+ /* range tables */
+ TBL_ICHG,
+ TBL_ITERM,
+ TBL_IPRECHG,
+ TBL_VREG,
+ TBL_BATCMP,
+ TBL_VCLAMP,
+ TBL_BOOSTV,
+ TBL_SYSVMIN,
+
+ /* lookup tables */
+ TBL_TREG,
+ TBL_BOOSTI,
+};
+
+/* Thermal Regulation Threshold lookup table, in degrees Celsius */
+static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 };
+
+#define BQ25890_TREG_TBL_SIZE ARRAY_SIZE(bq25890_treg_tbl)
+
+/* Boost mode current limit lookup table, in uA */
+static const u32 bq25890_boosti_tbl[] = {
+ 500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000
+};
+
+#define BQ25890_BOOSTI_TBL_SIZE ARRAY_SIZE(bq25890_boosti_tbl)
+
+struct bq25890_range {
+ u32 min;
+ u32 max;
+ u32 step;
+};
+
+struct bq25890_lookup {
+ const u32 *tbl;
+ u32 size;
+};
+
+static const union {
+ struct bq25890_range rt;
+ struct bq25890_lookup lt;
+} bq25890_tables[] = {
+ /* range tables */
+ [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */
+ [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */
+ [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */
+ [TBL_BATCMP] = { .rt = {0, 140, 20} }, /* mOhm */
+ [TBL_VCLAMP] = { .rt = {0, 224000, 32000} }, /* uV */
+ [TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */
+ [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */
+
+ /* lookup tables */
+ [TBL_TREG] = { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} },
+ [TBL_BOOSTI] = { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} }
+};
+
+static int bq25890_field_read(struct bq25890_device *bq,
+ enum bq25890_fields field_id)
+{
+ int ret;
+ int val;
+
+ ret = regmap_field_read(bq->rmap_fields[field_id], &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int bq25890_field_write(struct bq25890_device *bq,
+ enum bq25890_fields field_id, u8 val)
+{
+ return regmap_field_write(bq->rmap_fields[field_id], val);
+}
+
+static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id)
+{
+ u8 idx;
+
+ if (id >= TBL_TREG) {
+ const u32 *tbl = bq25890_tables[id].lt.tbl;
+ u32 tbl_size = bq25890_tables[id].lt.size;
+
+ for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++)
+ ;
+ } else {
+ const struct bq25890_range *rtbl = &bq25890_tables[id].rt;
+ u8 rtbl_size;
+
+ rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1;
+
+ for (idx = 1;
+ idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value);
+ idx++)
+ ;
+ }
+
+ return idx - 1;
+}
+
+static u32 bq25890_find_val(u8 idx, enum bq25890_table_ids id)
+{
+ const struct bq25890_range *rtbl;
+
+ /* lookup table? */
+ if (id >= TBL_TREG)
+ return bq25890_tables[id].lt.tbl[idx];
+
+ /* range table */
+ rtbl = &bq25890_tables[id].rt;
+
+ return (rtbl->min + idx * rtbl->step);
+}
+
+enum bq25890_status {
+ STATUS_NOT_CHARGING,
+ STATUS_PRE_CHARGING,
+ STATUS_FAST_CHARGING,
+ STATUS_TERMINATION_DONE,
+};
+
+enum bq25890_chrg_fault {
+ CHRG_FAULT_NORMAL,
+ CHRG_FAULT_INPUT,
+ CHRG_FAULT_THERMAL_SHUTDOWN,
+ CHRG_FAULT_TIMER_EXPIRED,
+};
+
+static int bq25890_power_supply_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret;
+ struct bq25890_device *bq = power_supply_get_drvdata(psy);
+ struct bq25890_state state;
+
+ mutex_lock(&bq->lock);
+ state = bq->state;
+ mutex_unlock(&bq->lock);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (!state.online)
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (state.chrg_status == STATUS_NOT_CHARGING)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else if (state.chrg_status == STATUS_PRE_CHARGING ||
+ state.chrg_status == STATUS_FAST_CHARGING)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (state.chrg_status == STATUS_TERMINATION_DONE)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+
+ break;
+
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = BQ25890_MANUFACTURER;
+ break;
+
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = state.online;
+ break;
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (!state.chrg_fault && !state.bat_fault && !state.boost_fault)
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ else if (state.bat_fault)
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ else if (state.chrg_fault == CHRG_FAULT_TIMER_EXPIRED)
+ val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+ else if (state.chrg_fault == CHRG_FAULT_THERMAL_SHUTDOWN)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */
+ if (ret < 0)
+ return ret;
+
+ /* converted_val = ADC_val * 50mA (table 10.3.19) */
+ val->intval = ret * 50000;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = bq25890_tables[TBL_ICHG].rt.max;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ if (!state.online) {
+ val->intval = 0;
+ break;
+ }
+
+ ret = bq25890_field_read(bq, F_BATV); /* read measured value */
+ if (ret < 0)
+ return ret;
+
+ /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */
+ val->intval = 2304000 + ret * 20000;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ val->intval = bq25890_tables[TBL_VREG].rt.max;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+ val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bq25890_get_chip_state(struct bq25890_device *bq,
+ struct bq25890_state *state)
+{
+ int i, ret;
+
+ struct {
+ enum bq25890_fields id;
+ u8 *data;
+ } state_fields[] = {
+ {F_CHG_STAT, &state->chrg_status},
+ {F_PG_STAT, &state->online},
+ {F_VSYS_STAT, &state->vsys_status},
+ {F_BOOST_FAULT, &state->boost_fault},
+ {F_BAT_FAULT, &state->bat_fault},
+ {F_CHG_FAULT, &state->chrg_fault}
+ };
+
+ for (i = 0; i < ARRAY_SIZE(state_fields); i++) {
+ ret = bq25890_field_read(bq, state_fields[i].id);
+ if (ret < 0)
+ return ret;
+
+ *state_fields[i].data = ret;
+ }
+
+ dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT=%d/%d/%d\n",
+ state->chrg_status, state->online, state->vsys_status,
+ state->chrg_fault, state->boost_fault, state->bat_fault);
+
+ return 0;
+}
+
+static bool bq25890_state_changed(struct bq25890_device *bq,
+ struct bq25890_state *new_state)
+{
+ struct bq25890_state old_state;
+
+ mutex_lock(&bq->lock);
+ old_state = bq->state;
+ mutex_unlock(&bq->lock);
+
+ return (old_state.chrg_status != new_state->chrg_status ||
+ old_state.chrg_fault != new_state->chrg_fault ||
+ old_state.online != new_state->online ||
+ old_state.bat_fault != new_state->bat_fault ||
+ old_state.boost_fault != new_state->boost_fault ||
+ old_state.vsys_status != new_state->vsys_status);
+}
+
+static void bq25890_handle_state_change(struct bq25890_device *bq,
+ struct bq25890_state *new_state)
+{
+ int ret;
+ struct bq25890_state old_state;
+
+ mutex_lock(&bq->lock);
+ old_state = bq->state;
+ mutex_unlock(&bq->lock);
+
+ if (!new_state->online) { /* power removed */
+ /* disable ADC */
+ ret = bq25890_field_write(bq, F_CONV_START, 0);
+ if (ret < 0)
+ goto error;
+ } else if (!old_state.online) { /* power inserted */
+ /* enable ADC, to have control of charge current/voltage */
+ ret = bq25890_field_write(bq, F_CONV_START, 1);
+ if (ret < 0)
+ goto error;
+ }
+
+ return;
+
+error:
+ dev_err(bq->dev, "Error communicating with the chip.\n");
+}
+
+static irqreturn_t bq25890_irq_handler_thread(int irq, void *private)
+{
+ struct bq25890_device *bq = private;
+ int ret;
+ struct bq25890_state state;
+
+ ret = bq25890_get_chip_state(bq, &state);
+ if (ret < 0)
+ goto handled;
+
+ if (!bq25890_state_changed(bq, &state))
+ goto handled;
+
+ bq25890_handle_state_change(bq, &state);
+
+ mutex_lock(&bq->lock);
+ bq->state = state;
+ mutex_unlock(&bq->lock);
+
+ power_supply_changed(bq->charger);
+
+handled:
+ return IRQ_HANDLED;
+}
+
+static int bq25890_chip_reset(struct bq25890_device *bq)
+{
+ int ret;
+ int rst_check_counter = 10;
+
+ ret = bq25890_field_write(bq, F_REG_RST, 1);
+ if (ret < 0)
+ return ret;
+
+ do {
+ ret = bq25890_field_read(bq, F_REG_RST);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(5, 10);
+ } while (ret == 1 && --rst_check_counter);
+
+ if (!rst_check_counter)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int bq25890_hw_init(struct bq25890_device *bq)
+{
+ int ret;
+ int i;
+ struct bq25890_state state;
+
+ const struct {
+ enum bq25890_fields id;
+ u32 value;
+ } init_data[] = {
+ {F_ICHG, bq->init_data.ichg},
+ {F_VREG, bq->init_data.vreg},
+ {F_ITERM, bq->init_data.iterm},
+ {F_IPRECHG, bq->init_data.iprechg},
+ {F_SYSVMIN, bq->init_data.sysvmin},
+ {F_BOOSTV, bq->init_data.boostv},
+ {F_BOOSTI, bq->init_data.boosti},
+ {F_BOOSTF, bq->init_data.boostf},
+ {F_EN_ILIM, bq->init_data.ilim_en},
+ {F_TREG, bq->init_data.treg}
+ };
+
+ ret = bq25890_chip_reset(bq);
+ if (ret < 0)
+ return ret;
+
+ /* disable watchdog */
+ ret = bq25890_field_write(bq, F_WD, 0);
+ if (ret < 0)
+ return ret;
+
+ /* initialize currents/voltages and other parameters */
+ for (i = 0; i < ARRAY_SIZE(init_data); i++) {
+ ret = bq25890_field_write(bq, init_data[i].id,
+ init_data[i].value);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Configure ADC for continuous conversions. This does not enable it. */
+ ret = bq25890_field_write(bq, F_CONV_RATE, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = bq25890_get_chip_state(bq, &state);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&bq->lock);
+ bq->state = state;
+ mutex_unlock(&bq->lock);
+
+ return 0;
+}
+
+static enum power_supply_property bq25890_power_supply_props[] = {
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+};
+
+static char *bq25890_charger_supplied_to[] = {
+ "main-battery",
+};
+
+static const struct power_supply_desc bq25890_power_supply_desc = {
+ .name = "bq25890-charger",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = bq25890_power_supply_props,
+ .num_properties = ARRAY_SIZE(bq25890_power_supply_props),
+ .get_property = bq25890_power_supply_get_property,
+};
+
+static int bq25890_power_supply_init(struct bq25890_device *bq)
+{
+ struct power_supply_config psy_cfg = { .drv_data = bq, };
+
+ psy_cfg.supplied_to = bq25890_charger_supplied_to;
+ psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to);
+
+ bq->charger = power_supply_register(bq->dev, &bq25890_power_supply_desc,
+ &psy_cfg);
+
+ return PTR_ERR_OR_ZERO(bq->charger);
+}
+
+static void bq25890_usb_work(struct work_struct *data)
+{
+ int ret;
+ struct bq25890_device *bq =
+ container_of(data, struct bq25890_device, usb_work);
+
+ switch (bq->usb_event) {
+ case USB_EVENT_ID:
+ /* Enable boost mode */
+ ret = bq25890_field_write(bq, F_OTG_CFG, 1);
+ if (ret < 0)
+ goto error;
+ break;
+
+ case USB_EVENT_NONE:
+ /* Disable boost mode */
+ ret = bq25890_field_write(bq, F_OTG_CFG, 0);
+ if (ret < 0)
+ goto error;
+
+ power_supply_changed(bq->charger);
+ break;
+ }
+
+ return;
+
+error:
+ dev_err(bq->dev, "Error switching to boost/charger mode.\n");
+}
+
+static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val,
+ void *priv)
+{
+ struct bq25890_device *bq =
+ container_of(nb, struct bq25890_device, usb_nb);
+
+ bq->usb_event = val;
+ queue_work(system_power_efficient_wq, &bq->usb_work);
+
+ return NOTIFY_OK;
+}
+
+static int bq25890_irq_probe(struct bq25890_device *bq)
+{
+ struct gpio_desc *irq;
+
+ irq = devm_gpiod_get_index(bq->dev, BQ25890_IRQ_PIN, 0, GPIOD_IN);
+ if (IS_ERR(irq)) {
+ dev_err(bq->dev, "Could not probe irq pin.\n");
+ return PTR_ERR(irq);
+ }
+
+ return gpiod_to_irq(irq);
+}
+
+static int bq25890_fw_read_u32_props(struct bq25890_device *bq)
+{
+ int ret;
+ u32 property;
+ int i;
+ struct bq25890_init_data *init = &bq->init_data;
+ struct {
+ char *name;
+ bool optional;
+ enum bq25890_table_ids tbl_id;
+ u8 *conv_data; /* holds converted value from given property */
+ } props[] = {
+ /* required properties */
+ {"ti,charge-current", false, TBL_ICHG, &init->ichg},
+ {"ti,battery-regulation-voltage", false, TBL_VREG, &init->vreg},
+ {"ti,termination-current", false, TBL_ITERM, &init->iterm},
+ {"ti,precharge-current", false, TBL_ITERM, &init->iprechg},
+ {"ti,minimum-sys-voltage", false, TBL_SYSVMIN, &init->sysvmin},
+ {"ti,boost-voltage", false, TBL_BOOSTV, &init->boostv},
+ {"ti,boost-max-current", false, TBL_BOOSTI, &init->boosti},
+
+ /* optional properties */
+ {"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg}
+ };
+
+ /* initialize data for optional properties */
+ init->treg = 3; /* 120 degrees Celsius */
+
+ for (i = 0; i < ARRAY_SIZE(props); i++) {
+ ret = device_property_read_u32(bq->dev, props[i].name,
+ &property);
+ if (ret < 0) {
+ if (props[i].optional)
+ continue;
+
+ return ret;
+ }
+
+ *props[i].conv_data = bq25890_find_idx(property,
+ props[i].tbl_id);
+ }
+
+ return 0;
+}
+
+static int bq25890_fw_probe(struct bq25890_device *bq)
+{
+ int ret;
+ struct bq25890_init_data *init = &bq->init_data;
+
+ ret = bq25890_fw_read_u32_props(bq);
+ if (ret < 0)
+ return ret;
+
+ init->ilim_en = device_property_read_bool(bq->dev, "ti,use-ilim-pin");
+ init->boostf = device_property_read_bool(bq->dev, "ti,boost-low-freq");
+
+ return 0;
+}
+
+static int bq25890_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct device *dev = &client->dev;
+ struct bq25890_device *bq;
+ int ret;
+ int i;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+ return -ENODEV;
+ }
+
+ bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
+ if (!bq)
+ return -ENOMEM;
+
+ bq->client = client;
+ bq->dev = dev;
+
+ mutex_init(&bq->lock);
+
+ bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config);
+ if (IS_ERR(bq->rmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(bq->rmap);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) {
+ const struct reg_field *reg_fields = bq25890_reg_fields;
+
+ bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap,
+ reg_fields[i]);
+ if (IS_ERR(bq->rmap_fields[i])) {
+ dev_err(dev, "cannot allocate regmap field\n");
+ return PTR_ERR(bq->rmap_fields[i]);
+ }
+ }
+
+ i2c_set_clientdata(client, bq);
+
+ bq->chip_id = bq25890_field_read(bq, F_PN);
+ if (bq->chip_id < 0) {
+ dev_err(dev, "Cannot read chip ID.\n");
+ return bq->chip_id;
+ }
+
+ if (bq->chip_id != BQ25890_ID) {
+ dev_err(dev, "Chip with ID=%d, not supported!\n", bq->chip_id);
+ return -ENODEV;
+ }
+
+ if (!dev->platform_data) {
+ ret = bq25890_fw_probe(bq);
+ if (ret < 0) {
+ dev_err(dev, "Cannot read device properties.\n");
+ return ret;
+ }
+ } else {
+ return -ENODEV;
+ }
+
+ ret = bq25890_hw_init(bq);
+ if (ret < 0) {
+ dev_err(dev, "Cannot initialize the chip.\n");
+ return ret;
+ }
+
+ if (client->irq <= 0)
+ client->irq = bq25890_irq_probe(bq);
+
+ if (client->irq < 0) {
+ dev_err(dev, "No irq resource found.\n");
+ return client->irq;
+ }
+
+ /* OTG reporting */
+ bq->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+ if (!IS_ERR_OR_NULL(bq->usb_phy)) {
+ INIT_WORK(&bq->usb_work, bq25890_usb_work);
+ bq->usb_nb.notifier_call = bq25890_usb_notifier;
+ usb_register_notifier(bq->usb_phy, &bq->usb_nb);
+ }
+
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ bq25890_irq_handler_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ BQ25890_IRQ_PIN, bq);
+ if (ret)
+ goto irq_fail;
+
+ ret = bq25890_power_supply_init(bq);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register power supply\n");
+ goto irq_fail;
+ }
+
+ return 0;
+
+irq_fail:
+ if (!IS_ERR_OR_NULL(bq->usb_phy))
+ usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
+
+ return ret;
+}
+
+static int bq25890_remove(struct i2c_client *client)
+{
+ struct bq25890_device *bq = i2c_get_clientdata(client);
+
+ power_supply_unregister(bq->charger);
+
+ if (!IS_ERR_OR_NULL(bq->usb_phy))
+ usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
+
+ /* reset all registers to default values */
+ bq25890_chip_reset(bq);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bq25890_suspend(struct device *dev)
+{
+ struct bq25890_device *bq = dev_get_drvdata(dev);
+
+ /*
+ * If charger is removed, while in suspend, make sure ADC is diabled
+ * since it consumes slightly more power.
+ */
+ return bq25890_field_write(bq, F_CONV_START, 0);
+}
+
+static int bq25890_resume(struct device *dev)
+{
+ int ret;
+ struct bq25890_state state;
+ struct bq25890_device *bq = dev_get_drvdata(dev);
+
+ ret = bq25890_get_chip_state(bq, &state);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&bq->lock);
+ bq->state = state;
+ mutex_unlock(&bq->lock);
+
+ /* Re-enable ADC only if charger is plugged in. */
+ if (state.online) {
+ ret = bq25890_field_write(bq, F_CONV_START, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* signal userspace, maybe state changed while suspended */
+ power_supply_changed(bq->charger);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops bq25890_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(bq25890_suspend, bq25890_resume)
+};
+
+static const struct i2c_device_id bq25890_i2c_ids[] = {
+ { "bq25890", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids);
+
+static const struct of_device_id bq25890_of_match[] = {
+ { .compatible = "ti,bq25890", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bq25890_of_match);
+
+static const struct acpi_device_id bq25890_acpi_match[] = {
+ {"BQ258900", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, bq25890_acpi_match);
+
+static struct i2c_driver bq25890_driver = {
+ .driver = {
+ .name = "bq25890-charger",
+ .of_match_table = of_match_ptr(bq25890_of_match),
+ .acpi_match_table = ACPI_PTR(bq25890_acpi_match),
+ .pm = &bq25890_pm,
+ },
+ .probe = bq25890_probe,
+ .remove = bq25890_remove,
+ .id_table = bq25890_i2c_ids,
+};
+module_i2c_driver(bq25890_driver);
+
+MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
+MODULE_DESCRIPTION("bq25890 charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c
index a57433de5c24..b6b98378faa3 100644
--- a/drivers/power/bq27x00_battery.c
+++ b/drivers/power/bq27x00_battery.c
@@ -1109,6 +1109,14 @@ static void __exit bq27x00_battery_exit(void)
}
module_exit(bq27x00_battery_exit);
+#ifdef CONFIG_BATTERY_BQ27X00_PLATFORM
+MODULE_ALIAS("platform:bq27000-battery");
+#endif
+
+#ifdef CONFIG_BATTERY_BQ27X00_I2C
+MODULE_ALIAS("i2c:bq27000-battery");
+#endif
+
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
MODULE_DESCRIPTION("BQ27x00 battery monitor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
index 0aed13f90891..1c202ccbd2a6 100644
--- a/drivers/power/charger-manager.c
+++ b/drivers/power/charger-manager.c
@@ -1768,7 +1768,8 @@ static int charger_manager_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
- cm->charger_psy = power_supply_register(NULL, &cm->charger_psy_desc,
+ cm->charger_psy = power_supply_register(&pdev->dev,
+ &cm->charger_psy_desc,
&psy_cfg);
if (IS_ERR(cm->charger_psy)) {
dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c
index 2da9ed8ccbb5..8a971b3dbe58 100644
--- a/drivers/power/collie_battery.c
+++ b/drivers/power/collie_battery.c
@@ -347,7 +347,7 @@ static int collie_bat_probe(struct ucb1x00_dev *dev)
goto err_psy_reg_main;
}
- psy_main_cfg.drv_data = &collie_bat_bu;
+ psy_bu_cfg.drv_data = &collie_bat_bu;
collie_bat_bu.psy = power_supply_register(&dev->ucb->dev,
&collie_bat_bu_desc,
&psy_bu_cfg);
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c
index 6cc5e87ec031..e89255764745 100644
--- a/drivers/power/max17042_battery.c
+++ b/drivers/power/max17042_battery.c
@@ -63,6 +63,8 @@
#define dP_ACC_100 0x1900
#define dP_ACC_200 0x3200
+#define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */
+
struct max17042_chip {
struct i2c_client *client;
struct regmap *regmap;
@@ -85,10 +87,94 @@ static enum power_supply_property max17042_battery_props[] = {
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
+ POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
+ POWER_SUPPLY_PROP_TEMP_MIN,
+ POWER_SUPPLY_PROP_TEMP_MAX,
+ POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
};
+static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
+{
+ int ret;
+ u32 data;
+ struct regmap *map = chip->regmap;
+
+ ret = regmap_read(map, MAX17042_TEMP, &data);
+ if (ret < 0)
+ return ret;
+
+ *temp = data;
+ /* The value is signed. */
+ if (*temp & 0x8000) {
+ *temp = (0x7fff & ~*temp) + 1;
+ *temp *= -1;
+ }
+
+ /* The value is converted into deci-centigrade scale */
+ /* Units of LSB = 1 / 256 degree Celsius */
+ *temp = *temp * 10 / 256;
+ return 0;
+}
+
+static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
+{
+ int temp, vavg, vbatt, ret;
+ u32 val;
+
+ ret = regmap_read(chip->regmap, MAX17042_AvgVCELL, &val);
+ if (ret < 0)
+ goto health_error;
+
+ /* bits [0-3] unused */
+ vavg = val * 625 / 8;
+ /* Convert to millivolts */
+ vavg /= 1000;
+
+ ret = regmap_read(chip->regmap, MAX17042_VCELL, &val);
+ if (ret < 0)
+ goto health_error;
+
+ /* bits [0-3] unused */
+ vbatt = val * 625 / 8;
+ /* Convert to millivolts */
+ vbatt /= 1000;
+
+ if (vavg < chip->pdata->vmin) {
+ *health = POWER_SUPPLY_HEALTH_DEAD;
+ goto out;
+ }
+
+ if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) {
+ *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ goto out;
+ }
+
+ ret = max17042_get_temperature(chip, &temp);
+ if (ret < 0)
+ goto health_error;
+
+ if (temp <= chip->pdata->temp_min) {
+ *health = POWER_SUPPLY_HEALTH_COLD;
+ goto out;
+ }
+
+ if (temp >= chip->pdata->temp_max) {
+ *health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ goto out;
+ }
+
+ *health = POWER_SUPPLY_HEALTH_GOOD;
+
+out:
+ return 0;
+
+health_error:
+ return ret;
+}
+
static int max17042_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -181,19 +267,34 @@ static int max17042_get_property(struct power_supply *psy,
val->intval = data * 1000 / 2;
break;
case POWER_SUPPLY_PROP_TEMP:
- ret = regmap_read(map, MAX17042_TEMP, &data);
+ ret = max17042_get_temperature(chip, &val->intval);
+ if (ret < 0)
+ return ret;
+ break;
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+ if (ret < 0)
+ return ret;
+ /* LSB is Alert Minimum. In deci-centigrade */
+ val->intval = (data & 0xff) * 10;
+ break;
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+ if (ret < 0)
+ return ret;
+ /* MSB is Alert Maximum. In deci-centigrade */
+ val->intval = (data >> 8) * 10;
+ break;
+ case POWER_SUPPLY_PROP_TEMP_MIN:
+ val->intval = chip->pdata->temp_min;
+ break;
+ case POWER_SUPPLY_PROP_TEMP_MAX:
+ val->intval = chip->pdata->temp_max;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ ret = max17042_get_battery_health(chip, &val->intval);
if (ret < 0)
return ret;
-
- val->intval = data;
- /* The value is signed. */
- if (val->intval & 0x8000) {
- val->intval = (0x7fff & ~val->intval) + 1;
- val->intval *= -1;
- }
- /* The value is converted into deci-centigrade scale */
- /* Units of LSB = 1 / 256 degree Celsius */
- val->intval = val->intval * 10 / 256;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
if (chip->pdata->enable_current_sense) {
@@ -237,6 +338,69 @@ static int max17042_get_property(struct power_supply *psy,
return 0;
}
+static int max17042_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct max17042_chip *chip = power_supply_get_drvdata(psy);
+ struct regmap *map = chip->regmap;
+ int ret = 0;
+ u32 data;
+ int8_t temp;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+ if (ret < 0)
+ return ret;
+
+ /* Input in deci-centigrade, convert to centigrade */
+ temp = val->intval / 10;
+ /* force min < max */
+ if (temp >= (int8_t)(data >> 8))
+ temp = (int8_t)(data >> 8) - 1;
+ /* Write both MAX and MIN ALERT */
+ data = (data & 0xff00) + temp;
+ ret = regmap_write(map, MAX17042_TALRT_Th, data);
+ break;
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+ if (ret < 0)
+ return ret;
+
+ /* Input in Deci-Centigrade, convert to centigrade */
+ temp = val->intval / 10;
+ /* force max > min */
+ if (temp <= (int8_t)(data & 0xff))
+ temp = (int8_t)(data & 0xff) + 1;
+ /* Write both MAX and MIN ALERT */
+ data = (data & 0xff) + (temp << 8);
+ ret = regmap_write(map, MAX17042_TALRT_Th, data);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int max17042_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+ case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+ ret = 1;
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value)
{
int retries = 8;
@@ -645,6 +809,15 @@ max17042_get_pdata(struct device *dev)
pdata->enable_current_sense = true;
}
+ if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min))
+ pdata->temp_min = INT_MIN;
+ if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max))
+ pdata->temp_max = INT_MAX;
+ if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin))
+ pdata->vmin = INT_MIN;
+ if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax))
+ pdata->vmax = INT_MAX;
+
return pdata;
}
#else
@@ -665,6 +838,8 @@ static const struct power_supply_desc max17042_psy_desc = {
.name = "max170xx_battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.get_property = max17042_get_property,
+ .set_property = max17042_set_property,
+ .property_is_writeable = max17042_property_is_writeable,
.properties = max17042_battery_props,
.num_properties = ARRAY_SIZE(max17042_battery_props),
};
@@ -673,6 +848,8 @@ static const struct power_supply_desc max17042_no_current_sense_psy_desc = {
.name = "max170xx_battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.get_property = max17042_get_property,
+ .set_property = max17042_set_property,
+ .property_is_writeable = max17042_property_is_writeable,
.properties = max17042_battery_props,
.num_properties = ARRAY_SIZE(max17042_battery_props) - 2,
};
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 2ed4a4a6b3c5..869284c2e1e8 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -30,6 +30,8 @@ EXPORT_SYMBOL_GPL(power_supply_notifier);
static struct device_type power_supply_dev_type;
+#define POWER_SUPPLY_DEFERRED_REGISTER_TIME msecs_to_jiffies(10)
+
static bool __power_supply_is_supplied_by(struct power_supply *supplier,
struct power_supply *supply)
{
@@ -121,6 +123,30 @@ void power_supply_changed(struct power_supply *psy)
}
EXPORT_SYMBOL_GPL(power_supply_changed);
+/*
+ * Notify that power supply was registered after parent finished the probing.
+ *
+ * Often power supply is registered from driver's probe function. However
+ * calling power_supply_changed() directly from power_supply_register()
+ * would lead to execution of get_property() function provided by the driver
+ * too early - before the probe ends.
+ *
+ * Avoid that by waiting on parent's mutex.
+ */
+static void power_supply_deferred_register_work(struct work_struct *work)
+{
+ struct power_supply *psy = container_of(work, struct power_supply,
+ deferred_register_work.work);
+
+ if (psy->dev.parent)
+ mutex_lock(&psy->dev.parent->mutex);
+
+ power_supply_changed(psy);
+
+ if (psy->dev.parent)
+ mutex_unlock(&psy->dev.parent->mutex);
+}
+
#ifdef CONFIG_OF
#include <linux/of.h>
@@ -420,6 +446,45 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np,
return psy;
}
EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
+
+static void devm_power_supply_put(struct device *dev, void *res)
+{
+ struct power_supply **psy = res;
+
+ power_supply_put(*psy);
+}
+
+/**
+ * devm_power_supply_get_by_phandle() - Resource managed version of
+ * power_supply_get_by_phandle()
+ * @dev: Pointer to device holding phandle property
+ * @phandle_name: Name of property holding a power supply phandle
+ *
+ * Return: On success returns a reference to a power supply with
+ * matching name equals to value under @property, NULL or ERR_PTR otherwise.
+ */
+struct power_supply *devm_power_supply_get_by_phandle(struct device *dev,
+ const char *property)
+{
+ struct power_supply **ptr, *psy;
+
+ if (!dev->of_node)
+ return ERR_PTR(-ENODEV);
+
+ ptr = devres_alloc(devm_power_supply_put, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ psy = power_supply_get_by_phandle(dev->of_node, property);
+ if (IS_ERR_OR_NULL(psy)) {
+ devres_free(ptr);
+ } else {
+ *ptr = psy;
+ devres_add(dev, ptr);
+ }
+ return psy;
+}
+EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
#endif /* CONFIG_OF */
int power_supply_get_property(struct power_supply *psy,
@@ -645,6 +710,10 @@ __power_supply_register(struct device *parent,
struct power_supply *psy;
int rc;
+ if (!parent)
+ pr_warn("%s: Expected proper parent device for '%s'\n",
+ __func__, desc->name);
+
psy = kzalloc(sizeof(*psy), GFP_KERNEL);
if (!psy)
return ERR_PTR(-ENOMEM);
@@ -659,7 +728,6 @@ __power_supply_register(struct device *parent,
dev->release = power_supply_dev_release;
dev_set_drvdata(dev, psy);
psy->desc = desc;
- atomic_inc(&psy->use_cnt);
if (cfg) {
psy->drv_data = cfg->drv_data;
psy->of_node = cfg->of_node;
@@ -672,6 +740,8 @@ __power_supply_register(struct device *parent,
goto dev_set_name_failed;
INIT_WORK(&psy->changed_work, power_supply_changed_work);
+ INIT_DELAYED_WORK(&psy->deferred_register_work,
+ power_supply_deferred_register_work);
rc = power_supply_check_supplies(psy);
if (rc) {
@@ -700,7 +770,20 @@ __power_supply_register(struct device *parent,
if (rc)
goto create_triggers_failed;
- power_supply_changed(psy);
+ /*
+ * Update use_cnt after any uevents (most notably from device_add()).
+ * We are here still during driver's probe but
+ * the power_supply_uevent() calls back driver's get_property
+ * method so:
+ * 1. Driver did not assigned the returned struct power_supply,
+ * 2. Driver could not finish initialization (anything in its probe
+ * after calling power_supply_register()).
+ */
+ atomic_inc(&psy->use_cnt);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &psy->deferred_register_work,
+ POWER_SUPPLY_DEFERRED_REGISTER_TIME);
return psy;
@@ -720,7 +803,8 @@ dev_set_name_failed:
/**
* power_supply_register() - Register new power supply
- * @parent: Device to be a parent of power supply's device
+ * @parent: Device to be a parent of power supply's device, usually
+ * the device which probe function calls this
* @desc: Description of power supply, must be valid through whole
* lifetime of this power supply
* @cfg: Run-time specific configuration accessed during registering,
@@ -740,8 +824,9 @@ struct power_supply *__must_check power_supply_register(struct device *parent,
EXPORT_SYMBOL_GPL(power_supply_register);
/**
- * power_supply_register() - Register new non-waking-source power supply
- * @parent: Device to be a parent of power supply's device
+ * power_supply_register_no_ws() - Register new non-waking-source power supply
+ * @parent: Device to be a parent of power supply's device, usually
+ * the device which probe function calls this
* @desc: Description of power supply, must be valid through whole
* lifetime of this power supply
* @cfg: Run-time specific configuration accessed during registering,
@@ -769,8 +854,9 @@ static void devm_power_supply_release(struct device *dev, void *res)
}
/**
- * power_supply_register() - Register managed power supply
- * @parent: Device to be a parent of power supply's device
+ * devm_power_supply_register() - Register managed power supply
+ * @parent: Device to be a parent of power supply's device, usually
+ * the device which probe function calls this
* @desc: Description of power supply, must be valid through whole
* lifetime of this power supply
* @cfg: Run-time specific configuration accessed during registering,
@@ -804,8 +890,9 @@ devm_power_supply_register(struct device *parent,
EXPORT_SYMBOL_GPL(devm_power_supply_register);
/**
- * power_supply_register() - Register managed non-waking-source power supply
- * @parent: Device to be a parent of power supply's device
+ * devm_power_supply_register_no_ws() - Register managed non-waking-source power supply
+ * @parent: Device to be a parent of power supply's device, usually
+ * the device which probe function calls this
* @desc: Description of power supply, must be valid through whole
* lifetime of this power supply
* @cfg: Run-time specific configuration accessed during registering,
@@ -849,6 +936,7 @@ void power_supply_unregister(struct power_supply *psy)
{
WARN_ON(atomic_dec_return(&psy->use_cnt));
cancel_work_sync(&psy->changed_work);
+ cancel_delayed_work_sync(&psy->deferred_register_work);
sysfs_remove_link(&psy->dev.kobj, "powers");
power_supply_remove_triggers(psy);
psy_unregister_cooler(psy);
diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c
index 2d41a43fc81a..2277ad9c2f68 100644
--- a/drivers/power/power_supply_leds.c
+++ b/drivers/power/power_supply_leds.c
@@ -25,7 +25,7 @@ static void power_supply_update_bat_leds(struct power_supply *psy)
unsigned long delay_on = 0;
unsigned long delay_off = 0;
- if (psy->desc->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
+ if (power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
return;
dev_dbg(&psy->dev, "%s %d\n", __func__, status.intval);
@@ -115,7 +115,7 @@ static void power_supply_update_gen_leds(struct power_supply *psy)
{
union power_supply_propval online;
- if (psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online))
+ if (power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online))
return;
dev_dbg(&psy->dev, "%s %d\n", __func__, online.intval);
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 9134e3d2d95e..ed2d7fd0c734 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -125,7 +125,7 @@ static ssize_t power_supply_store_property(struct device *dev,
value.intval = long_val;
- ret = psy->desc->set_property(psy, off, &value);
+ ret = power_supply_set_property(psy, off, &value);
if (ret < 0)
return ret;
@@ -223,7 +223,7 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj,
if (property == attrno) {
if (psy->desc->property_is_writeable &&
- power_supply_property_is_writeable(psy, property) > 0)
+ psy->desc->property_is_writeable(psy, property) > 0)
mode |= S_IWUSR;
return mode;
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index aad9c3318c02..17d93a73c513 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -41,6 +41,7 @@ config POWER_RESET_AXXIA
config POWER_RESET_BRCMSTB
bool "Broadcom STB reset driver"
depends on ARM || MIPS || COMPILE_TEST
+ depends on MFD_SYSCON
default ARCH_BRCMSTB
help
This driver provides restart support for Broadcom STB boards.
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
index 01c7055c4200..36dc52fb2ec8 100644
--- a/drivers/power/reset/at91-reset.c
+++ b/drivers/power/reset/at91-reset.c
@@ -212,9 +212,9 @@ static int at91_reset_platform_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1 );
at91_ramc_base[idx] = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
- if (IS_ERR(at91_ramc_base[idx])) {
+ if (!at91_ramc_base[idx]) {
dev_err(&pdev->dev, "Could not map ram controller address\n");
- return PTR_ERR(at91_ramc_base[idx]);
+ return -ENOMEM;
}
}
@@ -243,7 +243,7 @@ static int at91_reset_probe(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id at91_reset_plat_match[] = {
+static const struct platform_device_id at91_reset_plat_match[] = {
{ "at91-sam9260-reset", (unsigned long)at91sam9260_restart },
{ "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart },
{ /* sentinel */ }
diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c
index e5332f1db8a7..be3d81ff51cc 100644
--- a/drivers/power/reset/gpio-poweroff.c
+++ b/drivers/power/reset/gpio-poweroff.c
@@ -48,6 +48,7 @@ static void gpio_poweroff_do_poweroff(void)
static int gpio_poweroff_probe(struct platform_device *pdev)
{
bool input = false;
+ enum gpiod_flags flags;
/* If a pm_power_off function has already been added, leave it alone */
if (pm_power_off != NULL) {
@@ -57,25 +58,15 @@ static int gpio_poweroff_probe(struct platform_device *pdev)
return -EBUSY;
}
- reset_gpio = devm_gpiod_get(&pdev->dev, NULL);
- if (IS_ERR(reset_gpio))
- return PTR_ERR(reset_gpio);
-
input = of_property_read_bool(pdev->dev.of_node, "input");
+ if (input)
+ flags = GPIOD_IN;
+ else
+ flags = GPIOD_OUT_LOW;
- if (input) {
- if (gpiod_direction_input(reset_gpio)) {
- dev_err(&pdev->dev,
- "Could not set direction of reset GPIO to input\n");
- return -ENODEV;
- }
- } else {
- if (gpiod_direction_output(reset_gpio, 0)) {
- dev_err(&pdev->dev,
- "Could not set direction of reset GPIO\n");
- return -ENODEV;
- }
- }
+ reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
+ if (IS_ERR(reset_gpio))
+ return PTR_ERR(reset_gpio);
pm_power_off = &gpio_poweroff_do_poweroff;
return 0;
diff --git a/drivers/power/reset/gpio-restart.c b/drivers/power/reset/gpio-restart.c
index edb327efee8b..829b45f42021 100644
--- a/drivers/power/reset/gpio-restart.c
+++ b/drivers/power/reset/gpio-restart.c
@@ -78,7 +78,7 @@ static int gpio_restart_probe(struct platform_device *pdev)
}
gpio_restart->restart_handler.notifier_call = gpio_restart_notify;
- gpio_restart->restart_handler.priority = 128;
+ gpio_restart->restart_handler.priority = 129;
gpio_restart->active_delay_ms = 100;
gpio_restart->inactive_delay_ms = 100;
gpio_restart->wait_delay_ms = 3000;
diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c
index 7ef193b6f7fe..15fed9d8f871 100644
--- a/drivers/power/reset/ltc2952-poweroff.c
+++ b/drivers/power/reset/ltc2952-poweroff.c
@@ -120,18 +120,7 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer)
static void ltc2952_poweroff_start_wde(struct ltc2952_poweroff *data)
{
- if (hrtimer_start(&data->timer_wde, data->wde_interval,
- HRTIMER_MODE_REL)) {
- /*
- * The device will not toggle the watchdog reset,
- * thus shut down is only safe if the PowerPath controller
- * has a long enough time-off before triggering a hardware
- * power-off.
- *
- * Only sending a warning as the system will power-off anyway
- */
- dev_err(data->dev, "unable to start the timer\n");
- }
+ hrtimer_start(&data->timer_wde, data->wde_interval, HRTIMER_MODE_REL);
}
static enum hrtimer_restart
@@ -165,12 +154,10 @@ static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id)
}
if (gpiod_get_value(data->gpio_trigger)) {
- if (hrtimer_start(&data->timer_trigger, data->trigger_delay,
- HRTIMER_MODE_REL))
- dev_err(data->dev, "unable to start the wait timer\n");
+ hrtimer_start(&data->timer_trigger, data->trigger_delay,
+ HRTIMER_MODE_REL);
} else {
hrtimer_cancel(&data->timer_trigger);
- /* omitting return value check, timer should have been valid */
}
return IRQ_HANDLED;
}
@@ -214,16 +201,15 @@ static int ltc2952_poweroff_init(struct platform_device *pdev)
return ret;
}
- data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", GPIOD_IN);
+ data->gpio_trigger = devm_gpiod_get_optional(&pdev->dev, "trigger",
+ GPIOD_IN);
if (IS_ERR(data->gpio_trigger)) {
/*
* It's not a problem if the trigger gpio isn't available, but
* it is worth a warning if its use was defined in the device
* tree.
*/
- if (PTR_ERR(data->gpio_trigger) != -ENOENT)
- dev_err(&pdev->dev,
- "unable to claim gpio \"trigger\"\n");
+ dev_err(&pdev->dev, "unable to claim gpio \"trigger\"\n");
data->gpio_trigger = NULL;
}
diff --git a/drivers/power/rt9455_charger.c b/drivers/power/rt9455_charger.c
new file mode 100644
index 000000000000..08baac6e3ada
--- /dev/null
+++ b/drivers/power/rt9455_charger.c
@@ -0,0 +1,1752 @@
+/*
+ * Driver for Richtek RT9455WSC battery charger.
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/usb/phy.h>
+#include <linux/regmap.h>
+
+#define RT9455_MANUFACTURER "Richtek"
+#define RT9455_MODEL_NAME "RT9455"
+#define RT9455_DRIVER_NAME "rt9455-charger"
+
+#define RT9455_IRQ_NAME "interrupt"
+
+#define RT9455_PWR_RDY_DELAY 1 /* 1 second */
+#define RT9455_MAX_CHARGING_TIME 21600 /* 6 hrs */
+#define RT9455_BATT_PRESENCE_DELAY 60 /* 60 seconds */
+
+#define RT9455_CHARGE_MODE 0x00
+#define RT9455_BOOST_MODE 0x01
+
+#define RT9455_FAULT 0x03
+
+#define RT9455_IAICR_100MA 0x00
+#define RT9455_IAICR_500MA 0x01
+#define RT9455_IAICR_NO_LIMIT 0x03
+
+#define RT9455_CHARGE_DISABLE 0x00
+#define RT9455_CHARGE_ENABLE 0x01
+
+#define RT9455_PWR_FAULT 0x00
+#define RT9455_PWR_GOOD 0x01
+
+#define RT9455_REG_CTRL1 0x00 /* CTRL1 reg address */
+#define RT9455_REG_CTRL2 0x01 /* CTRL2 reg address */
+#define RT9455_REG_CTRL3 0x02 /* CTRL3 reg address */
+#define RT9455_REG_DEV_ID 0x03 /* DEV_ID reg address */
+#define RT9455_REG_CTRL4 0x04 /* CTRL4 reg address */
+#define RT9455_REG_CTRL5 0x05 /* CTRL5 reg address */
+#define RT9455_REG_CTRL6 0x06 /* CTRL6 reg address */
+#define RT9455_REG_CTRL7 0x07 /* CTRL7 reg address */
+#define RT9455_REG_IRQ1 0x08 /* IRQ1 reg address */
+#define RT9455_REG_IRQ2 0x09 /* IRQ2 reg address */
+#define RT9455_REG_IRQ3 0x0A /* IRQ3 reg address */
+#define RT9455_REG_MASK1 0x0B /* MASK1 reg address */
+#define RT9455_REG_MASK2 0x0C /* MASK2 reg address */
+#define RT9455_REG_MASK3 0x0D /* MASK3 reg address */
+
+enum rt9455_fields {
+ F_STAT, F_BOOST, F_PWR_RDY, F_OTG_PIN_POLARITY, /* CTRL1 reg fields */
+
+ F_IAICR, F_TE_SHDN_EN, F_HIGHER_OCP, F_TE, F_IAICR_INT, F_HIZ,
+ F_OPA_MODE, /* CTRL2 reg fields */
+
+ F_VOREG, F_OTG_PL, F_OTG_EN, /* CTRL3 reg fields */
+
+ F_VENDOR_ID, F_CHIP_REV, /* DEV_ID reg fields */
+
+ F_RST, /* CTRL4 reg fields */
+
+ F_TMR_EN, F_MIVR, F_IPREC, F_IEOC_PERCENTAGE, /* CTRL5 reg fields*/
+
+ F_IAICR_SEL, F_ICHRG, F_VPREC, /* CTRL6 reg fields */
+
+ F_BATD_EN, F_CHG_EN, F_VMREG, /* CTRL7 reg fields */
+
+ F_TSDI, F_VINOVPI, F_BATAB, /* IRQ1 reg fields */
+
+ F_CHRVPI, F_CHBATOVI, F_CHTERMI, F_CHRCHGI, F_CH32MI, F_CHTREGI,
+ F_CHMIVRI, /* IRQ2 reg fields */
+
+ F_BSTBUSOVI, F_BSTOLI, F_BSTLOWVI, F_BST32SI, /* IRQ3 reg fields */
+
+ F_TSDM, F_VINOVPIM, F_BATABM, /* MASK1 reg fields */
+
+ F_CHRVPIM, F_CHBATOVIM, F_CHTERMIM, F_CHRCHGIM, F_CH32MIM, F_CHTREGIM,
+ F_CHMIVRIM, /* MASK2 reg fields */
+
+ F_BSTVINOVIM, F_BSTOLIM, F_BSTLOWVIM, F_BST32SIM, /* MASK3 reg fields */
+
+ F_MAX_FIELDS
+};
+
+static const struct reg_field rt9455_reg_fields[] = {
+ [F_STAT] = REG_FIELD(RT9455_REG_CTRL1, 4, 5),
+ [F_BOOST] = REG_FIELD(RT9455_REG_CTRL1, 3, 3),
+ [F_PWR_RDY] = REG_FIELD(RT9455_REG_CTRL1, 2, 2),
+ [F_OTG_PIN_POLARITY] = REG_FIELD(RT9455_REG_CTRL1, 1, 1),
+
+ [F_IAICR] = REG_FIELD(RT9455_REG_CTRL2, 6, 7),
+ [F_TE_SHDN_EN] = REG_FIELD(RT9455_REG_CTRL2, 5, 5),
+ [F_HIGHER_OCP] = REG_FIELD(RT9455_REG_CTRL2, 4, 4),
+ [F_TE] = REG_FIELD(RT9455_REG_CTRL2, 3, 3),
+ [F_IAICR_INT] = REG_FIELD(RT9455_REG_CTRL2, 2, 2),
+ [F_HIZ] = REG_FIELD(RT9455_REG_CTRL2, 1, 1),
+ [F_OPA_MODE] = REG_FIELD(RT9455_REG_CTRL2, 0, 0),
+
+ [F_VOREG] = REG_FIELD(RT9455_REG_CTRL3, 2, 7),
+ [F_OTG_PL] = REG_FIELD(RT9455_REG_CTRL3, 1, 1),
+ [F_OTG_EN] = REG_FIELD(RT9455_REG_CTRL3, 0, 0),
+
+ [F_VENDOR_ID] = REG_FIELD(RT9455_REG_DEV_ID, 4, 7),
+ [F_CHIP_REV] = REG_FIELD(RT9455_REG_DEV_ID, 0, 3),
+
+ [F_RST] = REG_FIELD(RT9455_REG_CTRL4, 7, 7),
+
+ [F_TMR_EN] = REG_FIELD(RT9455_REG_CTRL5, 7, 7),
+ [F_MIVR] = REG_FIELD(RT9455_REG_CTRL5, 4, 5),
+ [F_IPREC] = REG_FIELD(RT9455_REG_CTRL5, 2, 3),
+ [F_IEOC_PERCENTAGE] = REG_FIELD(RT9455_REG_CTRL5, 0, 1),
+
+ [F_IAICR_SEL] = REG_FIELD(RT9455_REG_CTRL6, 7, 7),
+ [F_ICHRG] = REG_FIELD(RT9455_REG_CTRL6, 4, 6),
+ [F_VPREC] = REG_FIELD(RT9455_REG_CTRL6, 0, 2),
+
+ [F_BATD_EN] = REG_FIELD(RT9455_REG_CTRL7, 6, 6),
+ [F_CHG_EN] = REG_FIELD(RT9455_REG_CTRL7, 4, 4),
+ [F_VMREG] = REG_FIELD(RT9455_REG_CTRL7, 0, 3),
+
+ [F_TSDI] = REG_FIELD(RT9455_REG_IRQ1, 7, 7),
+ [F_VINOVPI] = REG_FIELD(RT9455_REG_IRQ1, 6, 6),
+ [F_BATAB] = REG_FIELD(RT9455_REG_IRQ1, 0, 0),
+
+ [F_CHRVPI] = REG_FIELD(RT9455_REG_IRQ2, 7, 7),
+ [F_CHBATOVI] = REG_FIELD(RT9455_REG_IRQ2, 5, 5),
+ [F_CHTERMI] = REG_FIELD(RT9455_REG_IRQ2, 4, 4),
+ [F_CHRCHGI] = REG_FIELD(RT9455_REG_IRQ2, 3, 3),
+ [F_CH32MI] = REG_FIELD(RT9455_REG_IRQ2, 2, 2),
+ [F_CHTREGI] = REG_FIELD(RT9455_REG_IRQ2, 1, 1),
+ [F_CHMIVRI] = REG_FIELD(RT9455_REG_IRQ2, 0, 0),
+
+ [F_BSTBUSOVI] = REG_FIELD(RT9455_REG_IRQ3, 7, 7),
+ [F_BSTOLI] = REG_FIELD(RT9455_REG_IRQ3, 6, 6),
+ [F_BSTLOWVI] = REG_FIELD(RT9455_REG_IRQ3, 5, 5),
+ [F_BST32SI] = REG_FIELD(RT9455_REG_IRQ3, 3, 3),
+
+ [F_TSDM] = REG_FIELD(RT9455_REG_MASK1, 7, 7),
+ [F_VINOVPIM] = REG_FIELD(RT9455_REG_MASK1, 6, 6),
+ [F_BATABM] = REG_FIELD(RT9455_REG_MASK1, 0, 0),
+
+ [F_CHRVPIM] = REG_FIELD(RT9455_REG_MASK2, 7, 7),
+ [F_CHBATOVIM] = REG_FIELD(RT9455_REG_MASK2, 5, 5),
+ [F_CHTERMIM] = REG_FIELD(RT9455_REG_MASK2, 4, 4),
+ [F_CHRCHGIM] = REG_FIELD(RT9455_REG_MASK2, 3, 3),
+ [F_CH32MIM] = REG_FIELD(RT9455_REG_MASK2, 2, 2),
+ [F_CHTREGIM] = REG_FIELD(RT9455_REG_MASK2, 1, 1),
+ [F_CHMIVRIM] = REG_FIELD(RT9455_REG_MASK2, 0, 0),
+
+ [F_BSTVINOVIM] = REG_FIELD(RT9455_REG_MASK3, 7, 7),
+ [F_BSTOLIM] = REG_FIELD(RT9455_REG_MASK3, 6, 6),
+ [F_BSTLOWVIM] = REG_FIELD(RT9455_REG_MASK3, 5, 5),
+ [F_BST32SIM] = REG_FIELD(RT9455_REG_MASK3, 3, 3),
+};
+
+#define GET_MASK(fid) (BIT(rt9455_reg_fields[fid].msb + 1) - \
+ BIT(rt9455_reg_fields[fid].lsb))
+
+/*
+ * Each array initialised below shows the possible real-world values for a
+ * group of bits belonging to RT9455 registers. The arrays are sorted in
+ * ascending order. The index of each real-world value represents the value
+ * that is encoded in the group of bits belonging to RT9455 registers.
+ */
+/* REG06[6:4] (ICHRG) in uAh */
+static const int rt9455_ichrg_values[] = {
+ 500000, 650000, 800000, 950000, 1100000, 1250000, 1400000, 1550000
+};
+
+/*
+ * When the charger is in charge mode, REG02[7:2] represent battery regulation
+ * voltage.
+ */
+/* REG02[7:2] (VOREG) in uV */
+static const int rt9455_voreg_values[] = {
+ 3500000, 3520000, 3540000, 3560000, 3580000, 3600000, 3620000, 3640000,
+ 3660000, 3680000, 3700000, 3720000, 3740000, 3760000, 3780000, 3800000,
+ 3820000, 3840000, 3860000, 3880000, 3900000, 3920000, 3940000, 3960000,
+ 3980000, 4000000, 4020000, 4040000, 4060000, 4080000, 4100000, 4120000,
+ 4140000, 4160000, 4180000, 4200000, 4220000, 4240000, 4260000, 4280000,
+ 4300000, 4330000, 4350000, 4370000, 4390000, 4410000, 4430000, 4450000,
+ 4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000,
+ 4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000
+};
+
+/*
+ * When the charger is in boost mode, REG02[7:2] represent boost output
+ * voltage.
+ */
+/* REG02[7:2] (Boost output voltage) in uV */
+static const int rt9455_boost_voltage_values[] = {
+ 4425000, 4450000, 4475000, 4500000, 4525000, 4550000, 4575000, 4600000,
+ 4625000, 4650000, 4675000, 4700000, 4725000, 4750000, 4775000, 4800000,
+ 4825000, 4850000, 4875000, 4900000, 4925000, 4950000, 4975000, 5000000,
+ 5025000, 5050000, 5075000, 5100000, 5125000, 5150000, 5175000, 5200000,
+ 5225000, 5250000, 5275000, 5300000, 5325000, 5350000, 5375000, 5400000,
+ 5425000, 5450000, 5475000, 5500000, 5525000, 5550000, 5575000, 5600000,
+ 5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000,
+ 5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000,
+};
+
+/* REG07[3:0] (VMREG) in uV */
+static const int rt9455_vmreg_values[] = {
+ 4200000, 4220000, 4240000, 4260000, 4280000, 4300000, 4320000, 4340000,
+ 4360000, 4380000, 4400000, 4430000, 4450000, 4450000, 4450000, 4450000
+};
+
+/* REG05[5:4] (IEOC_PERCENTAGE) */
+static const int rt9455_ieoc_percentage_values[] = {
+ 10, 30, 20, 30
+};
+
+/* REG05[1:0] (MIVR) in uV */
+static const int rt9455_mivr_values[] = {
+ 4000000, 4250000, 4500000, 5000000
+};
+
+/* REG05[1:0] (IAICR) in uA */
+static const int rt9455_iaicr_values[] = {
+ 100000, 500000, 1000000, 2000000
+};
+
+struct rt9455_info {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct regmap_field *regmap_fields[F_MAX_FIELDS];
+ struct power_supply *charger;
+#if IS_ENABLED(CONFIG_USB_PHY)
+ struct usb_phy *usb_phy;
+ struct notifier_block nb;
+#endif
+ struct delayed_work pwr_rdy_work;
+ struct delayed_work max_charging_time_work;
+ struct delayed_work batt_presence_work;
+ u32 voreg;
+ u32 boost_voltage;
+};
+
+/*
+ * Iterate through each element of the 'tbl' array until an element whose value
+ * is greater than v is found. Return the index of the respective element,
+ * or the index of the last element in the array, if no such element is found.
+ */
+static unsigned int rt9455_find_idx(const int tbl[], int tbl_size, int v)
+{
+ int i;
+
+ /*
+ * No need to iterate until the last index in the table because
+ * if no element greater than v is found in the table,
+ * or if only the last element is greater than v,
+ * function returns the index of the last element.
+ */
+ for (i = 0; i < tbl_size - 1; i++)
+ if (v <= tbl[i])
+ return i;
+
+ return (tbl_size - 1);
+}
+
+static int rt9455_get_field_val(struct rt9455_info *info,
+ enum rt9455_fields field,
+ const int tbl[], int tbl_size, int *val)
+{
+ unsigned int v;
+ int ret;
+
+ ret = regmap_field_read(info->regmap_fields[field], &v);
+ if (ret)
+ return ret;
+
+ v = (v >= tbl_size) ? (tbl_size - 1) : v;
+ *val = tbl[v];
+
+ return 0;
+}
+
+static int rt9455_set_field_val(struct rt9455_info *info,
+ enum rt9455_fields field,
+ const int tbl[], int tbl_size, int val)
+{
+ unsigned int idx = rt9455_find_idx(tbl, tbl_size, val);
+
+ return regmap_field_write(info->regmap_fields[field], idx);
+}
+
+static int rt9455_register_reset(struct rt9455_info *info)
+{
+ struct device *dev = &info->client->dev;
+ unsigned int v;
+ int ret, limit = 100;
+
+ ret = regmap_field_write(info->regmap_fields[F_RST], 0x01);
+ if (ret) {
+ dev_err(dev, "Failed to set RST bit\n");
+ return ret;
+ }
+
+ /*
+ * To make sure that reset operation has finished, loop until RST bit
+ * is set to 0.
+ */
+ do {
+ ret = regmap_field_read(info->regmap_fields[F_RST], &v);
+ if (ret) {
+ dev_err(dev, "Failed to read RST bit\n");
+ return ret;
+ }
+
+ if (!v)
+ break;
+
+ usleep_range(10, 100);
+ } while (--limit);
+
+ if (!limit)
+ return -EIO;
+
+ return 0;
+}
+
+/* Charger power supply property routines */
+static enum power_supply_property rt9455_charger_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_SCOPE,
+ POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static char *rt9455_charger_supplied_to[] = {
+ "main-battery",
+};
+
+static int rt9455_charger_get_status(struct rt9455_info *info,
+ union power_supply_propval *val)
+{
+ unsigned int v, pwr_rdy;
+ int ret;
+
+ ret = regmap_field_read(info->regmap_fields[F_PWR_RDY],
+ &pwr_rdy);
+ if (ret) {
+ dev_err(&info->client->dev, "Failed to read PWR_RDY bit\n");
+ return ret;
+ }
+
+ /*
+ * If PWR_RDY bit is unset, the battery is discharging. Otherwise,
+ * STAT bits value must be checked.
+ */
+ if (!pwr_rdy) {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ return 0;
+ }
+
+ ret = regmap_field_read(info->regmap_fields[F_STAT], &v);
+ if (ret) {
+ dev_err(&info->client->dev, "Failed to read STAT bits\n");
+ return ret;
+ }
+
+ switch (v) {
+ case 0:
+ /*
+ * If PWR_RDY bit is set, but STAT bits value is 0, the charger
+ * may be in one of the following cases:
+ * 1. CHG_EN bit is 0.
+ * 2. CHG_EN bit is 1 but the battery is not connected.
+ * In any of these cases, POWER_SUPPLY_STATUS_NOT_CHARGING is
+ * returned.
+ */
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ return 0;
+ case 1:
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ return 0;
+ case 2:
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ return 0;
+ default:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ return 0;
+ }
+}
+
+static int rt9455_charger_get_health(struct rt9455_info *info,
+ union power_supply_propval *val)
+{
+ struct device *dev = &info->client->dev;
+ unsigned int v;
+ int ret;
+
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+ ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &v);
+ if (ret) {
+ dev_err(dev, "Failed to read IRQ1 register\n");
+ return ret;
+ }
+
+ if (v & GET_MASK(F_TSDI)) {
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ return 0;
+ }
+ if (v & GET_MASK(F_VINOVPI)) {
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ return 0;
+ }
+ if (v & GET_MASK(F_BATAB)) {
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ return 0;
+ }
+
+ ret = regmap_read(info->regmap, RT9455_REG_IRQ2, &v);
+ if (ret) {
+ dev_err(dev, "Failed to read IRQ2 register\n");
+ return ret;
+ }
+
+ if (v & GET_MASK(F_CHBATOVI)) {
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ return 0;
+ }
+ if (v & GET_MASK(F_CH32MI)) {
+ val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+ return 0;
+ }
+
+ ret = regmap_read(info->regmap, RT9455_REG_IRQ3, &v);
+ if (ret) {
+ dev_err(dev, "Failed to read IRQ3 register\n");
+ return ret;
+ }
+
+ if (v & GET_MASK(F_BSTBUSOVI)) {
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ return 0;
+ }
+ if (v & GET_MASK(F_BSTOLI)) {
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ return 0;
+ }
+ if (v & GET_MASK(F_BSTLOWVI)) {
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ return 0;
+ }
+ if (v & GET_MASK(F_BST32SI)) {
+ val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+ return 0;
+ }
+
+ ret = regmap_field_read(info->regmap_fields[F_STAT], &v);
+ if (ret) {
+ dev_err(dev, "Failed to read STAT bits\n");
+ return ret;
+ }
+
+ if (v == RT9455_FAULT) {
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt9455_charger_get_battery_presence(struct rt9455_info *info,
+ union power_supply_propval *val)
+{
+ unsigned int v;
+ int ret;
+
+ ret = regmap_field_read(info->regmap_fields[F_BATAB], &v);
+ if (ret) {
+ dev_err(&info->client->dev, "Failed to read BATAB bit\n");
+ return ret;
+ }
+
+ /*
+ * Since BATAB is 1 when battery is NOT present and 0 otherwise,
+ * !BATAB is returned.
+ */
+ val->intval = !v;
+
+ return 0;
+}
+
+static int rt9455_charger_get_online(struct rt9455_info *info,
+ union power_supply_propval *val)
+{
+ unsigned int v;
+ int ret;
+
+ ret = regmap_field_read(info->regmap_fields[F_PWR_RDY], &v);
+ if (ret) {
+ dev_err(&info->client->dev, "Failed to read PWR_RDY bit\n");
+ return ret;
+ }
+
+ val->intval = (int)v;
+
+ return 0;
+}
+
+static int rt9455_charger_get_current(struct rt9455_info *info,
+ union power_supply_propval *val)
+{
+ int curr;
+ int ret;
+
+ ret = rt9455_get_field_val(info, F_ICHRG,
+ rt9455_ichrg_values,
+ ARRAY_SIZE(rt9455_ichrg_values),
+ &curr);
+ if (ret) {
+ dev_err(&info->client->dev, "Failed to read ICHRG value\n");
+ return ret;
+ }
+
+ val->intval = curr;
+
+ return 0;
+}
+
+static int rt9455_charger_get_current_max(struct rt9455_info *info,
+ union power_supply_propval *val)
+{
+ int idx = ARRAY_SIZE(rt9455_ichrg_values) - 1;
+
+ val->intval = rt9455_ichrg_values[idx];
+
+ return 0;
+}
+
+static int rt9455_charger_get_voltage(struct rt9455_info *info,
+ union power_supply_propval *val)
+{
+ int voltage;
+ int ret;
+
+ ret = rt9455_get_field_val(info, F_VOREG,
+ rt9455_voreg_values,
+ ARRAY_SIZE(rt9455_voreg_values),
+ &voltage);
+ if (ret) {
+ dev_err(&info->client->dev, "Failed to read VOREG value\n");
+ return ret;
+ }
+
+ val->intval = voltage;
+
+ return 0;
+}
+
+static int rt9455_charger_get_voltage_max(struct rt9455_info *info,
+ union power_supply_propval *val)
+{
+ int idx = ARRAY_SIZE(rt9455_vmreg_values) - 1;
+
+ val->intval = rt9455_vmreg_values[idx];
+
+ return 0;
+}
+
+static int rt9455_charger_get_term_current(struct rt9455_info *info,
+ union power_supply_propval *val)
+{
+ struct device *dev = &info->client->dev;
+ int ichrg, ieoc_percentage, ret;
+
+ ret = rt9455_get_field_val(info, F_ICHRG,
+ rt9455_ichrg_values,
+ ARRAY_SIZE(rt9455_ichrg_values),
+ &ichrg);
+ if (ret) {
+ dev_err(dev, "Failed to read ICHRG value\n");
+ return ret;
+ }
+
+ ret = rt9455_get_field_val(info, F_IEOC_PERCENTAGE,
+ rt9455_ieoc_percentage_values,
+ ARRAY_SIZE(rt9455_ieoc_percentage_values),
+ &ieoc_percentage);
+ if (ret) {
+ dev_err(dev, "Failed to read IEOC value\n");
+ return ret;
+ }
+
+ val->intval = ichrg * ieoc_percentage / 100;
+
+ return 0;
+}
+
+static int rt9455_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct rt9455_info *info = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ return rt9455_charger_get_status(info, val);
+ case POWER_SUPPLY_PROP_HEALTH:
+ return rt9455_charger_get_health(info, val);
+ case POWER_SUPPLY_PROP_PRESENT:
+ return rt9455_charger_get_battery_presence(info, val);
+ case POWER_SUPPLY_PROP_ONLINE:
+ return rt9455_charger_get_online(info, val);
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ return rt9455_charger_get_current(info, val);
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ return rt9455_charger_get_current_max(info, val);
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ return rt9455_charger_get_voltage(info, val);
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ return rt9455_charger_get_voltage_max(info, val);
+ case POWER_SUPPLY_PROP_SCOPE:
+ val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+ return 0;
+ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+ return rt9455_charger_get_term_current(info, val);
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = RT9455_MODEL_NAME;
+ return 0;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = RT9455_MANUFACTURER;
+ return 0;
+ default:
+ return -ENODATA;
+ }
+}
+
+static int rt9455_hw_init(struct rt9455_info *info, u32 ichrg,
+ u32 ieoc_percentage,
+ u32 mivr, u32 iaicr)
+{
+ struct device *dev = &info->client->dev;
+ int idx, ret;
+
+ ret = rt9455_register_reset(info);
+ if (ret) {
+ dev_err(dev, "Power On Reset failed\n");
+ return ret;
+ }
+
+ /* Set TE bit in order to enable end of charge detection */
+ ret = regmap_field_write(info->regmap_fields[F_TE], 1);
+ if (ret) {
+ dev_err(dev, "Failed to set TE bit\n");
+ return ret;
+ }
+
+ /* Set TE_SHDN_EN bit in order to enable end of charge detection */
+ ret = regmap_field_write(info->regmap_fields[F_TE_SHDN_EN], 1);
+ if (ret) {
+ dev_err(dev, "Failed to set TE_SHDN_EN bit\n");
+ return ret;
+ }
+
+ /*
+ * Set BATD_EN bit in order to enable battery detection
+ * when charging is done
+ */
+ ret = regmap_field_write(info->regmap_fields[F_BATD_EN], 1);
+ if (ret) {
+ dev_err(dev, "Failed to set BATD_EN bit\n");
+ return ret;
+ }
+
+ /*
+ * Disable Safety Timer. In charge mode, this timer terminates charging
+ * if no read or write via I2C is done within 32 minutes. This timer
+ * avoids overcharging the baterry when the OS is not loaded and the
+ * charger is connected to a power source.
+ * In boost mode, this timer triggers BST32SI interrupt if no read or
+ * write via I2C is done within 32 seconds.
+ * When the OS is loaded and the charger driver is inserted, it is used
+ * delayed_work, named max_charging_time_work, to avoid overcharging
+ * the battery.
+ */
+ ret = regmap_field_write(info->regmap_fields[F_TMR_EN], 0x00);
+ if (ret) {
+ dev_err(dev, "Failed to disable Safety Timer\n");
+ return ret;
+ }
+
+ /* Set ICHRG to value retrieved from device-specific data */
+ ret = rt9455_set_field_val(info, F_ICHRG,
+ rt9455_ichrg_values,
+ ARRAY_SIZE(rt9455_ichrg_values), ichrg);
+ if (ret) {
+ dev_err(dev, "Failed to set ICHRG value\n");
+ return ret;
+ }
+
+ /* Set IEOC Percentage to value retrieved from device-specific data */
+ ret = rt9455_set_field_val(info, F_IEOC_PERCENTAGE,
+ rt9455_ieoc_percentage_values,
+ ARRAY_SIZE(rt9455_ieoc_percentage_values),
+ ieoc_percentage);
+ if (ret) {
+ dev_err(dev, "Failed to set IEOC Percentage value\n");
+ return ret;
+ }
+
+ /* Set VOREG to value retrieved from device-specific data */
+ ret = rt9455_set_field_val(info, F_VOREG,
+ rt9455_voreg_values,
+ ARRAY_SIZE(rt9455_voreg_values),
+ info->voreg);
+ if (ret) {
+ dev_err(dev, "Failed to set VOREG value\n");
+ return ret;
+ }
+
+ /* Set VMREG value to maximum (4.45V). */
+ idx = ARRAY_SIZE(rt9455_vmreg_values) - 1;
+ ret = rt9455_set_field_val(info, F_VMREG,
+ rt9455_vmreg_values,
+ ARRAY_SIZE(rt9455_vmreg_values),
+ rt9455_vmreg_values[idx]);
+ if (ret) {
+ dev_err(dev, "Failed to set VMREG value\n");
+ return ret;
+ }
+
+ /*
+ * Set MIVR to value retrieved from device-specific data.
+ * If no value is specified, default value for MIVR is 4.5V.
+ */
+ if (mivr == -1)
+ mivr = 4500000;
+
+ ret = rt9455_set_field_val(info, F_MIVR,
+ rt9455_mivr_values,
+ ARRAY_SIZE(rt9455_mivr_values), mivr);
+ if (ret) {
+ dev_err(dev, "Failed to set MIVR value\n");
+ return ret;
+ }
+
+ /*
+ * Set IAICR to value retrieved from device-specific data.
+ * If no value is specified, default value for IAICR is 500 mA.
+ */
+ if (iaicr == -1)
+ iaicr = 500000;
+
+ ret = rt9455_set_field_val(info, F_IAICR,
+ rt9455_iaicr_values,
+ ARRAY_SIZE(rt9455_iaicr_values), iaicr);
+ if (ret) {
+ dev_err(dev, "Failed to set IAICR value\n");
+ return ret;
+ }
+
+ /*
+ * Set IAICR_INT bit so that IAICR value is determined by IAICR bits
+ * and not by OTG pin.
+ */
+ ret = regmap_field_write(info->regmap_fields[F_IAICR_INT], 0x01);
+ if (ret) {
+ dev_err(dev, "Failed to set IAICR_INT bit\n");
+ return ret;
+ }
+
+ /*
+ * Disable CHMIVRI interrupt. Because the driver sets MIVR value,
+ * CHMIVRI is triggered, but there is no action to be taken by the
+ * driver when CHMIVRI is triggered.
+ */
+ ret = regmap_field_write(info->regmap_fields[F_CHMIVRIM], 0x01);
+ if (ret) {
+ dev_err(dev, "Failed to mask CHMIVRI interrupt\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+/*
+ * Before setting the charger into boost mode, boost output voltage is
+ * set. This is needed because boost output voltage may differ from battery
+ * regulation voltage. F_VOREG bits represent either battery regulation voltage
+ * or boost output voltage, depending on the mode the charger is. Both battery
+ * regulation voltage and boost output voltage are read from DT/ACPI during
+ * probe.
+ */
+static int rt9455_set_boost_voltage_before_boost_mode(struct rt9455_info *info)
+{
+ struct device *dev = &info->client->dev;
+ int ret;
+
+ ret = rt9455_set_field_val(info, F_VOREG,
+ rt9455_boost_voltage_values,
+ ARRAY_SIZE(rt9455_boost_voltage_values),
+ info->boost_voltage);
+ if (ret) {
+ dev_err(dev, "Failed to set boost output voltage value\n");
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+/*
+ * Before setting the charger into charge mode, battery regulation voltage is
+ * set. This is needed because boost output voltage may differ from battery
+ * regulation voltage. F_VOREG bits represent either battery regulation voltage
+ * or boost output voltage, depending on the mode the charger is. Both battery
+ * regulation voltage and boost output voltage are read from DT/ACPI during
+ * probe.
+ */
+static int rt9455_set_voreg_before_charge_mode(struct rt9455_info *info)
+{
+ struct device *dev = &info->client->dev;
+ int ret;
+
+ ret = rt9455_set_field_val(info, F_VOREG,
+ rt9455_voreg_values,
+ ARRAY_SIZE(rt9455_voreg_values),
+ info->voreg);
+ if (ret) {
+ dev_err(dev, "Failed to set VOREG value\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rt9455_irq_handler_check_irq1_register(struct rt9455_info *info,
+ bool *_is_battery_absent,
+ bool *_alert_userspace)
+{
+ unsigned int irq1, mask1, mask2;
+ struct device *dev = &info->client->dev;
+ bool is_battery_absent = false;
+ bool alert_userspace = false;
+ int ret;
+
+ ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &irq1);
+ if (ret) {
+ dev_err(dev, "Failed to read IRQ1 register\n");
+ return ret;
+ }
+
+ ret = regmap_read(info->regmap, RT9455_REG_MASK1, &mask1);
+ if (ret) {
+ dev_err(dev, "Failed to read MASK1 register\n");
+ return ret;
+ }
+
+ if (irq1 & GET_MASK(F_TSDI)) {
+ dev_err(dev, "Thermal shutdown fault occurred\n");
+ alert_userspace = true;
+ }
+
+ if (irq1 & GET_MASK(F_VINOVPI)) {
+ dev_err(dev, "Overvoltage input occurred\n");
+ alert_userspace = true;
+ }
+
+ if (irq1 & GET_MASK(F_BATAB)) {
+ dev_err(dev, "Battery absence occurred\n");
+ is_battery_absent = true;
+ alert_userspace = true;
+
+ if ((mask1 & GET_MASK(F_BATABM)) == 0) {
+ ret = regmap_field_write(info->regmap_fields[F_BATABM],
+ 0x01);
+ if (ret) {
+ dev_err(dev, "Failed to mask BATAB interrupt\n");
+ return ret;
+ }
+ }
+
+ ret = regmap_read(info->regmap, RT9455_REG_MASK2, &mask2);
+ if (ret) {
+ dev_err(dev, "Failed to read MASK2 register\n");
+ return ret;
+ }
+
+ if (mask2 & GET_MASK(F_CHTERMIM)) {
+ ret = regmap_field_write(
+ info->regmap_fields[F_CHTERMIM], 0x00);
+ if (ret) {
+ dev_err(dev, "Failed to unmask CHTERMI interrupt\n");
+ return ret;
+ }
+ }
+
+ if (mask2 & GET_MASK(F_CHRCHGIM)) {
+ ret = regmap_field_write(
+ info->regmap_fields[F_CHRCHGIM], 0x00);
+ if (ret) {
+ dev_err(dev, "Failed to unmask CHRCHGI interrupt\n");
+ return ret;
+ }
+ }
+
+ /*
+ * When the battery is absent, max_charging_time_work is
+ * cancelled, since no charging is done.
+ */
+ cancel_delayed_work_sync(&info->max_charging_time_work);
+ /*
+ * Since no interrupt is triggered when the battery is
+ * reconnected, max_charging_time_work is not rescheduled.
+ * Therefore, batt_presence_work is scheduled to check whether
+ * the battery is still absent or not.
+ */
+ queue_delayed_work(system_power_efficient_wq,
+ &info->batt_presence_work,
+ RT9455_BATT_PRESENCE_DELAY * HZ);
+ }
+
+ *_is_battery_absent = is_battery_absent;
+
+ if (alert_userspace)
+ *_alert_userspace = alert_userspace;
+
+ return 0;
+}
+
+static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info,
+ bool is_battery_absent,
+ bool *_alert_userspace)
+{
+ unsigned int irq2, mask2;
+ struct device *dev = &info->client->dev;
+ bool alert_userspace = false;
+ int ret;
+
+ ret = regmap_read(info->regmap, RT9455_REG_IRQ2, &irq2);
+ if (ret) {
+ dev_err(dev, "Failed to read IRQ2 register\n");
+ return ret;
+ }
+
+ ret = regmap_read(info->regmap, RT9455_REG_MASK2, &mask2);
+ if (ret) {
+ dev_err(dev, "Failed to read MASK2 register\n");
+ return ret;
+ }
+
+ if (irq2 & GET_MASK(F_CHRVPI)) {
+ dev_dbg(dev, "Charger fault occurred\n");
+ alert_userspace = true;
+ /*
+ * CHRVPI bit is set in 2 cases:
+ * 1. when the power source is connected to the charger.
+ * 2. when the power source is disconnected from the charger.
+ * To identify the case, PWR_RDY bit is checked. Because
+ * PWR_RDY bit is set / cleared after CHRVPI interrupt is
+ * triggered, it is used delayed_work to later read PWR_RDY bit.
+ */
+ queue_delayed_work(system_power_efficient_wq,
+ &info->pwr_rdy_work,
+ RT9455_PWR_RDY_DELAY * HZ);
+ }
+ if (irq2 & GET_MASK(F_CHBATOVI)) {
+ dev_err(dev, "Battery OVP occurred\n");
+ alert_userspace = true;
+ }
+ if (irq2 & GET_MASK(F_CHTERMI)) {
+ dev_dbg(dev, "Charge terminated\n");
+ if (!is_battery_absent) {
+ if ((mask2 & GET_MASK(F_CHTERMIM)) == 0) {
+ ret = regmap_field_write(
+ info->regmap_fields[F_CHTERMIM], 0x01);
+ if (ret) {
+ dev_err(dev, "Failed to mask CHTERMI interrupt\n");
+ return ret;
+ }
+ /*
+ * Update MASK2 value, since CHTERMIM bit is
+ * set.
+ */
+ mask2 = mask2 | GET_MASK(F_CHTERMIM);
+ }
+ cancel_delayed_work_sync(&info->max_charging_time_work);
+ alert_userspace = true;
+ }
+ }
+ if (irq2 & GET_MASK(F_CHRCHGI)) {
+ dev_dbg(dev, "Recharge request\n");
+ ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
+ RT9455_CHARGE_ENABLE);
+ if (ret) {
+ dev_err(dev, "Failed to enable charging\n");
+ return ret;
+ }
+ if (mask2 & GET_MASK(F_CHTERMIM)) {
+ ret = regmap_field_write(
+ info->regmap_fields[F_CHTERMIM], 0x00);
+ if (ret) {
+ dev_err(dev, "Failed to unmask CHTERMI interrupt\n");
+ return ret;
+ }
+ /* Update MASK2 value, since CHTERMIM bit is cleared. */
+ mask2 = mask2 & ~GET_MASK(F_CHTERMIM);
+ }
+ if (!is_battery_absent) {
+ /*
+ * No need to check whether the charger is connected to
+ * power source when CHRCHGI is received, since CHRCHGI
+ * is not triggered if the charger is not connected to
+ * the power source.
+ */
+ queue_delayed_work(system_power_efficient_wq,
+ &info->max_charging_time_work,
+ RT9455_MAX_CHARGING_TIME * HZ);
+ alert_userspace = true;
+ }
+ }
+ if (irq2 & GET_MASK(F_CH32MI)) {
+ dev_err(dev, "Charger fault. 32 mins timeout occurred\n");
+ alert_userspace = true;
+ }
+ if (irq2 & GET_MASK(F_CHTREGI)) {
+ dev_warn(dev,
+ "Charger warning. Thermal regulation loop active\n");
+ alert_userspace = true;
+ }
+ if (irq2 & GET_MASK(F_CHMIVRI)) {
+ dev_dbg(dev,
+ "Charger warning. Input voltage MIVR loop active\n");
+ }
+
+ if (alert_userspace)
+ *_alert_userspace = alert_userspace;
+
+ return 0;
+}
+
+static int rt9455_irq_handler_check_irq3_register(struct rt9455_info *info,
+ bool *_alert_userspace)
+{
+ unsigned int irq3, mask3;
+ struct device *dev = &info->client->dev;
+ bool alert_userspace = false;
+ int ret;
+
+ ret = regmap_read(info->regmap, RT9455_REG_IRQ3, &irq3);
+ if (ret) {
+ dev_err(dev, "Failed to read IRQ3 register\n");
+ return ret;
+ }
+
+ ret = regmap_read(info->regmap, RT9455_REG_MASK3, &mask3);
+ if (ret) {
+ dev_err(dev, "Failed to read MASK3 register\n");
+ return ret;
+ }
+
+ if (irq3 & GET_MASK(F_BSTBUSOVI)) {
+ dev_err(dev, "Boost fault. Overvoltage input occurred\n");
+ alert_userspace = true;
+ }
+ if (irq3 & GET_MASK(F_BSTOLI)) {
+ dev_err(dev, "Boost fault. Overload\n");
+ alert_userspace = true;
+ }
+ if (irq3 & GET_MASK(F_BSTLOWVI)) {
+ dev_err(dev, "Boost fault. Battery voltage too low\n");
+ alert_userspace = true;
+ }
+ if (irq3 & GET_MASK(F_BST32SI)) {
+ dev_err(dev, "Boost fault. 32 seconds timeout occurred.\n");
+ alert_userspace = true;
+ }
+
+ if (alert_userspace) {
+ dev_info(dev, "Boost fault occurred, therefore the charger goes into charge mode\n");
+ ret = rt9455_set_voreg_before_charge_mode(info);
+ if (ret) {
+ dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+ return ret;
+ }
+ ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+ RT9455_CHARGE_MODE);
+ if (ret) {
+ dev_err(dev, "Failed to set charger in charge mode\n");
+ return ret;
+ }
+ *_alert_userspace = alert_userspace;
+ }
+
+ return 0;
+}
+
+static irqreturn_t rt9455_irq_handler_thread(int irq, void *data)
+{
+ struct rt9455_info *info = data;
+ struct device *dev;
+ bool alert_userspace = false;
+ bool is_battery_absent = false;
+ unsigned int status;
+ int ret;
+
+ if (!info)
+ return IRQ_NONE;
+
+ dev = &info->client->dev;
+
+ if (irq != info->client->irq) {
+ dev_err(dev, "Interrupt is not for RT9455 charger\n");
+ return IRQ_NONE;
+ }
+
+ ret = regmap_field_read(info->regmap_fields[F_STAT], &status);
+ if (ret) {
+ dev_err(dev, "Failed to read STAT bits\n");
+ return IRQ_HANDLED;
+ }
+ dev_dbg(dev, "Charger status is %d\n", status);
+
+ /*
+ * Each function that processes an IRQ register receives as output
+ * parameter alert_userspace pointer. alert_userspace is set to true
+ * in such a function only if an interrupt has occurred in the
+ * respective interrupt register. This way, it is avoided the following
+ * case: interrupt occurs only in IRQ1 register,
+ * rt9455_irq_handler_check_irq1_register() function sets to true
+ * alert_userspace, but rt9455_irq_handler_check_irq2_register()
+ * and rt9455_irq_handler_check_irq3_register() functions set to false
+ * alert_userspace and power_supply_changed() is never called.
+ */
+ ret = rt9455_irq_handler_check_irq1_register(info, &is_battery_absent,
+ &alert_userspace);
+ if (ret) {
+ dev_err(dev, "Failed to handle IRQ1 register\n");
+ return IRQ_HANDLED;
+ }
+
+ ret = rt9455_irq_handler_check_irq2_register(info, is_battery_absent,
+ &alert_userspace);
+ if (ret) {
+ dev_err(dev, "Failed to handle IRQ2 register\n");
+ return IRQ_HANDLED;
+ }
+
+ ret = rt9455_irq_handler_check_irq3_register(info, &alert_userspace);
+ if (ret) {
+ dev_err(dev, "Failed to handle IRQ3 register\n");
+ return IRQ_HANDLED;
+ }
+
+ if (alert_userspace) {
+ /*
+ * Sometimes, an interrupt occurs while rt9455_probe() function
+ * is executing and power_supply_register() is not yet called.
+ * Do not call power_supply_charged() in this case.
+ */
+ if (info->charger)
+ power_supply_changed(info->charger);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rt9455_discover_charger(struct rt9455_info *info, u32 *ichrg,
+ u32 *ieoc_percentage,
+ u32 *mivr, u32 *iaicr)
+{
+ struct device *dev = &info->client->dev;
+ int ret;
+
+ if (!dev->of_node && !ACPI_HANDLE(dev)) {
+ dev_err(dev, "No support for either device tree or ACPI\n");
+ return -EINVAL;
+ }
+ /*
+ * ICHRG, IEOC_PERCENTAGE, VOREG and boost output voltage are mandatory
+ * parameters.
+ */
+ ret = device_property_read_u32(dev, "richtek,output-charge-current",
+ ichrg);
+ if (ret) {
+ dev_err(dev, "Error: missing \"output-charge-current\" property\n");
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev, "richtek,end-of-charge-percentage",
+ ieoc_percentage);
+ if (ret) {
+ dev_err(dev, "Error: missing \"end-of-charge-percentage\" property\n");
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev,
+ "richtek,battery-regulation-voltage",
+ &info->voreg);
+ if (ret) {
+ dev_err(dev, "Error: missing \"battery-regulation-voltage\" property\n");
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev, "richtek,boost-output-voltage",
+ &info->boost_voltage);
+ if (ret) {
+ dev_err(dev, "Error: missing \"boost-output-voltage\" property\n");
+ return ret;
+ }
+
+ /*
+ * MIVR and IAICR are optional parameters. Do not return error if one of
+ * them is not present in ACPI table or device tree specification.
+ */
+ device_property_read_u32(dev, "richtek,min-input-voltage-regulation",
+ mivr);
+ device_property_read_u32(dev, "richtek,avg-input-current-regulation",
+ iaicr);
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+static int rt9455_usb_event_none(struct rt9455_info *info,
+ u8 opa_mode, u8 iaicr)
+{
+ struct device *dev = &info->client->dev;
+ int ret;
+
+ if (opa_mode == RT9455_BOOST_MODE) {
+ ret = rt9455_set_voreg_before_charge_mode(info);
+ if (ret) {
+ dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+ return ret;
+ }
+ /*
+ * If the charger is in boost mode, and it has received
+ * USB_EVENT_NONE, this means the consumer device powered by the
+ * charger is not connected anymore.
+ * In this case, the charger goes into charge mode.
+ */
+ dev_dbg(dev, "USB_EVENT_NONE received, therefore the charger goes into charge mode\n");
+ ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+ RT9455_CHARGE_MODE);
+ if (ret) {
+ dev_err(dev, "Failed to set charger in charge mode\n");
+ return NOTIFY_DONE;
+ }
+ }
+
+ dev_dbg(dev, "USB_EVENT_NONE received, therefore IAICR is set to its minimum value\n");
+ if (iaicr != RT9455_IAICR_100MA) {
+ ret = regmap_field_write(info->regmap_fields[F_IAICR],
+ RT9455_IAICR_100MA);
+ if (ret) {
+ dev_err(dev, "Failed to set IAICR value\n");
+ return NOTIFY_DONE;
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static int rt9455_usb_event_vbus(struct rt9455_info *info,
+ u8 opa_mode, u8 iaicr)
+{
+ struct device *dev = &info->client->dev;
+ int ret;
+
+ if (opa_mode == RT9455_BOOST_MODE) {
+ ret = rt9455_set_voreg_before_charge_mode(info);
+ if (ret) {
+ dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+ return ret;
+ }
+ /*
+ * If the charger is in boost mode, and it has received
+ * USB_EVENT_VBUS, this means the consumer device powered by the
+ * charger is not connected anymore.
+ * In this case, the charger goes into charge mode.
+ */
+ dev_dbg(dev, "USB_EVENT_VBUS received, therefore the charger goes into charge mode\n");
+ ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+ RT9455_CHARGE_MODE);
+ if (ret) {
+ dev_err(dev, "Failed to set charger in charge mode\n");
+ return NOTIFY_DONE;
+ }
+ }
+
+ dev_dbg(dev, "USB_EVENT_VBUS received, therefore IAICR is set to 500 mA\n");
+ if (iaicr != RT9455_IAICR_500MA) {
+ ret = regmap_field_write(info->regmap_fields[F_IAICR],
+ RT9455_IAICR_500MA);
+ if (ret) {
+ dev_err(dev, "Failed to set IAICR value\n");
+ return NOTIFY_DONE;
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static int rt9455_usb_event_id(struct rt9455_info *info,
+ u8 opa_mode, u8 iaicr)
+{
+ struct device *dev = &info->client->dev;
+ int ret;
+
+ if (opa_mode == RT9455_CHARGE_MODE) {
+ ret = rt9455_set_boost_voltage_before_boost_mode(info);
+ if (ret) {
+ dev_err(dev, "Failed to set boost output voltage before entering boost mode\n");
+ return ret;
+ }
+ /*
+ * If the charger is in charge mode, and it has received
+ * USB_EVENT_ID, this means a consumer device is connected and
+ * it should be powered by the charger.
+ * In this case, the charger goes into boost mode.
+ */
+ dev_dbg(dev, "USB_EVENT_ID received, therefore the charger goes into boost mode\n");
+ ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+ RT9455_BOOST_MODE);
+ if (ret) {
+ dev_err(dev, "Failed to set charger in boost mode\n");
+ return NOTIFY_DONE;
+ }
+ }
+
+ dev_dbg(dev, "USB_EVENT_ID received, therefore IAICR is set to its minimum value\n");
+ if (iaicr != RT9455_IAICR_100MA) {
+ ret = regmap_field_write(info->regmap_fields[F_IAICR],
+ RT9455_IAICR_100MA);
+ if (ret) {
+ dev_err(dev, "Failed to set IAICR value\n");
+ return NOTIFY_DONE;
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static int rt9455_usb_event_charger(struct rt9455_info *info,
+ u8 opa_mode, u8 iaicr)
+{
+ struct device *dev = &info->client->dev;
+ int ret;
+
+ if (opa_mode == RT9455_BOOST_MODE) {
+ ret = rt9455_set_voreg_before_charge_mode(info);
+ if (ret) {
+ dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+ return ret;
+ }
+ /*
+ * If the charger is in boost mode, and it has received
+ * USB_EVENT_CHARGER, this means the consumer device powered by
+ * the charger is not connected anymore.
+ * In this case, the charger goes into charge mode.
+ */
+ dev_dbg(dev, "USB_EVENT_CHARGER received, therefore the charger goes into charge mode\n");
+ ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+ RT9455_CHARGE_MODE);
+ if (ret) {
+ dev_err(dev, "Failed to set charger in charge mode\n");
+ return NOTIFY_DONE;
+ }
+ }
+
+ dev_dbg(dev, "USB_EVENT_CHARGER received, therefore IAICR is set to no current limit\n");
+ if (iaicr != RT9455_IAICR_NO_LIMIT) {
+ ret = regmap_field_write(info->regmap_fields[F_IAICR],
+ RT9455_IAICR_NO_LIMIT);
+ if (ret) {
+ dev_err(dev, "Failed to set IAICR value\n");
+ return NOTIFY_DONE;
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static int rt9455_usb_event(struct notifier_block *nb,
+ unsigned long event, void *power)
+{
+ struct rt9455_info *info = container_of(nb, struct rt9455_info, nb);
+ struct device *dev = &info->client->dev;
+ unsigned int opa_mode, iaicr;
+ int ret;
+
+ /*
+ * Determine whether the charger is in charge mode
+ * or in boost mode.
+ */
+ ret = regmap_field_read(info->regmap_fields[F_OPA_MODE],
+ &opa_mode);
+ if (ret) {
+ dev_err(dev, "Failed to read OPA_MODE value\n");
+ return NOTIFY_DONE;
+ }
+
+ ret = regmap_field_read(info->regmap_fields[F_IAICR],
+ &iaicr);
+ if (ret) {
+ dev_err(dev, "Failed to read IAICR value\n");
+ return NOTIFY_DONE;
+ }
+
+ dev_dbg(dev, "Received USB event %lu\n", event);
+ switch (event) {
+ case USB_EVENT_NONE:
+ return rt9455_usb_event_none(info, opa_mode, iaicr);
+ case USB_EVENT_VBUS:
+ return rt9455_usb_event_vbus(info, opa_mode, iaicr);
+ case USB_EVENT_ID:
+ return rt9455_usb_event_id(info, opa_mode, iaicr);
+ case USB_EVENT_CHARGER:
+ return rt9455_usb_event_charger(info, opa_mode, iaicr);
+ default:
+ dev_err(dev, "Unknown USB event\n");
+ }
+ return NOTIFY_DONE;
+}
+#endif
+
+static void rt9455_pwr_rdy_work_callback(struct work_struct *work)
+{
+ struct rt9455_info *info = container_of(work, struct rt9455_info,
+ pwr_rdy_work.work);
+ struct device *dev = &info->client->dev;
+ unsigned int pwr_rdy;
+ int ret;
+
+ ret = regmap_field_read(info->regmap_fields[F_PWR_RDY], &pwr_rdy);
+ if (ret) {
+ dev_err(dev, "Failed to read PWR_RDY bit\n");
+ return;
+ }
+ switch (pwr_rdy) {
+ case RT9455_PWR_FAULT:
+ dev_dbg(dev, "Charger disconnected from power source\n");
+ cancel_delayed_work_sync(&info->max_charging_time_work);
+ break;
+ case RT9455_PWR_GOOD:
+ dev_dbg(dev, "Charger connected to power source\n");
+ ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
+ RT9455_CHARGE_ENABLE);
+ if (ret) {
+ dev_err(dev, "Failed to enable charging\n");
+ return;
+ }
+ queue_delayed_work(system_power_efficient_wq,
+ &info->max_charging_time_work,
+ RT9455_MAX_CHARGING_TIME * HZ);
+ break;
+ }
+}
+
+static void rt9455_max_charging_time_work_callback(struct work_struct *work)
+{
+ struct rt9455_info *info = container_of(work, struct rt9455_info,
+ max_charging_time_work.work);
+ struct device *dev = &info->client->dev;
+ int ret;
+
+ dev_err(dev, "Battery has been charging for at least 6 hours and is not yet fully charged. Battery is dead, therefore charging is disabled.\n");
+ ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
+ RT9455_CHARGE_DISABLE);
+ if (ret)
+ dev_err(dev, "Failed to disable charging\n");
+}
+
+static void rt9455_batt_presence_work_callback(struct work_struct *work)
+{
+ struct rt9455_info *info = container_of(work, struct rt9455_info,
+ batt_presence_work.work);
+ struct device *dev = &info->client->dev;
+ unsigned int irq1, mask1;
+ int ret;
+
+ ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &irq1);
+ if (ret) {
+ dev_err(dev, "Failed to read IRQ1 register\n");
+ return;
+ }
+
+ /*
+ * If the battery is still absent, batt_presence_work is rescheduled.
+ * Otherwise, max_charging_time is scheduled.
+ */
+ if (irq1 & GET_MASK(F_BATAB)) {
+ queue_delayed_work(system_power_efficient_wq,
+ &info->batt_presence_work,
+ RT9455_BATT_PRESENCE_DELAY * HZ);
+ } else {
+ queue_delayed_work(system_power_efficient_wq,
+ &info->max_charging_time_work,
+ RT9455_MAX_CHARGING_TIME * HZ);
+
+ ret = regmap_read(info->regmap, RT9455_REG_MASK1, &mask1);
+ if (ret) {
+ dev_err(dev, "Failed to read MASK1 register\n");
+ return;
+ }
+
+ if (mask1 & GET_MASK(F_BATABM)) {
+ ret = regmap_field_write(info->regmap_fields[F_BATABM],
+ 0x00);
+ if (ret)
+ dev_err(dev, "Failed to unmask BATAB interrupt\n");
+ }
+ }
+}
+
+static const struct power_supply_desc rt9455_charger_desc = {
+ .name = RT9455_DRIVER_NAME,
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = rt9455_charger_properties,
+ .num_properties = ARRAY_SIZE(rt9455_charger_properties),
+ .get_property = rt9455_charger_get_property,
+};
+
+static bool rt9455_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT9455_REG_DEV_ID:
+ case RT9455_REG_IRQ1:
+ case RT9455_REG_IRQ2:
+ case RT9455_REG_IRQ3:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool rt9455_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT9455_REG_DEV_ID:
+ case RT9455_REG_CTRL5:
+ case RT9455_REG_CTRL6:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config rt9455_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = rt9455_is_writeable_reg,
+ .volatile_reg = rt9455_is_volatile_reg,
+ .max_register = RT9455_REG_MASK3,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int rt9455_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct device *dev = &client->dev;
+ struct rt9455_info *info;
+ struct power_supply_config rt9455_charger_config = {};
+ /*
+ * Mandatory device-specific data values. Also, VOREG and boost output
+ * voltage are mandatory values, but they are stored in rt9455_info
+ * structure.
+ */
+ u32 ichrg, ieoc_percentage;
+ /* Optional device-specific data values. */
+ u32 mivr = -1, iaicr = -1;
+ int i, ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+ return -ENODEV;
+ }
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->client = client;
+ i2c_set_clientdata(client, info);
+
+ info->regmap = devm_regmap_init_i2c(client,
+ &rt9455_regmap_config);
+ if (IS_ERR(info->regmap)) {
+ dev_err(dev, "Failed to initialize register map\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < F_MAX_FIELDS; i++) {
+ info->regmap_fields[i] =
+ devm_regmap_field_alloc(dev, info->regmap,
+ rt9455_reg_fields[i]);
+ if (IS_ERR(info->regmap_fields[i])) {
+ dev_err(dev,
+ "Failed to allocate regmap field = %d\n", i);
+ return PTR_ERR(info->regmap_fields[i]);
+ }
+ }
+
+ ret = rt9455_discover_charger(info, &ichrg, &ieoc_percentage,
+ &mivr, &iaicr);
+ if (ret) {
+ dev_err(dev, "Failed to discover charger\n");
+ return ret;
+ }
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+ info->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+ if (IS_ERR(info->usb_phy)) {
+ dev_err(dev, "Failed to get USB transceiver\n");
+ } else {
+ info->nb.notifier_call = rt9455_usb_event;
+ ret = usb_register_notifier(info->usb_phy, &info->nb);
+ if (ret) {
+ dev_err(dev, "Failed to register USB notifier\n");
+ /*
+ * If usb_register_notifier() fails, set notifier_call
+ * to NULL, to avoid calling usb_unregister_notifier().
+ */
+ info->nb.notifier_call = NULL;
+ }
+ }
+#endif
+
+ INIT_DEFERRABLE_WORK(&info->pwr_rdy_work, rt9455_pwr_rdy_work_callback);
+ INIT_DEFERRABLE_WORK(&info->max_charging_time_work,
+ rt9455_max_charging_time_work_callback);
+ INIT_DEFERRABLE_WORK(&info->batt_presence_work,
+ rt9455_batt_presence_work_callback);
+
+ rt9455_charger_config.of_node = dev->of_node;
+ rt9455_charger_config.drv_data = info;
+ rt9455_charger_config.supplied_to = rt9455_charger_supplied_to;
+ rt9455_charger_config.num_supplicants =
+ ARRAY_SIZE(rt9455_charger_supplied_to);
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ rt9455_irq_handler_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ RT9455_DRIVER_NAME, info);
+ if (ret) {
+ dev_err(dev, "Failed to register IRQ handler\n");
+ goto put_usb_notifier;
+ }
+
+ ret = rt9455_hw_init(info, ichrg, ieoc_percentage, mivr, iaicr);
+ if (ret) {
+ dev_err(dev, "Failed to set charger to its default values\n");
+ goto put_usb_notifier;
+ }
+
+ info->charger = devm_power_supply_register(dev, &rt9455_charger_desc,
+ &rt9455_charger_config);
+ if (IS_ERR(info->charger)) {
+ dev_err(dev, "Failed to register charger\n");
+ ret = PTR_ERR(info->charger);
+ goto put_usb_notifier;
+ }
+
+ return 0;
+
+put_usb_notifier:
+#if IS_ENABLED(CONFIG_USB_PHY)
+ if (info->nb.notifier_call) {
+ usb_unregister_notifier(info->usb_phy, &info->nb);
+ info->nb.notifier_call = NULL;
+ }
+#endif
+ return ret;
+}
+
+static int rt9455_remove(struct i2c_client *client)
+{
+ int ret;
+ struct rt9455_info *info = i2c_get_clientdata(client);
+
+ ret = rt9455_register_reset(info);
+ if (ret)
+ dev_err(&info->client->dev, "Failed to set charger to its default values\n");
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+ if (info->nb.notifier_call)
+ usb_unregister_notifier(info->usb_phy, &info->nb);
+#endif
+
+ cancel_delayed_work_sync(&info->pwr_rdy_work);
+ cancel_delayed_work_sync(&info->max_charging_time_work);
+ cancel_delayed_work_sync(&info->batt_presence_work);
+
+ return ret;
+}
+
+static const struct i2c_device_id rt9455_i2c_id_table[] = {
+ { RT9455_DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, rt9455_i2c_id_table);
+
+static const struct of_device_id rt9455_of_match[] = {
+ { .compatible = "richtek,rt9455", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rt9455_of_match);
+
+static const struct acpi_device_id rt9455_i2c_acpi_match[] = {
+ { "RT945500", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt9455_i2c_acpi_match);
+
+static struct i2c_driver rt9455_driver = {
+ .probe = rt9455_probe,
+ .remove = rt9455_remove,
+ .id_table = rt9455_i2c_id_table,
+ .driver = {
+ .name = RT9455_DRIVER_NAME,
+ .of_match_table = of_match_ptr(rt9455_of_match),
+ .acpi_match_table = ACPI_PTR(rt9455_i2c_acpi_match),
+ },
+};
+module_i2c_driver(rt9455_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anda-Maria Nicolae <anda-maria.nicolae@intel.com>");
+MODULE_ALIAS("i2c:rt9455-charger");
+MODULE_DESCRIPTION("Richtek RT9455 Charger Driver");
diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c
index de1178659d4b..d6226d68b574 100644
--- a/drivers/power/sbs-battery.c
+++ b/drivers/power/sbs-battery.c
@@ -28,6 +28,7 @@
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/of.h>
+#include <linux/stat.h>
#include <linux/power/sbs-battery.h>
@@ -170,6 +171,7 @@ struct sbs_info {
static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1];
+static bool force_load;
static int sbs_read_word_data(struct i2c_client *client, u8 address)
{
@@ -885,14 +887,17 @@ static int sbs_probe(struct i2c_client *client,
skip_gpio:
/*
- * Before we register, we need to make sure we can actually talk
+ * Before we register, we might need to make sure we can actually talk
* to the battery.
*/
- rc = sbs_read_word_data(client, sbs_data[REG_STATUS].addr);
- if (rc < 0) {
- dev_err(&client->dev, "%s: Failed to get device status\n",
- __func__);
- goto exit_psupply;
+ if (!force_load) {
+ rc = sbs_read_word_data(client, sbs_data[REG_STATUS].addr);
+
+ if (rc < 0) {
+ dev_err(&client->dev, "%s: Failed to get device status\n",
+ __func__);
+ goto exit_psupply;
+ }
}
chip->power_supply = power_supply_register(&client->dev, sbs_desc,
@@ -991,3 +996,7 @@ module_i2c_driver(sbs_battery_driver);
MODULE_DESCRIPTION("SBS battery monitor driver");
MODULE_LICENSE("GPL");
+
+module_param(force_load, bool, S_IRUSR | S_IRGRP | S_IROTH);
+MODULE_PARM_DESC(force_load,
+ "Attempt to load the driver even if no battery is connected");
diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c
index 0161bdabd5a3..db11ae6599f3 100644
--- a/drivers/power/wm831x_power.c
+++ b/drivers/power/wm831x_power.c
@@ -609,6 +609,7 @@ static int wm831x_power_probe(struct platform_device *pdev)
return ret;
err_bat_irq:
+ --i;
for (; i >= 0; i--) {
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
free_irq(irq, power);
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index fd243231620a..482b22ddc7b2 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -187,6 +187,7 @@ struct rapl_package {
};
struct rapl_defaults {
+ u8 floor_freq_reg_addr;
int (*check_unit)(struct rapl_package *rp, int cpu);
void (*set_floor_freq)(struct rapl_domain *rd, bool mode);
u64 (*compute_time_window)(struct rapl_package *rp, u64 val,
@@ -196,7 +197,8 @@ struct rapl_defaults {
static struct rapl_defaults *rapl_defaults;
/* Sideband MBI registers */
-#define IOSF_CPU_POWER_BUDGET_CTL (0x2)
+#define IOSF_CPU_POWER_BUDGET_CTL_BYT (0x2)
+#define IOSF_CPU_POWER_BUDGET_CTL_TNG (0xdf)
#define PACKAGE_PLN_INT_SAVED BIT(0)
#define MAX_PRIM_NAME (32)
@@ -358,7 +360,8 @@ static int set_domain_enable(struct powercap_zone *power_zone, bool mode)
get_online_cpus();
rapl_write_data_raw(rd, PL1_ENABLE, mode);
- rapl_defaults->set_floor_freq(rd, mode);
+ if (rapl_defaults->set_floor_freq)
+ rapl_defaults->set_floor_freq(rd, mode);
put_online_cpus();
return 0;
@@ -979,16 +982,22 @@ static void set_floor_freq_atom(struct rapl_domain *rd, bool enable)
static u32 power_ctrl_orig_val;
u32 mdata;
+ if (!rapl_defaults->floor_freq_reg_addr) {
+ pr_err("Invalid floor frequency config register\n");
+ return;
+ }
+
if (!power_ctrl_orig_val)
iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_PMC_READ,
- IOSF_CPU_POWER_BUDGET_CTL, &power_ctrl_orig_val);
+ rapl_defaults->floor_freq_reg_addr,
+ &power_ctrl_orig_val);
mdata = power_ctrl_orig_val;
if (enable) {
mdata &= ~(0x7f << 8);
mdata |= 1 << 8;
}
iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_PMC_WRITE,
- IOSF_CPU_POWER_BUDGET_CTL, mdata);
+ rapl_defaults->floor_freq_reg_addr, mdata);
}
static u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value,
@@ -1029,6 +1038,7 @@ static u64 rapl_compute_time_window_atom(struct rapl_package *rp, u64 value,
}
static const struct rapl_defaults rapl_defaults_core = {
+ .floor_freq_reg_addr = 0,
.check_unit = rapl_check_unit_core,
.set_floor_freq = set_floor_freq_default,
.compute_time_window = rapl_compute_time_window_core,
@@ -1041,12 +1051,34 @@ static const struct rapl_defaults rapl_defaults_hsw_server = {
.dram_domain_energy_unit = 15300,
};
-static const struct rapl_defaults rapl_defaults_atom = {
+static const struct rapl_defaults rapl_defaults_byt = {
+ .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_BYT,
+ .check_unit = rapl_check_unit_atom,
+ .set_floor_freq = set_floor_freq_atom,
+ .compute_time_window = rapl_compute_time_window_atom,
+};
+
+static const struct rapl_defaults rapl_defaults_tng = {
+ .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_TNG,
.check_unit = rapl_check_unit_atom,
.set_floor_freq = set_floor_freq_atom,
.compute_time_window = rapl_compute_time_window_atom,
};
+static const struct rapl_defaults rapl_defaults_ann = {
+ .floor_freq_reg_addr = 0,
+ .check_unit = rapl_check_unit_atom,
+ .set_floor_freq = NULL,
+ .compute_time_window = rapl_compute_time_window_atom,
+};
+
+static const struct rapl_defaults rapl_defaults_cht = {
+ .floor_freq_reg_addr = 0,
+ .check_unit = rapl_check_unit_atom,
+ .set_floor_freq = NULL,
+ .compute_time_window = rapl_compute_time_window_atom,
+};
+
#define RAPL_CPU(_model, _ops) { \
.vendor = X86_VENDOR_INTEL, \
.family = 6, \
@@ -1057,7 +1089,7 @@ static const struct rapl_defaults rapl_defaults_atom = {
static const struct x86_cpu_id rapl_ids[] __initconst = {
RAPL_CPU(0x2a, rapl_defaults_core),/* Sandy Bridge */
RAPL_CPU(0x2d, rapl_defaults_core),/* Sandy Bridge EP */
- RAPL_CPU(0x37, rapl_defaults_atom),/* Valleyview */
+ RAPL_CPU(0x37, rapl_defaults_byt),/* Valleyview */
RAPL_CPU(0x3a, rapl_defaults_core),/* Ivy Bridge */
RAPL_CPU(0x3c, rapl_defaults_core),/* Haswell */
RAPL_CPU(0x3d, rapl_defaults_core),/* Broadwell */
@@ -1065,10 +1097,11 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
RAPL_CPU(0x4f, rapl_defaults_hsw_server),/* Broadwell servers */
RAPL_CPU(0x45, rapl_defaults_core),/* Haswell ULT */
RAPL_CPU(0x4E, rapl_defaults_core),/* Skylake */
- RAPL_CPU(0x4C, rapl_defaults_atom),/* Braswell */
- RAPL_CPU(0x4A, rapl_defaults_atom),/* Tangier */
+ RAPL_CPU(0x4C, rapl_defaults_cht),/* Braswell/Cherryview */
+ RAPL_CPU(0x4A, rapl_defaults_tng),/* Tangier */
RAPL_CPU(0x56, rapl_defaults_core),/* Future Xeon */
- RAPL_CPU(0x5A, rapl_defaults_atom),/* Annidale */
+ RAPL_CPU(0x5A, rapl_defaults_ann),/* Annidale */
+ RAPL_CPU(0x57, rapl_defaults_hsw_server),/* Knights Landing */
{}
};
MODULE_DEVICE_TABLE(x86cpu, rapl_ids);
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index ba34c7d89042..3a7769fe53de 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -223,13 +223,16 @@ void *pwm_get_chip_data(struct pwm_device *pwm)
EXPORT_SYMBOL_GPL(pwm_get_chip_data);
/**
- * pwmchip_add() - register a new PWM chip
+ * pwmchip_add_with_polarity() - register a new PWM chip
* @chip: the PWM chip to add
+ * @polarity: initial polarity of PWM channels
*
* Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
- * will be used.
+ * will be used. The initial polarity for all channels is specified by the
+ * @polarity parameter.
*/
-int pwmchip_add(struct pwm_chip *chip)
+int pwmchip_add_with_polarity(struct pwm_chip *chip,
+ enum pwm_polarity polarity)
{
struct pwm_device *pwm;
unsigned int i;
@@ -259,6 +262,7 @@ int pwmchip_add(struct pwm_chip *chip)
pwm->chip = chip;
pwm->pwm = chip->base + i;
pwm->hwpwm = i;
+ pwm->polarity = polarity;
radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
}
@@ -279,6 +283,19 @@ out:
mutex_unlock(&pwm_lock);
return ret;
}
+EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity);
+
+/**
+ * pwmchip_add() - register a new PWM chip
+ * @chip: the PWM chip to add
+ *
+ * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
+ * will be used. The initial polarity for all channels is normal.
+ */
+int pwmchip_add(struct pwm_chip *chip)
+{
+ return pwmchip_add_with_polarity(chip, PWM_POLARITY_NORMAL);
+}
EXPORT_SYMBOL_GPL(pwmchip_add);
/**
@@ -586,6 +603,23 @@ void pwm_add_table(struct pwm_lookup *table, size_t num)
}
/**
+ * pwm_remove_table() - unregister PWM device consumers
+ * @table: array of consumers to unregister
+ * @num: number of consumers in table
+ */
+void pwm_remove_table(struct pwm_lookup *table, size_t num)
+{
+ mutex_lock(&pwm_lookup_lock);
+
+ while (num--) {
+ list_del(&table->list);
+ table++;
+ }
+
+ mutex_unlock(&pwm_lookup_lock);
+}
+
+/**
* pwm_get() - look up and request a PWM device
* @dev: device for PWM consumer
* @con_id: consumer name
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index d3c22de9ee47..a947c9095d9d 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -8,9 +8,11 @@
*/
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
@@ -21,6 +23,7 @@
#define PWM_ENA 0x04
#define PWM_DIS 0x08
#define PWM_SR 0x0C
+#define PWM_ISR 0x1C
/* Bit field in SR */
#define PWM_SR_ALL_CH_ON 0x0F
@@ -60,6 +63,9 @@ struct atmel_pwm_chip {
struct clk *clk;
void __iomem *base;
+ unsigned int updated_pwms;
+ struct mutex isr_lock; /* ISR is cleared when read, ensure only one thread does that */
+
void (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
unsigned long dty, unsigned long prd);
};
@@ -144,6 +150,10 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
atmel_pwm->config(chip, pwm, dty, prd);
+ mutex_lock(&atmel_pwm->isr_lock);
+ atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
+ atmel_pwm->updated_pwms &= ~(1 << pwm->hwpwm);
+ mutex_unlock(&atmel_pwm->isr_lock);
clk_disable(atmel_pwm->clk);
return ret;
@@ -155,24 +165,25 @@ static void atmel_pwm_config_v1(struct pwm_chip *chip, struct pwm_device *pwm,
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
unsigned int val;
- if (test_bit(PWMF_ENABLED, &pwm->flags)) {
- /*
- * If the PWM channel is enabled, using the update register,
- * it needs to set bit 10 of CMR to 0
- */
- atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CUPD, dty);
- val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
- val &= ~PWM_CMR_UPD_CDTY;
- atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
- } else {
- /*
- * If the PWM channel is disabled, write value to duty and
- * period registers directly.
- */
- atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CDTY, dty);
- atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CPRD, prd);
- }
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CUPD, dty);
+
+ val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
+ val &= ~PWM_CMR_UPD_CDTY;
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
+
+ /*
+ * If the PWM channel is enabled, only update CDTY by using the update
+ * register, it needs to set bit 10 of CMR to 0
+ */
+ if (test_bit(PWMF_ENABLED, &pwm->flags))
+ return;
+ /*
+ * If the PWM channel is disabled, write value to duty and period
+ * registers directly.
+ */
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CDTY, dty);
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CPRD, prd);
}
static void atmel_pwm_config_v2(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -242,7 +253,22 @@ static int atmel_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+ unsigned long timeout = jiffies + 2 * HZ;
+
+ /*
+ * Wait for at least a complete period to have passed before disabling a
+ * channel to be sure that CDTY has been updated
+ */
+ mutex_lock(&atmel_pwm->isr_lock);
+ atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
+
+ while (!(atmel_pwm->updated_pwms & (1 << pwm->hwpwm)) &&
+ time_before(jiffies, timeout)) {
+ usleep_range(10, 100);
+ atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
+ }
+ mutex_unlock(&atmel_pwm->isr_lock);
atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << pwm->hwpwm);
clk_disable(atmel_pwm->clk);
@@ -357,6 +383,8 @@ static int atmel_pwm_probe(struct platform_device *pdev)
atmel_pwm->chip.npwm = 4;
atmel_pwm->chip.can_sleep = true;
atmel_pwm->config = data->config;
+ atmel_pwm->updated_pwms = 0;
+ mutex_init(&atmel_pwm->isr_lock);
ret = pwmchip_add(&atmel_pwm->chip);
if (ret < 0) {
@@ -378,6 +406,7 @@ static int atmel_pwm_remove(struct platform_device *pdev)
struct atmel_pwm_chip *atmel_pwm = platform_get_drvdata(pdev);
clk_unprepare(atmel_pwm->clk);
+ mutex_destroy(&atmel_pwm->isr_lock);
return pwmchip_remove(&atmel_pwm->chip);
}
diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c
index 02bc048892a9..7af8fea2dc5b 100644
--- a/drivers/pwm/pwm-bcm-kona.c
+++ b/drivers/pwm/pwm-bcm-kona.c
@@ -266,18 +266,15 @@ static int kona_pwmc_probe(struct platform_device *pdev)
return ret;
}
- /* Set smooth mode, push/pull, and normal polarity for all channels */
- for (chan = 0; chan < kp->chip.npwm; chan++) {
- value |= (1 << PWM_CONTROL_SMOOTH_SHIFT(chan));
+ /* Set push/pull for all channels */
+ for (chan = 0; chan < kp->chip.npwm; chan++)
value |= (1 << PWM_CONTROL_TYPE_SHIFT(chan));
- value |= (1 << PWM_CONTROL_POLARITY_SHIFT(chan));
- }
writel(value, kp->base + PWM_CONTROL_OFFSET);
clk_disable_unprepare(kp->clk);
- ret = pwmchip_add(&kp->chip);
+ ret = pwmchip_add_with_polarity(&kp->chip, PWM_POLARITY_INVERSED);
if (ret < 0)
dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c
index 476171a768d6..8a029f9bc18c 100644
--- a/drivers/pwm/pwm-img.c
+++ b/drivers/pwm/pwm-img.c
@@ -16,6 +16,7 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/regmap.h>
@@ -38,7 +39,22 @@
#define PERIP_PWM_PDM_CONTROL_CH_MASK 0x1
#define PERIP_PWM_PDM_CONTROL_CH_SHIFT(ch) ((ch) * 4)
-#define MAX_TMBASE_STEPS 65536
+/*
+ * PWM period is specified with a timebase register,
+ * in number of step periods. The PWM duty cycle is also
+ * specified in step periods, in the [0, $timebase] range.
+ * In other words, the timebase imposes the duty cycle
+ * resolution. Therefore, let's constraint the timebase to
+ * a minimum value to allow a sane range of duty cycle values.
+ * Imposing a minimum timebase, will impose a maximum PWM frequency.
+ *
+ * The value chosen is completely arbitrary.
+ */
+#define MIN_TMBASE_STEPS 16
+
+struct img_pwm_soc_data {
+ u32 max_timebase;
+};
struct img_pwm_chip {
struct device *dev;
@@ -47,6 +63,9 @@ struct img_pwm_chip {
struct clk *sys_clk;
void __iomem *base;
struct regmap *periph_regs;
+ int max_period_ns;
+ int min_period_ns;
+ const struct img_pwm_soc_data *data;
};
static inline struct img_pwm_chip *to_img_pwm_chip(struct pwm_chip *chip)
@@ -72,24 +91,31 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
u32 val, div, duty, timebase;
unsigned long mul, output_clk_hz, input_clk_hz;
struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip);
+ unsigned int max_timebase = pwm_chip->data->max_timebase;
+
+ if (period_ns < pwm_chip->min_period_ns ||
+ period_ns > pwm_chip->max_period_ns) {
+ dev_err(chip->dev, "configured period not in range\n");
+ return -ERANGE;
+ }
input_clk_hz = clk_get_rate(pwm_chip->pwm_clk);
output_clk_hz = DIV_ROUND_UP(NSEC_PER_SEC, period_ns);
mul = DIV_ROUND_UP(input_clk_hz, output_clk_hz);
- if (mul <= MAX_TMBASE_STEPS) {
+ if (mul <= max_timebase) {
div = PWM_CTRL_CFG_NO_SUB_DIV;
timebase = DIV_ROUND_UP(mul, 1);
- } else if (mul <= MAX_TMBASE_STEPS * 8) {
+ } else if (mul <= max_timebase * 8) {
div = PWM_CTRL_CFG_SUB_DIV0;
timebase = DIV_ROUND_UP(mul, 8);
- } else if (mul <= MAX_TMBASE_STEPS * 64) {
+ } else if (mul <= max_timebase * 64) {
div = PWM_CTRL_CFG_SUB_DIV1;
timebase = DIV_ROUND_UP(mul, 64);
- } else if (mul <= MAX_TMBASE_STEPS * 512) {
+ } else if (mul <= max_timebase * 512) {
div = PWM_CTRL_CFG_SUB_DIV0_DIV1;
timebase = DIV_ROUND_UP(mul, 512);
- } else if (mul > MAX_TMBASE_STEPS * 512) {
+ } else if (mul > max_timebase * 512) {
dev_err(chip->dev,
"failed to configure timebase steps/divider value\n");
return -EINVAL;
@@ -143,11 +169,27 @@ static const struct pwm_ops img_pwm_ops = {
.owner = THIS_MODULE,
};
+static const struct img_pwm_soc_data pistachio_pwm = {
+ .max_timebase = 255,
+};
+
+static const struct of_device_id img_pwm_of_match[] = {
+ {
+ .compatible = "img,pistachio-pwm",
+ .data = &pistachio_pwm,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, img_pwm_of_match);
+
static int img_pwm_probe(struct platform_device *pdev)
{
int ret;
+ u64 val;
+ unsigned long clk_rate;
struct resource *res;
struct img_pwm_chip *pwm;
+ const struct of_device_id *of_dev_id;
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
if (!pwm)
@@ -160,6 +202,11 @@ static int img_pwm_probe(struct platform_device *pdev)
if (IS_ERR(pwm->base))
return PTR_ERR(pwm->base);
+ of_dev_id = of_match_device(img_pwm_of_match, &pdev->dev);
+ if (!of_dev_id)
+ return -ENODEV;
+ pwm->data = of_dev_id->data;
+
pwm->periph_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"img,cr-periph");
if (IS_ERR(pwm->periph_regs))
@@ -189,6 +236,17 @@ static int img_pwm_probe(struct platform_device *pdev)
goto disable_sysclk;
}
+ clk_rate = clk_get_rate(pwm->pwm_clk);
+
+ /* The maximum input clock divider is 512 */
+ val = (u64)NSEC_PER_SEC * 512 * pwm->data->max_timebase;
+ do_div(val, clk_rate);
+ pwm->max_period_ns = val;
+
+ val = (u64)NSEC_PER_SEC * MIN_TMBASE_STEPS;
+ do_div(val, clk_rate);
+ pwm->min_period_ns = val;
+
pwm->chip.dev = &pdev->dev;
pwm->chip.ops = &img_pwm_ops;
pwm->chip.base = -1;
@@ -228,12 +286,6 @@ static int img_pwm_remove(struct platform_device *pdev)
return pwmchip_remove(&pwm_chip->chip);
}
-static const struct of_device_id img_pwm_of_match[] = {
- { .compatible = "img,pistachio-pwm", },
- { }
-};
-MODULE_DEVICE_TABLE(of, img_pwm_of_match);
-
static struct platform_driver img_pwm_driver = {
.driver = {
.name = "img-pwm",
diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c
index cf20d2beacdd..45042c1b2046 100644
--- a/drivers/pwm/pwm-lpss-pci.c
+++ b/drivers/pwm/pwm-lpss-pci.c
@@ -44,8 +44,10 @@ static void pwm_lpss_remove_pci(struct pci_dev *pdev)
}
static const struct pci_device_id pwm_lpss_pci_ids[] = {
+ { PCI_VDEVICE(INTEL, 0x0ac8), (unsigned long)&pwm_lpss_bsw_info},
{ PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info},
{ PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&pwm_lpss_byt_info},
+ { PCI_VDEVICE(INTEL, 0x1ac8), (unsigned long)&pwm_lpss_bsw_info},
{ PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info},
{ PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info},
{ },
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c
index ff201e1b9219..ada2d326dc3e 100644
--- a/drivers/pwm/pwm-samsung.c
+++ b/drivers/pwm/pwm-samsung.c
@@ -456,6 +456,7 @@ static const struct of_device_id samsung_pwm_matches[] = {
{ .compatible = "samsung,exynos4210-pwm", .data = &s5p64x0_variant },
{},
};
+MODULE_DEVICE_TABLE(of, samsung_pwm_matches);
static int pwm_samsung_parse_dt(struct samsung_pwm_chip *chip)
{
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 47a1b2ea76c4..d6a126c17c03 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -83,7 +83,7 @@ static u16 rio_destid_alloc(struct rio_net *net)
* @destid: destID to reserve
*
* Tries to reserve the specified destID.
- * Returns 0 if successfull.
+ * Returns 0 if successful.
*/
static int rio_destid_reserve(struct rio_net *net, u16 destid)
{
diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c
index c3d15427adc7..b100a63ff3b3 100644
--- a/drivers/regulator/88pm8607.c
+++ b/drivers/regulator/88pm8607.c
@@ -404,7 +404,7 @@ static int pm8607_regulator_probe(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id pm8607_regulator_driver_ids[] = {
+static const struct platform_device_id pm8607_regulator_driver_ids[] = {
{
.name = "88pm860x-regulator",
.driver_data = 0,
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index a6f116aa5235..bef3bde6971b 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -178,6 +178,16 @@ config REGULATOR_DA9055
This driver can also be built as a module. If so, the module
will be called da9055-regulator.
+config REGULATOR_DA9062
+ tristate "Dialog Semiconductor DA9062 regulators"
+ depends on MFD_DA9062
+ help
+ Say y here to support the BUCKs and LDOs regulators found on
+ DA9062 PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called da9062-regulator.
+
config REGULATOR_DA9063
tristate "Dialog Semiconductor DA9063 regulators"
depends on MFD_DA9063
@@ -233,7 +243,7 @@ config REGULATOR_FAN53555
config REGULATOR_GPIO
tristate "GPIO regulator support"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
This driver provides support for regulators that can be
controlled via gpios.
@@ -512,6 +522,17 @@ config REGULATOR_QCOM_RPM
Qualcomm RPM as a module. The module will be named
"qcom_rpm-regulator".
+config REGULATOR_QCOM_SPMI
+ tristate "Qualcomm SPMI regulator driver"
+ depends on SPMI || COMPILE_TEST
+ help
+ If you say yes to this option, support will be included for the
+ regulators found in Qualcomm SPMI PMICs.
+
+ Say M here if you want to include support for the regulators on the
+ Qualcomm SPMI PMICs as a module. The module will be named
+ "qcom_spmi-regulator".
+
config REGULATOR_RC5T583
tristate "RICOH RC5T583 Power regulators"
depends on MFD_RC5T583
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 2c4da15e1545..91bf76267404 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
+obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o
obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o
obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o
obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o
@@ -61,6 +62,7 @@ obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c
index a1d07d347c20..90941632efa9 100644
--- a/drivers/regulator/arizona-ldo1.c
+++ b/drivers/regulator/arizona-ldo1.c
@@ -178,6 +178,16 @@ static const struct regulator_init_data arizona_ldo1_default = {
.num_consumer_supplies = 1,
};
+static const struct regulator_init_data arizona_ldo1_wm5110 = {
+ .constraints = {
+ .min_uV = 1175000,
+ .max_uV = 1200000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+};
+
static int arizona_ldo1_of_get_pdata(struct arizona *arizona,
struct regulator_config *config,
const struct regulator_desc *desc)
@@ -243,6 +253,11 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
desc = &arizona_ldo1_hc;
ldo1->init_data = arizona_ldo1_dvfs;
break;
+ case WM5110:
+ case WM8280:
+ desc = &arizona_ldo1;
+ ldo1->init_data = arizona_ldo1_wm5110;
+ break;
default:
desc = &arizona_ldo1;
ldo1->init_data = arizona_ldo1_default;
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index e4331f5e5d7d..646829132b59 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -27,20 +27,24 @@
#define AXP20X_IO_ENABLED 0x03
#define AXP20X_IO_DISABLED 0x07
+#define AXP22X_IO_ENABLED 0x04
+#define AXP22X_IO_DISABLED 0x03
+
#define AXP20X_WORKMODE_DCDC2_MASK BIT(2)
#define AXP20X_WORKMODE_DCDC3_MASK BIT(1)
+#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT(x)
#define AXP20X_FREQ_DCDC_MASK 0x0f
-#define AXP20X_DESC_IO(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \
- _ereg, _emask, _enable_val, _disable_val) \
- [AXP20X_##_id] = { \
+#define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask, _enable_val, _disable_val) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
.n_voltages = (((_max) - (_min)) / (_step) + 1), \
.owner = THIS_MODULE, \
.min_uV = (_min) * 1000, \
@@ -54,15 +58,15 @@
.ops = &axp20x_ops, \
}
-#define AXP20X_DESC(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \
- _ereg, _emask) \
- [AXP20X_##_id] = { \
+#define AXP_DESC(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
.n_voltages = (((_max) - (_min)) / (_step) + 1), \
.owner = THIS_MODULE, \
.min_uV = (_min) * 1000, \
@@ -74,29 +78,49 @@
.ops = &axp20x_ops, \
}
-#define AXP20X_DESC_FIXED(_id, _match, _supply, _volt) \
- [AXP20X_##_id] = { \
+#define AXP_DESC_SW(_family, _id, _match, _supply, _min, _max, _step, _vreg, \
+ _vmask, _ereg, _emask) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
+ .n_voltages = (((_max) - (_min)) / (_step) + 1), \
+ .owner = THIS_MODULE, \
+ .min_uV = (_min) * 1000, \
+ .uV_step = (_step) * 1000, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .ops = &axp20x_ops_sw, \
+ }
+
+#define AXP_DESC_FIXED(_family, _id, _match, _supply, _volt) \
+ [_family##_##_id] = { \
+ .name = #_id, \
+ .supply_name = (_supply), \
+ .of_match = of_match_ptr(_match), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _family##_##_id, \
.n_voltages = 1, \
.owner = THIS_MODULE, \
.min_uV = (_volt) * 1000, \
.ops = &axp20x_ops_fixed \
}
-#define AXP20X_DESC_TABLE(_id, _match, _supply, _table, _vreg, _vmask, _ereg, \
- _emask) \
- [AXP20X_##_id] = { \
+#define AXP_DESC_TABLE(_family, _id, _match, _supply, _table, _vreg, _vmask, \
+ _ereg, _emask) \
+ [_family##_##_id] = { \
.name = #_id, \
.supply_name = (_supply), \
.of_match = of_match_ptr(_match), \
.regulators_node = of_match_ptr("regulators"), \
.type = REGULATOR_VOLTAGE, \
- .id = AXP20X_##_id, \
+ .id = _family##_##_id, \
.n_voltages = ARRAY_SIZE(_table), \
.owner = THIS_MODULE, \
.vsel_reg = (_vreg), \
@@ -135,38 +159,118 @@ static struct regulator_ops axp20x_ops = {
.is_enabled = regulator_is_enabled_regmap,
};
+static struct regulator_ops axp20x_ops_sw = {
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
static const struct regulator_desc axp20x_regulators[] = {
- AXP20X_DESC(DCDC2, "dcdc2", "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT,
- 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
- AXP20X_DESC(DCDC3, "dcdc3", "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT,
- 0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
- AXP20X_DESC_FIXED(LDO1, "ldo1", "acin", 1300),
- AXP20X_DESC(LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
- AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
- AXP20X_DESC(LDO3, "ldo3", "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT,
- 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
- AXP20X_DESC_TABLE(LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
- AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
- AXP20X_DESC_IO(LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
- AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
- AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
+ AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25,
+ AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
+ AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25,
+ AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
+ AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300),
+ AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
+ AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
+ AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25,
+ AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
+ AXP_DESC_TABLE(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
+ AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
+ AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
+ AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
+ AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
+};
+
+static const struct regulator_desc axp22x_regulators[] = {
+ AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
+ AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)),
+ AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20,
+ AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)),
+ AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20,
+ AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+ AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20,
+ AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+ AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50,
+ AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(4)),
+ /* secondary switchable output of DCDC1 */
+ AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", "dcdc1", 1600, 3400, 100,
+ AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(7)),
+ /* LDO regulator internally chained to DCDC5 */
+ AXP_DESC(AXP22X, DC5LDO, "dc5ldo", "dcdc5", 700, 1400, 100,
+ AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)),
+ AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)),
+ AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)),
+ AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
+ AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
+ AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
+ AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)),
+ AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+ AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
+ AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
+ AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+ AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+ AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
+ AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+ AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 1800, 3300, 100,
+ AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 1800, 3300, 100,
+ AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+ AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+ AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000),
};
static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ u32 min, max, def, step;
+
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ min = 750;
+ max = 1875;
+ def = 1500;
+ step = 75;
+ break;
+ case AXP221_ID:
+ min = 1800;
+ max = 4050;
+ def = 3000;
+ step = 150;
+ break;
+ default:
+ dev_err(&pdev->dev,
+ "Setting DCDC frequency for unsupported AXP variant\n");
+ return -EINVAL;
+ }
+
+ if (dcdcfreq == 0)
+ dcdcfreq = def;
- if (dcdcfreq < 750) {
- dcdcfreq = 750;
- dev_warn(&pdev->dev, "DCDC frequency too low. Set to 750kHz\n");
+ if (dcdcfreq < min) {
+ dcdcfreq = min;
+ dev_warn(&pdev->dev, "DCDC frequency too low. Set to %ukHz\n",
+ min);
}
- if (dcdcfreq > 1875) {
- dcdcfreq = 1875;
- dev_warn(&pdev->dev, "DCDC frequency too high. Set to 1875kHz\n");
+ if (dcdcfreq > max) {
+ dcdcfreq = max;
+ dev_warn(&pdev->dev, "DCDC frequency too high. Set to %ukHz\n",
+ max);
}
- dcdcfreq = (dcdcfreq - 750) / 75;
+ dcdcfreq = (dcdcfreq - min) / step;
return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ,
AXP20X_FREQ_DCDC_MASK, dcdcfreq);
@@ -176,7 +280,7 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
{
struct device_node *np, *regulators;
int ret;
- u32 dcdcfreq;
+ u32 dcdcfreq = 0;
np = of_node_get(pdev->dev.parent->of_node);
if (!np)
@@ -186,7 +290,6 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
if (!regulators) {
dev_warn(&pdev->dev, "regulators node not found\n");
} else {
- dcdcfreq = 1500;
of_property_read_u32(regulators, "x-powers,dcdc-freq", &dcdcfreq);
ret = axp20x_set_dcdc_freq(pdev, dcdcfreq);
if (ret < 0) {
@@ -202,15 +305,35 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode)
{
- unsigned int mask = AXP20X_WORKMODE_DCDC2_MASK;
+ struct axp20x_dev *axp20x = rdev_get_drvdata(rdev);
+ unsigned int mask;
- if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
- return -EINVAL;
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
+ return -EINVAL;
+
+ mask = AXP20X_WORKMODE_DCDC2_MASK;
+ if (id == AXP20X_DCDC3)
+ mask = AXP20X_WORKMODE_DCDC3_MASK;
+
+ workmode <<= ffs(mask) - 1;
+ break;
- if (id == AXP20X_DCDC3)
- mask = AXP20X_WORKMODE_DCDC3_MASK;
+ case AXP221_ID:
+ if (id < AXP22X_DCDC1 || id > AXP22X_DCDC5)
+ return -EINVAL;
- workmode <<= ffs(mask) - 1;
+ mask = AXP22X_WORKMODE_DCDCX_MASK(id - AXP22X_DCDC1);
+ workmode <<= id - AXP22X_DCDC1;
+ break;
+
+ default:
+ /* should not happen */
+ WARN_ON(1);
+ return -EINVAL;
+ }
return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode);
}
@@ -219,22 +342,40 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
{
struct regulator_dev *rdev;
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ const struct regulator_desc *regulators;
struct regulator_config config = {
.dev = pdev->dev.parent,
.regmap = axp20x->regmap,
+ .driver_data = axp20x,
};
- int ret, i;
+ int ret, i, nregulators;
u32 workmode;
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ regulators = axp20x_regulators;
+ nregulators = AXP20X_REG_ID_MAX;
+ break;
+ case AXP221_ID:
+ regulators = axp22x_regulators;
+ nregulators = AXP22X_REG_ID_MAX;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
+ axp20x->variant);
+ return -EINVAL;
+ }
+
/* This only sets the dcdc freq. Ignore any errors */
axp20x_regulator_parse_dt(pdev);
- for (i = 0; i < AXP20X_REG_ID_MAX; i++) {
- rdev = devm_regulator_register(&pdev->dev, &axp20x_regulators[i],
+ for (i = 0; i < nregulators; i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i],
&config);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "Failed to register %s\n",
- axp20x_regulators[i].name);
+ regulators[i].name);
return PTR_ERR(rdev);
}
@@ -245,7 +386,7 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
if (!ret) {
if (axp20x_set_dcdc_workmode(rdev, i, workmode))
dev_err(&pdev->dev, "Failed to set workmode on %s\n",
- axp20x_regulators[i].name);
+ rdev->desc->name);
}
}
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 443eaab933fc..c9f72019bd68 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -678,6 +678,8 @@ static int drms_uA_update(struct regulator_dev *rdev)
list_for_each_entry(sibling, &rdev->consumer_list, list)
current_uA += sibling->uA_load;
+ current_uA += rdev->constraints->system_load;
+
if (rdev->desc->ops->set_load) {
/* set the optimum mode for our new total regulator load */
err = rdev->desc->ops->set_load(rdev, current_uA);
@@ -779,59 +781,64 @@ static int suspend_prepare(struct regulator_dev *rdev, suspend_state_t state)
static void print_constraints(struct regulator_dev *rdev)
{
struct regulation_constraints *constraints = rdev->constraints;
- char buf[80] = "";
+ char buf[160] = "";
+ size_t len = sizeof(buf) - 1;
int count = 0;
int ret;
if (constraints->min_uV && constraints->max_uV) {
if (constraints->min_uV == constraints->max_uV)
- count += sprintf(buf + count, "%d mV ",
- constraints->min_uV / 1000);
+ count += scnprintf(buf + count, len - count, "%d mV ",
+ constraints->min_uV / 1000);
else
- count += sprintf(buf + count, "%d <--> %d mV ",
- constraints->min_uV / 1000,
- constraints->max_uV / 1000);
+ count += scnprintf(buf + count, len - count,
+ "%d <--> %d mV ",
+ constraints->min_uV / 1000,
+ constraints->max_uV / 1000);
}
if (!constraints->min_uV ||
constraints->min_uV != constraints->max_uV) {
ret = _regulator_get_voltage(rdev);
if (ret > 0)
- count += sprintf(buf + count, "at %d mV ", ret / 1000);
+ count += scnprintf(buf + count, len - count,
+ "at %d mV ", ret / 1000);
}
if (constraints->uV_offset)
- count += sprintf(buf, "%dmV offset ",
- constraints->uV_offset / 1000);
+ count += scnprintf(buf + count, len - count, "%dmV offset ",
+ constraints->uV_offset / 1000);
if (constraints->min_uA && constraints->max_uA) {
if (constraints->min_uA == constraints->max_uA)
- count += sprintf(buf + count, "%d mA ",
- constraints->min_uA / 1000);
+ count += scnprintf(buf + count, len - count, "%d mA ",
+ constraints->min_uA / 1000);
else
- count += sprintf(buf + count, "%d <--> %d mA ",
- constraints->min_uA / 1000,
- constraints->max_uA / 1000);
+ count += scnprintf(buf + count, len - count,
+ "%d <--> %d mA ",
+ constraints->min_uA / 1000,
+ constraints->max_uA / 1000);
}
if (!constraints->min_uA ||
constraints->min_uA != constraints->max_uA) {
ret = _regulator_get_current_limit(rdev);
if (ret > 0)
- count += sprintf(buf + count, "at %d mA ", ret / 1000);
+ count += scnprintf(buf + count, len - count,
+ "at %d mA ", ret / 1000);
}
if (constraints->valid_modes_mask & REGULATOR_MODE_FAST)
- count += sprintf(buf + count, "fast ");
+ count += scnprintf(buf + count, len - count, "fast ");
if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL)
- count += sprintf(buf + count, "normal ");
+ count += scnprintf(buf + count, len - count, "normal ");
if (constraints->valid_modes_mask & REGULATOR_MODE_IDLE)
- count += sprintf(buf + count, "idle ");
+ count += scnprintf(buf + count, len - count, "idle ");
if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
- count += sprintf(buf + count, "standby");
+ count += scnprintf(buf + count, len - count, "standby");
if (!count)
- sprintf(buf, "no parameters");
+ scnprintf(buf, len, "no parameters");
rdev_dbg(rdev, "%s\n", buf);
@@ -1006,6 +1013,15 @@ static int set_machine_constraints(struct regulator_dev *rdev,
if (ret != 0)
goto out;
+ if (rdev->constraints->ilim_uA && ops->set_input_current_limit) {
+ ret = ops->set_input_current_limit(rdev,
+ rdev->constraints->ilim_uA);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set input limit\n");
+ goto out;
+ }
+ }
+
/* do we need to setup our suspend state */
if (rdev->constraints->initial_state) {
ret = suspend_prepare(rdev, rdev->constraints->initial_state);
@@ -1049,6 +1065,22 @@ static int set_machine_constraints(struct regulator_dev *rdev,
}
}
+ if (rdev->constraints->pull_down && ops->set_pull_down) {
+ ret = ops->set_pull_down(rdev);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set pull down\n");
+ goto out;
+ }
+ }
+
+ if (rdev->constraints->soft_start && ops->set_soft_start) {
+ ret = ops->set_soft_start(rdev);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set soft start\n");
+ goto out;
+ }
+ }
+
print_constraints(rdev);
return 0;
out:
@@ -1192,10 +1224,10 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
if (regulator->supply_name == NULL)
goto overflow_err;
- err = sysfs_create_link(&rdev->dev.kobj, &dev->kobj,
+ err = sysfs_create_link_nowarn(&rdev->dev.kobj, &dev->kobj,
buf);
if (err) {
- rdev_warn(rdev, "could not add device link %s err %d\n",
+ rdev_dbg(rdev, "could not add device link %s err %d\n",
dev->kobj.name, err);
/* non-fatal */
}
diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c
index 8a4df7a1f2ee..e628d4c2f2ae 100644
--- a/drivers/regulator/da9052-regulator.c
+++ b/drivers/regulator/da9052-regulator.c
@@ -394,6 +394,7 @@ static inline struct da9052_regulator_info *find_regulator_info(u8 chip_id,
static int da9052_regulator_probe(struct platform_device *pdev)
{
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
struct regulator_config config = { };
struct da9052_regulator *regulator;
struct da9052 *da9052;
@@ -409,7 +410,7 @@ static int da9052_regulator_probe(struct platform_device *pdev)
regulator->da9052 = da9052;
regulator->info = find_regulator_info(regulator->da9052->chip_id,
- pdev->id);
+ cell->id);
if (regulator->info == NULL) {
dev_err(&pdev->dev, "invalid regulator ID specified\n");
return -EINVAL;
@@ -419,7 +420,7 @@ static int da9052_regulator_probe(struct platform_device *pdev)
config.driver_data = regulator;
config.regmap = da9052->regmap;
if (pdata && pdata->regulators) {
- config.init_data = pdata->regulators[pdev->id];
+ config.init_data = pdata->regulators[cell->id];
} else {
#ifdef CONFIG_OF
struct device_node *nproot = da9052->dev->of_node;
diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c
new file mode 100644
index 000000000000..dd76da09b3c7
--- /dev/null
+++ b/drivers/regulator/da9062-regulator.c
@@ -0,0 +1,842 @@
+/*
+ * da9062-regulator.c - REGULATOR device driver for DA9062
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/mfd/da9062/registers.h>
+
+/* Regulator IDs */
+enum {
+ DA9062_ID_BUCK1,
+ DA9062_ID_BUCK2,
+ DA9062_ID_BUCK3,
+ DA9062_ID_BUCK4,
+ DA9062_ID_LDO1,
+ DA9062_ID_LDO2,
+ DA9062_ID_LDO3,
+ DA9062_ID_LDO4,
+ DA9062_MAX_REGULATORS,
+};
+
+/* Regulator capabilities and registers description */
+struct da9062_regulator_info {
+ struct regulator_desc desc;
+ /* Current limiting */
+ unsigned int n_current_limits;
+ const int *current_limits;
+ /* Main register fields */
+ struct reg_field mode;
+ struct reg_field suspend;
+ struct reg_field sleep;
+ struct reg_field suspend_sleep;
+ unsigned int suspend_vsel_reg;
+ struct reg_field ilimit;
+ /* Event detection bit */
+ struct reg_field oc_event;
+};
+
+/* Single regulator settings */
+struct da9062_regulator {
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct da9062 *hw;
+ const struct da9062_regulator_info *info;
+
+ struct regmap_field *mode;
+ struct regmap_field *suspend;
+ struct regmap_field *sleep;
+ struct regmap_field *suspend_sleep;
+ struct regmap_field *ilimit;
+};
+
+/* Encapsulates all information for the regulators driver */
+struct da9062_regulators {
+ int irq_ldo_lim;
+ unsigned n_regulators;
+ /* Array size to be defined during init. Keep at end. */
+ struct da9062_regulator regulator[0];
+};
+
+/* BUCK modes */
+enum {
+ BUCK_MODE_MANUAL, /* 0 */
+ BUCK_MODE_SLEEP, /* 1 */
+ BUCK_MODE_SYNC, /* 2 */
+ BUCK_MODE_AUTO /* 3 */
+};
+
+/* Regulator operations */
+
+/* Current limits array (in uA) BUCK1 and BUCK3.
+ Entry indexes corresponds to register values. */
+static const int da9062_buck_a_limits[] = {
+ 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000,
+ 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000
+};
+
+/* Current limits array (in uA) for BUCK2.
+ Entry indexes corresponds to register values. */
+static const int da9062_buck_b_limits[] = {
+ 1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
+ 2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000
+};
+
+static int da9062_set_current_limit(struct regulator_dev *rdev,
+ int min_ua, int max_ua)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ int n, tval;
+
+ for (n = 0; n < rinfo->n_current_limits; n++) {
+ tval = rinfo->current_limits[n];
+ if (tval >= min_ua && tval <= max_ua)
+ return regmap_field_write(regl->ilimit, n);
+ }
+
+ return -EINVAL;
+}
+
+static int da9062_get_current_limit(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ unsigned int sel;
+ int ret;
+
+ ret = regmap_field_read(regl->ilimit, &sel);
+ if (ret < 0)
+ return ret;
+
+ if (sel >= rinfo->n_current_limits)
+ sel = rinfo->n_current_limits - 1;
+
+ return rinfo->current_limits[sel];
+}
+
+static int da9062_buck_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+/*
+ * Bucks use single mode register field for normal operation
+ * and suspend state.
+ * There are 3 modes to map to: FAST, NORMAL, and STANDBY.
+ */
+
+static unsigned da9062_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ unsigned int val, mode = 0;
+ int ret;
+
+ ret = regmap_field_read(regl->mode, &val);
+ if (ret < 0)
+ return ret;
+
+ switch (val) {
+ default:
+ case BUCK_MODE_MANUAL:
+ mode = REGULATOR_MODE_FAST | REGULATOR_MODE_STANDBY;
+ /* Sleep flag bit decides the mode */
+ break;
+ case BUCK_MODE_SLEEP:
+ return REGULATOR_MODE_STANDBY;
+ case BUCK_MODE_SYNC:
+ return REGULATOR_MODE_FAST;
+ case BUCK_MODE_AUTO:
+ return REGULATOR_MODE_NORMAL;
+ }
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ mode &= REGULATOR_MODE_STANDBY;
+ else
+ mode &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+
+ return mode;
+}
+
+/*
+ * LDOs use sleep flags - one for normal and one for suspend state.
+ * There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state.
+ */
+
+static int da9062_ldo_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->sleep, val);
+}
+
+static unsigned da9062_ldo_get_mode(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ int ret, val;
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ return REGULATOR_MODE_STANDBY;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int da9062_buck_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9062_buck_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9062_ldo_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9062_ldo_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9062_set_suspend_voltage(struct regulator_dev *rdev, int uv)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ int ret, sel;
+
+ sel = regulator_map_voltage_linear(rdev, uv, uv);
+ if (sel < 0)
+ return sel;
+
+ sel <<= ffs(rdev->desc->vsel_mask) - 1;
+
+ ret = regmap_update_bits(regl->hw->regmap, rinfo->suspend_vsel_reg,
+ rdev->desc->vsel_mask, sel);
+
+ return ret;
+}
+
+static int da9062_suspend_enable(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 1);
+}
+
+static int da9062_suspend_disable(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 0);
+}
+
+static int da9062_buck_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ int val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+static int da9062_ldo_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->suspend_sleep, val);
+}
+
+static struct regulator_ops da9062_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = da9062_set_current_limit,
+ .get_current_limit = da9062_get_current_limit,
+ .set_mode = da9062_buck_set_mode,
+ .get_mode = da9062_buck_get_mode,
+ .get_status = da9062_buck_get_status,
+ .set_suspend_voltage = da9062_set_suspend_voltage,
+ .set_suspend_enable = da9062_suspend_enable,
+ .set_suspend_disable = da9062_suspend_disable,
+ .set_suspend_mode = da9062_buck_set_suspend_mode,
+};
+
+static struct regulator_ops da9062_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_mode = da9062_ldo_set_mode,
+ .get_mode = da9062_ldo_get_mode,
+ .get_status = da9062_ldo_get_status,
+ .set_suspend_voltage = da9062_set_suspend_voltage,
+ .set_suspend_enable = da9062_suspend_enable,
+ .set_suspend_disable = da9062_suspend_disable,
+ .set_suspend_mode = da9062_ldo_set_suspend_mode,
+};
+
+/* Regulator information */
+static const struct da9062_regulator_info local_regulator_info[] = {
+ {
+ .desc.id = DA9062_ID_BUCK1,
+ .desc.name = "DA9062 BUCK1",
+ .desc.of_match = of_match_ptr("buck1"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (300) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1570) - (300))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK1_CONT,
+ .desc.enable_mask = DA9062AA_BUCK1_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK1_A,
+ .desc.vsel_mask = DA9062AA_VBUCK1_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK1_A,
+ __builtin_ffs((int)DA9062AA_BUCK1_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK1_B,
+ __builtin_ffs((int)DA9062AA_BUCK1_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK1_B,
+ .mode = REG_FIELD(DA9062AA_BUCK1_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK1_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK1_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK1_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+ __builtin_ffs((int)DA9062AA_BUCK1_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK2,
+ .desc.name = "DA9062 BUCK2",
+ .desc.of_match = of_match_ptr("buck2"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (300) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1570) - (300))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK2_CONT,
+ .desc.enable_mask = DA9062AA_BUCK2_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK2_A,
+ .desc.vsel_mask = DA9062AA_VBUCK2_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK2_A,
+ __builtin_ffs((int)DA9062AA_BUCK2_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK2_B,
+ __builtin_ffs((int)DA9062AA_BUCK2_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK2_B,
+ .mode = REG_FIELD(DA9062AA_BUCK2_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK2_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK2_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK2_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+ __builtin_ffs((int)DA9062AA_BUCK2_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK3,
+ .desc.name = "DA9062 BUCK3",
+ .desc.of_match = of_match_ptr("buck3"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (800) * 1000,
+ .desc.uV_step = (20) * 1000,
+ .desc.n_voltages = ((3340) - (800))/(20) + 1,
+ .current_limits = da9062_buck_b_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_b_limits),
+ .desc.enable_reg = DA9062AA_BUCK3_CONT,
+ .desc.enable_mask = DA9062AA_BUCK3_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK3_A,
+ .desc.vsel_mask = DA9062AA_VBUCK3_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK3_A,
+ __builtin_ffs((int)DA9062AA_BUCK3_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK3_B,
+ __builtin_ffs((int)DA9062AA_BUCK3_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK3_B,
+ .mode = REG_FIELD(DA9062AA_BUCK3_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK3_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK3_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK3_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_A,
+ __builtin_ffs((int)DA9062AA_BUCK3_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK4,
+ .desc.name = "DA9062 BUCK4",
+ .desc.of_match = of_match_ptr("buck4"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (530) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1800) - (530))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK4_CONT,
+ .desc.enable_mask = DA9062AA_BUCK4_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK4_A,
+ .desc.vsel_mask = DA9062AA_VBUCK4_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK4_A,
+ __builtin_ffs((int)DA9062AA_BUCK4_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK4_B,
+ __builtin_ffs((int)DA9062AA_BUCK4_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK4_B,
+ .mode = REG_FIELD(DA9062AA_BUCK4_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK4_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK4_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK4_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_B,
+ __builtin_ffs((int)DA9062AA_BUCK4_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO1,
+ .desc.name = "DA9062 LDO1",
+ .desc.of_match = of_match_ptr("ldo1"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO1_CONT,
+ .desc.enable_mask = DA9062AA_LDO1_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO1_A,
+ .desc.vsel_mask = DA9062AA_VLDO1_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO1_A,
+ __builtin_ffs((int)DA9062AA_LDO1_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO1_B,
+ __builtin_ffs((int)DA9062AA_LDO1_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO1_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO1_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO1_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO1_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO2,
+ .desc.name = "DA9062 LDO2",
+ .desc.of_match = of_match_ptr("ldo2"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (600))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO2_CONT,
+ .desc.enable_mask = DA9062AA_LDO2_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO2_A,
+ .desc.vsel_mask = DA9062AA_VLDO2_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO2_A,
+ __builtin_ffs((int)DA9062AA_LDO2_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO2_B,
+ __builtin_ffs((int)DA9062AA_LDO2_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO2_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO2_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO2_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO2_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO3,
+ .desc.name = "DA9062 LDO3",
+ .desc.of_match = of_match_ptr("ldo3"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO3_CONT,
+ .desc.enable_mask = DA9062AA_LDO3_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO3_A,
+ .desc.vsel_mask = DA9062AA_VLDO3_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO3_A,
+ __builtin_ffs((int)DA9062AA_LDO3_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO3_B,
+ __builtin_ffs((int)DA9062AA_LDO3_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO3_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO3_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO3_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO3_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO4,
+ .desc.name = "DA9062 LDO4",
+ .desc.of_match = of_match_ptr("ldo4"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO4_CONT,
+ .desc.enable_mask = DA9062AA_LDO4_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO4_A,
+ .desc.vsel_mask = DA9062AA_VLDO4_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO4_A,
+ __builtin_ffs((int)DA9062AA_LDO4_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO4_B,
+ __builtin_ffs((int)DA9062AA_LDO4_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO4_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO4_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO4_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO4_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_ILIM_MASK)) - 1),
+ },
+};
+
+/* Regulator interrupt handlers */
+static irqreturn_t da9062_ldo_lim_event(int irq, void *data)
+{
+ struct da9062_regulators *regulators = data;
+ struct da9062 *hw = regulators->regulator[0].hw;
+ struct da9062_regulator *regl;
+ int handled = IRQ_NONE;
+ int bits, i, ret;
+
+ ret = regmap_read(hw->regmap, DA9062AA_STATUS_D, &bits);
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Failed to read LDO overcurrent indicator\n");
+ goto ldo_lim_error;
+ }
+
+ for (i = regulators->n_regulators - 1; i >= 0; i--) {
+ regl = &regulators->regulator[i];
+ if (regl->info->oc_event.reg != DA9062AA_STATUS_D)
+ continue;
+
+ if (BIT(regl->info->oc_event.lsb) & bits) {
+ regulator_notifier_call_chain(regl->rdev,
+ REGULATOR_EVENT_OVER_CURRENT, NULL);
+ handled = IRQ_HANDLED;
+ }
+ }
+
+ldo_lim_error:
+ return handled;
+}
+
+static int da9062_regulator_probe(struct platform_device *pdev)
+{
+ struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
+ struct da9062_regulators *regulators;
+ struct da9062_regulator *regl;
+ struct regulator_config config = { };
+ int irq, n, ret;
+ size_t size;
+
+ /* Allocate memory required by usable regulators */
+ size = sizeof(struct da9062_regulators) +
+ DA9062_MAX_REGULATORS * sizeof(struct da9062_regulator);
+ regulators = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ if (!regulators)
+ return -ENOMEM;
+
+ regulators->n_regulators = DA9062_MAX_REGULATORS;
+ platform_set_drvdata(pdev, regulators);
+
+ n = 0;
+ while (n < regulators->n_regulators) {
+ /* Initialise regulator structure */
+ regl = &regulators->regulator[n];
+ regl->hw = chip;
+ regl->info = &local_regulator_info[n];
+ regl->desc = regl->info->desc;
+ regl->desc.type = REGULATOR_VOLTAGE;
+ regl->desc.owner = THIS_MODULE;
+
+ if (regl->info->mode.reg)
+ regl->mode = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->mode);
+ if (regl->info->suspend.reg)
+ regl->suspend = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->suspend);
+ if (regl->info->sleep.reg)
+ regl->sleep = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->sleep);
+ if (regl->info->suspend_sleep.reg)
+ regl->suspend_sleep = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->suspend_sleep);
+ if (regl->info->ilimit.reg)
+ regl->ilimit = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->ilimit);
+
+ /* Register regulator */
+ memset(&config, 0, sizeof(config));
+ config.dev = chip->dev;
+ config.driver_data = regl;
+ config.regmap = chip->regmap;
+
+ regl->rdev = devm_regulator_register(&pdev->dev, &regl->desc,
+ &config);
+ if (IS_ERR(regl->rdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register %s regulator\n",
+ regl->desc.name);
+ return PTR_ERR(regl->rdev);
+ }
+
+ n++;
+ }
+
+ /* LDOs overcurrent event support */
+ irq = platform_get_irq_byname(pdev, "LDO_LIM");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get IRQ.\n");
+ return irq;
+ }
+ regulators->irq_ldo_lim = irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
+ NULL, da9062_ldo_lim_event,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "LDO_LIM", regulators);
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "Failed to request LDO_LIM IRQ.\n");
+ regulators->irq_ldo_lim = -ENXIO;
+ }
+
+ return 0;
+}
+
+static struct platform_driver da9062_regulator_driver = {
+ .driver = {
+ .name = "da9062-regulators",
+ .owner = THIS_MODULE,
+ },
+ .probe = da9062_regulator_probe,
+};
+
+static int __init da9062_regulator_init(void)
+{
+ return platform_driver_register(&da9062_regulator_driver);
+}
+subsys_initcall(da9062_regulator_init);
+
+static void __exit da9062_regulator_cleanup(void)
+{
+ platform_driver_unregister(&da9062_regulator_driver);
+}
+module_exit(da9062_regulator_cleanup);
+
+/* Module information */
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("REGULATOR device driver for Dialog DA9062");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9062-regulators");
diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c
index 31c2c593ae0b..aed1ad3dc964 100644
--- a/drivers/regulator/da9063-regulator.c
+++ b/drivers/regulator/da9063-regulator.c
@@ -117,9 +117,6 @@ struct da9063_regulator {
/* Encapsulates all information for the regulators driver */
struct da9063_regulators {
- int irq_ldo_lim;
- int irq_uvov;
-
unsigned n_regulators;
/* Array size to be defined during init. Keep at end. */
struct da9063_regulator regulator[0];
@@ -867,35 +864,23 @@ static int da9063_regulator_probe(struct platform_device *pdev)
return irq;
}
- ret = request_threaded_irq(irq,
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
NULL, da9063_ldo_lim_event,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"LDO_LIM", regulators);
if (ret) {
- dev_err(&pdev->dev,
- "Failed to request LDO_LIM IRQ.\n");
- regulators->irq_ldo_lim = -ENXIO;
+ dev_err(&pdev->dev, "Failed to request LDO_LIM IRQ.\n");
+ return ret;
}
return 0;
}
-static int da9063_regulator_remove(struct platform_device *pdev)
-{
- struct da9063_regulators *regulators = platform_get_drvdata(pdev);
-
- free_irq(regulators->irq_ldo_lim, regulators);
- free_irq(regulators->irq_uvov, regulators);
-
- return 0;
-}
-
static struct platform_driver da9063_regulator_driver = {
.driver = {
.name = DA9063_DRVNAME_REGULATORS,
},
.probe = da9063_regulator_probe,
- .remove = da9063_regulator_remove,
};
static int __init da9063_regulator_init(void)
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
index 3c25db89a021..42865681c00b 100644
--- a/drivers/regulator/fan53555.c
+++ b/drivers/regulator/fan53555.c
@@ -182,6 +182,7 @@ static int fan53555_set_ramp(struct regulator_dev *rdev, int ramp)
static struct regulator_ops fan53555_regulator_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
.map_voltage = regulator_map_voltage_linear,
.list_voltage = regulator_list_voltage_linear,
.set_suspend_voltage = fan53555_set_suspend_voltage,
diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c
index cbc39096c78d..3bbb32680a94 100644
--- a/drivers/regulator/helpers.c
+++ b/drivers/regulator/helpers.c
@@ -275,7 +275,7 @@ int regulator_map_voltage_linear(struct regulator_dev *rdev,
EXPORT_SYMBOL_GPL(regulator_map_voltage_linear);
/**
- * regulator_map_voltage_linear - map_voltage() for multiple linear ranges
+ * regulator_map_voltage_linear_range - map_voltage() for multiple linear ranges
*
* @rdev: Regulator to operate on
* @min_uV: Lower bound for voltage
diff --git a/drivers/regulator/lp8755.c b/drivers/regulator/lp8755.c
index 4a415d4ee463..d6773da925ba 100644
--- a/drivers/regulator/lp8755.c
+++ b/drivers/regulator/lp8755.c
@@ -419,20 +419,16 @@ static int lp8755_int_config(struct lp8755_chip *pchip)
}
ret = lp8755_read(pchip, 0x0F, &regval);
- if (ret < 0)
- goto err_i2c;
- pchip->irqmask = regval;
- ret = request_threaded_irq(pchip->irq, NULL, lp8755_irq_handler,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "lp8755-irq", pchip);
- if (ret)
+ if (ret < 0) {
+ dev_err(pchip->dev, "i2c acceess error %s\n", __func__);
return ret;
+ }
- return ret;
-
-err_i2c:
- dev_err(pchip->dev, "i2c acceess error %s\n", __func__);
- return ret;
+ pchip->irqmask = regval;
+ return devm_request_threaded_irq(pchip->dev, pchip->irq, NULL,
+ lp8755_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "lp8755-irq", pchip);
}
static const struct regmap_config lp8755_regmap = {
@@ -514,9 +510,6 @@ static int lp8755_remove(struct i2c_client *client)
for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++)
lp8755_write(pchip, icnt, 0x00);
- if (pchip->irq != 0)
- free_irq(pchip->irq, pchip);
-
return 0;
}
diff --git a/drivers/regulator/max14577.c b/drivers/regulator/max14577.c
index b3678d289619..b2daa6641417 100644
--- a/drivers/regulator/max14577.c
+++ b/drivers/regulator/max14577.c
@@ -100,31 +100,34 @@ static struct regulator_ops max14577_charger_ops = {
.set_current_limit = max14577_reg_set_current_limit,
};
+#define MAX14577_SAFEOUT_REG { \
+ .name = "SAFEOUT", \
+ .of_match = of_match_ptr("SAFEOUT"), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX14577_SAFEOUT, \
+ .ops = &max14577_safeout_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .n_voltages = 1, \
+ .min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE, \
+ .enable_reg = MAX14577_REG_CONTROL2, \
+ .enable_mask = CTRL2_SFOUTORD_MASK, \
+}
+#define MAX14577_CHARGER_REG { \
+ .name = "CHARGER", \
+ .of_match = of_match_ptr("CHARGER"), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX14577_CHARGER, \
+ .ops = &max14577_charger_ops, \
+ .type = REGULATOR_CURRENT, \
+ .owner = THIS_MODULE, \
+ .enable_reg = MAX14577_CHG_REG_CHG_CTRL2, \
+ .enable_mask = CHGCTRL2_MBCHOSTEN_MASK, \
+}
+
static const struct regulator_desc max14577_supported_regulators[] = {
- [MAX14577_SAFEOUT] = {
- .name = "SAFEOUT",
- .of_match = of_match_ptr("SAFEOUT"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX14577_SAFEOUT,
- .ops = &max14577_safeout_ops,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = 1,
- .min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
- .enable_reg = MAX14577_REG_CONTROL2,
- .enable_mask = CTRL2_SFOUTORD_MASK,
- },
- [MAX14577_CHARGER] = {
- .name = "CHARGER",
- .of_match = of_match_ptr("CHARGER"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX14577_CHARGER,
- .ops = &max14577_charger_ops,
- .type = REGULATOR_CURRENT,
- .owner = THIS_MODULE,
- .enable_reg = MAX14577_CHG_REG_CHG_CTRL2,
- .enable_mask = CHGCTRL2_MBCHOSTEN_MASK,
- },
+ [MAX14577_SAFEOUT] = MAX14577_SAFEOUT_REG,
+ [MAX14577_CHARGER] = MAX14577_CHARGER_REG,
};
static struct regulator_ops max77836_ldo_ops = {
@@ -138,63 +141,28 @@ static struct regulator_ops max77836_ldo_ops = {
/* TODO: add .set_suspend_mode */
};
+#define MAX77836_LDO_REG(num) { \
+ .name = "LDO" # num, \
+ .of_match = of_match_ptr("LDO" # num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX77836_LDO ## num, \
+ .ops = &max77836_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM, \
+ .min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN, \
+ .uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP, \
+ .enable_reg = MAX77836_LDO_REG_CNFG1_LDO ## num, \
+ .enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK, \
+ .vsel_reg = MAX77836_LDO_REG_CNFG1_LDO ## num, \
+ .vsel_mask = MAX77836_CNFG1_LDO_TV_MASK, \
+}
+
static const struct regulator_desc max77836_supported_regulators[] = {
- [MAX14577_SAFEOUT] = {
- .name = "SAFEOUT",
- .of_match = of_match_ptr("SAFEOUT"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX14577_SAFEOUT,
- .ops = &max14577_safeout_ops,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = 1,
- .min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
- .enable_reg = MAX14577_REG_CONTROL2,
- .enable_mask = CTRL2_SFOUTORD_MASK,
- },
- [MAX14577_CHARGER] = {
- .name = "CHARGER",
- .of_match = of_match_ptr("CHARGER"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX14577_CHARGER,
- .ops = &max14577_charger_ops,
- .type = REGULATOR_CURRENT,
- .owner = THIS_MODULE,
- .enable_reg = MAX14577_CHG_REG_CHG_CTRL2,
- .enable_mask = CHGCTRL2_MBCHOSTEN_MASK,
- },
- [MAX77836_LDO1] = {
- .name = "LDO1",
- .of_match = of_match_ptr("LDO1"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX77836_LDO1,
- .ops = &max77836_ldo_ops,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
- .min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
- .uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
- .enable_reg = MAX77836_LDO_REG_CNFG1_LDO1,
- .enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK,
- .vsel_reg = MAX77836_LDO_REG_CNFG1_LDO1,
- .vsel_mask = MAX77836_CNFG1_LDO_TV_MASK,
- },
- [MAX77836_LDO2] = {
- .name = "LDO2",
- .of_match = of_match_ptr("LDO2"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX77836_LDO2,
- .ops = &max77836_ldo_ops,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
- .min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
- .uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
- .enable_reg = MAX77836_LDO_REG_CNFG1_LDO2,
- .enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK,
- .vsel_reg = MAX77836_LDO_REG_CNFG1_LDO2,
- .vsel_mask = MAX77836_CNFG1_LDO_TV_MASK,
- },
+ [MAX14577_SAFEOUT] = MAX14577_SAFEOUT_REG,
+ [MAX14577_CHARGER] = MAX14577_CHARGER_REG,
+ [MAX77836_LDO1] = MAX77836_LDO_REG(1),
+ [MAX77836_LDO2] = MAX77836_LDO_REG(2),
};
#ifdef CONFIG_OF
diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c
index 15fb1416bfbd..17ccf365a9c0 100644
--- a/drivers/regulator/max77686.c
+++ b/drivers/regulator/max77686.c
@@ -2,7 +2,7 @@
* max77686.c - Regulator driver for the Maxim 77686
*
* Copyright (C) 2012 Samsung Electronics
- * Chiwoong Byun <woong.byun@smasung.com>
+ * Chiwoong Byun <woong.byun@samsung.com>
* Jonghwa Lee <jonghwa3.lee@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -88,7 +88,7 @@ enum max77686_ramp_rate {
};
struct max77686_data {
- u64 gpio_enabled:MAX77686_REGULATORS;
+ DECLARE_BITMAP(gpio_enabled, MAX77686_REGULATORS);
/* Array indexed by regulator id */
unsigned int opmode[MAX77686_REGULATORS];
@@ -121,7 +121,7 @@ static unsigned int max77686_map_normal_mode(struct max77686_data *max77686,
case MAX77686_BUCK8:
case MAX77686_BUCK9:
case MAX77686_LDO20 ... MAX77686_LDO22:
- if (max77686->gpio_enabled & (1 << id))
+ if (test_bit(id, max77686->gpio_enabled))
return MAX77686_GPIO_CONTROL;
}
@@ -277,7 +277,7 @@ static int max77686_of_parse_cb(struct device_node *np,
}
if (gpio_is_valid(config->ena_gpio)) {
- max77686->gpio_enabled |= (1 << desc->id);
+ set_bit(desc->id, max77686->gpio_enabled);
return regmap_update_bits(config->regmap, desc->enable_reg,
desc->enable_mask,
diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c
index 9665a488e2f1..38722c8311a5 100644
--- a/drivers/regulator/max77693.c
+++ b/drivers/regulator/max77693.c
@@ -35,20 +35,6 @@
#define CHGIN_ILIM_STEP_20mA 20000
-/* CHARGER regulator ops */
-/* CHARGER regulator uses two bits for enabling */
-static int max77693_chg_is_enabled(struct regulator_dev *rdev)
-{
- int ret;
- unsigned int val;
-
- ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
- if (ret)
- return ret;
-
- return (val & rdev->desc->enable_mask) == rdev->desc->enable_mask;
-}
-
/*
* CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA
* 0x00, 0x01, 0x2, 0x03 = 60 mA
@@ -118,7 +104,7 @@ static struct regulator_ops max77693_safeout_ops = {
};
static struct regulator_ops max77693_charger_ops = {
- .is_enabled = max77693_chg_is_enabled,
+ .is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.get_current_limit = max77693_chg_get_current_limit,
@@ -155,6 +141,7 @@ static const struct regulator_desc regulators[] = {
.enable_reg = MAX77693_CHG_REG_CHG_CNFG_00,
.enable_mask = CHG_CNFG_00_CHG_MASK |
CHG_CNFG_00_BUCK_MASK,
+ .enable_val = CHG_CNFG_00_CHG_MASK | CHG_CNFG_00_BUCK_MASK,
},
};
diff --git a/drivers/regulator/max77843.c b/drivers/regulator/max77843.c
index c132ef527cdd..f4fd0d3cfa6e 100644
--- a/drivers/regulator/max77843.c
+++ b/drivers/regulator/max77843.c
@@ -33,21 +33,6 @@ static const unsigned int max77843_safeout_voltage_table[] = {
3300000,
};
-static int max77843_reg_is_enabled(struct regulator_dev *rdev)
-{
- struct regmap *regmap = rdev->regmap;
- int ret;
- unsigned int reg;
-
- ret = regmap_read(regmap, rdev->desc->enable_reg, &reg);
- if (ret) {
- dev_err(&rdev->dev, "Fialed to read charger register\n");
- return ret;
- }
-
- return (reg & rdev->desc->enable_mask) == rdev->desc->enable_mask;
-}
-
static int max77843_reg_get_current_limit(struct regulator_dev *rdev)
{
struct regmap *regmap = rdev->regmap;
@@ -96,7 +81,7 @@ static int max77843_reg_set_current_limit(struct regulator_dev *rdev,
}
static struct regulator_ops max77843_charger_ops = {
- .is_enabled = max77843_reg_is_enabled,
+ .is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.get_current_limit = max77843_reg_get_current_limit,
@@ -112,37 +97,25 @@ static struct regulator_ops max77843_regulator_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
+#define MAX77843_SAFEOUT(num) { \
+ .name = "SAFEOUT" # num, \
+ .id = MAX77843_SAFEOUT ## num, \
+ .ops = &max77843_regulator_ops, \
+ .of_match = of_match_ptr("SAFEOUT" # num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(max77843_safeout_voltage_table), \
+ .volt_table = max77843_safeout_voltage_table, \
+ .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \
+ .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT ## num, \
+ .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \
+ .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT ## num ## _MASK, \
+}
+
static const struct regulator_desc max77843_supported_regulators[] = {
- [MAX77843_SAFEOUT1] = {
- .name = "SAFEOUT1",
- .id = MAX77843_SAFEOUT1,
- .ops = &max77843_regulator_ops,
- .of_match = of_match_ptr("SAFEOUT1"),
- .regulators_node = of_match_ptr("regulators"),
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = ARRAY_SIZE(max77843_safeout_voltage_table),
- .volt_table = max77843_safeout_voltage_table,
- .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL,
- .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT1,
- .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL,
- .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT1_MASK,
- },
- [MAX77843_SAFEOUT2] = {
- .name = "SAFEOUT2",
- .id = MAX77843_SAFEOUT2,
- .ops = &max77843_regulator_ops,
- .of_match = of_match_ptr("SAFEOUT2"),
- .regulators_node = of_match_ptr("regulators"),
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = ARRAY_SIZE(max77843_safeout_voltage_table),
- .volt_table = max77843_safeout_voltage_table,
- .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL,
- .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT2,
- .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL,
- .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT2_MASK,
- },
+ [MAX77843_SAFEOUT1] = MAX77843_SAFEOUT(1),
+ [MAX77843_SAFEOUT2] = MAX77843_SAFEOUT(2),
[MAX77843_CHARGER] = {
.name = "CHARGER",
.id = MAX77843_CHARGER,
@@ -152,7 +125,8 @@ static const struct regulator_desc max77843_supported_regulators[] = {
.type = REGULATOR_CURRENT,
.owner = THIS_MODULE,
.enable_reg = MAX77843_CHG_REG_CHG_CNFG_00,
- .enable_mask = MAX77843_CHG_MASK,
+ .enable_mask = MAX77843_CHG_MASK | MAX77843_CHG_BUCK_MASK,
+ .enable_val = MAX77843_CHG_MASK | MAX77843_CHG_BUCK_MASK,
},
};
diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c
index c3d55c2db593..6f2bdad8b4d8 100644
--- a/drivers/regulator/max8973-regulator.c
+++ b/drivers/regulator/max8973-regulator.c
@@ -27,12 +27,14 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/max8973-regulator.h>
#include <linux/regulator/of_regulator.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/regmap.h>
@@ -66,6 +68,7 @@
#define MAX8973_RAMP_25mV_PER_US 0x1
#define MAX8973_RAMP_50mV_PER_US 0x2
#define MAX8973_RAMP_200mV_PER_US 0x3
+#define MAX8973_RAMP_MASK 0x3
/* MAX8973_CONTROL2 */
#define MAX8973_WDTMR_ENABLE BIT(6)
@@ -89,19 +92,25 @@
#define MAX8973_VOLATGE_STEP 6250
#define MAX8973_BUCK_N_VOLTAGE 0x80
+enum device_id {
+ MAX8973,
+ MAX77621
+};
+
/* Maxim 8973 chip information */
struct max8973_chip {
struct device *dev;
struct regulator_desc desc;
struct regmap *regmap;
bool enable_external_control;
+ int enable_gpio;
int dvs_gpio;
int lru_index[MAX8973_MAX_VOUT_REG];
int curr_vout_val[MAX8973_MAX_VOUT_REG];
int curr_vout_reg;
int curr_gpio_val;
- bool valid_dvs_gpio;
struct regulator_ops ops;
+ enum device_id id;
};
/*
@@ -174,7 +183,7 @@ static int max8973_dcdc_set_voltage_sel(struct regulator_dev *rdev,
* If gpios are available to select the VOUT register then least
* recently used register for new configuration.
*/
- if (max->valid_dvs_gpio)
+ if (gpio_is_valid(max->dvs_gpio))
found = find_voltage_set_register(max, vsel,
&vout_reg, &gpio_val);
@@ -191,7 +200,7 @@ static int max8973_dcdc_set_voltage_sel(struct regulator_dev *rdev,
}
/* Select proper VOUT register vio gpios */
- if (max->valid_dvs_gpio) {
+ if (gpio_is_valid(max->dvs_gpio)) {
gpio_set_value_cansleep(max->dvs_gpio, gpio_val & 0x1);
max->curr_gpio_val = gpio_val;
}
@@ -242,12 +251,45 @@ static unsigned int max8973_dcdc_get_mode(struct regulator_dev *rdev)
REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
}
+static int max8973_set_ramp_delay(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ struct max8973_chip *max = rdev_get_drvdata(rdev);
+ unsigned int control;
+ int ret;
+ int ret_val;
+
+ /* Set ramp delay */
+ if (ramp_delay < 25000) {
+ control = MAX8973_RAMP_12mV_PER_US;
+ ret_val = 12000;
+ } else if (ramp_delay < 50000) {
+ control = MAX8973_RAMP_25mV_PER_US;
+ ret_val = 25000;
+ } else if (ramp_delay < 200000) {
+ control = MAX8973_RAMP_50mV_PER_US;
+ ret_val = 50000;
+ } else {
+ control = MAX8973_RAMP_200mV_PER_US;
+ ret_val = 200000;
+ }
+
+ ret = regmap_update_bits(max->regmap, MAX8973_CONTROL1,
+ MAX8973_RAMP_MASK, control);
+ if (ret < 0)
+ dev_err(max->dev, "register %d update failed, %d",
+ MAX8973_CONTROL1, ret);
+ return ret;
+}
+
static const struct regulator_ops max8973_dcdc_ops = {
.get_voltage_sel = max8973_dcdc_get_voltage_sel,
.set_voltage_sel = max8973_dcdc_set_voltage_sel,
.list_voltage = regulator_list_voltage_linear,
.set_mode = max8973_dcdc_set_mode,
.get_mode = max8973_dcdc_get_mode,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = max8973_set_ramp_delay,
};
static int max8973_init_dcdc(struct max8973_chip *max,
@@ -256,6 +298,29 @@ static int max8973_init_dcdc(struct max8973_chip *max,
int ret;
uint8_t control1 = 0;
uint8_t control2 = 0;
+ unsigned int data;
+
+ ret = regmap_read(max->regmap, MAX8973_CONTROL1, &data);
+ if (ret < 0) {
+ dev_err(max->dev, "register %d read failed, err = %d",
+ MAX8973_CONTROL1, ret);
+ return ret;
+ }
+ control1 = data & MAX8973_RAMP_MASK;
+ switch (control1) {
+ case MAX8973_RAMP_12mV_PER_US:
+ max->desc.ramp_delay = 12000;
+ break;
+ case MAX8973_RAMP_25mV_PER_US:
+ max->desc.ramp_delay = 25000;
+ break;
+ case MAX8973_RAMP_50mV_PER_US:
+ max->desc.ramp_delay = 50000;
+ break;
+ case MAX8973_RAMP_200mV_PER_US:
+ max->desc.ramp_delay = 200000;
+ break;
+ }
if (pdata->control_flags & MAX8973_CONTROL_REMOTE_SENSE_ENABLE)
control1 |= MAX8973_SNS_ENABLE;
@@ -266,28 +331,16 @@ static int max8973_init_dcdc(struct max8973_chip *max,
if (pdata->control_flags & MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE)
control1 |= MAX8973_AD_ENABLE;
- if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE)
+ if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE) {
control1 |= MAX8973_BIAS_ENABLE;
+ max->desc.enable_time = 20;
+ } else {
+ max->desc.enable_time = 240;
+ }
if (pdata->control_flags & MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE)
control1 |= MAX8973_FREQSHIFT_9PER;
- /* Set ramp delay */
- if (pdata->reg_init_data &&
- pdata->reg_init_data->constraints.ramp_delay) {
- if (pdata->reg_init_data->constraints.ramp_delay < 25000)
- control1 |= MAX8973_RAMP_12mV_PER_US;
- else if (pdata->reg_init_data->constraints.ramp_delay < 50000)
- control1 |= MAX8973_RAMP_25mV_PER_US;
- else if (pdata->reg_init_data->constraints.ramp_delay < 200000)
- control1 |= MAX8973_RAMP_50mV_PER_US;
- else
- control1 |= MAX8973_RAMP_200mV_PER_US;
- } else {
- control1 |= MAX8973_RAMP_12mV_PER_US;
- max->desc.ramp_delay = 12500;
- }
-
if (!(pdata->control_flags & MAX8973_CONTROL_PULL_DOWN_ENABLE))
control2 |= MAX8973_DISCH_ENBABLE;
@@ -344,7 +397,7 @@ static int max8973_init_dcdc(struct max8973_chip *max,
}
/* If external control is enabled then disable EN bit */
- if (max->enable_external_control) {
+ if (max->enable_external_control && (max->id == MAX8973)) {
ret = regmap_update_bits(max->regmap, MAX8973_VOUT,
MAX8973_VOUT_ENABLE, 0);
if (ret < 0)
@@ -361,22 +414,82 @@ static const struct regmap_config max8973_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+static struct max8973_regulator_platform_data *max8973_parse_dt(
+ struct device *dev)
+{
+ struct max8973_regulator_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+ int ret;
+ u32 pval;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->enable_ext_control = of_property_read_bool(np,
+ "maxim,externally-enable");
+ pdata->enable_gpio = of_get_named_gpio(np, "maxim,enable-gpio", 0);
+ pdata->dvs_gpio = of_get_named_gpio(np, "maxim,dvs-gpio", 0);
+
+ ret = of_property_read_u32(np, "maxim,dvs-default-state", &pval);
+ if (!ret)
+ pdata->dvs_def_state = pval;
+
+ if (of_property_read_bool(np, "maxim,enable-remote-sense"))
+ pdata->control_flags |= MAX8973_CONTROL_REMOTE_SENSE_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-falling-slew-rate"))
+ pdata->control_flags |=
+ MAX8973_CONTROL_FALLING_SLEW_RATE_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-active-discharge"))
+ pdata->control_flags |=
+ MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-frequency-shift"))
+ pdata->control_flags |= MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-bias-control"))
+ pdata->control_flags |= MAX8973_BIAS_ENABLE;
+
+ return pdata;
+}
+
+static const struct of_device_id of_max8973_match_tbl[] = {
+ { .compatible = "maxim,max8973", .data = (void *)MAX8973, },
+ { .compatible = "maxim,max77621", .data = (void *)MAX77621, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_max8973_match_tbl);
+
static int max8973_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max8973_regulator_platform_data *pdata;
+ struct regulator_init_data *ridata;
struct regulator_config config = { };
struct regulator_dev *rdev;
struct max8973_chip *max;
+ bool pdata_from_dt = false;
+ unsigned int chip_id;
int ret;
pdata = dev_get_platdata(&client->dev);
- if (!pdata && !client->dev.of_node) {
+ if (!pdata && client->dev.of_node) {
+ pdata = max8973_parse_dt(&client->dev);
+ pdata_from_dt = true;
+ }
+
+ if (!pdata) {
dev_err(&client->dev, "No Platform data");
return -EIO;
}
+ if ((pdata->dvs_gpio == -EPROBE_DEFER) ||
+ (pdata->enable_gpio == -EPROBE_DEFER))
+ return -EPROBE_DEFER;
+
max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL);
if (!max)
return -ENOMEM;
@@ -388,6 +501,27 @@ static int max8973_probe(struct i2c_client *client,
return ret;
}
+ if (client->dev.of_node) {
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(of_max8973_match_tbl),
+ &client->dev);
+ if (!match)
+ return -ENODATA;
+ max->id = (u32)((uintptr_t)match->data);
+ } else {
+ max->id = id->driver_data;
+ }
+
+ ret = regmap_read(max->regmap, MAX8973_CHIPID1, &chip_id);
+ if (ret < 0) {
+ dev_err(&client->dev, "register CHIPID1 read failed, %d", ret);
+ return ret;
+ }
+
+ dev_info(&client->dev, "CHIP-ID OTP: 0x%02x ID_M: 0x%02x\n",
+ (chip_id >> 4) & 0xF, (chip_id >> 1) & 0x7);
+
i2c_set_clientdata(client, max);
max->ops = max8973_dcdc_ops;
max->dev = &client->dev;
@@ -400,23 +534,14 @@ static int max8973_probe(struct i2c_client *client,
max->desc.uV_step = MAX8973_VOLATGE_STEP;
max->desc.n_voltages = MAX8973_BUCK_N_VOLTAGE;
- if (!pdata || !pdata->enable_ext_control) {
- max->desc.enable_reg = MAX8973_VOUT;
- max->desc.enable_mask = MAX8973_VOUT_ENABLE;
- max->ops.enable = regulator_enable_regmap;
- max->ops.disable = regulator_disable_regmap;
- max->ops.is_enabled = regulator_is_enabled_regmap;
- }
+ max->dvs_gpio = (pdata->dvs_gpio) ? pdata->dvs_gpio : -EINVAL;
+ max->enable_gpio = (pdata->enable_gpio) ? pdata->enable_gpio : -EINVAL;
+ max->enable_external_control = pdata->enable_ext_control;
+ max->curr_gpio_val = pdata->dvs_def_state;
+ max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
- if (pdata) {
- max->dvs_gpio = pdata->dvs_gpio;
- max->enable_external_control = pdata->enable_ext_control;
- max->curr_gpio_val = pdata->dvs_def_state;
- max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
- } else {
- max->dvs_gpio = -EINVAL;
- max->curr_vout_reg = MAX8973_VOUT;
- }
+ if (gpio_is_valid(max->enable_gpio))
+ max->enable_external_control = true;
max->lru_index[0] = max->curr_vout_reg;
@@ -434,7 +559,6 @@ static int max8973_probe(struct i2c_client *client,
max->dvs_gpio, ret);
return ret;
}
- max->valid_dvs_gpio = true;
/*
* Initialize the lru index with vout_reg id
@@ -444,22 +568,64 @@ static int max8973_probe(struct i2c_client *client,
max->lru_index[i] = i;
max->lru_index[0] = max->curr_vout_reg;
max->lru_index[max->curr_vout_reg] = 0;
- } else {
- max->valid_dvs_gpio = false;
}
- if (pdata) {
- ret = max8973_init_dcdc(max, pdata);
- if (ret < 0) {
- dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret);
- return ret;
+ if (pdata_from_dt)
+ pdata->reg_init_data = of_get_regulator_init_data(&client->dev,
+ client->dev.of_node, &max->desc);
+
+ ridata = pdata->reg_init_data;
+ switch (max->id) {
+ case MAX8973:
+ if (!pdata->enable_ext_control) {
+ max->desc.enable_reg = MAX8973_VOUT;
+ max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+ max->ops.enable = regulator_enable_regmap;
+ max->ops.disable = regulator_disable_regmap;
+ max->ops.is_enabled = regulator_is_enabled_regmap;
+ break;
+ }
+
+ if (gpio_is_valid(max->enable_gpio)) {
+ config.ena_gpio_flags = GPIOF_OUT_INIT_LOW;
+ if (ridata && (ridata->constraints.always_on ||
+ ridata->constraints.boot_on))
+ config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
+ config.ena_gpio = max->enable_gpio;
}
+ break;
+
+ case MAX77621:
+ if (gpio_is_valid(max->enable_gpio)) {
+ ret = devm_gpio_request_one(&client->dev,
+ max->enable_gpio, GPIOF_OUT_INIT_HIGH,
+ "max8973-en-gpio");
+ if (ret) {
+ dev_err(&client->dev,
+ "gpio_request for gpio %d failed: %d\n",
+ max->enable_gpio, ret);
+ return ret;
+ }
+ }
+
+ max->desc.enable_reg = MAX8973_VOUT;
+ max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+ max->ops.enable = regulator_enable_regmap;
+ max->ops.disable = regulator_disable_regmap;
+ max->ops.is_enabled = regulator_is_enabled_regmap;
+ break;
+ default:
+ break;
+ }
+
+ ret = max8973_init_dcdc(max, pdata);
+ if (ret < 0) {
+ dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret);
+ return ret;
}
config.dev = &client->dev;
- config.init_data = pdata ? pdata->reg_init_data :
- of_get_regulator_init_data(&client->dev, client->dev.of_node,
- &max->desc);
+ config.init_data = pdata->reg_init_data;
config.driver_data = max;
config.of_node = client->dev.of_node;
config.regmap = max->regmap;
@@ -476,15 +642,16 @@ static int max8973_probe(struct i2c_client *client,
}
static const struct i2c_device_id max8973_id[] = {
- {.name = "max8973",},
+ {.name = "max8973", .driver_data = MAX8973},
+ {.name = "max77621", .driver_data = MAX77621},
{},
};
-
MODULE_DEVICE_TABLE(i2c, max8973_id);
static struct i2c_driver max8973_i2c_driver = {
.driver = {
.name = "max8973",
+ .of_match_table = of_max8973_match_tbl,
.owner = THIS_MODULE,
},
.probe = max8973_probe,
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 24e812c48d93..b1c485b24ab2 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -58,6 +58,10 @@ static void of_get_regulation_constraints(struct device_node *np,
if (!of_property_read_u32(np, "regulator-max-microamp", &pval))
constraints->max_uA = pval;
+ if (!of_property_read_u32(np, "regulator-input-current-limit-microamp",
+ &pval))
+ constraints->ilim_uA = pval;
+
/* Current change possible? */
if (constraints->min_uA != constraints->max_uA)
constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT;
@@ -67,6 +71,8 @@ static void of_get_regulation_constraints(struct device_node *np,
if (!constraints->always_on) /* status change should be possible. */
constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
+ constraints->pull_down = of_property_read_bool(np, "regulator-pull-down");
+
if (of_property_read_bool(np, "regulator-allow-bypass"))
constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS;
@@ -82,6 +88,9 @@ static void of_get_regulation_constraints(struct device_node *np,
if (!ret)
constraints->enable_time = pval;
+ constraints->soft_start = of_property_read_bool(np,
+ "regulator-soft-start");
+
if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) {
if (desc && desc->of_map_mode) {
ret = desc->of_map_mode(pval);
@@ -95,6 +104,9 @@ static void of_get_regulation_constraints(struct device_node *np,
}
}
+ if (!of_property_read_u32(np, "regulator-system-load", &pval))
+ constraints->system_load = pval;
+
for (i = 0; i < ARRAY_SIZE(regulator_states); i++) {
switch (i) {
case PM_SUSPEND_MEM:
@@ -108,7 +120,7 @@ static void of_get_regulation_constraints(struct device_node *np,
case PM_SUSPEND_STANDBY:
default:
continue;
- };
+ }
suspend_np = of_get_child_by_name(np, regulator_states[i]);
if (!suspend_np || !suspend_state)
@@ -292,7 +304,7 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
return NULL;
}
- for_each_child_of_node(search, child) {
+ for_each_available_child_of_node(search, child) {
name = of_get_property(child, "regulator-compatible", NULL);
if (!name)
name = child->name;
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
index 253833ae35f3..ffa96124a5e7 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -21,10 +21,8 @@
#include <linux/pwm.h>
struct pwm_regulator_data {
- struct regulator_desc desc;
struct pwm_voltages *duty_cycle_table;
struct pwm_device *pwm;
- bool enabled;
int state;
};
@@ -33,17 +31,17 @@ struct pwm_voltages {
unsigned int dutycycle;
};
-static int pwm_regulator_get_voltage_sel(struct regulator_dev *dev)
+static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
{
- struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
return drvdata->state;
}
-static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
+static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev,
unsigned selector)
{
- struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
unsigned int pwm_reg_period;
int dutycycle;
int ret;
@@ -55,30 +53,27 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
ret = pwm_config(drvdata->pwm, dutycycle, pwm_reg_period);
if (ret) {
- dev_err(&dev->dev, "Failed to configure PWM\n");
+ dev_err(&rdev->dev, "Failed to configure PWM\n");
return ret;
}
drvdata->state = selector;
- if (!drvdata->enabled) {
- ret = pwm_enable(drvdata->pwm);
- if (ret) {
- dev_err(&dev->dev, "Failed to enable PWM\n");
- return ret;
- }
- drvdata->enabled = true;
+ ret = pwm_enable(drvdata->pwm);
+ if (ret) {
+ dev_err(&rdev->dev, "Failed to enable PWM\n");
+ return ret;
}
return 0;
}
-static int pwm_regulator_list_voltage(struct regulator_dev *dev,
+static int pwm_regulator_list_voltage(struct regulator_dev *rdev,
unsigned selector)
{
- struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
- if (selector >= drvdata->desc.n_voltages)
+ if (selector >= rdev->desc->n_voltages)
return -EINVAL;
return drvdata->duty_cycle_table[selector].uV;
@@ -91,7 +86,7 @@ static struct regulator_ops pwm_regulator_voltage_ops = {
.map_voltage = regulator_map_voltage_iterate,
};
-static const struct regulator_desc pwm_regulator_desc = {
+static struct regulator_desc pwm_regulator_desc = {
.name = "pwm-regulator",
.ops = &pwm_regulator_voltage_ops,
.type = REGULATOR_VOLTAGE,
@@ -117,8 +112,6 @@ static int pwm_regulator_probe(struct platform_device *pdev)
if (!drvdata)
return -ENOMEM;
- memcpy(&drvdata->desc, &pwm_regulator_desc, sizeof(pwm_regulator_desc));
-
/* determine the number of voltage-table */
prop = of_find_property(np, "voltage-table", &length);
if (!prop) {
@@ -133,7 +126,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
return -EINVAL;
}
- drvdata->desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table);
+ pwm_regulator_desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table);
drvdata->duty_cycle_table = devm_kzalloc(&pdev->dev,
length, GFP_KERNEL);
@@ -150,7 +143,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
}
config.init_data = of_get_regulator_init_data(&pdev->dev, np,
- &drvdata->desc);
+ &pwm_regulator_desc);
if (!config.init_data)
return -ENOMEM;
@@ -165,10 +158,10 @@ static int pwm_regulator_probe(struct platform_device *pdev)
}
regulator = devm_regulator_register(&pdev->dev,
- &drvdata->desc, &config);
+ &pwm_regulator_desc, &config);
if (IS_ERR(regulator)) {
dev_err(&pdev->dev, "Failed to register regulator %s\n",
- drvdata->desc.name);
+ pwm_regulator_desc.name);
return PTR_ERR(regulator);
}
diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c
new file mode 100644
index 000000000000..850a30a95b5b
--- /dev/null
+++ b/drivers/regulator/qcom_spmi-regulator.c
@@ -0,0 +1,1435 @@
+/*
+ * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/ktime.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+#include <linux/list.h>
+
+/* These types correspond to unique register layouts. */
+enum spmi_regulator_logical_type {
+ SPMI_REGULATOR_LOGICAL_TYPE_SMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_LDO,
+ SPMI_REGULATOR_LOGICAL_TYPE_VS,
+ SPMI_REGULATOR_LOGICAL_TYPE_BOOST,
+ SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_BOOST_BYP,
+ SPMI_REGULATOR_LOGICAL_TYPE_LN_LDO,
+ SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO,
+};
+
+enum spmi_regulator_type {
+ SPMI_REGULATOR_TYPE_BUCK = 0x03,
+ SPMI_REGULATOR_TYPE_LDO = 0x04,
+ SPMI_REGULATOR_TYPE_VS = 0x05,
+ SPMI_REGULATOR_TYPE_BOOST = 0x1b,
+ SPMI_REGULATOR_TYPE_FTS = 0x1c,
+ SPMI_REGULATOR_TYPE_BOOST_BYP = 0x1f,
+ SPMI_REGULATOR_TYPE_ULT_LDO = 0x21,
+ SPMI_REGULATOR_TYPE_ULT_BUCK = 0x22,
+};
+
+enum spmi_regulator_subtype {
+ SPMI_REGULATOR_SUBTYPE_GP_CTL = 0x08,
+ SPMI_REGULATOR_SUBTYPE_RF_CTL = 0x09,
+ SPMI_REGULATOR_SUBTYPE_N50 = 0x01,
+ SPMI_REGULATOR_SUBTYPE_N150 = 0x02,
+ SPMI_REGULATOR_SUBTYPE_N300 = 0x03,
+ SPMI_REGULATOR_SUBTYPE_N600 = 0x04,
+ SPMI_REGULATOR_SUBTYPE_N1200 = 0x05,
+ SPMI_REGULATOR_SUBTYPE_N600_ST = 0x06,
+ SPMI_REGULATOR_SUBTYPE_N1200_ST = 0x07,
+ SPMI_REGULATOR_SUBTYPE_N900_ST = 0x14,
+ SPMI_REGULATOR_SUBTYPE_N300_ST = 0x15,
+ SPMI_REGULATOR_SUBTYPE_P50 = 0x08,
+ SPMI_REGULATOR_SUBTYPE_P150 = 0x09,
+ SPMI_REGULATOR_SUBTYPE_P300 = 0x0a,
+ SPMI_REGULATOR_SUBTYPE_P600 = 0x0b,
+ SPMI_REGULATOR_SUBTYPE_P1200 = 0x0c,
+ SPMI_REGULATOR_SUBTYPE_LN = 0x10,
+ SPMI_REGULATOR_SUBTYPE_LV_P50 = 0x28,
+ SPMI_REGULATOR_SUBTYPE_LV_P150 = 0x29,
+ SPMI_REGULATOR_SUBTYPE_LV_P300 = 0x2a,
+ SPMI_REGULATOR_SUBTYPE_LV_P600 = 0x2b,
+ SPMI_REGULATOR_SUBTYPE_LV_P1200 = 0x2c,
+ SPMI_REGULATOR_SUBTYPE_LV_P450 = 0x2d,
+ SPMI_REGULATOR_SUBTYPE_LV100 = 0x01,
+ SPMI_REGULATOR_SUBTYPE_LV300 = 0x02,
+ SPMI_REGULATOR_SUBTYPE_MV300 = 0x08,
+ SPMI_REGULATOR_SUBTYPE_MV500 = 0x09,
+ SPMI_REGULATOR_SUBTYPE_HDMI = 0x10,
+ SPMI_REGULATOR_SUBTYPE_OTG = 0x11,
+ SPMI_REGULATOR_SUBTYPE_5V_BOOST = 0x01,
+ SPMI_REGULATOR_SUBTYPE_FTS_CTL = 0x08,
+ SPMI_REGULATOR_SUBTYPE_FTS2p5_CTL = 0x09,
+ SPMI_REGULATOR_SUBTYPE_BB_2A = 0x01,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL1 = 0x0d,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL2 = 0x0e,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL3 = 0x0f,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL4 = 0x10,
+};
+
+enum spmi_common_regulator_registers {
+ SPMI_COMMON_REG_DIG_MAJOR_REV = 0x01,
+ SPMI_COMMON_REG_TYPE = 0x04,
+ SPMI_COMMON_REG_SUBTYPE = 0x05,
+ SPMI_COMMON_REG_VOLTAGE_RANGE = 0x40,
+ SPMI_COMMON_REG_VOLTAGE_SET = 0x41,
+ SPMI_COMMON_REG_MODE = 0x45,
+ SPMI_COMMON_REG_ENABLE = 0x46,
+ SPMI_COMMON_REG_PULL_DOWN = 0x48,
+ SPMI_COMMON_REG_SOFT_START = 0x4c,
+ SPMI_COMMON_REG_STEP_CTRL = 0x61,
+};
+
+enum spmi_vs_registers {
+ SPMI_VS_REG_OCP = 0x4a,
+ SPMI_VS_REG_SOFT_START = 0x4c,
+};
+
+enum spmi_boost_registers {
+ SPMI_BOOST_REG_CURRENT_LIMIT = 0x4a,
+};
+
+enum spmi_boost_byp_registers {
+ SPMI_BOOST_BYP_REG_CURRENT_LIMIT = 0x4b,
+};
+
+/* Used for indexing into ctrl_reg. These are offets from 0x40 */
+enum spmi_common_control_register_index {
+ SPMI_COMMON_IDX_VOLTAGE_RANGE = 0,
+ SPMI_COMMON_IDX_VOLTAGE_SET = 1,
+ SPMI_COMMON_IDX_MODE = 5,
+ SPMI_COMMON_IDX_ENABLE = 6,
+};
+
+/* Common regulator control register layout */
+#define SPMI_COMMON_ENABLE_MASK 0x80
+#define SPMI_COMMON_ENABLE 0x80
+#define SPMI_COMMON_DISABLE 0x00
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN3_MASK 0x08
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN2_MASK 0x04
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN1_MASK 0x02
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN0_MASK 0x01
+#define SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK 0x0f
+
+/* Common regulator mode register layout */
+#define SPMI_COMMON_MODE_HPM_MASK 0x80
+#define SPMI_COMMON_MODE_AUTO_MASK 0x40
+#define SPMI_COMMON_MODE_BYPASS_MASK 0x20
+#define SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK 0x10
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN3_MASK 0x08
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN2_MASK 0x04
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN1_MASK 0x02
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN0_MASK 0x01
+#define SPMI_COMMON_MODE_FOLLOW_ALL_MASK 0x1f
+
+/* Common regulator pull down control register layout */
+#define SPMI_COMMON_PULL_DOWN_ENABLE_MASK 0x80
+
+/* LDO regulator current limit control register layout */
+#define SPMI_LDO_CURRENT_LIMIT_ENABLE_MASK 0x80
+
+/* LDO regulator soft start control register layout */
+#define SPMI_LDO_SOFT_START_ENABLE_MASK 0x80
+
+/* VS regulator over current protection control register layout */
+#define SPMI_VS_OCP_OVERRIDE 0x01
+#define SPMI_VS_OCP_NO_OVERRIDE 0x00
+
+/* VS regulator soft start control register layout */
+#define SPMI_VS_SOFT_START_ENABLE_MASK 0x80
+#define SPMI_VS_SOFT_START_SEL_MASK 0x03
+
+/* Boost regulator current limit control register layout */
+#define SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK 0x80
+#define SPMI_BOOST_CURRENT_LIMIT_MASK 0x07
+
+#define SPMI_VS_OCP_DEFAULT_MAX_RETRIES 10
+#define SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS 30
+#define SPMI_VS_OCP_FALL_DELAY_US 90
+#define SPMI_VS_OCP_FAULT_DELAY_US 20000
+
+#define SPMI_FTSMPS_STEP_CTRL_STEP_MASK 0x18
+#define SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT 3
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_MASK 0x07
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT 0
+
+/* Clock rate in kHz of the FTSMPS regulator reference clock. */
+#define SPMI_FTSMPS_CLOCK_RATE 19200
+
+/* Minimum voltage stepper delay for each step. */
+#define SPMI_FTSMPS_STEP_DELAY 8
+
+/*
+ * The ratio SPMI_FTSMPS_STEP_MARGIN_NUM/SPMI_FTSMPS_STEP_MARGIN_DEN is used to
+ * adjust the step rate in order to account for oscillator variance.
+ */
+#define SPMI_FTSMPS_STEP_MARGIN_NUM 4
+#define SPMI_FTSMPS_STEP_MARGIN_DEN 5
+
+/*
+ * This voltage in uV is returned by get_voltage functions when there is no way
+ * to determine the current voltage level. It is needed because the regulator
+ * framework treats a 0 uV voltage as an error.
+ */
+#define VOLTAGE_UNKNOWN 1
+
+/* VSET value to decide the range of ULT SMPS */
+#define ULT_SMPS_RANGE_SPLIT 0x60
+
+/**
+ * struct spmi_voltage_range - regulator set point voltage mapping description
+ * @min_uV: Minimum programmable output voltage resulting from
+ * set point register value 0x00
+ * @max_uV: Maximum programmable output voltage
+ * @step_uV: Output voltage increase resulting from the set point
+ * register value increasing by 1
+ * @set_point_min_uV: Minimum allowed voltage
+ * @set_point_max_uV: Maximum allowed voltage. This may be tweaked in order
+ * to pick which range should be used in the case of
+ * overlapping set points.
+ * @n_voltages: Number of preferred voltage set points present in this
+ * range
+ * @range_sel: Voltage range register value corresponding to this range
+ *
+ * The following relationships must be true for the values used in this struct:
+ * (max_uV - min_uV) % step_uV == 0
+ * (set_point_min_uV - min_uV) % step_uV == 0*
+ * (set_point_max_uV - min_uV) % step_uV == 0*
+ * n_voltages = (set_point_max_uV - set_point_min_uV) / step_uV + 1
+ *
+ * *Note, set_point_min_uV == set_point_max_uV == 0 is allowed in order to
+ * specify that the voltage range has meaning, but is not preferred.
+ */
+struct spmi_voltage_range {
+ int min_uV;
+ int max_uV;
+ int step_uV;
+ int set_point_min_uV;
+ int set_point_max_uV;
+ unsigned n_voltages;
+ u8 range_sel;
+};
+
+/*
+ * The ranges specified in the spmi_voltage_set_points struct must be listed
+ * so that range[i].set_point_max_uV < range[i+1].set_point_min_uV.
+ */
+struct spmi_voltage_set_points {
+ struct spmi_voltage_range *range;
+ int count;
+ unsigned n_voltages;
+};
+
+struct spmi_regulator {
+ struct regulator_desc desc;
+ struct device *dev;
+ struct delayed_work ocp_work;
+ struct regmap *regmap;
+ struct spmi_voltage_set_points *set_points;
+ enum spmi_regulator_logical_type logical_type;
+ int ocp_irq;
+ int ocp_count;
+ int ocp_max_retries;
+ int ocp_retry_delay_ms;
+ int hpm_min_load;
+ int slew_rate;
+ ktime_t vs_enable_time;
+ u16 base;
+ struct list_head node;
+};
+
+struct spmi_regulator_mapping {
+ enum spmi_regulator_type type;
+ enum spmi_regulator_subtype subtype;
+ enum spmi_regulator_logical_type logical_type;
+ u32 revision_min;
+ u32 revision_max;
+ struct regulator_ops *ops;
+ struct spmi_voltage_set_points *set_points;
+ int hpm_min_load;
+};
+
+struct spmi_regulator_data {
+ const char *name;
+ u16 base;
+ const char *supply;
+ const char *ocp;
+ u16 force_type;
+};
+
+#define SPMI_VREG(_type, _subtype, _dig_major_min, _dig_major_max, \
+ _logical_type, _ops_val, _set_points_val, _hpm_min_load) \
+ { \
+ .type = SPMI_REGULATOR_TYPE_##_type, \
+ .subtype = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+ .revision_min = _dig_major_min, \
+ .revision_max = _dig_major_max, \
+ .logical_type = SPMI_REGULATOR_LOGICAL_TYPE_##_logical_type, \
+ .ops = &spmi_##_ops_val##_ops, \
+ .set_points = &_set_points_val##_set_points, \
+ .hpm_min_load = _hpm_min_load, \
+ }
+
+#define SPMI_VREG_VS(_subtype, _dig_major_min, _dig_major_max) \
+ { \
+ .type = SPMI_REGULATOR_TYPE_VS, \
+ .subtype = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+ .revision_min = _dig_major_min, \
+ .revision_max = _dig_major_max, \
+ .logical_type = SPMI_REGULATOR_LOGICAL_TYPE_VS, \
+ .ops = &spmi_vs_ops, \
+ }
+
+#define SPMI_VOLTAGE_RANGE(_range_sel, _min_uV, _set_point_min_uV, \
+ _set_point_max_uV, _max_uV, _step_uV) \
+ { \
+ .min_uV = _min_uV, \
+ .max_uV = _max_uV, \
+ .set_point_min_uV = _set_point_min_uV, \
+ .set_point_max_uV = _set_point_max_uV, \
+ .step_uV = _step_uV, \
+ .range_sel = _range_sel, \
+ }
+
+#define DEFINE_SPMI_SET_POINTS(name) \
+struct spmi_voltage_set_points name##_set_points = { \
+ .range = name##_ranges, \
+ .count = ARRAY_SIZE(name##_ranges), \
+}
+
+/*
+ * These tables contain the physically available PMIC regulator voltage setpoint
+ * ranges. Where two ranges overlap in hardware, one of the ranges is trimmed
+ * to ensure that the setpoints available to software are monotonically
+ * increasing and unique. The set_voltage callback functions expect these
+ * properties to hold.
+ */
+static struct spmi_voltage_range pldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(2, 750000, 750000, 1537500, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(3, 1500000, 1550000, 3075000, 3075000, 25000),
+ SPMI_VOLTAGE_RANGE(4, 1750000, 3100000, 4900000, 4900000, 50000),
+};
+
+static struct spmi_voltage_range nldo1_ranges[] = {
+ SPMI_VOLTAGE_RANGE(2, 750000, 750000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo2_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 0, 0, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 375000, 375000, 768750, 768750, 6250),
+ SPMI_VOLTAGE_RANGE(2, 750000, 775000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo3_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1537500, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 375000, 0, 0, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(2, 750000, 0, 0, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ln_ldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(1, 690000, 690000, 1110000, 1110000, 60000),
+ SPMI_VOLTAGE_RANGE(0, 1380000, 1380000, 2220000, 2220000, 120000),
+};
+
+static struct spmi_voltage_range smps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1562500, 1562500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 1550000, 1575000, 3125000, 3125000, 25000),
+};
+
+static struct spmi_voltage_range ftsmps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 0, 350000, 1275000, 1275000, 5000),
+ SPMI_VOLTAGE_RANGE(1, 0, 1280000, 2040000, 2040000, 10000),
+};
+
+static struct spmi_voltage_range ftsmps2p5_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 80000, 350000, 1355000, 1355000, 5000),
+ SPMI_VOLTAGE_RANGE(1, 160000, 1360000, 2200000, 2200000, 10000),
+};
+
+static struct spmi_voltage_range boost_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 4000000, 4000000, 5550000, 5550000, 50000),
+};
+
+static struct spmi_voltage_range boost_byp_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 2500000, 2500000, 5200000, 5650000, 50000),
+};
+
+static struct spmi_voltage_range ult_lo_smps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1562500, 1562500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 750000, 0, 0, 1525000, 25000),
+};
+
+static struct spmi_voltage_range ult_ho_smps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 1550000, 1550000, 2325000, 2325000, 25000),
+};
+
+static struct spmi_voltage_range ult_nldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ult_pldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 1750000, 1750000, 3337500, 3337500, 12500),
+};
+
+static DEFINE_SPMI_SET_POINTS(pldo);
+static DEFINE_SPMI_SET_POINTS(nldo1);
+static DEFINE_SPMI_SET_POINTS(nldo2);
+static DEFINE_SPMI_SET_POINTS(nldo3);
+static DEFINE_SPMI_SET_POINTS(ln_ldo);
+static DEFINE_SPMI_SET_POINTS(smps);
+static DEFINE_SPMI_SET_POINTS(ftsmps);
+static DEFINE_SPMI_SET_POINTS(ftsmps2p5);
+static DEFINE_SPMI_SET_POINTS(boost);
+static DEFINE_SPMI_SET_POINTS(boost_byp);
+static DEFINE_SPMI_SET_POINTS(ult_lo_smps);
+static DEFINE_SPMI_SET_POINTS(ult_ho_smps);
+static DEFINE_SPMI_SET_POINTS(ult_nldo);
+static DEFINE_SPMI_SET_POINTS(ult_pldo);
+
+static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf,
+ int len)
+{
+ return regmap_bulk_read(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static inline int spmi_vreg_write(struct spmi_regulator *vreg, u16 addr,
+ u8 *buf, int len)
+{
+ return regmap_bulk_write(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static int spmi_vreg_update_bits(struct spmi_regulator *vreg, u16 addr, u8 val,
+ u8 mask)
+{
+ return regmap_update_bits(vreg->regmap, vreg->base + addr, mask, val);
+}
+
+static int spmi_regulator_common_is_enabled(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 reg;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_ENABLE, &reg, 1);
+
+ return (reg & SPMI_COMMON_ENABLE_MASK) == SPMI_COMMON_ENABLE;
+}
+
+static int spmi_regulator_common_enable(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+ SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+}
+
+static int spmi_regulator_vs_enable(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+ if (vreg->ocp_irq) {
+ vreg->ocp_count = 0;
+ vreg->vs_enable_time = ktime_get();
+ }
+
+ return spmi_regulator_common_enable(rdev);
+}
+
+static int spmi_regulator_common_disable(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+ SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+}
+
+static int spmi_regulator_select_voltage(struct spmi_regulator *vreg,
+ int min_uV, int max_uV, u8 *range_sel, u8 *voltage_sel,
+ unsigned *selector)
+{
+ const struct spmi_voltage_range *range;
+ int uV = min_uV;
+ int lim_min_uV, lim_max_uV, i, range_id, range_max_uV;
+
+ /* Check if request voltage is outside of physically settable range. */
+ lim_min_uV = vreg->set_points->range[0].set_point_min_uV;
+ lim_max_uV =
+ vreg->set_points->range[vreg->set_points->count - 1].set_point_max_uV;
+
+ if (uV < lim_min_uV && max_uV >= lim_min_uV)
+ uV = lim_min_uV;
+
+ if (uV < lim_min_uV || uV > lim_max_uV) {
+ dev_err(vreg->dev,
+ "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+ min_uV, max_uV, lim_min_uV, lim_max_uV);
+ return -EINVAL;
+ }
+
+ /* Find the range which uV is inside of. */
+ for (i = vreg->set_points->count - 1; i > 0; i--) {
+ range_max_uV = vreg->set_points->range[i - 1].set_point_max_uV;
+ if (uV > range_max_uV && range_max_uV > 0)
+ break;
+ }
+
+ range_id = i;
+ range = &vreg->set_points->range[range_id];
+ *range_sel = range->range_sel;
+
+ /*
+ * Force uV to be an allowed set point by applying a ceiling function to
+ * the uV value.
+ */
+ *voltage_sel = (uV - range->min_uV + range->step_uV - 1)
+ / range->step_uV;
+ uV = *voltage_sel * range->step_uV + range->min_uV;
+
+ if (uV > max_uV) {
+ dev_err(vreg->dev,
+ "request v=[%d, %d] cannot be met by any set point; "
+ "next set point: %d\n",
+ min_uV, max_uV, uV);
+ return -EINVAL;
+ }
+
+ *selector = 0;
+ for (i = 0; i < range_id; i++)
+ *selector += vreg->set_points->range[i].n_voltages;
+ *selector += (uV - range->set_point_min_uV) / range->step_uV;
+
+ return 0;
+}
+
+static const struct spmi_voltage_range *
+spmi_regulator_find_range(struct spmi_regulator *vreg)
+{
+ u8 range_sel;
+ const struct spmi_voltage_range *range, *end;
+
+ range = vreg->set_points->range;
+ end = range + vreg->set_points->count;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, &range_sel, 1);
+
+ for (; range < end; range++)
+ if (range->range_sel == range_sel)
+ return range;
+
+ return NULL;
+}
+
+static int spmi_regulator_select_voltage_same_range(struct spmi_regulator *vreg,
+ int min_uV, int max_uV, u8 *range_sel, u8 *voltage_sel,
+ unsigned *selector)
+{
+ const struct spmi_voltage_range *range;
+ int uV = min_uV;
+ int i;
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ goto different_range;
+
+ if (uV < range->min_uV && max_uV >= range->min_uV)
+ uV = range->min_uV;
+
+ if (uV < range->min_uV || uV > range->max_uV) {
+ /* Current range doesn't support the requested voltage. */
+ goto different_range;
+ }
+
+ /*
+ * Force uV to be an allowed set point by applying a ceiling function to
+ * the uV value.
+ */
+ *voltage_sel = DIV_ROUND_UP(uV - range->min_uV, range->step_uV);
+ uV = *voltage_sel * range->step_uV + range->min_uV;
+
+ if (uV > max_uV) {
+ /*
+ * No set point in the current voltage range is within the
+ * requested min_uV to max_uV range.
+ */
+ goto different_range;
+ }
+
+ *selector = 0;
+ for (i = 0; i < vreg->set_points->count; i++) {
+ if (uV >= vreg->set_points->range[i].set_point_min_uV
+ && uV <= vreg->set_points->range[i].set_point_max_uV) {
+ *selector +=
+ (uV - vreg->set_points->range[i].set_point_min_uV)
+ / vreg->set_points->range[i].step_uV;
+ break;
+ }
+
+ *selector += vreg->set_points->range[i].n_voltages;
+ }
+
+ if (*selector >= vreg->set_points->n_voltages)
+ goto different_range;
+
+ return 0;
+
+different_range:
+ return spmi_regulator_select_voltage(vreg, min_uV, max_uV,
+ range_sel, voltage_sel, selector);
+}
+
+static int spmi_regulator_common_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int ret;
+ u8 buf[2];
+ u8 range_sel, voltage_sel;
+
+ /*
+ * Favor staying in the current voltage range if possible. This avoids
+ * voltage spikes that occur when changing the voltage range.
+ */
+ ret = spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+ &range_sel, &voltage_sel, selector);
+ if (ret)
+ return ret;
+
+ buf[0] = range_sel;
+ buf[1] = voltage_sel;
+ return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, buf, 2);
+}
+
+static int spmi_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_selector, unsigned int new_selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range;
+ int diff_uV;
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return -EINVAL;
+
+ diff_uV = abs(new_selector - old_selector) * range->step_uV;
+
+ return DIV_ROUND_UP(diff_uV, vreg->slew_rate);
+}
+
+static int spmi_regulator_common_get_voltage(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range;
+ u8 voltage_sel;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return VOLTAGE_UNKNOWN;
+
+ return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_single_range_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int ret;
+ u8 range_sel, sel;
+
+ ret = spmi_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
+ &sel, selector);
+ if (ret) {
+ dev_err(vreg->dev, "could not set voltage, ret=%d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Certain types of regulators do not have a range select register so
+ * only voltage set register needs to be written.
+ */
+ return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &sel, 1);
+}
+
+static int spmi_regulator_single_range_get_voltage(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range = vreg->set_points->range;
+ u8 voltage_sel;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+ return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_ult_lo_smps_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int ret;
+ u8 range_sel, voltage_sel;
+
+ /*
+ * Favor staying in the current voltage range if possible. This avoids
+ * voltage spikes that occur when changing the voltage range.
+ */
+ ret = spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+ &range_sel, &voltage_sel, selector);
+ if (ret)
+ return ret;
+
+ /*
+ * Calculate VSET based on range
+ * In case of range 0: voltage_sel is a 7 bit value, can be written
+ * witout any modification.
+ * In case of range 1: voltage_sel is a 5 bit value, bits[7-5] set to
+ * [011].
+ */
+ if (range_sel == 1)
+ voltage_sel |= ULT_SMPS_RANGE_SPLIT;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_VOLTAGE_SET,
+ voltage_sel, 0xff);
+}
+
+static int spmi_regulator_ult_lo_smps_get_voltage(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range;
+ u8 voltage_sel;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return VOLTAGE_UNKNOWN;
+
+ if (range->range_sel == 1)
+ voltage_sel &= ~ULT_SMPS_RANGE_SPLIT;
+
+ return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_common_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int uV = 0;
+ int i;
+
+ if (selector >= vreg->set_points->n_voltages)
+ return 0;
+
+ for (i = 0; i < vreg->set_points->count; i++) {
+ if (selector < vreg->set_points->range[i].n_voltages) {
+ uV = selector * vreg->set_points->range[i].step_uV
+ + vreg->set_points->range[i].set_point_min_uV;
+ break;
+ }
+
+ selector -= vreg->set_points->range[i].n_voltages;
+ }
+
+ return uV;
+}
+
+static int
+spmi_regulator_common_set_bypass(struct regulator_dev *rdev, bool enable)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 mask = SPMI_COMMON_MODE_BYPASS_MASK;
+ u8 val = 0;
+
+ if (enable)
+ val = mask;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_get_bypass(struct regulator_dev *rdev, bool *enable)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 val;
+ int ret;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &val, 1);
+ *enable = val & SPMI_COMMON_MODE_BYPASS_MASK;
+
+ return ret;
+}
+
+static unsigned int spmi_regulator_common_get_mode(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 reg;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &reg, 1);
+
+ if (reg & SPMI_COMMON_MODE_HPM_MASK)
+ return REGULATOR_MODE_NORMAL;
+
+ return REGULATOR_MODE_IDLE;
+}
+
+static int
+spmi_regulator_common_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 mask = SPMI_COMMON_MODE_HPM_MASK;
+ u8 val = 0;
+
+ if (mode == REGULATOR_MODE_NORMAL)
+ val = mask;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_set_load(struct regulator_dev *rdev, int load_uA)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ unsigned int mode;
+
+ if (load_uA >= vreg->hpm_min_load)
+ mode = REGULATOR_MODE_NORMAL;
+ else
+ mode = REGULATOR_MODE_IDLE;
+
+ return spmi_regulator_common_set_mode(rdev, mode);
+}
+
+static int spmi_regulator_common_set_pull_down(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ unsigned int mask = SPMI_COMMON_PULL_DOWN_ENABLE_MASK;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_PULL_DOWN,
+ mask, mask);
+}
+
+static int spmi_regulator_common_set_soft_start(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ unsigned int mask = SPMI_LDO_SOFT_START_ENABLE_MASK;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_SOFT_START,
+ mask, mask);
+}
+
+static int spmi_regulator_set_ilim(struct regulator_dev *rdev, int ilim_uA)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ enum spmi_regulator_logical_type type = vreg->logical_type;
+ unsigned int current_reg;
+ u8 reg;
+ u8 mask = SPMI_BOOST_CURRENT_LIMIT_MASK |
+ SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+ int max = (SPMI_BOOST_CURRENT_LIMIT_MASK + 1) * 500;
+
+ if (type == SPMI_REGULATOR_LOGICAL_TYPE_BOOST)
+ current_reg = SPMI_BOOST_REG_CURRENT_LIMIT;
+ else
+ current_reg = SPMI_BOOST_BYP_REG_CURRENT_LIMIT;
+
+ if (ilim_uA > max || ilim_uA <= 0)
+ return -EINVAL;
+
+ reg = (ilim_uA - 1) / 500;
+ reg |= SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+
+ return spmi_vreg_update_bits(vreg, current_reg, reg, mask);
+}
+
+static int spmi_regulator_vs_clear_ocp(struct spmi_regulator *vreg)
+{
+ int ret;
+
+ ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+ SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+
+ vreg->vs_enable_time = ktime_get();
+
+ ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+ SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+
+ return ret;
+}
+
+static void spmi_regulator_vs_ocp_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct spmi_regulator *vreg
+ = container_of(dwork, struct spmi_regulator, ocp_work);
+
+ spmi_regulator_vs_clear_ocp(vreg);
+}
+
+static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data)
+{
+ struct spmi_regulator *vreg = data;
+ ktime_t ocp_irq_time;
+ s64 ocp_trigger_delay_us;
+
+ ocp_irq_time = ktime_get();
+ ocp_trigger_delay_us = ktime_us_delta(ocp_irq_time,
+ vreg->vs_enable_time);
+
+ /*
+ * Reset the OCP count if there is a large delay between switch enable
+ * and when OCP triggers. This is indicative of a hotplug event as
+ * opposed to a fault.
+ */
+ if (ocp_trigger_delay_us > SPMI_VS_OCP_FAULT_DELAY_US)
+ vreg->ocp_count = 0;
+
+ /* Wait for switch output to settle back to 0 V after OCP triggered. */
+ udelay(SPMI_VS_OCP_FALL_DELAY_US);
+
+ vreg->ocp_count++;
+
+ if (vreg->ocp_count == 1) {
+ /* Immediately clear the over current condition. */
+ spmi_regulator_vs_clear_ocp(vreg);
+ } else if (vreg->ocp_count <= vreg->ocp_max_retries) {
+ /* Schedule the over current clear task to run later. */
+ schedule_delayed_work(&vreg->ocp_work,
+ msecs_to_jiffies(vreg->ocp_retry_delay_ms) + 1);
+ } else {
+ dev_err(vreg->dev,
+ "OCP triggered %d times; no further retries\n",
+ vreg->ocp_count);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct regulator_ops spmi_smps_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_common_set_voltage,
+ .get_voltage = spmi_regulator_common_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ldo_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_common_set_voltage,
+ .get_voltage = spmi_regulator_common_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_bypass = spmi_regulator_common_set_bypass,
+ .get_bypass = spmi_regulator_common_get_bypass,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+ .set_soft_start = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_ln_ldo_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_common_set_voltage,
+ .get_voltage = spmi_regulator_common_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_bypass = spmi_regulator_common_set_bypass,
+ .get_bypass = spmi_regulator_common_get_bypass,
+};
+
+static struct regulator_ops spmi_vs_ops = {
+ .enable = spmi_regulator_vs_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+ .set_soft_start = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_boost_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_single_range_set_voltage,
+ .get_voltage = spmi_regulator_single_range_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_input_current_limit = spmi_regulator_set_ilim,
+};
+
+static struct regulator_ops spmi_ftsmps_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_common_set_voltage,
+ .set_voltage_time_sel = spmi_regulator_set_voltage_time_sel,
+ .get_voltage = spmi_regulator_common_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_lo_smps_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_ult_lo_smps_set_voltage,
+ .get_voltage = spmi_regulator_ult_lo_smps_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ho_smps_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_single_range_set_voltage,
+ .get_voltage = spmi_regulator_single_range_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ldo_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_single_range_set_voltage,
+ .get_voltage = spmi_regulator_single_range_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_bypass = spmi_regulator_common_set_bypass,
+ .get_bypass = spmi_regulator_common_get_bypass,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+ .set_soft_start = spmi_regulator_common_set_soft_start,
+};
+
+/* Maximum possible digital major revision value */
+#define INF 0xFF
+
+static const struct spmi_regulator_mapping supported_regulators[] = {
+ /* type subtype dig_min dig_max ltype ops setpoints hpm_min */
+ SPMI_VREG(BUCK, GP_CTL, 0, INF, SMPS, smps, smps, 100000),
+ SPMI_VREG(LDO, N300, 0, INF, LDO, ldo, nldo1, 10000),
+ SPMI_VREG(LDO, N600, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N1200, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N600, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, N1200, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, N600_ST, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N1200_ST, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N600_ST, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, N1200_ST, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, P50, 0, INF, LDO, ldo, pldo, 5000),
+ SPMI_VREG(LDO, P150, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, P300, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, P600, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, P1200, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LN, 0, INF, LN_LDO, ln_ldo, ln_ldo, 0),
+ SPMI_VREG(LDO, LV_P50, 0, INF, LDO, ldo, pldo, 5000),
+ SPMI_VREG(LDO, LV_P150, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LV_P300, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LV_P600, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LV_P1200, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG_VS(LV100, 0, INF),
+ SPMI_VREG_VS(LV300, 0, INF),
+ SPMI_VREG_VS(MV300, 0, INF),
+ SPMI_VREG_VS(MV500, 0, INF),
+ SPMI_VREG_VS(HDMI, 0, INF),
+ SPMI_VREG_VS(OTG, 0, INF),
+ SPMI_VREG(BOOST, 5V_BOOST, 0, INF, BOOST, boost, boost, 0),
+ SPMI_VREG(FTS, FTS_CTL, 0, INF, FTSMPS, ftsmps, ftsmps, 100000),
+ SPMI_VREG(FTS, FTS2p5_CTL, 0, INF, FTSMPS, ftsmps, ftsmps2p5, 100000),
+ SPMI_VREG(BOOST_BYP, BB_2A, 0, INF, BOOST_BYP, boost, boost_byp, 0),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL1, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+ ult_lo_smps, 100000),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL2, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+ ult_lo_smps, 100000),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL3, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+ ult_lo_smps, 100000),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL4, 0, INF, ULT_HO_SMPS, ult_ho_smps,
+ ult_ho_smps, 100000),
+ SPMI_VREG(ULT_LDO, N300_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, N600_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, N900_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, N1200_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, LV_P150, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, LV_P300, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, LV_P450, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, P600, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, P150, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, P50, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 5000),
+};
+
+static void spmi_calculate_num_voltages(struct spmi_voltage_set_points *points)
+{
+ unsigned int n;
+ struct spmi_voltage_range *range = points->range;
+
+ for (; range < points->range + points->count; range++) {
+ n = 0;
+ if (range->set_point_max_uV) {
+ n = range->set_point_max_uV - range->set_point_min_uV;
+ n = (n / range->step_uV) + 1;
+ }
+ range->n_voltages = n;
+ points->n_voltages += n;
+ }
+}
+
+static int spmi_regulator_match(struct spmi_regulator *vreg, u16 force_type)
+{
+ const struct spmi_regulator_mapping *mapping;
+ int ret, i;
+ u32 dig_major_rev;
+ u8 version[SPMI_COMMON_REG_SUBTYPE - SPMI_COMMON_REG_DIG_MAJOR_REV + 1];
+ u8 type, subtype;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_DIG_MAJOR_REV, version,
+ ARRAY_SIZE(version));
+ if (ret) {
+ dev_err(vreg->dev, "could not read version registers\n");
+ return ret;
+ }
+ dig_major_rev = version[SPMI_COMMON_REG_DIG_MAJOR_REV
+ - SPMI_COMMON_REG_DIG_MAJOR_REV];
+ if (!force_type) {
+ type = version[SPMI_COMMON_REG_TYPE -
+ SPMI_COMMON_REG_DIG_MAJOR_REV];
+ subtype = version[SPMI_COMMON_REG_SUBTYPE -
+ SPMI_COMMON_REG_DIG_MAJOR_REV];
+ } else {
+ type = force_type >> 8;
+ subtype = force_type;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
+ mapping = &supported_regulators[i];
+ if (mapping->type == type && mapping->subtype == subtype
+ && mapping->revision_min <= dig_major_rev
+ && mapping->revision_max >= dig_major_rev)
+ goto found;
+ }
+
+ dev_err(vreg->dev,
+ "unsupported regulator: name=%s type=0x%02X, subtype=0x%02X, dig major rev=0x%02X\n",
+ vreg->desc.name, type, subtype, dig_major_rev);
+
+ return -ENODEV;
+
+found:
+ vreg->logical_type = mapping->logical_type;
+ vreg->set_points = mapping->set_points;
+ vreg->hpm_min_load = mapping->hpm_min_load;
+ vreg->desc.ops = mapping->ops;
+
+ if (mapping->set_points) {
+ if (!mapping->set_points->n_voltages)
+ spmi_calculate_num_voltages(mapping->set_points);
+ vreg->desc.n_voltages = mapping->set_points->n_voltages;
+ }
+
+ return 0;
+}
+
+static int spmi_regulator_ftsmps_init_slew_rate(struct spmi_regulator *vreg)
+{
+ int ret;
+ u8 reg = 0;
+ int step, delay, slew_rate;
+ const struct spmi_voltage_range *range;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_STEP_CTRL, &reg, 1);
+ if (ret) {
+ dev_err(vreg->dev, "spmi read failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return -EINVAL;
+
+ step = reg & SPMI_FTSMPS_STEP_CTRL_STEP_MASK;
+ step >>= SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT;
+
+ delay = reg & SPMI_FTSMPS_STEP_CTRL_DELAY_MASK;
+ delay >>= SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT;
+
+ /* slew_rate has units of uV/us */
+ slew_rate = SPMI_FTSMPS_CLOCK_RATE * range->step_uV * (1 << step);
+ slew_rate /= 1000 * (SPMI_FTSMPS_STEP_DELAY << delay);
+ slew_rate *= SPMI_FTSMPS_STEP_MARGIN_NUM;
+ slew_rate /= SPMI_FTSMPS_STEP_MARGIN_DEN;
+
+ /* Ensure that the slew rate is greater than 0 */
+ vreg->slew_rate = max(slew_rate, 1);
+
+ return ret;
+}
+
+static unsigned int spmi_regulator_of_map_mode(unsigned int mode)
+{
+ if (mode)
+ return REGULATOR_MODE_NORMAL;
+
+ return REGULATOR_MODE_IDLE;
+}
+
+static int spmi_regulator_of_parse(struct device_node *node,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct spmi_regulator *vreg = config->driver_data;
+ struct device *dev = config->dev;
+ int ret;
+
+ vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES;
+ vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS;
+
+ if (vreg->logical_type == SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS) {
+ ret = spmi_regulator_ftsmps_init_slew_rate(vreg);
+ if (ret)
+ return ret;
+ }
+
+ if (vreg->logical_type != SPMI_REGULATOR_LOGICAL_TYPE_VS)
+ vreg->ocp_irq = 0;
+
+ if (vreg->ocp_irq) {
+ ret = devm_request_irq(dev, vreg->ocp_irq,
+ spmi_regulator_vs_ocp_isr, IRQF_TRIGGER_RISING, "ocp",
+ vreg);
+ if (ret < 0) {
+ dev_err(dev, "failed to request irq %d, ret=%d\n",
+ vreg->ocp_irq, ret);
+ return ret;
+ }
+
+ INIT_DELAYED_WORK(&vreg->ocp_work, spmi_regulator_vs_ocp_work);
+ }
+
+ return 0;
+}
+
+static const struct spmi_regulator_data pm8941_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "l1", 0x4000, "vdd_l1_l3", },
+ { "l2", 0x4100, "vdd_l2_lvs_1_2_3", },
+ { "l3", 0x4200, "vdd_l1_l3", },
+ { "l4", 0x4300, "vdd_l4_l11", },
+ { "l5", 0x4400, "vdd_l5_l7", NULL, 0x0410 },
+ { "l6", 0x4500, "vdd_l6_l12_l14_l15", },
+ { "l7", 0x4600, "vdd_l5_l7", NULL, 0x0410 },
+ { "l8", 0x4700, "vdd_l8_l16_l18_19", },
+ { "l9", 0x4800, "vdd_l9_l10_l17_l22", },
+ { "l10", 0x4900, "vdd_l9_l10_l17_l22", },
+ { "l11", 0x4a00, "vdd_l4_l11", },
+ { "l12", 0x4b00, "vdd_l6_l12_l14_l15", },
+ { "l13", 0x4c00, "vdd_l13_l20_l23_l24", },
+ { "l14", 0x4d00, "vdd_l6_l12_l14_l15", },
+ { "l15", 0x4e00, "vdd_l6_l12_l14_l15", },
+ { "l16", 0x4f00, "vdd_l8_l16_l18_19", },
+ { "l17", 0x5000, "vdd_l9_l10_l17_l22", },
+ { "l18", 0x5100, "vdd_l8_l16_l18_19", },
+ { "l19", 0x5200, "vdd_l8_l16_l18_19", },
+ { "l20", 0x5300, "vdd_l13_l20_l23_l24", },
+ { "l21", 0x5400, "vdd_l21", },
+ { "l22", 0x5500, "vdd_l9_l10_l17_l22", },
+ { "l23", 0x5600, "vdd_l13_l20_l23_l24", },
+ { "l24", 0x5700, "vdd_l13_l20_l23_l24", },
+ { "lvs1", 0x8000, "vdd_l2_lvs_1_2_3", },
+ { "lvs2", 0x8100, "vdd_l2_lvs_1_2_3", },
+ { "lvs3", 0x8200, "vdd_l2_lvs_1_2_3", },
+ { "mvs1", 0x8300, "vin_5vs", },
+ { "mvs2", 0x8400, "vin_5vs", },
+ { }
+};
+
+static const struct spmi_regulator_data pm8841_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", NULL, 0x1c08 },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "s4", 0x1d00, "vdd_s4", NULL, 0x1c08 },
+ { "s5", 0x2000, "vdd_s5", NULL, 0x1c08 },
+ { "s6", 0x2300, "vdd_s6", NULL, 0x1c08 },
+ { "s7", 0x2600, "vdd_s7", NULL, 0x1c08 },
+ { "s8", 0x2900, "vdd_s8", NULL, 0x1c08 },
+ { }
+};
+
+static const struct spmi_regulator_data pm8916_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "s4", 0x1d00, "vdd_s4", },
+ { "l1", 0x4000, "vdd_l1_l3", },
+ { "l2", 0x4100, "vdd_l2", },
+ { "l3", 0x4200, "vdd_l1_l3", },
+ { "l4", 0x4300, "vdd_l4_l5_l6", },
+ { "l5", 0x4400, "vdd_l4_l5_l6", },
+ { "l6", 0x4500, "vdd_l4_l5_l6", },
+ { "l7", 0x4600, "vdd_l7", },
+ { "l8", 0x4700, "vdd_l8_l11_l14_l15_l16", },
+ { "l9", 0x4800, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l10", 0x4900, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l11", 0x4a00, "vdd_l8_l11_l14_l15_l16", },
+ { "l12", 0x4b00, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l13", 0x4c00, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l14", 0x4d00, "vdd_l8_l11_l14_l15_l16", },
+ { "l15", 0x4e00, "vdd_l8_l11_l14_l15_l16", },
+ { "l16", 0x4f00, "vdd_l8_l11_l14_l15_l16", },
+ { "l17", 0x5000, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l18", 0x5100, "vdd_l9_l10_l12_l13_l17_l18", },
+ { }
+};
+
+static const struct of_device_id qcom_spmi_regulator_match[] = {
+ { .compatible = "qcom,pm8841-regulators", .data = &pm8841_regulators },
+ { .compatible = "qcom,pm8916-regulators", .data = &pm8916_regulators },
+ { .compatible = "qcom,pm8941-regulators", .data = &pm8941_regulators },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_spmi_regulator_match);
+
+static int qcom_spmi_regulator_probe(struct platform_device *pdev)
+{
+ const struct spmi_regulator_data *reg;
+ const struct of_device_id *match;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct spmi_regulator *vreg;
+ struct regmap *regmap;
+ const char *name;
+ struct device *dev = &pdev->dev;
+ int ret;
+ struct list_head *vreg_list;
+
+ vreg_list = devm_kzalloc(dev, sizeof(*vreg_list), GFP_KERNEL);
+ if (!vreg_list)
+ return -ENOMEM;
+ INIT_LIST_HEAD(vreg_list);
+ platform_set_drvdata(pdev, vreg_list);
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ match = of_match_device(qcom_spmi_regulator_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ for (reg = match->data; reg->name; reg++) {
+ vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg)
+ return -ENOMEM;
+
+ vreg->dev = dev;
+ vreg->base = reg->base;
+ vreg->regmap = regmap;
+
+ if (reg->ocp) {
+ vreg->ocp_irq = platform_get_irq_byname(pdev, reg->ocp);
+ if (vreg->ocp_irq < 0) {
+ ret = vreg->ocp_irq;
+ goto err;
+ }
+ }
+
+ vreg->desc.id = -1;
+ vreg->desc.owner = THIS_MODULE;
+ vreg->desc.type = REGULATOR_VOLTAGE;
+ vreg->desc.name = name = reg->name;
+ vreg->desc.supply_name = reg->supply;
+ vreg->desc.of_match = reg->name;
+ vreg->desc.of_parse_cb = spmi_regulator_of_parse;
+ vreg->desc.of_map_mode = spmi_regulator_of_map_mode;
+
+ ret = spmi_regulator_match(vreg, reg->force_type);
+ if (ret)
+ goto err;
+
+ config.dev = dev;
+ config.driver_data = vreg;
+ rdev = devm_regulator_register(dev, &vreg->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register %s\n", name);
+ ret = PTR_ERR(rdev);
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&vreg->node);
+ list_add(&vreg->node, vreg_list);
+ }
+
+ return 0;
+
+err:
+ list_for_each_entry(vreg, vreg_list, node)
+ if (vreg->ocp_irq)
+ cancel_delayed_work_sync(&vreg->ocp_work);
+ return ret;
+}
+
+static int qcom_spmi_regulator_remove(struct platform_device *pdev)
+{
+ struct spmi_regulator *vreg;
+ struct list_head *vreg_list = platform_get_drvdata(pdev);
+
+ list_for_each_entry(vreg, vreg_list, node)
+ if (vreg->ocp_irq)
+ cancel_delayed_work_sync(&vreg->ocp_work);
+
+ return 0;
+}
+
+static struct platform_driver qcom_spmi_regulator_driver = {
+ .driver = {
+ .name = "qcom-spmi-regulator",
+ .of_match_table = qcom_spmi_regulator_match,
+ },
+ .probe = qcom_spmi_regulator_probe,
+ .remove = qcom_spmi_regulator_remove,
+};
+module_platform_driver(qcom_spmi_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qcom-spmi-regulator");
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index ff828117798f..326ffb553371 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -515,7 +515,7 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev)
break;
default:
return -EINVAL;
- };
+ }
return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
rdev->desc->enable_mask, val);
@@ -538,7 +538,7 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev)
default:
state = S2MPS14_ENABLE_SUSPEND;
break;
- };
+ }
break;
case S2MPU02:
switch (rdev_id) {
@@ -552,11 +552,11 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev)
default:
state = S2MPU02_ENABLE_SUSPEND;
break;
- };
+ }
break;
default:
return -EINVAL;
- };
+ }
ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
if (ret < 0)
@@ -977,7 +977,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Invalid device type: %u\n",
s2mps11->dev_type);
return -EINVAL;
- };
+ }
s2mps11->ext_control_gpio = devm_kmalloc(&pdev->dev,
sizeof(*s2mps11->ext_control_gpio) * s2mps11->rdev_num,
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
index 0d7e164a5e76..8cbb82ceec40 100644
--- a/drivers/regulator/wm831x-dcdc.c
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -533,7 +533,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_dcdc_uv_irq,
- IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name, dcdc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret);
@@ -543,7 +544,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_dcdc_oc_irq,
- IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name, dcdc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n",
irq, ret);
@@ -669,7 +671,8 @@ static int wm831x_buckp_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_dcdc_uv_irq,
- IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name, dcdc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret);
@@ -785,7 +788,8 @@ static int wm831x_boostp_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_dcdc_uv_irq,
- IRQF_TRIGGER_RISING, dcdc->name,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name,
dcdc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c
index 1e88391a1628..1442828fcd9a 100644
--- a/drivers/regulator/wm831x-isink.c
+++ b/drivers/regulator/wm831x-isink.c
@@ -204,7 +204,8 @@ static int wm831x_isink_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_isink_irq,
- IRQF_TRIGGER_RISING, isink->name,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ isink->name,
isink);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n",
diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c
index 7ae2dc82f636..5a7b65e8a529 100644
--- a/drivers/regulator/wm831x-ldo.c
+++ b/drivers/regulator/wm831x-ldo.c
@@ -287,7 +287,8 @@ static int wm831x_gp_ldo_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_ldo_uv_irq,
- IRQF_TRIGGER_RISING, ldo->name,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ ldo->name,
ldo);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
@@ -496,7 +497,8 @@ static int wm831x_aldo_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_ldo_uv_irq,
- IRQF_TRIGGER_RISING, ldo->name, ldo);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ ldo->name, ldo);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret);
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 6149ae01e11f..5e963df9e565 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -164,6 +164,16 @@ config RTC_DRV_ABB5ZES3
This driver can also be built as a module. If so, the module
will be called rtc-ab-b5ze-s3.
+config RTC_DRV_ABX80X
+ tristate "Abracon ABx80x"
+ help
+ If you say yes here you get support for Abracon AB080X and AB180X
+ families of ultra-low-power battery- and capacitor-backed real-time
+ clock chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-abx80x.
+
config RTC_DRV_AS3722
tristate "ams AS3722 RTC driver"
depends on MFD_AS3722
@@ -1510,6 +1520,17 @@ config RTC_DRV_SIRFSOC
Say "yes" here to support the real time clock on SiRF SOC chips.
This driver can also be built as a module called rtc-sirfsoc.
+config RTC_DRV_ST_LPC
+ tristate "STMicroelectronics LPC RTC"
+ depends on ARCH_STI
+ depends on OF
+ help
+ Say Y here to include STMicroelectronics Low Power Controller
+ (LPC) based RTC support.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-st-lpc.
+
config RTC_DRV_MOXART
tristate "MOXA ART RTC"
depends on ARCH_MOXART || COMPILE_TEST
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index c31731c29762..ebe2c085d01c 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o
+obj-$(CONFIG_RTC_DRV_ABX80X) += rtc-abx80x.o
obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o
obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
@@ -153,4 +154,5 @@ obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o
obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o
+obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
new file mode 100644
index 000000000000..4337c3bc6ace
--- /dev/null
+++ b/drivers/rtc/rtc-abx80x.c
@@ -0,0 +1,307 @@
+/*
+ * A driver for the I2C members of the Abracon AB x8xx RTC family,
+ * and compatible: AB 1805 and AB 0805
+ *
+ * Copyright 2014-2015 Macq S.A.
+ *
+ * Author: Philippe De Muyter <phdm@macqel.be>
+ * Author: Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+
+#define ABX8XX_REG_HTH 0x00
+#define ABX8XX_REG_SC 0x01
+#define ABX8XX_REG_MN 0x02
+#define ABX8XX_REG_HR 0x03
+#define ABX8XX_REG_DA 0x04
+#define ABX8XX_REG_MO 0x05
+#define ABX8XX_REG_YR 0x06
+#define ABX8XX_REG_WD 0x07
+
+#define ABX8XX_REG_CTRL1 0x10
+#define ABX8XX_CTRL_WRITE BIT(1)
+#define ABX8XX_CTRL_12_24 BIT(6)
+
+#define ABX8XX_REG_CFG_KEY 0x1f
+#define ABX8XX_CFG_KEY_MISC 0x9d
+
+#define ABX8XX_REG_ID0 0x28
+
+#define ABX8XX_REG_TRICKLE 0x20
+#define ABX8XX_TRICKLE_CHARGE_ENABLE 0xa0
+#define ABX8XX_TRICKLE_STANDARD_DIODE 0x8
+#define ABX8XX_TRICKLE_SCHOTTKY_DIODE 0x4
+
+static u8 trickle_resistors[] = {0, 3, 6, 11};
+
+enum abx80x_chip {AB0801, AB0803, AB0804, AB0805,
+ AB1801, AB1803, AB1804, AB1805, ABX80X};
+
+struct abx80x_cap {
+ u16 pn;
+ bool has_tc;
+};
+
+static struct abx80x_cap abx80x_caps[] = {
+ [AB0801] = {.pn = 0x0801},
+ [AB0803] = {.pn = 0x0803},
+ [AB0804] = {.pn = 0x0804, .has_tc = true},
+ [AB0805] = {.pn = 0x0805, .has_tc = true},
+ [AB1801] = {.pn = 0x1801},
+ [AB1803] = {.pn = 0x1803},
+ [AB1804] = {.pn = 0x1804, .has_tc = true},
+ [AB1805] = {.pn = 0x1805, .has_tc = true},
+ [ABX80X] = {.pn = 0}
+};
+
+static struct i2c_driver abx80x_driver;
+
+static int abx80x_enable_trickle_charger(struct i2c_client *client,
+ u8 trickle_cfg)
+{
+ int err;
+
+ /*
+ * Write the configuration key register to enable access to the Trickle
+ * register
+ */
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY,
+ ABX8XX_CFG_KEY_MISC);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write configuration key\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_TRICKLE,
+ ABX8XX_TRICKLE_CHARGE_ENABLE |
+ trickle_cfg);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write trickle register\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[8];
+ int err;
+
+ err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_HTH,
+ sizeof(buf), buf);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to read date\n");
+ return -EIO;
+ }
+
+ tm->tm_sec = bcd2bin(buf[ABX8XX_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[ABX8XX_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[ABX8XX_REG_HR] & 0x3F);
+ tm->tm_wday = buf[ABX8XX_REG_WD] & 0x7;
+ tm->tm_mday = bcd2bin(buf[ABX8XX_REG_DA] & 0x3F);
+ tm->tm_mon = bcd2bin(buf[ABX8XX_REG_MO] & 0x1F) - 1;
+ tm->tm_year = bcd2bin(buf[ABX8XX_REG_YR]) + 100;
+
+ err = rtc_valid_tm(tm);
+ if (err < 0)
+ dev_err(&client->dev, "retrieved date/time is not valid.\n");
+
+ return err;
+}
+
+static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[8];
+ int err;
+
+ if (tm->tm_year < 100)
+ return -EINVAL;
+
+ buf[ABX8XX_REG_HTH] = 0;
+ buf[ABX8XX_REG_SC] = bin2bcd(tm->tm_sec);
+ buf[ABX8XX_REG_MN] = bin2bcd(tm->tm_min);
+ buf[ABX8XX_REG_HR] = bin2bcd(tm->tm_hour);
+ buf[ABX8XX_REG_DA] = bin2bcd(tm->tm_mday);
+ buf[ABX8XX_REG_MO] = bin2bcd(tm->tm_mon + 1);
+ buf[ABX8XX_REG_YR] = bin2bcd(tm->tm_year - 100);
+ buf[ABX8XX_REG_WD] = tm->tm_wday;
+
+ err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_HTH,
+ sizeof(buf), buf);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write to date registers\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct rtc_class_ops abx80x_rtc_ops = {
+ .read_time = abx80x_rtc_read_time,
+ .set_time = abx80x_rtc_set_time,
+};
+
+static int abx80x_dt_trickle_cfg(struct device_node *np)
+{
+ const char *diode;
+ int trickle_cfg = 0;
+ int i, ret;
+ u32 tmp;
+
+ ret = of_property_read_string(np, "abracon,tc-diode", &diode);
+ if (ret)
+ return ret;
+
+ if (!strcmp(diode, "standard"))
+ trickle_cfg |= ABX8XX_TRICKLE_STANDARD_DIODE;
+ else if (!strcmp(diode, "schottky"))
+ trickle_cfg |= ABX8XX_TRICKLE_SCHOTTKY_DIODE;
+ else
+ return -EINVAL;
+
+ ret = of_property_read_u32(np, "abracon,tc-resistor", &tmp);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < sizeof(trickle_resistors); i++)
+ if (trickle_resistors[i] == tmp)
+ break;
+
+ if (i == sizeof(trickle_resistors))
+ return -EINVAL;
+
+ return (trickle_cfg | i);
+}
+
+static int abx80x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device_node *np = client->dev.of_node;
+ struct rtc_device *rtc;
+ int i, data, err, trickle_cfg = -EINVAL;
+ char buf[7];
+ unsigned int part = id->driver_data;
+ unsigned int partnumber;
+ unsigned int majrev, minrev;
+ unsigned int lot;
+ unsigned int wafer;
+ unsigned int uid;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ID0,
+ sizeof(buf), buf);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to read partnumber\n");
+ return -EIO;
+ }
+
+ partnumber = (buf[0] << 8) | buf[1];
+ majrev = buf[2] >> 3;
+ minrev = buf[2] & 0x7;
+ lot = ((buf[4] & 0x80) << 2) | ((buf[6] & 0x80) << 1) | buf[3];
+ uid = ((buf[4] & 0x7f) << 8) | buf[5];
+ wafer = (buf[6] & 0x7c) >> 2;
+ dev_info(&client->dev, "model %04x, revision %u.%u, lot %x, wafer %x, uid %x\n",
+ partnumber, majrev, minrev, lot, wafer, uid);
+
+ data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL1);
+ if (data < 0) {
+ dev_err(&client->dev, "Unable to read control register\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL1,
+ ((data & ~ABX8XX_CTRL_12_24) |
+ ABX8XX_CTRL_WRITE));
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write control register\n");
+ return -EIO;
+ }
+
+ /* part autodetection */
+ if (part == ABX80X) {
+ for (i = 0; abx80x_caps[i].pn; i++)
+ if (partnumber == abx80x_caps[i].pn)
+ break;
+ if (abx80x_caps[i].pn == 0) {
+ dev_err(&client->dev, "Unknown part: %04x\n",
+ partnumber);
+ return -EINVAL;
+ }
+ part = i;
+ }
+
+ if (partnumber != abx80x_caps[part].pn) {
+ dev_err(&client->dev, "partnumber mismatch %04x != %04x\n",
+ partnumber, abx80x_caps[part].pn);
+ return -EINVAL;
+ }
+
+ if (np && abx80x_caps[part].has_tc)
+ trickle_cfg = abx80x_dt_trickle_cfg(np);
+
+ if (trickle_cfg > 0) {
+ dev_info(&client->dev, "Enabling trickle charger: %02x\n",
+ trickle_cfg);
+ abx80x_enable_trickle_charger(client, trickle_cfg);
+ }
+
+ rtc = devm_rtc_device_register(&client->dev, abx80x_driver.driver.name,
+ &abx80x_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ return 0;
+}
+
+static int abx80x_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id abx80x_id[] = {
+ { "abx80x", ABX80X },
+ { "ab0801", AB0801 },
+ { "ab0803", AB0803 },
+ { "ab0804", AB0804 },
+ { "ab0805", AB0805 },
+ { "ab1801", AB1801 },
+ { "ab1803", AB1803 },
+ { "ab1804", AB1804 },
+ { "ab1805", AB1805 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, abx80x_id);
+
+static struct i2c_driver abx80x_driver = {
+ .driver = {
+ .name = "rtc-abx80x",
+ },
+ .probe = abx80x_probe,
+ .remove = abx80x_remove,
+ .id_table = abx80x_id,
+};
+
+module_i2c_driver(abx80x_driver);
+
+MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
+MODULE_DESCRIPTION("Abracon ABX80X RTC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
index 43e04af39e09..4b62d1a875e4 100644
--- a/drivers/rtc/rtc-armada38x.c
+++ b/drivers/rtc/rtc-armada38x.c
@@ -40,6 +40,13 @@ struct armada38x_rtc {
void __iomem *regs;
void __iomem *regs_soc;
spinlock_t lock;
+ /*
+ * While setting the time, the RTC TIME register should not be
+ * accessed. Setting the RTC time involves sleeping during
+ * 100ms, so a mutex instead of a spinlock is used to protect
+ * it
+ */
+ struct mutex mutex_time;
int irq;
};
@@ -57,10 +64,9 @@ static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset)
static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
- unsigned long time, time_check, flags;
-
- spin_lock_irqsave(&rtc->lock, flags);
+ unsigned long time, time_check;
+ mutex_lock(&rtc->mutex_time);
time = readl(rtc->regs + RTC_TIME);
/*
* WA for failing time set attempts. As stated in HW ERRATA if
@@ -71,7 +77,7 @@ static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
if ((time_check - time) > 1)
time_check = readl(rtc->regs + RTC_TIME);
- spin_unlock_irqrestore(&rtc->lock, flags);
+ mutex_unlock(&rtc->mutex_time);
rtc_time_to_tm(time_check, tm);
@@ -94,19 +100,12 @@ static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
* then wait for 100ms before writing to the time register to be
* sure that the data will be taken into account.
*/
- spin_lock_irqsave(&rtc->lock, flags);
-
+ mutex_lock(&rtc->mutex_time);
rtc_delayed_write(0, rtc, RTC_STATUS);
-
- spin_unlock_irqrestore(&rtc->lock, flags);
-
msleep(100);
-
- spin_lock_irqsave(&rtc->lock, flags);
-
rtc_delayed_write(time, rtc, RTC_TIME);
+ mutex_unlock(&rtc->mutex_time);
- spin_unlock_irqrestore(&rtc->lock, flags);
out:
return ret;
}
@@ -230,6 +229,7 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev)
return -ENOMEM;
spin_lock_init(&rtc->lock);
+ mutex_init(&rtc->mutex_time);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc");
rtc->regs = devm_ioremap_resource(&pdev->dev, res);
diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
new file mode 100644
index 000000000000..3f9d0acb81c7
--- /dev/null
+++ b/drivers/rtc/rtc-st-lpc.c
@@ -0,0 +1,354 @@
+/*
+ * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
+ *
+ * Copyright (C) 2014 STMicroelectronics Limited
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ * Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * Based on the original driver written by Stuart Menefy.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include <dt-bindings/mfd/st-lpc.h>
+
+/* Low Power Timer */
+#define LPC_LPT_LSB_OFF 0x400
+#define LPC_LPT_MSB_OFF 0x404
+#define LPC_LPT_START_OFF 0x408
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF 0x410
+#define LPC_LPA_MSB_OFF 0x414
+#define LPC_LPA_START_OFF 0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF 0x510
+#define LPC_WDT_FLAG_OFF 0x514
+
+struct st_rtc {
+ struct rtc_device *rtc_dev;
+ struct rtc_wkalrm alarm;
+ struct resource *res;
+ struct clk *clk;
+ unsigned long clkrate;
+ void __iomem *ioaddr;
+ bool irq_enabled:1;
+ spinlock_t lock;
+ short irq;
+};
+
+static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
+ unsigned long msb, unsigned long lsb)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
+
+ writel_relaxed(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
+ writel_relaxed(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
+ writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF);
+
+ writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+}
+
+static irqreturn_t st_rtc_handler(int this_irq, void *data)
+{
+ struct st_rtc *rtc = (struct st_rtc *)data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long lpt_lsb, lpt_msb;
+ unsigned long long lpt;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ do {
+ lpt_msb = readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF);
+ lpt_lsb = readl_relaxed(rtc->ioaddr + LPC_LPT_LSB_OFF);
+ } while (readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
+ do_div(lpt, rtc->clkrate);
+ rtc_time_to_tm(lpt, tm);
+
+ return 0;
+}
+
+static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long long lpt;
+ unsigned long secs, flags;
+ int ret;
+
+ ret = rtc_tm_to_time(tm, &secs);
+ if (ret)
+ return ret;
+
+ lpt = (unsigned long long)secs * rtc->clkrate;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ writel_relaxed(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
+ writel_relaxed(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
+ writel_relaxed(1, rtc->ioaddr + LPC_LPT_START_OFF);
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ return 0;
+}
+
+static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ return 0;
+}
+
+static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+
+ if (enabled && !rtc->irq_enabled) {
+ enable_irq(rtc->irq);
+ rtc->irq_enabled = true;
+ } else if (!enabled && rtc->irq_enabled) {
+ disable_irq(rtc->irq);
+ rtc->irq_enabled = false;
+ }
+
+ return 0;
+}
+
+static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+ struct rtc_time now;
+ unsigned long now_secs;
+ unsigned long alarm_secs;
+ unsigned long long lpa;
+
+ st_rtc_read_time(dev, &now);
+ rtc_tm_to_time(&now, &now_secs);
+ rtc_tm_to_time(&t->time, &alarm_secs);
+
+ /* Invalid alarm time */
+ if (now_secs > alarm_secs)
+ return -EINVAL;
+
+ memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
+
+ /* Now many secs to fire */
+ alarm_secs -= now_secs;
+ lpa = (unsigned long long)alarm_secs * rtc->clkrate;
+
+ st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
+ st_rtc_alarm_irq_enable(dev, t->enabled);
+
+ return 0;
+}
+
+static struct rtc_class_ops st_rtc_ops = {
+ .read_time = st_rtc_read_time,
+ .set_time = st_rtc_set_time,
+ .read_alarm = st_rtc_read_alarm,
+ .set_alarm = st_rtc_set_alarm,
+ .alarm_irq_enable = st_rtc_alarm_irq_enable,
+};
+
+static int st_rtc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct st_rtc *rtc;
+ struct resource *res;
+ struct rtc_time tm_check;
+ uint32_t mode;
+ int ret = 0;
+
+ ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+ if (ret) {
+ dev_err(&pdev->dev, "An LPC mode must be provided\n");
+ return -EINVAL;
+ }
+
+ /* LPC can either run in RTC or WDT mode */
+ if (mode != ST_LPC_MODE_RTC)
+ return -ENODEV;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ spin_lock_init(&rtc->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rtc->ioaddr))
+ return PTR_ERR(rtc->ioaddr);
+
+ rtc->irq = irq_of_parse_and_map(np, 0);
+ if (!rtc->irq) {
+ dev_err(&pdev->dev, "IRQ missing or invalid\n");
+ return -EINVAL;
+ }
+
+ ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
+ pdev->name, rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
+ return ret;
+ }
+
+ enable_irq_wake(rtc->irq);
+ disable_irq(rtc->irq);
+
+ rtc->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(rtc->clk)) {
+ dev_err(&pdev->dev, "Unable to request clock\n");
+ return PTR_ERR(rtc->clk);
+ }
+
+ clk_prepare_enable(rtc->clk);
+
+ rtc->clkrate = clk_get_rate(rtc->clk);
+ if (!rtc->clkrate) {
+ dev_err(&pdev->dev, "Unable to fetch clock rate\n");
+ return -EINVAL;
+ }
+
+ device_set_wakeup_capable(&pdev->dev, 1);
+
+ platform_set_drvdata(pdev, rtc);
+
+ /*
+ * The RTC-LPC is able to manage date.year > 2038
+ * but currently the kernel can not manage this date!
+ * If the RTC-LPC has a date.year > 2038 then
+ * it's set to the epoch "Jan 1st 2000"
+ */
+ st_rtc_read_time(&pdev->dev, &tm_check);
+
+ if (tm_check.tm_year >= (2038 - 1900)) {
+ memset(&tm_check, 0, sizeof(tm_check));
+ tm_check.tm_year = 100;
+ tm_check.tm_mday = 1;
+ st_rtc_set_time(&pdev->dev, &tm_check);
+ }
+
+ rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
+ &st_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev)) {
+ clk_disable_unprepare(rtc->clk);
+ return PTR_ERR(rtc->rtc_dev);
+ }
+
+ return 0;
+}
+
+static int st_rtc_remove(struct platform_device *pdev)
+{
+ struct st_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (likely(rtc->rtc_dev))
+ rtc_device_unregister(rtc->rtc_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int st_rtc_suspend(struct device *dev)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ return 0;
+
+ writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
+ writel_relaxed(0, rtc->ioaddr + LPC_LPA_START_OFF);
+ writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
+
+ return 0;
+}
+
+static int st_rtc_resume(struct device *dev)
+{
+ struct st_rtc *rtc = dev_get_drvdata(dev);
+
+ rtc_alarm_irq_enable(rtc->rtc_dev, 0);
+
+ /*
+ * clean 'rtc->alarm' to allow a new
+ * .set_alarm to the upper RTC layer
+ */
+ memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
+
+ writel_relaxed(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
+ writel_relaxed(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
+ writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
+ writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF);
+ writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
+
+static const struct of_device_id st_rtc_match[] = {
+ { .compatible = "st,stih407-lpc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, st_rtc_match);
+
+static struct platform_driver st_rtc_platform_driver = {
+ .driver = {
+ .name = "st-lpc-rtc",
+ .pm = &st_rtc_pm_ops,
+ .of_match_table = st_rtc_match,
+ },
+ .probe = st_rtc_probe,
+ .remove = st_rtc_remove,
+};
+
+module_platform_driver(st_rtc_platform_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 75f4bfc2b98a..b3c6ff49103b 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -297,7 +297,6 @@ static struct scsi_host_template zfcp_scsi_host_template = {
* ZFCP_QDIO_MAX_SBALS_PER_REQ) - 2) * 8,
/* GCD, adjusted later */
.dma_boundary = ZFCP_QDIO_SBALE_LEN - 1,
- .cmd_per_lun = 1,
.use_clustering = 1,
.shost_attrs = zfcp_sysfs_shost_attrs,
.sdev_attrs = zfcp_sysfs_sdev_attrs,
diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c
index 7600639db4c4..add419d6ff34 100644
--- a/drivers/scsi/3w-9xxx.c
+++ b/drivers/scsi/3w-9xxx.c
@@ -149,7 +149,6 @@ static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset);
static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry *sglistarg);
static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id);
static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code);
-static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id);
/* Functions */
@@ -1340,11 +1339,11 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance)
}
/* Now complete the io */
+ scsi_dma_unmap(cmd);
+ cmd->scsi_done(cmd);
tw_dev->state[request_id] = TW_S_COMPLETED;
twa_free_request_id(tw_dev, request_id);
tw_dev->posted_request_count--;
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
- twa_unmap_scsi_data(tw_dev, request_id);
}
/* Check for valid status after each drain */
@@ -1402,26 +1401,6 @@ static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_comm
}
} /* End twa_load_sgl() */
-/* This function will perform a pci-dma mapping for a scatter gather list */
-static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id)
-{
- int use_sg;
- struct scsi_cmnd *cmd = tw_dev->srb[request_id];
-
- use_sg = scsi_dma_map(cmd);
- if (!use_sg)
- return 0;
- else if (use_sg < 0) {
- TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list");
- return 0;
- }
-
- cmd->SCp.phase = TW_PHASE_SGLIST;
- cmd->SCp.have_data_in = use_sg;
-
- return use_sg;
-} /* End twa_map_scsi_sg_data() */
-
/* This function will poll for a response interrupt of a request */
static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
{
@@ -1600,9 +1579,11 @@ static int twa_reset_device_extension(TW_Device_Extension *tw_dev)
(tw_dev->state[i] != TW_S_INITIAL) &&
(tw_dev->state[i] != TW_S_COMPLETED)) {
if (tw_dev->srb[i]) {
- tw_dev->srb[i]->result = (DID_RESET << 16);
- tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
- twa_unmap_scsi_data(tw_dev, i);
+ struct scsi_cmnd *cmd = tw_dev->srb[i];
+
+ cmd->result = (DID_RESET << 16);
+ scsi_dma_unmap(cmd);
+ cmd->scsi_done(cmd);
}
}
}
@@ -1781,21 +1762,18 @@ static int twa_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_
/* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt;
- /* Initialize phase to zero */
- SCpnt->SCp.phase = TW_PHASE_INITIAL;
-
retval = twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
switch (retval) {
case SCSI_MLQUEUE_HOST_BUSY:
+ scsi_dma_unmap(SCpnt);
twa_free_request_id(tw_dev, request_id);
- twa_unmap_scsi_data(tw_dev, request_id);
break;
case 1:
- tw_dev->state[request_id] = TW_S_COMPLETED;
- twa_free_request_id(tw_dev, request_id);
- twa_unmap_scsi_data(tw_dev, request_id);
SCpnt->result = (DID_ERROR << 16);
+ scsi_dma_unmap(SCpnt);
done(SCpnt);
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twa_free_request_id(tw_dev, request_id);
retval = 0;
}
out:
@@ -1863,8 +1841,8 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
command_packet->sg_list[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
command_packet->sg_list[0].length = cpu_to_le32(TW_MIN_SGL_LENGTH);
} else {
- sg_count = twa_map_scsi_sg_data(tw_dev, request_id);
- if (sg_count == 0)
+ sg_count = scsi_dma_map(srb);
+ if (sg_count < 0)
goto out;
scsi_for_each_sg(srb, sg, sg_count, i) {
@@ -1979,15 +1957,6 @@ static char *twa_string_lookup(twa_message_type *table, unsigned int code)
return(table[index].text);
} /* End twa_string_lookup() */
-/* This function will perform a pci-dma unmap */
-static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
-{
- struct scsi_cmnd *cmd = tw_dev->srb[request_id];
-
- if (cmd->SCp.phase == TW_PHASE_SGLIST)
- scsi_dma_unmap(cmd);
-} /* End twa_unmap_scsi_data() */
-
/* This function gets called when a disk is coming on-line */
static int twa_slave_configure(struct scsi_device *sdev)
{
diff --git a/drivers/scsi/3w-9xxx.h b/drivers/scsi/3w-9xxx.h
index 040f7214e5b7..0fdc83cfa0e1 100644
--- a/drivers/scsi/3w-9xxx.h
+++ b/drivers/scsi/3w-9xxx.h
@@ -324,11 +324,6 @@ static twa_message_type twa_error_table[] = {
#define TW_CURRENT_DRIVER_BUILD 0
#define TW_CURRENT_DRIVER_BRANCH 0
-/* Phase defines */
-#define TW_PHASE_INITIAL 0
-#define TW_PHASE_SINGLE 1
-#define TW_PHASE_SGLIST 2
-
/* Misc defines */
#define TW_9550SX_DRAIN_COMPLETED 0xFFFF
#define TW_SECTOR_SIZE 512
diff --git a/drivers/scsi/3w-sas.c b/drivers/scsi/3w-sas.c
index 2361772d5909..f8374850f714 100644
--- a/drivers/scsi/3w-sas.c
+++ b/drivers/scsi/3w-sas.c
@@ -290,26 +290,6 @@ static int twl_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
return 0;
} /* End twl_post_command_packet() */
-/* This function will perform a pci-dma mapping for a scatter gather list */
-static int twl_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id)
-{
- int use_sg;
- struct scsi_cmnd *cmd = tw_dev->srb[request_id];
-
- use_sg = scsi_dma_map(cmd);
- if (!use_sg)
- return 0;
- else if (use_sg < 0) {
- TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Failed to map scatter gather list");
- return 0;
- }
-
- cmd->SCp.phase = TW_PHASE_SGLIST;
- cmd->SCp.have_data_in = use_sg;
-
- return use_sg;
-} /* End twl_map_scsi_sg_data() */
-
/* This function hands scsi cdb's to the firmware */
static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry_ISO *sglistarg)
{
@@ -357,8 +337,8 @@ static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
if (!sglistarg) {
/* Map sglist from scsi layer to cmd packet */
if (scsi_sg_count(srb)) {
- sg_count = twl_map_scsi_sg_data(tw_dev, request_id);
- if (sg_count == 0)
+ sg_count = scsi_dma_map(srb);
+ if (sg_count <= 0)
goto out;
scsi_for_each_sg(srb, sg, sg_count, i) {
@@ -1102,15 +1082,6 @@ out:
return retval;
} /* End twl_initialize_device_extension() */
-/* This function will perform a pci-dma unmap */
-static void twl_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
-{
- struct scsi_cmnd *cmd = tw_dev->srb[request_id];
-
- if (cmd->SCp.phase == TW_PHASE_SGLIST)
- scsi_dma_unmap(cmd);
-} /* End twl_unmap_scsi_data() */
-
/* This function will handle attention interrupts */
static int twl_handle_attention_interrupt(TW_Device_Extension *tw_dev)
{
@@ -1251,11 +1222,11 @@ static irqreturn_t twl_interrupt(int irq, void *dev_instance)
}
/* Now complete the io */
+ scsi_dma_unmap(cmd);
+ cmd->scsi_done(cmd);
tw_dev->state[request_id] = TW_S_COMPLETED;
twl_free_request_id(tw_dev, request_id);
tw_dev->posted_request_count--;
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
- twl_unmap_scsi_data(tw_dev, request_id);
}
/* Check for another response interrupt */
@@ -1400,10 +1371,12 @@ static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_res
if ((tw_dev->state[i] != TW_S_FINISHED) &&
(tw_dev->state[i] != TW_S_INITIAL) &&
(tw_dev->state[i] != TW_S_COMPLETED)) {
- if (tw_dev->srb[i]) {
- tw_dev->srb[i]->result = (DID_RESET << 16);
- tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
- twl_unmap_scsi_data(tw_dev, i);
+ struct scsi_cmnd *cmd = tw_dev->srb[i];
+
+ if (cmd) {
+ cmd->result = (DID_RESET << 16);
+ scsi_dma_unmap(cmd);
+ cmd->scsi_done(cmd);
}
}
}
@@ -1507,9 +1480,6 @@ static int twl_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_
/* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt;
- /* Initialize phase to zero */
- SCpnt->SCp.phase = TW_PHASE_INITIAL;
-
retval = twl_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
if (retval) {
tw_dev->state[request_id] = TW_S_COMPLETED;
diff --git a/drivers/scsi/3w-sas.h b/drivers/scsi/3w-sas.h
index d474892701d4..fec6449c7595 100644
--- a/drivers/scsi/3w-sas.h
+++ b/drivers/scsi/3w-sas.h
@@ -103,10 +103,6 @@ static char *twl_aen_severity_table[] =
#define TW_CURRENT_DRIVER_BUILD 0
#define TW_CURRENT_DRIVER_BRANCH 0
-/* Phase defines */
-#define TW_PHASE_INITIAL 0
-#define TW_PHASE_SGLIST 2
-
/* Misc defines */
#define TW_SECTOR_SIZE 512
#define TW_MAX_UNITS 32
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c
index c75f2048319f..2940bd769936 100644
--- a/drivers/scsi/3w-xxxx.c
+++ b/drivers/scsi/3w-xxxx.c
@@ -1271,32 +1271,6 @@ static int tw_initialize_device_extension(TW_Device_Extension *tw_dev)
return 0;
} /* End tw_initialize_device_extension() */
-static int tw_map_scsi_sg_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
-{
- int use_sg;
-
- dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data()\n");
-
- use_sg = scsi_dma_map(cmd);
- if (use_sg < 0) {
- printk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data(): pci_map_sg() failed.\n");
- return 0;
- }
-
- cmd->SCp.phase = TW_PHASE_SGLIST;
- cmd->SCp.have_data_in = use_sg;
-
- return use_sg;
-} /* End tw_map_scsi_sg_data() */
-
-static void tw_unmap_scsi_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
-{
- dprintk(KERN_WARNING "3w-xxxx: tw_unmap_scsi_data()\n");
-
- if (cmd->SCp.phase == TW_PHASE_SGLIST)
- scsi_dma_unmap(cmd);
-} /* End tw_unmap_scsi_data() */
-
/* This function will reset a device extension */
static int tw_reset_device_extension(TW_Device_Extension *tw_dev)
{
@@ -1319,8 +1293,8 @@ static int tw_reset_device_extension(TW_Device_Extension *tw_dev)
srb = tw_dev->srb[i];
if (srb != NULL) {
srb->result = (DID_RESET << 16);
- tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
- tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[i]);
+ scsi_dma_unmap(srb);
+ srb->scsi_done(srb);
}
}
}
@@ -1767,8 +1741,8 @@ static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
command_packet->byte8.io.lba = lba;
command_packet->byte6.block_count = num_sectors;
- use_sg = tw_map_scsi_sg_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
- if (!use_sg)
+ use_sg = scsi_dma_map(srb);
+ if (use_sg <= 0)
return 1;
scsi_for_each_sg(tw_dev->srb[request_id], sg, use_sg, i) {
@@ -1955,9 +1929,6 @@ static int tw_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_c
/* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt;
- /* Initialize phase to zero */
- SCpnt->SCp.phase = TW_PHASE_INITIAL;
-
switch (*command) {
case READ_10:
case READ_6:
@@ -2185,12 +2156,11 @@ static irqreturn_t tw_interrupt(int irq, void *dev_instance)
/* Now complete the io */
if ((error != TW_ISR_DONT_COMPLETE)) {
+ scsi_dma_unmap(tw_dev->srb[request_id]);
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
tw_dev->posted_request_count--;
- tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
-
- tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
}
}
diff --git a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h
index 29b0b84ed69e..6f65e663d393 100644
--- a/drivers/scsi/3w-xxxx.h
+++ b/drivers/scsi/3w-xxxx.h
@@ -195,11 +195,6 @@ static unsigned char tw_sense_table[][4] =
#define TW_AEN_SMART_FAIL 0x000F
#define TW_AEN_SBUF_FAIL 0x0024
-/* Phase defines */
-#define TW_PHASE_INITIAL 0
-#define TW_PHASE_SINGLE 1
-#define TW_PHASE_SGLIST 2
-
/* Misc defines */
#define TW_ALIGNMENT_6000 64 /* 64 bytes */
#define TW_ALIGNMENT_7000 4 /* 4 bytes */
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index b021bcb88537..456e1567841c 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -52,7 +52,7 @@ config SCSI_MQ_DEFAULT
This option enables the new blk-mq based I/O path for SCSI
devices by default. With the option the scsi_mod.use_blk_mq
module/boot option defaults to Y, without it to N, but it can
- still be overriden either way.
+ still be overridden either way.
If unsure say N.
@@ -503,7 +503,7 @@ config SCSI_DPT_I2O
config SCSI_ADVANSYS
tristate "AdvanSys SCSI support"
- depends on SCSI && VIRT_TO_BUS && !ARM
+ depends on SCSI
depends on ISA || EISA || PCI
help
This is a driver for all SCSI host adapters manufactured by
@@ -634,6 +634,23 @@ config FCOE_FNIC
<file:Documentation/scsi/scsi.txt>.
The module will be called fnic.
+config SCSI_SNIC
+ tristate "Cisco SNIC Driver"
+ depends on PCI && SCSI
+ help
+ This is support for the Cisco PCI-Express SCSI HBA.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/scsi/scsi.txt>.
+ The module will be called snic.
+
+config SCSI_SNIC_DEBUG_FS
+ bool "Cisco SNIC Driver Debugfs Support"
+ depends on SCSI_SNIC && DEBUG_FS
+ help
+ This enables to list debugging information from SNIC Driver
+ available via debugfs file system
+
config SCSI_DMX3191D
tristate "DMX3191D SCSI support"
depends on PCI && SCSI
@@ -1743,7 +1760,6 @@ config SCSI_BFA_FC
config SCSI_VIRTIO
tristate "virtio-scsi support"
depends on VIRTIO
- select BLK_DEV_INTEGRITY
help
This is the virtual HBA driver for virtio. If the kernel will
be used in a virtual machine, say Y or M.
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index dee160a4f163..91209e3d27e3 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_LIBFC) += libfc/
obj-$(CONFIG_LIBFCOE) += fcoe/
obj-$(CONFIG_FCOE) += fcoe/
obj-$(CONFIG_FCOE_FNIC) += fnic/
+obj-$(CONFIG_SCSI_SNIC) += snic/
obj-$(CONFIG_SCSI_BNX2X_FCOE) += libfc/ fcoe/ bnx2fc/
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
@@ -161,6 +162,7 @@ obj-$(CONFIG_SCSI_OSD_INITIATOR) += osd/
obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o
scsi_mod-y += scsi.o hosts.o scsi_ioctl.o \
scsicam.o scsi_error.o scsi_lib.o
+scsi_mod-y += scsi_common.o
scsi_mod-$(CONFIG_SCSI_CONSTANTS) += constants.o
scsi_mod-$(CONFIG_SCSI_DMA) += scsi_lib_dma.o
scsi_mod-y += scsi_scan.o scsi_sysfs.o scsi_devinfo.o
diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c
index 42c7161474f7..6e110c630d2c 100644
--- a/drivers/scsi/NCR53c406a.c
+++ b/drivers/scsi/NCR53c406a.c
@@ -1064,7 +1064,6 @@ static struct scsi_host_template driver_template =
.can_queue = 1 /* can_queue */,
.this_id = 7 /* SCSI ID of the chip */,
.sg_tablesize = 32 /*SG_ALL*/ /*SG_NONE*/,
- .cmd_per_lun = 1 /* commands per lun */,
.unchecked_isa_dma = 1 /* unchecked_isa_dma */,
.use_clustering = ENABLE_CLUSTERING,
};
diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c
index 7e33a61c1ba4..cac6b37d7b1b 100644
--- a/drivers/scsi/a100u2w.c
+++ b/drivers/scsi/a100u2w.c
@@ -1078,7 +1078,6 @@ static struct scsi_host_template inia100_template = {
.can_queue = 1,
.this_id = 1,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = ENABLE_CLUSTERING,
};
diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c
index 4596e9dd757c..e63cf9f22f36 100644
--- a/drivers/scsi/aacraid/src.c
+++ b/drivers/scsi/aacraid/src.c
@@ -46,7 +46,7 @@
static int aac_src_get_sync_status(struct aac_dev *dev);
-irqreturn_t aac_src_intr_message(int irq, void *dev_id)
+static irqreturn_t aac_src_intr_message(int irq, void *dev_id)
{
struct aac_msix_ctx *ctx;
struct aac_dev *dev;
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index ae95e347f37d..4305178e4e01 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -1,12 +1,10 @@
-#define DRV_NAME "advansys"
-#define ASC_VERSION "3.4" /* AdvanSys Driver Version */
-
/*
* advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
*
* Copyright (c) 1995-2000 Advanced System Products, Inc.
* Copyright (c) 2000-2001 ConnectCom Solutions, Inc.
* Copyright (c) 2007 Matthew Wilcox <matthew@wil.cx>
+ * Copyright (c) 2014 Hannes Reinecke <hare@suse.de>
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -39,6 +37,7 @@
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
+#include <linux/dmapool.h>
#include <asm/io.h>
#include <asm/dma.h>
@@ -49,26 +48,15 @@
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
+#define DRV_NAME "advansys"
+#define ASC_VERSION "3.5" /* AdvanSys Driver Version */
+
/* FIXME:
*
- * 1. Although all of the necessary command mapping places have the
- * appropriate dma_map.. APIs, the driver still processes its internal
- * queue using bus_to_virt() and virt_to_bus() which are illegal under
- * the API. The entire queue processing structure will need to be
- * altered to fix this.
- * 2. Need to add memory mapping workaround. Test the memory mapping.
- * If it doesn't work revert to I/O port access. Can a test be done
- * safely?
- * 3. Handle an interrupt not working. Keep an interrupt counter in
- * the interrupt handler. In the timeout function if the interrupt
- * has not occurred then print a message and run in polled mode.
- * 4. Need to add support for target mode commands, cf. CAM XPT.
- * 5. check DMA mapping functions for failure
- * 6. Use scsi_transport_spi
- * 7. advansys_info is not safe against multiple simultaneous callers
- * 8. Add module_param to override ISA/VLB ioport array
+ * 1. Use scsi_transport_spi
+ * 2. advansys_info is not safe against multiple simultaneous callers
+ * 3. Add module_param to override ISA/VLB ioport array
*/
-#warning this driver is still not properly converted to the DMA API
/* Enable driver /proc statistics. */
#define ADVANSYS_STATS
@@ -76,31 +64,8 @@
/* Enable driver tracing. */
#undef ADVANSYS_DEBUG
-/*
- * Portable Data Types
- *
- * Any instance where a 32-bit long or pointer type is assumed
- * for precision or HW defined structures, the following define
- * types must be used. In Linux the char, short, and int types
- * are all consistent at 8, 16, and 32 bits respectively. Pointers
- * and long types are 64 bits on Alpha and UltraSPARC.
- */
-#define ASC_PADDR __u32 /* Physical/Bus address data type. */
-#define ASC_VADDR __u32 /* Virtual address data type. */
-#define ASC_DCNT __u32 /* Unsigned Data count type. */
-#define ASC_SDCNT __s32 /* Signed Data count type. */
-
typedef unsigned char uchar;
-#ifndef TRUE
-#define TRUE (1)
-#endif
-#ifndef FALSE
-#define FALSE (0)
-#endif
-
-#define ERR (-1)
-#define UW_ERR (uint)(0xFFFF)
#define isodd_word(val) ((((uint)val) & (uint)0x0001) != 0)
#define PCI_VENDOR_ID_ASP 0x10cd
@@ -111,15 +76,6 @@ typedef unsigned char uchar;
#define PCI_DEVICE_ID_38C0800_REV1 0x2500
#define PCI_DEVICE_ID_38C1600_REV1 0x2700
-/*
- * Enable CC_VERY_LONG_SG_LIST to support up to 64K element SG lists.
- * The SRB structure will have to be changed and the ASC_SRB2SCSIQ()
- * macro re-defined to be able to obtain a ASC_SCSI_Q pointer from the
- * SRB structure.
- */
-#define CC_VERY_LONG_SG_LIST 0
-#define ASC_SRB2SCSIQ(srb_ptr) (srb_ptr)
-
#define PortAddr unsigned int /* port address size */
#define inp(port) inb(port)
#define outp(port, byte) outb((byte), (port))
@@ -307,15 +263,15 @@ typedef struct asc_scsiq_1 {
uchar sg_queue_cnt;
uchar target_id;
uchar target_lun;
- ASC_PADDR data_addr;
- ASC_DCNT data_cnt;
- ASC_PADDR sense_addr;
+ __le32 data_addr;
+ __le32 data_cnt;
+ __le32 sense_addr;
uchar sense_len;
uchar extra_bytes;
} ASC_SCSIQ_1;
typedef struct asc_scsiq_2 {
- ASC_VADDR srb_ptr;
+ u32 srb_tag;
uchar target_ix;
uchar flag;
uchar cdb_len;
@@ -338,8 +294,8 @@ typedef struct asc_scsiq_4 {
uchar y_res;
ushort x_req_count;
ushort x_reconnect_rtn;
- ASC_PADDR x_saved_data_addr;
- ASC_DCNT x_saved_data_cnt;
+ __le32 x_saved_data_addr;
+ __le32 x_saved_data_cnt;
} ASC_SCSIQ_4;
typedef struct asc_q_done_info {
@@ -351,12 +307,12 @@ typedef struct asc_q_done_info {
uchar sense_len;
uchar extra_bytes;
uchar res;
- ASC_DCNT remain_bytes;
+ u32 remain_bytes;
} ASC_QDONE_INFO;
typedef struct asc_sg_list {
- ASC_PADDR addr;
- ASC_DCNT bytes;
+ __le32 addr;
+ __le32 bytes;
} ASC_SG_LIST;
typedef struct asc_sg_head {
@@ -376,17 +332,6 @@ typedef struct asc_scsi_q {
ushort next_sg_index;
} ASC_SCSI_Q;
-typedef struct asc_scsi_req_q {
- ASC_SCSIQ_1 r1;
- ASC_SCSIQ_2 r2;
- uchar *cdbptr;
- ASC_SG_HEAD *sg_head;
- uchar *sense_ptr;
- ASC_SCSIQ_3 r3;
- uchar cdb[ASC_MAX_CDB_LEN];
- uchar sense[ASC_MIN_SENSE_LEN];
-} ASC_SCSI_REQ_Q;
-
typedef struct asc_scsi_bios_req_q {
ASC_SCSIQ_1 r1;
ASC_SCSIQ_2 r2;
@@ -570,7 +515,7 @@ typedef struct asc_dvc_var {
dma_addr_t overrun_dma;
uchar scsi_reset_wait;
uchar chip_no;
- char is_in_int;
+ bool is_in_int;
uchar max_total_qng;
uchar cur_total_qng;
uchar in_critical_cnt;
@@ -586,15 +531,13 @@ typedef struct asc_dvc_var {
char redo_scam;
ushort res2;
uchar dos_int13_table[ASC_MAX_TID + 1];
- ASC_DCNT max_dma_count;
+ unsigned int max_dma_count;
ASC_SCSI_BIT_ID_TYPE no_scam;
ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer;
uchar min_sdtr_index;
uchar max_sdtr_index;
struct asc_board *drv_ptr;
- int ptr_map_count;
- void **ptr_map;
- ASC_DCNT uc_break;
+ unsigned int uc_break;
} ASC_DVC_VAR;
typedef struct asc_dvc_inq_info {
@@ -602,8 +545,8 @@ typedef struct asc_dvc_inq_info {
} ASC_DVC_INQ_INFO;
typedef struct asc_cap_info {
- ASC_DCNT lba;
- ASC_DCNT blk_size;
+ u32 lba;
+ u32 blk_size;
} ASC_CAP_INFO;
typedef struct asc_cap_info_array {
@@ -929,31 +872,6 @@ typedef struct asc_mc_saved {
#define AscReadChipDvcID(port) (uchar)inp((port)+IOP_REG_ID)
#define AscWriteChipDvcID(port, data) outp((port)+IOP_REG_ID, data)
-/*
- * Portable Data Types
- *
- * Any instance where a 32-bit long or pointer type is assumed
- * for precision or HW defined structures, the following define
- * types must be used. In Linux the char, short, and int types
- * are all consistent at 8, 16, and 32 bits respectively. Pointers
- * and long types are 64 bits on Alpha and UltraSPARC.
- */
-#define ADV_PADDR __u32 /* Physical address data type. */
-#define ADV_VADDR __u32 /* Virtual address data type. */
-#define ADV_DCNT __u32 /* Unsigned Data count type. */
-#define ADV_SDCNT __s32 /* Signed Data count type. */
-
-/*
- * These macros are used to convert a virtual address to a
- * 32-bit value. This currently can be used on Linux Alpha
- * which uses 64-bit virtual address but a 32-bit bus address.
- * This is likely to break in the future, but doing this now
- * will give us time to change the HW and FW to handle 64-bit
- * addresses.
- */
-#define ADV_VADDR_TO_U32 virt_to_bus
-#define ADV_U32_TO_VADDR bus_to_virt
-
#define AdvPortAddr void __iomem * /* Virtual memory address size */
/*
@@ -965,8 +883,6 @@ typedef struct asc_mc_saved {
#define ADV_MEM_WRITEW(addr, word) writew(word, addr)
#define ADV_MEM_WRITEDW(addr, dword) writel(dword, addr)
-#define ADV_CARRIER_COUNT (ASC_DEF_MAX_HOST_QNG + 15)
-
/*
* Define total number of simultaneous maximum element scatter-gather
* request blocks per wide adapter. ASC_DEF_MAX_HOST_QNG (253) is the
@@ -1747,44 +1663,37 @@ typedef struct adveep_38C1600_config {
* little-endian.
*/
typedef struct adv_carr_t {
- ADV_VADDR carr_va; /* Carrier Virtual Address */
- ADV_PADDR carr_pa; /* Carrier Physical Address */
- ADV_VADDR areq_vpa; /* ASC_SCSI_REQ_Q Virtual or Physical Address */
+ __le32 carr_va; /* Carrier Virtual Address */
+ __le32 carr_pa; /* Carrier Physical Address */
+ __le32 areq_vpa; /* ADV_SCSI_REQ_Q Virtual or Physical Address */
/*
* next_vpa [31:4] Carrier Virtual or Physical Next Pointer
*
* next_vpa [3:1] Reserved Bits
* next_vpa [0] Done Flag set in Response Queue.
*/
- ADV_VADDR next_vpa;
+ __le32 next_vpa;
} ADV_CARR_T;
/*
* Mask used to eliminate low 4 bits of carrier 'next_vpa' field.
*/
-#define ASC_NEXT_VPA_MASK 0xFFFFFFF0
-
-#define ASC_RQ_DONE 0x00000001
-#define ASC_RQ_GOOD 0x00000002
-#define ASC_CQ_STOPPER 0x00000000
+#define ADV_NEXT_VPA_MASK 0xFFFFFFF0
-#define ASC_GET_CARRP(carrp) ((carrp) & ASC_NEXT_VPA_MASK)
+#define ADV_RQ_DONE 0x00000001
+#define ADV_RQ_GOOD 0x00000002
+#define ADV_CQ_STOPPER 0x00000000
-#define ADV_CARRIER_NUM_PAGE_CROSSING \
- (((ADV_CARRIER_COUNT * sizeof(ADV_CARR_T)) + (PAGE_SIZE - 1))/PAGE_SIZE)
-
-#define ADV_CARRIER_BUFSIZE \
- ((ADV_CARRIER_COUNT + ADV_CARRIER_NUM_PAGE_CROSSING) * sizeof(ADV_CARR_T))
+#define ADV_GET_CARRP(carrp) ((carrp) & ADV_NEXT_VPA_MASK)
/*
- * ASC_SCSI_REQ_Q 'a_flag' definitions
- *
- * The Adv Library should limit use to the lower nibble (4 bits) of
- * a_flag. Drivers are free to use the upper nibble (4 bits) of a_flag.
+ * Each carrier is 64 bytes, and we need three additional
+ * carrier for icq, irq, and the termination carrier.
*/
-#define ADV_POLL_REQUEST 0x01 /* poll for request completion */
-#define ADV_SCSIQ_DONE 0x02 /* request done */
-#define ADV_DONT_RETRY 0x08 /* don't do retry */
+#define ADV_CARRIER_COUNT (ASC_DEF_MAX_HOST_QNG + 3)
+
+#define ADV_CARRIER_BUFSIZE \
+ (ADV_CARRIER_COUNT * sizeof(ADV_CARR_T))
#define ADV_CHIP_ASC3550 0x01 /* Ultra-Wide IC */
#define ADV_CHIP_ASC38C0800 0x02 /* Ultra2-Wide/LVD IC */
@@ -1816,15 +1725,15 @@ typedef struct adv_dvc_cfg {
struct adv_dvc_var;
struct adv_scsi_req_q;
-typedef struct asc_sg_block {
+typedef struct adv_sg_block {
uchar reserved1;
uchar reserved2;
uchar reserved3;
uchar sg_cnt; /* Valid entries in block. */
- ADV_PADDR sg_ptr; /* Pointer to next sg block. */
+ __le32 sg_ptr; /* Pointer to next sg block. */
struct {
- ADV_PADDR sg_addr; /* SG element address. */
- ADV_DCNT sg_count; /* SG element count. */
+ __le32 sg_addr; /* SG element address. */
+ __le32 sg_count; /* SG element count. */
} sg_list[NO_OF_SG_PER_BLOCK];
} ADV_SG_BLOCK;
@@ -1844,10 +1753,10 @@ typedef struct adv_scsi_req_q {
uchar target_cmd;
uchar target_id; /* Device target identifier. */
uchar target_lun; /* Device target logical unit number. */
- ADV_PADDR data_addr; /* Data buffer physical address. */
- ADV_DCNT data_cnt; /* Data count. Ucode sets to residual. */
- ADV_PADDR sense_addr;
- ADV_PADDR carr_pa;
+ __le32 data_addr; /* Data buffer physical address. */
+ __le32 data_cnt; /* Data count. Ucode sets to residual. */
+ __le32 sense_addr;
+ __le32 carr_pa;
uchar mflag;
uchar sense_len;
uchar cdb_len; /* SCSI CDB length. Must <= 16 bytes. */
@@ -1857,29 +1766,26 @@ typedef struct adv_scsi_req_q {
uchar host_status; /* Ucode host status. */
uchar sg_working_ix;
uchar cdb[12]; /* SCSI CDB bytes 0-11. */
- ADV_PADDR sg_real_addr; /* SG list physical address. */
- ADV_PADDR scsiq_rptr;
+ __le32 sg_real_addr; /* SG list physical address. */
+ __le32 scsiq_rptr;
uchar cdb16[4]; /* SCSI CDB bytes 12-15. */
- ADV_VADDR scsiq_ptr;
- ADV_VADDR carr_va;
+ __le32 scsiq_ptr;
+ __le32 carr_va;
/*
* End of microcode structure - 60 bytes. The rest of the structure
* is used by the Adv Library and ignored by the microcode.
*/
- ADV_VADDR srb_ptr;
+ u32 srb_tag;
ADV_SG_BLOCK *sg_list_ptr; /* SG list virtual address. */
- char *vdata_addr; /* Data buffer virtual address. */
- uchar a_flag;
- uchar pad[2]; /* Pad out to a word boundary. */
} ADV_SCSI_REQ_Q;
/*
* The following two structures are used to process Wide Board requests.
*
* The ADV_SCSI_REQ_Q structure in adv_req_t is passed to the Adv Library
- * and microcode with the ADV_SCSI_REQ_Q field 'srb_ptr' pointing to the
- * adv_req_t. The adv_req_t structure 'cmndp' field in turn points to the
- * Mid-Level SCSI request structure.
+ * and microcode with the ADV_SCSI_REQ_Q field 'srb_tag' set to the
+ * SCSI request tag. The adv_req_t structure 'cmndp' field in turn points
+ * to the Mid-Level SCSI request structure.
*
* Zero or more ADV_SG_BLOCK are used with each ADV_SCSI_REQ_Q. Each
* ADV_SG_BLOCK structure holds 15 scatter-gather elements. Under Linux
@@ -1890,17 +1796,17 @@ typedef struct adv_scsi_req_q {
*/
typedef struct adv_sgblk {
ADV_SG_BLOCK sg_block; /* Sgblock structure. */
- uchar align[32]; /* Sgblock structure padding. */
+ dma_addr_t sg_addr; /* Physical address */
struct adv_sgblk *next_sgblkp; /* Next scatter-gather structure. */
} adv_sgblk_t;
typedef struct adv_req {
ADV_SCSI_REQ_Q scsi_req_q; /* Adv Library request structure. */
- uchar align[32]; /* Request structure padding. */
+ uchar align[24]; /* Request structure padding. */
struct scsi_cmnd *cmndp; /* Mid-Level SCSI command pointer. */
+ dma_addr_t req_addr;
adv_sgblk_t *sgblkp; /* Adv Library scatter-gather pointer. */
- struct adv_req *next_reqp; /* Next Request Structure. */
-} adv_req_t;
+} adv_req_t __aligned(32);
/*
* Adapter operation variable structure.
@@ -1937,12 +1843,12 @@ typedef struct adv_dvc_var {
uchar chip_scsi_id; /* chip SCSI target ID */
uchar chip_type;
uchar bist_err_code;
- ADV_CARR_T *carrier_buf;
+ ADV_CARR_T *carrier;
ADV_CARR_T *carr_freelist; /* Carrier free list. */
+ dma_addr_t carrier_addr;
ADV_CARR_T *icq_sp; /* Initiator command queue stopper pointer. */
ADV_CARR_T *irq_sp; /* Initiator response queue stopper pointer. */
ushort carr_pending_cnt; /* Count of pending carriers. */
- struct adv_req *orig_reqp; /* adv_req_t memory block. */
/*
* Note: The following fields will not be used after initialization. The
* driver may discard the buffer after initialization is done.
@@ -2068,8 +1974,8 @@ do { \
AdvReadByteRegister((iop_base), IOPB_CHIP_TYPE_REV)
/*
- * Abort an SRB in the chip's RISC Memory. The 'srb_ptr' argument must
- * match the ASC_SCSI_REQ_Q 'srb_ptr' field.
+ * Abort an SRB in the chip's RISC Memory. The 'srb_tag' argument must
+ * match the ADV_SCSI_REQ_Q 'srb_tag' field.
*
* If the request has not yet been sent to the device it will simply be
* aborted from RISC memory. If the request is disconnected it will be
@@ -2079,9 +1985,9 @@ do { \
* ADV_TRUE(1) - Queue was successfully aborted.
* ADV_FALSE(0) - Queue was not found on the active queue list.
*/
-#define AdvAbortQueue(asc_dvc, scsiq) \
- AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \
- (ADV_DCNT) (scsiq))
+#define AdvAbortQueue(asc_dvc, srb_tag) \
+ AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \
+ (ADV_DCNT) (srb_tag))
/*
* Send a Bus Device Reset Message to the specified target ID.
@@ -2095,8 +2001,8 @@ do { \
* are not purged.
*/
#define AdvResetDevice(asc_dvc, target_id) \
- AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \
- (ADV_DCNT) (target_id))
+ AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \
+ (ADV_DCNT) (target_id))
/*
* SCSI Wide Type definition.
@@ -2115,7 +2021,7 @@ do { \
#define ADV_TID_TO_TIDMASK(tid) (0x01 << ((tid) & ADV_MAX_TID))
/*
- * ASC_SCSI_REQ_Q 'done_status' and 'host_status' return values.
+ * ADV_SCSI_REQ_Q 'done_status' and 'host_status' return values.
*/
#define QD_NO_STATUS 0x00 /* Request not completed yet. */
@@ -2153,8 +2059,6 @@ do { \
#define QHSTA_M_SGBACKUP_ERROR 0x47 /* Scatter-Gather backup error */
/* Return the address that is aligned at the next doubleword >= to 'addr'. */
-#define ADV_8BALIGN(addr) (((ulong) (addr) + 0x7) & ~0x7)
-#define ADV_16BALIGN(addr) (((ulong) (addr) + 0xF) & ~0xF)
#define ADV_32BALIGN(addr) (((ulong) (addr) + 0x1F) & ~0x1F)
/*
@@ -2315,24 +2219,24 @@ do { \
/* Per board statistics structure */
struct asc_stats {
/* Driver Entrypoint Statistics */
- ADV_DCNT queuecommand; /* # calls to advansys_queuecommand() */
- ADV_DCNT reset; /* # calls to advansys_eh_bus_reset() */
- ADV_DCNT biosparam; /* # calls to advansys_biosparam() */
- ADV_DCNT interrupt; /* # advansys_interrupt() calls */
- ADV_DCNT callback; /* # calls to asc/adv_isr_callback() */
- ADV_DCNT done; /* # calls to request's scsi_done function */
- ADV_DCNT build_error; /* # asc/adv_build_req() ASC_ERROR returns. */
- ADV_DCNT adv_build_noreq; /* # adv_build_req() adv_req_t alloc. fail. */
- ADV_DCNT adv_build_nosg; /* # adv_build_req() adv_sgblk_t alloc. fail. */
+ unsigned int queuecommand; /* # calls to advansys_queuecommand() */
+ unsigned int reset; /* # calls to advansys_eh_bus_reset() */
+ unsigned int biosparam; /* # calls to advansys_biosparam() */
+ unsigned int interrupt; /* # advansys_interrupt() calls */
+ unsigned int callback; /* # calls to asc/adv_isr_callback() */
+ unsigned int done; /* # calls to request's scsi_done function */
+ unsigned int build_error; /* # asc/adv_build_req() ASC_ERROR returns. */
+ unsigned int adv_build_noreq; /* # adv_build_req() adv_req_t alloc. fail. */
+ unsigned int adv_build_nosg; /* # adv_build_req() adv_sgblk_t alloc. fail. */
/* AscExeScsiQueue()/AdvExeScsiQueue() Statistics */
- ADV_DCNT exe_noerror; /* # ASC_NOERROR returns. */
- ADV_DCNT exe_busy; /* # ASC_BUSY returns. */
- ADV_DCNT exe_error; /* # ASC_ERROR returns. */
- ADV_DCNT exe_unknown; /* # unknown returns. */
+ unsigned int exe_noerror; /* # ASC_NOERROR returns. */
+ unsigned int exe_busy; /* # ASC_BUSY returns. */
+ unsigned int exe_error; /* # ASC_ERROR returns. */
+ unsigned int exe_unknown; /* # unknown returns. */
/* Data Transfer Statistics */
- ADV_DCNT xfer_cnt; /* # I/O requests received */
- ADV_DCNT xfer_elem; /* # scatter-gather elements */
- ADV_DCNT xfer_sect; /* # 512-byte blocks */
+ unsigned int xfer_cnt; /* # I/O requests received */
+ unsigned int xfer_elem; /* # scatter-gather elements */
+ unsigned int xfer_sect; /* # 512-byte blocks */
};
#endif /* ADVANSYS_STATS */
@@ -2345,6 +2249,7 @@ struct asc_stats {
*/
struct asc_board {
struct device *dev;
+ struct Scsi_Host *shost;
uint flags; /* Board flags */
unsigned int irq;
union {
@@ -2366,7 +2271,6 @@ struct asc_board {
ADVEEP_38C0800_CONFIG adv_38C0800_eep; /* 38C0800 EEPROM config. */
ADVEEP_38C1600_CONFIG adv_38C1600_eep; /* 38C1600 EEPROM config. */
} eep_config;
- ulong last_reset; /* Saved last reset time */
/* /proc/scsi/advansys/[0...] */
#ifdef ADVANSYS_STATS
struct asc_stats asc_stats; /* Board statistics */
@@ -2381,7 +2285,9 @@ struct asc_board {
void __iomem *ioremap_addr; /* I/O Memory remap address. */
ushort ioport; /* I/O Port address. */
adv_req_t *adv_reqp; /* Request structures. */
- adv_sgblk_t *adv_sgblkp; /* Scatter-gather structures. */
+ dma_addr_t adv_reqp_addr;
+ size_t adv_reqp_size;
+ struct dma_pool *adv_sgblk_pool; /* Scatter-gather structures. */
ushort bios_signature; /* BIOS Signature. */
ushort bios_version; /* BIOS Version. */
ushort bios_codeseg; /* BIOS Code Segment. */
@@ -2470,12 +2376,11 @@ static void asc_prt_adv_dvc_var(ADV_DVC_VAR *h)
printk(" start_motor 0x%x, scsi_reset_wait 0x%x\n",
(unsigned)h->start_motor, (unsigned)h->scsi_reset_wait);
- printk(" max_host_qng %u, max_dvc_qng %u, carr_freelist 0x%lxn\n",
+ printk(" max_host_qng %u, max_dvc_qng %u, carr_freelist 0x%p\n",
(unsigned)h->max_host_qng, (unsigned)h->max_dvc_qng,
- (ulong)h->carr_freelist);
+ h->carr_freelist);
- printk(" icq_sp 0x%lx, irq_sp 0x%lx\n",
- (ulong)h->icq_sp, (ulong)h->irq_sp);
+ printk(" icq_sp 0x%p, irq_sp 0x%p\n", h->icq_sp, h->irq_sp);
printk(" no_scam 0x%x, tagqng_able 0x%x\n",
(unsigned)h->no_scam, (unsigned)h->tagqng_able);
@@ -2600,8 +2505,8 @@ static void asc_prt_asc_scsi_q(ASC_SCSI_Q *q)
printk("ASC_SCSI_Q at addr 0x%lx\n", (ulong)q);
printk
- (" target_ix 0x%x, target_lun %u, srb_ptr 0x%lx, tag_code 0x%x,\n",
- q->q2.target_ix, q->q1.target_lun, (ulong)q->q2.srb_ptr,
+ (" target_ix 0x%x, target_lun %u, srb_tag 0x%x, tag_code 0x%x,\n",
+ q->q2.target_ix, q->q1.target_lun, q->q2.srb_tag,
q->q2.tag_code);
printk
@@ -2634,8 +2539,8 @@ static void asc_prt_asc_scsi_q(ASC_SCSI_Q *q)
static void asc_prt_asc_qdone_info(ASC_QDONE_INFO *q)
{
printk("ASC_QDONE_INFO at addr 0x%lx\n", (ulong)q);
- printk(" srb_ptr 0x%lx, target_ix %u, cdb_len %u, tag_code %u,\n",
- (ulong)q->d2.srb_ptr, q->d2.target_ix, q->d2.cdb_len,
+ printk(" srb_tag 0x%x, target_ix %u, cdb_len %u, tag_code %u,\n",
+ q->d2.srb_tag, q->d2.target_ix, q->d2.cdb_len,
q->d2.tag_code);
printk
(" done_stat 0x%x, host_stat 0x%x, scsi_stat 0x%x, scsi_msg 0x%x\n",
@@ -2651,17 +2556,17 @@ static void asc_prt_adv_sgblock(int sgblockno, ADV_SG_BLOCK *b)
{
int i;
- printk(" ASC_SG_BLOCK at addr 0x%lx (sgblockno %d)\n",
+ printk(" ADV_SG_BLOCK at addr 0x%lx (sgblockno %d)\n",
(ulong)b, sgblockno);
- printk(" sg_cnt %u, sg_ptr 0x%lx\n",
- b->sg_cnt, (ulong)le32_to_cpu(b->sg_ptr));
+ printk(" sg_cnt %u, sg_ptr 0x%x\n",
+ b->sg_cnt, (u32)le32_to_cpu(b->sg_ptr));
BUG_ON(b->sg_cnt > NO_OF_SG_PER_BLOCK);
if (b->sg_ptr != 0)
BUG_ON(b->sg_cnt != NO_OF_SG_PER_BLOCK);
for (i = 0; i < b->sg_cnt; i++) {
- printk(" [%u]: sg_addr 0x%lx, sg_count 0x%lx\n",
- i, (ulong)b->sg_list[i].sg_addr,
- (ulong)b->sg_list[i].sg_count);
+ printk(" [%u]: sg_addr 0x%x, sg_count 0x%x\n",
+ i, (u32)le32_to_cpu(b->sg_list[i].sg_addr),
+ (u32)le32_to_cpu(b->sg_list[i].sg_count));
}
}
@@ -2673,15 +2578,16 @@ static void asc_prt_adv_sgblock(int sgblockno, ADV_SG_BLOCK *b)
static void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q)
{
int sg_blk_cnt;
- struct asc_sg_block *sg_ptr;
+ struct adv_sg_block *sg_ptr;
+ adv_sgblk_t *sgblkp;
printk("ADV_SCSI_REQ_Q at addr 0x%lx\n", (ulong)q);
- printk(" target_id %u, target_lun %u, srb_ptr 0x%lx, a_flag 0x%x\n",
- q->target_id, q->target_lun, (ulong)q->srb_ptr, q->a_flag);
+ printk(" target_id %u, target_lun %u, srb_tag 0x%x\n",
+ q->target_id, q->target_lun, q->srb_tag);
- printk(" cntl 0x%x, data_addr 0x%lx, vdata_addr 0x%lx\n",
- q->cntl, (ulong)le32_to_cpu(q->data_addr), (ulong)q->vdata_addr);
+ printk(" cntl 0x%x, data_addr 0x%lx\n",
+ q->cntl, (ulong)le32_to_cpu(q->data_addr));
printk(" data_cnt %lu, sense_addr 0x%lx, sense_len %u,\n",
(ulong)le32_to_cpu(q->data_cnt),
@@ -2700,21 +2606,15 @@ static void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q)
/* Display the request's ADV_SG_BLOCK structures. */
if (q->sg_list_ptr != NULL) {
+ sgblkp = container_of(q->sg_list_ptr, adv_sgblk_t, sg_block);
sg_blk_cnt = 0;
- while (1) {
- /*
- * 'sg_ptr' is a physical address. Convert it to a virtual
- * address by indexing 'sg_blk_cnt' into the virtual address
- * array 'sg_list_ptr'.
- *
- * XXX - Assumes all SG physical blocks are virtually contiguous.
- */
- sg_ptr =
- &(((ADV_SG_BLOCK *)(q->sg_list_ptr))[sg_blk_cnt]);
+ while (sgblkp) {
+ sg_ptr = &sgblkp->sg_block;
asc_prt_adv_sgblock(sg_blk_cnt, sg_ptr);
if (sg_ptr->sg_ptr == 0) {
break;
}
+ sgblkp = sgblkp->next_sgblkp;
sg_blk_cnt++;
}
}
@@ -2722,59 +2622,6 @@ static void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q)
#endif /* ADVANSYS_DEBUG */
/*
- * The advansys chip/microcode contains a 32-bit identifier for each command
- * known as the 'srb'. I don't know what it stands for. The driver used
- * to encode the scsi_cmnd pointer by calling virt_to_bus and retrieve it
- * with bus_to_virt. Now the driver keeps a per-host map of integers to
- * pointers. It auto-expands when full, unless it can't allocate memory.
- * Note that an srb of 0 is treated specially by the chip/firmware, hence
- * the return of i+1 in this routine, and the corresponding subtraction in
- * the inverse routine.
- */
-#define BAD_SRB 0
-static u32 advansys_ptr_to_srb(struct asc_dvc_var *asc_dvc, void *ptr)
-{
- int i;
- void **new_ptr;
-
- for (i = 0; i < asc_dvc->ptr_map_count; i++) {
- if (!asc_dvc->ptr_map[i])
- goto out;
- }
-
- if (asc_dvc->ptr_map_count == 0)
- asc_dvc->ptr_map_count = 1;
- else
- asc_dvc->ptr_map_count *= 2;
-
- new_ptr = krealloc(asc_dvc->ptr_map,
- asc_dvc->ptr_map_count * sizeof(void *), GFP_ATOMIC);
- if (!new_ptr)
- return BAD_SRB;
- asc_dvc->ptr_map = new_ptr;
- out:
- ASC_DBG(3, "Putting ptr %p into array offset %d\n", ptr, i);
- asc_dvc->ptr_map[i] = ptr;
- return i + 1;
-}
-
-static void * advansys_srb_to_ptr(struct asc_dvc_var *asc_dvc, u32 srb)
-{
- void *ptr;
-
- srb--;
- if (srb >= asc_dvc->ptr_map_count) {
- printk("advansys: bad SRB %u, max %u\n", srb,
- asc_dvc->ptr_map_count);
- return NULL;
- }
- ptr = asc_dvc->ptr_map[srb];
- asc_dvc->ptr_map[srb] = NULL;
- ASC_DBG(3, "Returning ptr %p from array offset %d\n", ptr, srb);
- return ptr;
-}
-
-/*
* advansys_info()
*
* Return suitable for printing on the console with the argument
@@ -3350,7 +3197,7 @@ static void asc_prt_driver_conf(struct seq_file *m, struct Scsi_Host *shost)
seq_printf(m,
" flags 0x%x, last_reset 0x%lx, jiffies 0x%lx, asc_n_io_port 0x%x\n",
- boardp->flags, boardp->last_reset, jiffies,
+ boardp->flags, shost->last_reset, jiffies,
boardp->asc_n_io_port);
seq_printf(m, " io_port 0x%lx\n", shost->io_port);
@@ -3844,7 +3691,7 @@ static int AscStartChip(PortAddr iop_base)
return (1);
}
-static int AscStopChip(PortAddr iop_base)
+static bool AscStopChip(PortAddr iop_base)
{
uchar cc_val;
@@ -3855,22 +3702,22 @@ static int AscStopChip(PortAddr iop_base)
AscSetChipIH(iop_base, INS_HALT);
AscSetChipIH(iop_base, INS_RFLAG_WTM);
if ((AscGetChipStatus(iop_base) & CSW_HALTED) == 0) {
- return (0);
+ return false;
}
- return (1);
+ return true;
}
-static int AscIsChipHalted(PortAddr iop_base)
+static bool AscIsChipHalted(PortAddr iop_base)
{
if ((AscGetChipStatus(iop_base) & CSW_HALTED) != 0) {
if ((AscGetChipControl(iop_base) & CC_HALT) != 0) {
- return (1);
+ return true;
}
}
- return (0);
+ return false;
}
-static int AscResetChipAndScsiBus(ASC_DVC_VAR *asc_dvc)
+static bool AscResetChipAndScsiBus(ASC_DVC_VAR *asc_dvc)
{
PortAddr iop_base;
int i = 10;
@@ -3953,20 +3800,6 @@ static ushort AscReadLramWord(PortAddr iop_base, ushort addr)
return (word_data);
}
-#if CC_VERY_LONG_SG_LIST
-static ASC_DCNT AscReadLramDWord(PortAddr iop_base, ushort addr)
-{
- ushort val_low, val_high;
- ASC_DCNT dword_data;
-
- AscSetChipLramAddr(iop_base, addr);
- val_low = AscGetChipLramData(iop_base);
- val_high = AscGetChipLramData(iop_base);
- dword_data = ((ASC_DCNT) val_high << 16) | (ASC_DCNT) val_low;
- return (dword_data);
-}
-#endif /* CC_VERY_LONG_SG_LIST */
-
static void
AscMemWordSetLram(PortAddr iop_base, ushort s_addr, ushort set_wval, int words)
{
@@ -4068,27 +3901,24 @@ AscMemWordCopyPtrFromLram(PortAddr iop_base,
}
}
-static ASC_DCNT AscMemSumLramWord(PortAddr iop_base, ushort s_addr, int words)
+static u32 AscMemSumLramWord(PortAddr iop_base, ushort s_addr, int words)
{
- ASC_DCNT sum;
+ u32 sum = 0;
int i;
- sum = 0L;
for (i = 0; i < words; i++, s_addr += 2) {
sum += AscReadLramWord(iop_base, s_addr);
}
return (sum);
}
-static ushort AscInitLram(ASC_DVC_VAR *asc_dvc)
+static void AscInitLram(ASC_DVC_VAR *asc_dvc)
{
uchar i;
ushort s_addr;
PortAddr iop_base;
- ushort warn_code;
iop_base = asc_dvc->iop_base;
- warn_code = 0;
AscMemWordSetLram(iop_base, ASC_QADR_BEG, 0,
(ushort)(((int)(asc_dvc->max_total_qng + 2 + 1) *
64) >> 1));
@@ -4127,14 +3957,13 @@ static ushort AscInitLram(ASC_DVC_VAR *asc_dvc)
AscWriteLramByte(iop_base,
(ushort)(s_addr + (ushort)ASC_SCSIQ_B_QNO), i);
}
- return warn_code;
}
-static ASC_DCNT
+static u32
AscLoadMicroCode(PortAddr iop_base, ushort s_addr,
const uchar *mcode_buf, ushort mcode_size)
{
- ASC_DCNT chksum;
+ u32 chksum;
ushort mcode_word_size;
ushort mcode_chksum;
@@ -4186,13 +4015,13 @@ static void AscInitQLinkVar(ASC_DVC_VAR *asc_dvc)
}
}
-static ushort AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
+static int AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
{
int i;
- ushort warn_code;
+ int warn_code;
PortAddr iop_base;
- ASC_PADDR phy_addr;
- ASC_DCNT phy_size;
+ __le32 phy_addr;
+ __le32 phy_size;
struct asc_board *board = asc_dvc_to_board(asc_dvc);
iop_base = asc_dvc->iop_base;
@@ -4231,12 +4060,12 @@ static ushort AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR;
- warn_code = UW_ERR;
+ warn_code = -EINVAL;
goto err_mcode_start;
}
if (AscStartChip(iop_base) != 1) {
asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
- warn_code = UW_ERR;
+ warn_code = -EIO;
goto err_mcode_start;
}
@@ -4250,13 +4079,13 @@ err_dma_map:
return warn_code;
}
-static ushort AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
+static int AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
{
const struct firmware *fw;
const char fwname[] = "advansys/mcode.bin";
int err;
unsigned long chksum;
- ushort warn_code;
+ int warn_code;
PortAddr iop_base;
iop_base = asc_dvc->iop_base;
@@ -4268,15 +4097,13 @@ static ushort AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
}
asc_dvc->init_state |= ASC_INIT_STATE_BEG_LOAD_MC;
if (asc_dvc->err_code != 0)
- return UW_ERR;
+ return ASC_ERROR;
if (!AscFindSignature(asc_dvc->iop_base)) {
asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
return warn_code;
}
AscDisableInterrupt(iop_base);
- warn_code |= AscInitLram(asc_dvc);
- if (asc_dvc->err_code != 0)
- return UW_ERR;
+ AscInitLram(asc_dvc);
err = request_firmware(&fw, fwname, asc_dvc->drv_ptr->dev);
if (err) {
@@ -4336,7 +4163,7 @@ static int AdvLoadMicrocode(AdvPortAddr iop_base, const unsigned char *buf,
int size, int memsize, int chksum)
{
int i, j, end, len = 0;
- ADV_DCNT sum;
+ u32 sum;
AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
@@ -4382,38 +4209,72 @@ static int AdvLoadMicrocode(AdvPortAddr iop_base, const unsigned char *buf,
return 0;
}
-static void AdvBuildCarrierFreelist(struct adv_dvc_var *asc_dvc)
+static void AdvBuildCarrierFreelist(struct adv_dvc_var *adv_dvc)
{
- ADV_CARR_T *carrp;
- ADV_SDCNT buf_size;
- ADV_PADDR carr_paddr;
+ off_t carr_offset = 0, next_offset;
+ dma_addr_t carr_paddr;
+ int carr_num = ADV_CARRIER_BUFSIZE / sizeof(ADV_CARR_T), i;
- carrp = (ADV_CARR_T *) ADV_16BALIGN(asc_dvc->carrier_buf);
- asc_dvc->carr_freelist = NULL;
- if (carrp == asc_dvc->carrier_buf) {
- buf_size = ADV_CARRIER_BUFSIZE;
- } else {
- buf_size = ADV_CARRIER_BUFSIZE - sizeof(ADV_CARR_T);
+ for (i = 0; i < carr_num; i++) {
+ carr_offset = i * sizeof(ADV_CARR_T);
+ /* Get physical address of the carrier 'carrp'. */
+ carr_paddr = adv_dvc->carrier_addr + carr_offset;
+
+ adv_dvc->carrier[i].carr_pa = cpu_to_le32(carr_paddr);
+ adv_dvc->carrier[i].carr_va = cpu_to_le32(carr_offset);
+ adv_dvc->carrier[i].areq_vpa = 0;
+ next_offset = carr_offset + sizeof(ADV_CARR_T);
+ if (i == carr_num)
+ next_offset = ~0;
+ adv_dvc->carrier[i].next_vpa = cpu_to_le32(next_offset);
}
+ /*
+ * We cannot have a carrier with 'carr_va' of '0', as
+ * a reference to this carrier would be interpreted as
+ * list termination.
+ * So start at carrier 1 with the freelist.
+ */
+ adv_dvc->carr_freelist = &adv_dvc->carrier[1];
+}
- do {
- /* Get physical address of the carrier 'carrp'. */
- carr_paddr = cpu_to_le32(virt_to_bus(carrp));
+static ADV_CARR_T *adv_get_carrier(struct adv_dvc_var *adv_dvc, u32 offset)
+{
+ int index;
- buf_size -= sizeof(ADV_CARR_T);
+ BUG_ON(offset > ADV_CARRIER_BUFSIZE);
- carrp->carr_pa = carr_paddr;
- carrp->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(carrp));
+ index = offset / sizeof(ADV_CARR_T);
+ return &adv_dvc->carrier[index];
+}
- /*
- * Insert the carrier at the beginning of the freelist.
- */
- carrp->next_vpa =
- cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist));
- asc_dvc->carr_freelist = carrp;
+static ADV_CARR_T *adv_get_next_carrier(struct adv_dvc_var *adv_dvc)
+{
+ ADV_CARR_T *carrp = adv_dvc->carr_freelist;
+ u32 next_vpa = le32_to_cpu(carrp->next_vpa);
+
+ if (next_vpa == 0 || next_vpa == ~0) {
+ ASC_DBG(1, "invalid vpa offset 0x%x\n", next_vpa);
+ return NULL;
+ }
+
+ adv_dvc->carr_freelist = adv_get_carrier(adv_dvc, next_vpa);
+ /*
+ * insert stopper carrier to terminate list
+ */
+ carrp->next_vpa = cpu_to_le32(ADV_CQ_STOPPER);
+
+ return carrp;
+}
+
+/*
+ * 'offset' is the index in the request pointer array
+ */
+static adv_req_t * adv_get_reqp(struct adv_dvc_var *adv_dvc, u32 offset)
+{
+ struct asc_board *boardp = adv_dvc->drv_ptr;
- carrp++;
- } while (buf_size > 0);
+ BUG_ON(offset > adv_dvc->max_host_qng);
+ return &boardp->adv_reqp[offset];
}
/*
@@ -4432,10 +4293,9 @@ static void AdvBuildCarrierFreelist(struct adv_dvc_var *asc_dvc)
*/
static int
AdvSendIdleCmd(ADV_DVC_VAR *asc_dvc,
- ushort idle_cmd, ADV_DCNT idle_cmd_parameter)
+ ushort idle_cmd, u32 idle_cmd_parameter)
{
- int result;
- ADV_DCNT i, j;
+ int result, i, j;
AdvPortAddr iop_base;
iop_base = asc_dvc->iop_base;
@@ -4902,17 +4762,11 @@ static int AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc)
* Set-up the Host->RISC Initiator Command Queue (ICQ).
*/
- if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->icq_sp) {
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
-
- /*
- * The first command issued will be placed in the stopper carrier.
- */
- asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC ICQ physical address start value.
@@ -4922,21 +4776,11 @@ static int AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc)
/*
* Set-up the RISC->Host Initiator Response Queue (IRQ).
*/
- if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->irq_sp) {
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
-
- /*
- * The first command completed by the RISC will be placed in
- * the stopper.
- *
- * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
- * completed the RISC will set the ASC_RQ_STOPPER bit.
- */
- asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC IRQ physical address start value.
@@ -5399,17 +5243,12 @@ static int AdvInitAsc38C0800Driver(ADV_DVC_VAR *asc_dvc)
* Set-up the Host->RISC Initiator Command Queue (ICQ).
*/
- if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->icq_sp) {
+ ASC_DBG(0, "Failed to get ICQ carrier\n");
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
-
- /*
- * The first command issued will be placed in the stopper carrier.
- */
- asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC ICQ physical address start value.
@@ -5420,21 +5259,12 @@ static int AdvInitAsc38C0800Driver(ADV_DVC_VAR *asc_dvc)
/*
* Set-up the RISC->Host Initiator Response Queue (IRQ).
*/
- if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->irq_sp) {
+ ASC_DBG(0, "Failed to get IRQ carrier\n");
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
-
- /*
- * The first command completed by the RISC will be placed in
- * the stopper.
- *
- * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
- * completed the RISC will set the ASC_RQ_STOPPER bit.
- */
- asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC IRQ physical address start value.
@@ -5909,17 +5739,11 @@ static int AdvInitAsc38C1600Driver(ADV_DVC_VAR *asc_dvc)
/*
* Set-up the Host->RISC Initiator Command Queue (ICQ).
*/
- if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->icq_sp) {
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
-
- /*
- * The first command issued will be placed in the stopper carrier.
- */
- asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC ICQ physical address start value. Initialize the
@@ -5933,21 +5757,11 @@ static int AdvInitAsc38C1600Driver(ADV_DVC_VAR *asc_dvc)
/*
* Set-up the RISC->Host Initiator Response Queue (IRQ).
*/
- if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) {
+ asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc);
+ if (!asc_dvc->irq_sp) {
asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
return ADV_ERROR;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
-
- /*
- * The first command completed by the RISC will be placed in
- * the stopper.
- *
- * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
- * completed the RISC will set the ASC_RQ_STOPPER bit.
- */
- asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
/*
* Set RISC IRQ physical address start value.
@@ -6134,15 +5948,16 @@ static void adv_async_callback(ADV_DVC_VAR *adv_dvc_varp, uchar code)
*/
static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
{
- struct asc_board *boardp;
+ struct asc_board *boardp = adv_dvc_varp->drv_ptr;
+ u32 srb_tag;
adv_req_t *reqp;
adv_sgblk_t *sgblkp;
struct scsi_cmnd *scp;
- struct Scsi_Host *shost;
- ADV_DCNT resid_cnt;
+ u32 resid_cnt;
+ dma_addr_t sense_addr;
- ASC_DBG(1, "adv_dvc_varp 0x%lx, scsiqp 0x%lx\n",
- (ulong)adv_dvc_varp, (ulong)scsiqp);
+ ASC_DBG(1, "adv_dvc_varp 0x%p, scsiqp 0x%p\n",
+ adv_dvc_varp, scsiqp);
ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
/*
@@ -6150,22 +5965,9 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
* completed. The adv_req_t structure actually contains the
* completed ADV_SCSI_REQ_Q structure.
*/
- reqp = (adv_req_t *)ADV_U32_TO_VADDR(scsiqp->srb_ptr);
- ASC_DBG(1, "reqp 0x%lx\n", (ulong)reqp);
- if (reqp == NULL) {
- ASC_PRINT("adv_isr_callback: reqp is NULL\n");
- return;
- }
+ srb_tag = le32_to_cpu(scsiqp->srb_tag);
+ scp = scsi_host_find_tag(boardp->shost, scsiqp->srb_tag);
- /*
- * Get the struct scsi_cmnd structure and Scsi_Host structure for the
- * command that has been completed.
- *
- * Note: The adv_req_t request structure and adv_sgblk_t structure,
- * if any, are dropped, because a board structure pointer can not be
- * determined.
- */
- scp = reqp->cmndp;
ASC_DBG(1, "scp 0x%p\n", scp);
if (scp == NULL) {
ASC_PRINT
@@ -6174,12 +5976,25 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
}
ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
- shost = scp->device->host;
- ASC_STATS(shost, callback);
- ASC_DBG(1, "shost 0x%p\n", shost);
+ reqp = (adv_req_t *)scp->host_scribble;
+ ASC_DBG(1, "reqp 0x%lx\n", (ulong)reqp);
+ if (reqp == NULL) {
+ ASC_PRINT("adv_isr_callback: reqp is NULL\n");
+ return;
+ }
+ /*
+ * Remove backreferences to avoid duplicate
+ * command completions.
+ */
+ scp->host_scribble = NULL;
+ reqp->cmndp = NULL;
+
+ ASC_STATS(boardp->shost, callback);
+ ASC_DBG(1, "shost 0x%p\n", boardp->shost);
- boardp = shost_priv(shost);
- BUG_ON(adv_dvc_varp != &boardp->dvc_var.adv_dvc_var);
+ sense_addr = le32_to_cpu(scsiqp->sense_addr);
+ dma_unmap_single(boardp->dev, sense_addr,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
/*
* 'done_status' contains the command's ending status.
@@ -6272,18 +6087,10 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
/* Remove 'sgblkp' from the request list. */
reqp->sgblkp = sgblkp->next_sgblkp;
- /* Add 'sgblkp' to the board free list. */
- sgblkp->next_sgblkp = boardp->adv_sgblkp;
- boardp->adv_sgblkp = sgblkp;
+ dma_pool_free(boardp->adv_sgblk_pool, sgblkp,
+ sgblkp->sg_addr);
}
- /*
- * Free the adv_req_t structure used with the command by adding
- * it back to the board free list.
- */
- reqp->next_reqp = boardp->adv_reqp;
- boardp->adv_reqp = reqp;
-
ASC_DBG(1, "done\n");
}
@@ -6312,8 +6119,9 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
uchar int_stat;
ushort target_bit;
ADV_CARR_T *free_carrp;
- ADV_VADDR irq_next_vpa;
+ __le32 irq_next_vpa;
ADV_SCSI_REQ_Q *scsiq;
+ adv_req_t *reqp;
iop_base = asc_dvc->iop_base;
@@ -6356,25 +6164,28 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
* Check if the IRQ stopper carrier contains a completed request.
*/
while (((irq_next_vpa =
- le32_to_cpu(asc_dvc->irq_sp->next_vpa)) & ASC_RQ_DONE) != 0) {
+ le32_to_cpu(asc_dvc->irq_sp->next_vpa)) & ADV_RQ_DONE) != 0) {
/*
* Get a pointer to the newly completed ADV_SCSI_REQ_Q structure.
* The RISC will have set 'areq_vpa' to a virtual address.
*
- * The firmware will have copied the ASC_SCSI_REQ_Q.scsiq_ptr
+ * The firmware will have copied the ADV_SCSI_REQ_Q.scsiq_ptr
* field to the carrier ADV_CARR_T.areq_vpa field. The conversion
- * below complements the conversion of ASC_SCSI_REQ_Q.scsiq_ptr'
+ * below complements the conversion of ADV_SCSI_REQ_Q.scsiq_ptr'
* in AdvExeScsiQueue().
*/
- scsiq = (ADV_SCSI_REQ_Q *)
- ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->areq_vpa));
+ u32 pa_offset = le32_to_cpu(asc_dvc->irq_sp->areq_vpa);
+ ASC_DBG(1, "irq_sp %p areq_vpa %u\n",
+ asc_dvc->irq_sp, pa_offset);
+ reqp = adv_get_reqp(asc_dvc, pa_offset);
+ scsiq = &reqp->scsi_req_q;
/*
* Request finished with good status and the queue was not
* DMAed to host memory by the firmware. Set all status fields
* to indicate good status.
*/
- if ((irq_next_vpa & ASC_RQ_GOOD) != 0) {
+ if ((irq_next_vpa & ADV_RQ_GOOD) != 0) {
scsiq->done_status = QD_NO_ERROR;
scsiq->host_status = scsiq->scsi_status = 0;
scsiq->data_cnt = 0L;
@@ -6386,11 +6197,10 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
* stopper carrier.
*/
free_carrp = asc_dvc->irq_sp;
- asc_dvc->irq_sp = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(ASC_GET_CARRP(irq_next_vpa));
+ asc_dvc->irq_sp = adv_get_carrier(asc_dvc,
+ ADV_GET_CARRP(irq_next_vpa));
- free_carrp->next_vpa =
- cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist));
+ free_carrp->next_vpa = asc_dvc->carr_freelist->carr_va;
asc_dvc->carr_freelist = free_carrp;
asc_dvc->carr_pending_cnt--;
@@ -6405,7 +6215,6 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
* Notify the driver of the completed request by passing
* the ADV_SCSI_REQ_Q pointer to its callback function.
*/
- scsiq->a_flag |= ADV_SCSIQ_DONE;
adv_isr_callback(asc_dvc, scsiq);
/*
* Note: After the driver callback function is called, 'scsiq'
@@ -6521,11 +6330,11 @@ AscCalSDTRData(ASC_DVC_VAR *asc_dvc, uchar sdtr_period, uchar syn_offset)
return byte;
}
-static int AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data)
+static bool AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data)
{
ASC_SCSI_BIT_ID_TYPE org_id;
int i;
- int sta = TRUE;
+ bool sta = true;
AscSetBank(iop_base, 1);
org_id = AscReadChipDvcID(iop_base);
@@ -6539,10 +6348,10 @@ static int AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data)
AscSetBank(iop_base, 0);
AscSetChipSyn(iop_base, sdtr_data);
if (AscGetChipSyn(iop_base) != sdtr_data) {
- sta = FALSE;
+ sta = false;
}
} else {
- sta = FALSE;
+ sta = false;
}
AscSetBank(iop_base, 1);
AscWriteChipDvcID(iop_base, org_id);
@@ -6556,12 +6365,12 @@ static void AscSetChipSDTR(PortAddr iop_base, uchar sdtr_data, uchar tid_no)
AscPutMCodeSDTRDoneAtID(iop_base, tid_no, sdtr_data);
}
-static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
+static void AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
{
EXT_MSG ext_msg;
EXT_MSG out_msg;
ushort halt_q_addr;
- int sdtr_accept;
+ bool sdtr_accept;
ushort int_halt_code;
ASC_SCSI_BIT_ID_TYPE scsi_busy;
ASC_SCSI_BIT_ID_TYPE target_id;
@@ -6603,14 +6412,14 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
boardp->sdtr_data[tid_no] = 0;
}
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else if (int_halt_code == ASC_HALT_ENABLE_ASYN_USE_SYN_FIX) {
if (asc_dvc->pci_fix_asyn_xfer & target_id) {
AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
boardp->sdtr_data[tid_no] = asyn_sdtr;
}
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else if (int_halt_code == ASC_HALT_EXTMSG_IN) {
AscMemWordCopyPtrFromLram(iop_base,
ASCV_MSGIN_BEG,
@@ -6620,10 +6429,10 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
if (ext_msg.msg_type == EXTENDED_MESSAGE &&
ext_msg.msg_req == EXTENDED_SDTR &&
ext_msg.msg_len == MS_SDTR_LEN) {
- sdtr_accept = TRUE;
+ sdtr_accept = true;
if ((ext_msg.req_ack_offset > ASC_SYN_MAX_OFFSET)) {
- sdtr_accept = FALSE;
+ sdtr_accept = false;
ext_msg.req_ack_offset = ASC_SYN_MAX_OFFSET;
}
if ((ext_msg.xfer_period <
@@ -6631,7 +6440,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
|| (ext_msg.xfer_period >
asc_dvc->sdtr_period_tbl[asc_dvc->
max_sdtr_index])) {
- sdtr_accept = FALSE;
+ sdtr_accept = false;
ext_msg.xfer_period =
asc_dvc->sdtr_period_tbl[asc_dvc->
min_sdtr_index];
@@ -6696,7 +6505,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
(ushort)ASC_SCSIQ_B_CNTL),
q_cntl);
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else if (ext_msg.msg_type == EXTENDED_MESSAGE &&
ext_msg.msg_req == EXTENDED_WDTR &&
ext_msg.msg_len == MS_WDTR_LEN) {
@@ -6712,7 +6521,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
(ushort)ASC_SCSIQ_B_CNTL),
q_cntl);
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else {
ext_msg.msg_type = MESSAGE_REJECT;
@@ -6726,7 +6535,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
(ushort)ASC_SCSIQ_B_CNTL),
q_cntl);
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
}
} else if (int_halt_code == ASC_HALT_CHK_CONDITION) {
@@ -6783,7 +6592,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
AscWriteLramByte(iop_base, (ushort)ASCV_SCSIBUSY_B, scsi_busy);
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else if (int_halt_code == ASC_HALT_SDTR_REJECTED) {
AscMemWordCopyPtrFromLram(iop_base,
@@ -6805,7 +6614,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
(ushort)(halt_q_addr +
(ushort)ASC_SCSIQ_B_CNTL), q_cntl);
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
} else if (int_halt_code == ASC_HALT_SS_QUEUE_FULL) {
scsi_status = AscReadLramByte(iop_base,
@@ -6850,166 +6659,9 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
}
}
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
- }
-#if CC_VERY_LONG_SG_LIST
- else if (int_halt_code == ASC_HALT_HOST_COPY_SG_LIST_TO_RISC) {
- uchar q_no;
- ushort q_addr;
- uchar sg_wk_q_no;
- uchar first_sg_wk_q_no;
- ASC_SCSI_Q *scsiq; /* Ptr to driver request. */
- ASC_SG_HEAD *sg_head; /* Ptr to driver SG request. */
- ASC_SG_LIST_Q scsi_sg_q; /* Structure written to queue. */
- ushort sg_list_dwords;
- ushort sg_entry_cnt;
- uchar next_qp;
- int i;
-
- q_no = AscReadLramByte(iop_base, (ushort)ASCV_REQ_SG_LIST_QP);
- if (q_no == ASC_QLINK_END)
- return 0;
-
- q_addr = ASC_QNO_TO_QADDR(q_no);
-
- /*
- * Convert the request's SRB pointer to a host ASC_SCSI_REQ
- * structure pointer using a macro provided by the driver.
- * The ASC_SCSI_REQ pointer provides a pointer to the
- * host ASC_SG_HEAD structure.
- */
- /* Read request's SRB pointer. */
- scsiq = (ASC_SCSI_Q *)
- ASC_SRB2SCSIQ(ASC_U32_TO_VADDR(AscReadLramDWord(iop_base,
- (ushort)
- (q_addr +
- ASC_SCSIQ_D_SRBPTR))));
-
- /*
- * Get request's first and working SG queue.
- */
- sg_wk_q_no = AscReadLramByte(iop_base,
- (ushort)(q_addr +
- ASC_SCSIQ_B_SG_WK_QP));
-
- first_sg_wk_q_no = AscReadLramByte(iop_base,
- (ushort)(q_addr +
- ASC_SCSIQ_B_FIRST_SG_WK_QP));
-
- /*
- * Reset request's working SG queue back to the
- * first SG queue.
- */
- AscWriteLramByte(iop_base,
- (ushort)(q_addr +
- (ushort)ASC_SCSIQ_B_SG_WK_QP),
- first_sg_wk_q_no);
-
- sg_head = scsiq->sg_head;
-
- /*
- * Set sg_entry_cnt to the number of SG elements
- * that will be completed on this interrupt.
- *
- * Note: The allocated SG queues contain ASC_MAX_SG_LIST - 1
- * SG elements. The data_cnt and data_addr fields which
- * add 1 to the SG element capacity are not used when
- * restarting SG handling after a halt.
- */
- if (scsiq->remain_sg_entry_cnt > (ASC_MAX_SG_LIST - 1)) {
- sg_entry_cnt = ASC_MAX_SG_LIST - 1;
-
- /*
- * Keep track of remaining number of SG elements that
- * will need to be handled on the next interrupt.
- */
- scsiq->remain_sg_entry_cnt -= (ASC_MAX_SG_LIST - 1);
- } else {
- sg_entry_cnt = scsiq->remain_sg_entry_cnt;
- scsiq->remain_sg_entry_cnt = 0;
- }
-
- /*
- * Copy SG elements into the list of allocated SG queues.
- *
- * Last index completed is saved in scsiq->next_sg_index.
- */
- next_qp = first_sg_wk_q_no;
- q_addr = ASC_QNO_TO_QADDR(next_qp);
- scsi_sg_q.sg_head_qp = q_no;
- scsi_sg_q.cntl = QCSG_SG_XFER_LIST;
- for (i = 0; i < sg_head->queue_cnt; i++) {
- scsi_sg_q.seq_no = i + 1;
- if (sg_entry_cnt > ASC_SG_LIST_PER_Q) {
- sg_list_dwords = (uchar)(ASC_SG_LIST_PER_Q * 2);
- sg_entry_cnt -= ASC_SG_LIST_PER_Q;
- /*
- * After very first SG queue RISC FW uses next
- * SG queue first element then checks sg_list_cnt
- * against zero and then decrements, so set
- * sg_list_cnt 1 less than number of SG elements
- * in each SG queue.
- */
- scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q - 1;
- scsi_sg_q.sg_cur_list_cnt =
- ASC_SG_LIST_PER_Q - 1;
- } else {
- /*
- * This is the last SG queue in the list of
- * allocated SG queues. If there are more
- * SG elements than will fit in the allocated
- * queues, then set the QCSG_SG_XFER_MORE flag.
- */
- if (scsiq->remain_sg_entry_cnt != 0) {
- scsi_sg_q.cntl |= QCSG_SG_XFER_MORE;
- } else {
- scsi_sg_q.cntl |= QCSG_SG_XFER_END;
- }
- /* equals sg_entry_cnt * 2 */
- sg_list_dwords = sg_entry_cnt << 1;
- scsi_sg_q.sg_list_cnt = sg_entry_cnt - 1;
- scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt - 1;
- sg_entry_cnt = 0;
- }
-
- scsi_sg_q.q_no = next_qp;
- AscMemWordCopyPtrToLram(iop_base,
- q_addr + ASC_SCSIQ_SGHD_CPY_BEG,
- (uchar *)&scsi_sg_q,
- sizeof(ASC_SG_LIST_Q) >> 1);
-
- AscMemDWordCopyPtrToLram(iop_base,
- q_addr + ASC_SGQ_LIST_BEG,
- (uchar *)&sg_head->
- sg_list[scsiq->next_sg_index],
- sg_list_dwords);
-
- scsiq->next_sg_index += ASC_SG_LIST_PER_Q;
-
- /*
- * If the just completed SG queue contained the
- * last SG element, then no more SG queues need
- * to be written.
- */
- if (scsi_sg_q.cntl & QCSG_SG_XFER_END) {
- break;
- }
-
- next_qp = AscReadLramByte(iop_base,
- (ushort)(q_addr +
- ASC_SCSIQ_B_FWD));
- q_addr = ASC_QNO_TO_QADDR(next_qp);
- }
-
- /*
- * Clear the halt condition so the RISC will be restarted
- * after the return.
- */
- AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
- return (0);
+ return;
}
-#endif /* CC_VERY_LONG_SG_LIST */
- return (0);
+ return;
}
/*
@@ -7043,7 +6695,7 @@ DvcGetQinfo(PortAddr iop_base, ushort s_addr, uchar *inbuf, int words)
static uchar
_AscCopyLramScsiDoneQ(PortAddr iop_base,
ushort q_addr,
- ASC_QDONE_INFO *scsiq, ASC_DCNT max_dma_count)
+ ASC_QDONE_INFO *scsiq, unsigned int max_dma_count)
{
ushort _val;
uchar sg_queue_cnt;
@@ -7070,10 +6722,10 @@ _AscCopyLramScsiDoneQ(PortAddr iop_base,
/*
* Read high word of remain bytes from alternate location.
*/
- scsiq->remain_bytes = (((ADV_DCNT)AscReadLramWord(iop_base,
- (ushort)(q_addr +
- (ushort)
- ASC_SCSIQ_W_ALT_DC1)))
+ scsiq->remain_bytes = (((u32)AscReadLramWord(iop_base,
+ (ushort)(q_addr +
+ (ushort)
+ ASC_SCSIQ_W_ALT_DC1)))
<< 16);
/*
* Read low word of remain bytes from original location.
@@ -7093,25 +6745,24 @@ _AscCopyLramScsiDoneQ(PortAddr iop_base,
*/
static void asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
{
- struct asc_board *boardp;
+ struct asc_board *boardp = asc_dvc_varp->drv_ptr;
+ u32 srb_tag;
struct scsi_cmnd *scp;
- struct Scsi_Host *shost;
ASC_DBG(1, "asc_dvc_varp 0x%p, qdonep 0x%p\n", asc_dvc_varp, qdonep);
ASC_DBG_PRT_ASC_QDONE_INFO(2, qdonep);
- scp = advansys_srb_to_ptr(asc_dvc_varp, qdonep->d2.srb_ptr);
+ /*
+ * Decrease the srb_tag by 1 to find the SCSI command
+ */
+ srb_tag = qdonep->d2.srb_tag - 1;
+ scp = scsi_host_find_tag(boardp->shost, srb_tag);
if (!scp)
return;
ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
- shost = scp->device->host;
- ASC_STATS(shost, callback);
- ASC_DBG(1, "shost 0x%p\n", shost);
-
- boardp = shost_priv(shost);
- BUG_ON(asc_dvc_varp != &boardp->dvc_var.asc_dvc_var);
+ ASC_STATS(boardp->shost, callback);
dma_unmap_single(boardp->dev, scp->SCp.dma_handle,
SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
@@ -7220,7 +6871,7 @@ static int AscIsrQDone(ASC_DVC_VAR *asc_dvc)
uchar cur_target_qng;
ASC_QDONE_INFO scsiq_buf;
ASC_QDONE_INFO *scsiq;
- int false_overrun;
+ bool false_overrun;
iop_base = asc_dvc->iop_base;
n_q_used = 1;
@@ -7294,14 +6945,17 @@ static int AscIsrQDone(ASC_DVC_VAR *asc_dvc)
scsiq->d3.done_stat = QD_WITH_ERROR;
goto FATAL_ERR_QDONE;
}
- if ((scsiq->d2.srb_ptr == 0UL) ||
+ if ((scsiq->d2.srb_tag == 0UL) ||
((scsiq->q_status & QS_ABORTED) != 0)) {
return (0x11);
} else if (scsiq->q_status == QS_DONE) {
- false_overrun = FALSE;
+ /*
+ * This is also curious.
+ * false_overrun will _always_ be set to 'false'
+ */
+ false_overrun = false;
if (scsiq->extra_bytes != 0) {
- scsiq->remain_bytes +=
- (ADV_DCNT)scsiq->extra_bytes;
+ scsiq->remain_bytes += scsiq->extra_bytes;
}
if (scsiq->d3.done_stat == QD_WITH_ERROR) {
if (scsiq->d3.host_stat ==
@@ -7372,23 +7026,23 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
uchar host_flag;
iop_base = asc_dvc->iop_base;
- int_pending = FALSE;
+ int_pending = ASC_FALSE;
if (AscIsIntPending(iop_base) == 0)
return int_pending;
if ((asc_dvc->init_state & ASC_INIT_STATE_END_LOAD_MC) == 0) {
- return ERR;
+ return ASC_ERROR;
}
if (asc_dvc->in_critical_cnt != 0) {
AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_ON_CRITICAL);
- return ERR;
+ return ASC_ERROR;
}
if (asc_dvc->is_in_int) {
AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_RE_ENTRY);
- return ERR;
+ return ASC_ERROR;
}
- asc_dvc->is_in_int = TRUE;
+ asc_dvc->is_in_int = true;
ctrl_reg = AscGetChipControl(iop_base);
saved_ctrl_reg = ctrl_reg & (~(CC_SCSI_RESET | CC_CHIP_RESET |
CC_SINGLE_STEP | CC_DIAG | CC_TEST));
@@ -7396,7 +7050,7 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
if (chipstat & CSW_SCSI_RESET_LATCH) {
if (!(asc_dvc->bus_type & (ASC_IS_VL | ASC_IS_EISA))) {
int i = 10;
- int_pending = TRUE;
+ int_pending = ASC_TRUE;
asc_dvc->sdtr_done = 0;
saved_ctrl_reg &= (uchar)(~CC_HALT);
while ((AscGetChipStatus(iop_base) &
@@ -7418,15 +7072,11 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
(uchar)(host_flag | (uchar)ASC_HOST_FLAG_IN_ISR));
if ((chipstat & CSW_INT_PENDING) || (int_pending)) {
AscAckInterrupt(iop_base);
- int_pending = TRUE;
+ int_pending = ASC_TRUE;
if ((chipstat & CSW_HALTED) && (ctrl_reg & CC_SINGLE_STEP)) {
- if (AscIsrChipHalted(asc_dvc) == ERR) {
- goto ISR_REPORT_QDONE_FATAL_ERROR;
- } else {
- saved_ctrl_reg &= (uchar)(~CC_HALT);
- }
+ AscIsrChipHalted(asc_dvc);
+ saved_ctrl_reg &= (uchar)(~CC_HALT);
} else {
- ISR_REPORT_QDONE_FATAL_ERROR:
if ((asc_dvc->dvc_cntl & ASC_CNTL_INT_MULTI_Q) != 0) {
while (((status =
AscIsrQDone(asc_dvc)) & 0x01) != 0) {
@@ -7440,20 +7090,20 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
} while (status == 0x11);
}
if ((status & 0x80) != 0)
- int_pending = ERR;
+ int_pending = ASC_ERROR;
}
}
AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B, host_flag);
AscSetChipLramAddr(iop_base, saved_ram_addr);
AscSetChipControl(iop_base, saved_ctrl_reg);
- asc_dvc->is_in_int = FALSE;
+ asc_dvc->is_in_int = false;
return int_pending;
}
/*
* advansys_reset()
*
- * Reset the bus associated with the command 'scp'.
+ * Reset the host associated with the command 'scp'.
*
* This function runs its own thread. Interrupts must be blocked but
* sleeping is allowed and no locking other than for host structures is
@@ -7471,7 +7121,7 @@ static int advansys_reset(struct scsi_cmnd *scp)
ASC_STATS(shost, reset);
- scmd_printk(KERN_INFO, scp, "SCSI bus reset started...\n");
+ scmd_printk(KERN_INFO, scp, "SCSI host reset started...\n");
if (ASC_NARROW_BOARD(boardp)) {
ASC_DVC_VAR *asc_dvc = &boardp->dvc_var.asc_dvc_var;
@@ -7482,20 +7132,19 @@ static int advansys_reset(struct scsi_cmnd *scp)
/* Refer to ASC_IERR_* definitions for meaning of 'err_code'. */
if (asc_dvc->err_code || !asc_dvc->overrun_dma) {
- scmd_printk(KERN_INFO, scp, "SCSI bus reset error: "
+ scmd_printk(KERN_INFO, scp, "SCSI host reset error: "
"0x%x, status: 0x%x\n", asc_dvc->err_code,
status);
ret = FAILED;
} else if (status) {
- scmd_printk(KERN_INFO, scp, "SCSI bus reset warning: "
+ scmd_printk(KERN_INFO, scp, "SCSI host reset warning: "
"0x%x\n", status);
} else {
- scmd_printk(KERN_INFO, scp, "SCSI bus reset "
+ scmd_printk(KERN_INFO, scp, "SCSI host reset "
"successful\n");
}
ASC_DBG(1, "after AscInitAsc1000Driver()\n");
- spin_lock_irqsave(shost->host_lock, flags);
} else {
/*
* If the suggest reset bus flags are set, then reset the bus.
@@ -7504,28 +7153,25 @@ static int advansys_reset(struct scsi_cmnd *scp)
ADV_DVC_VAR *adv_dvc = &boardp->dvc_var.adv_dvc_var;
/*
- * Reset the target's SCSI bus.
+ * Reset the chip and SCSI bus.
*/
ASC_DBG(1, "before AdvResetChipAndSB()\n");
switch (AdvResetChipAndSB(adv_dvc)) {
case ASC_TRUE:
- scmd_printk(KERN_INFO, scp, "SCSI bus reset "
+ scmd_printk(KERN_INFO, scp, "SCSI host reset "
"successful\n");
break;
case ASC_FALSE:
default:
- scmd_printk(KERN_INFO, scp, "SCSI bus reset error\n");
+ scmd_printk(KERN_INFO, scp, "SCSI host reset error\n");
ret = FAILED;
break;
}
spin_lock_irqsave(shost->host_lock, flags);
AdvISR(adv_dvc);
+ spin_unlock_irqrestore(shost->host_lock, flags);
}
- /* Save the time of the most recently completed reset. */
- boardp->last_reset = jiffies;
- spin_unlock_irqrestore(shost->host_lock, flags);
-
ASC_DBG(1, "ret %d\n", ret);
return ret;
@@ -7584,9 +7230,10 @@ static irqreturn_t advansys_interrupt(int irq, void *dev_id)
struct Scsi_Host *shost = dev_id;
struct asc_board *boardp = shost_priv(shost);
irqreturn_t result = IRQ_NONE;
+ unsigned long flags;
ASC_DBG(2, "boardp 0x%p\n", boardp);
- spin_lock(shost->host_lock);
+ spin_lock_irqsave(shost->host_lock, flags);
if (ASC_NARROW_BOARD(boardp)) {
if (AscIsIntPending(shost->io_port)) {
result = IRQ_HANDLED;
@@ -7601,38 +7248,38 @@ static irqreturn_t advansys_interrupt(int irq, void *dev_id)
ASC_STATS(shost, interrupt);
}
}
- spin_unlock(shost->host_lock);
+ spin_unlock_irqrestore(shost->host_lock, flags);
ASC_DBG(1, "end\n");
return result;
}
-static int AscHostReqRiscHalt(PortAddr iop_base)
+static bool AscHostReqRiscHalt(PortAddr iop_base)
{
int count = 0;
- int sta = 0;
+ bool sta = false;
uchar saved_stop_code;
if (AscIsChipHalted(iop_base))
- return (1);
+ return true;
saved_stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B);
AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
ASC_STOP_HOST_REQ_RISC_HALT | ASC_STOP_REQ_RISC_STOP);
do {
if (AscIsChipHalted(iop_base)) {
- sta = 1;
+ sta = true;
break;
}
mdelay(100);
} while (count++ < 20);
AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, saved_stop_code);
- return (sta);
+ return sta;
}
-static int
+static bool
AscSetRunChipSynRegAtID(PortAddr iop_base, uchar tid_no, uchar sdtr_data)
{
- int sta = FALSE;
+ bool sta = false;
if (AscHostReqRiscHalt(iop_base)) {
sta = AscSetChipSynRegAtID(iop_base, tid_no, sdtr_data);
@@ -7851,13 +7498,17 @@ static int advansys_slave_configure(struct scsi_device *sdev)
return 0;
}
-static __le32 advansys_get_sense_buffer_dma(struct scsi_cmnd *scp)
+static __le32 asc_get_sense_buffer_dma(struct scsi_cmnd *scp)
{
struct asc_board *board = shost_priv(scp->device->host);
+
scp->SCp.dma_handle = dma_map_single(board->dev, scp->sense_buffer,
- SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
- dma_cache_sync(board->dev, scp->sense_buffer,
- SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ SCSI_SENSE_BUFFERSIZE,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(board->dev, scp->SCp.dma_handle)) {
+ ASC_DBG(1, "failed to map sense buffer\n");
+ return 0;
+ }
return cpu_to_le32(scp->SCp.dma_handle);
}
@@ -7866,17 +7517,16 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
{
struct asc_dvc_var *asc_dvc = &boardp->dvc_var.asc_dvc_var;
int use_sg;
+ u32 srb_tag;
memset(asc_scsi_q, 0, sizeof(*asc_scsi_q));
/*
- * Point the ASC_SCSI_Q to the 'struct scsi_cmnd'.
+ * Set the srb_tag to the command tag + 1, as
+ * srb_tag '0' is used internally by the chip.
*/
- asc_scsi_q->q2.srb_ptr = advansys_ptr_to_srb(asc_dvc, scp);
- if (asc_scsi_q->q2.srb_ptr == BAD_SRB) {
- scp->result = HOST_BYTE(DID_SOFT_ERROR);
- return ASC_ERROR;
- }
+ srb_tag = scp->request->tag + 1;
+ asc_scsi_q->q2.srb_tag = srb_tag;
/*
* Build the ASC_SCSI_Q request.
@@ -7887,8 +7537,10 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
asc_scsi_q->q1.target_lun = scp->device->lun;
asc_scsi_q->q2.target_ix =
ASC_TIDLUN_TO_IX(scp->device->id, scp->device->lun);
- asc_scsi_q->q1.sense_addr = advansys_get_sense_buffer_dma(scp);
+ asc_scsi_q->q1.sense_addr = asc_get_sense_buffer_dma(scp);
asc_scsi_q->q1.sense_len = SCSI_SENSE_BUFFERSIZE;
+ if (!asc_scsi_q->q1.sense_addr)
+ return ASC_BUSY;
/*
* If there are any outstanding requests for the current target,
@@ -7910,7 +7562,10 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
/* Build ASC_SCSI_Q */
use_sg = scsi_dma_map(scp);
- if (use_sg != 0) {
+ if (use_sg < 0) {
+ ASC_DBG(1, "failed to map sglist\n");
+ return ASC_BUSY;
+ } else if (use_sg > 0) {
int sgcnt;
struct scatterlist *slp;
struct asc_sg_head *asc_sg_head;
@@ -7975,20 +7630,19 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
* ADV_ERROR(-1) - SG List creation failed
*/
static int
-adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
- int use_sg)
+adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp,
+ ADV_SCSI_REQ_Q *scsiqp, struct scsi_cmnd *scp, int use_sg)
{
- adv_sgblk_t *sgblkp;
- ADV_SCSI_REQ_Q *scsiqp;
+ adv_sgblk_t *sgblkp, *prev_sgblkp;
struct scatterlist *slp;
int sg_elem_cnt;
ADV_SG_BLOCK *sg_block, *prev_sg_block;
- ADV_PADDR sg_block_paddr;
+ dma_addr_t sgblk_paddr;
int i;
- scsiqp = (ADV_SCSI_REQ_Q *)ADV_32BALIGN(&reqp->scsi_req_q);
slp = scsi_sglist(scp);
sg_elem_cnt = use_sg;
+ prev_sgblkp = NULL;
prev_sg_block = NULL;
reqp->sgblkp = NULL;
@@ -7998,7 +7652,9 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
* list. One 'adv_sgblk_t' structure holds NO_OF_SG_PER_BLOCK
* (15) scatter-gather elements.
*/
- if ((sgblkp = boardp->adv_sgblkp) == NULL) {
+ sgblkp = dma_pool_alloc(boardp->adv_sgblk_pool, GFP_ATOMIC,
+ &sgblk_paddr);
+ if (!sgblkp) {
ASC_DBG(1, "no free adv_sgblk_t\n");
ASC_STATS(scp->device->host, adv_build_nosg);
@@ -8009,24 +7665,16 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
while ((sgblkp = reqp->sgblkp) != NULL) {
/* Remove 'sgblkp' from the request list. */
reqp->sgblkp = sgblkp->next_sgblkp;
-
- /* Add 'sgblkp' to the board free list. */
- sgblkp->next_sgblkp = boardp->adv_sgblkp;
- boardp->adv_sgblkp = sgblkp;
+ sgblkp->next_sgblkp = NULL;
+ dma_pool_free(boardp->adv_sgblk_pool, sgblkp,
+ sgblkp->sg_addr);
}
return ASC_BUSY;
}
-
/* Complete 'adv_sgblk_t' board allocation. */
- boardp->adv_sgblkp = sgblkp->next_sgblkp;
+ sgblkp->sg_addr = sgblk_paddr;
sgblkp->next_sgblkp = NULL;
-
- /*
- * Get 8 byte aligned virtual and physical addresses
- * for the allocated ADV_SG_BLOCK structure.
- */
- sg_block = (ADV_SG_BLOCK *)ADV_8BALIGN(&sgblkp->sg_block);
- sg_block_paddr = virt_to_bus(sg_block);
+ sg_block = &sgblkp->sg_block;
/*
* Check if this is the first 'adv_sgblk_t' for the
@@ -8041,17 +7689,16 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
* address pointers.
*/
scsiqp->sg_list_ptr = sg_block;
- scsiqp->sg_real_addr = cpu_to_le32(sg_block_paddr);
+ scsiqp->sg_real_addr = cpu_to_le32(sgblk_paddr);
} else {
/* Request's second or later scatter-gather block. */
- sgblkp->next_sgblkp = reqp->sgblkp;
- reqp->sgblkp = sgblkp;
+ prev_sgblkp->next_sgblkp = sgblkp;
/*
* Point the previous ADV_SG_BLOCK structure to
* the newly allocated ADV_SG_BLOCK structure.
*/
- prev_sg_block->sg_ptr = cpu_to_le32(sg_block_paddr);
+ prev_sg_block->sg_ptr = cpu_to_le32(sgblk_paddr);
}
for (i = 0; i < NO_OF_SG_PER_BLOCK; i++) {
@@ -8062,15 +7709,19 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
ASC_STATS_ADD(scp->device->host, xfer_sect,
DIV_ROUND_UP(sg_dma_len(slp), 512));
- if (--sg_elem_cnt == 0) { /* Last ADV_SG_BLOCK and scatter-gather entry. */
+ if (--sg_elem_cnt == 0) {
+ /*
+ * Last ADV_SG_BLOCK and scatter-gather entry.
+ */
sg_block->sg_cnt = i + 1;
- sg_block->sg_ptr = 0L; /* Last ADV_SG_BLOCK in list. */
+ sg_block->sg_ptr = 0L; /* Last ADV_SG_BLOCK in list. */
return ADV_SUCCESS;
}
slp++;
}
sg_block->sg_cnt = NO_OF_SG_PER_BLOCK;
prev_sg_block = sg_block;
+ prev_sgblkp = sgblkp;
}
}
@@ -8080,38 +7731,35 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
* If an adv_req_t can not be allocated to issue the request,
* then return ASC_BUSY. If an error occurs, then return ASC_ERROR.
*
- * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the
+ * Multi-byte fields in the ADV_SCSI_REQ_Q that are used by the
* microcode for DMA addresses or math operations are byte swapped
* to little-endian order.
*/
static int
adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
- ADV_SCSI_REQ_Q **adv_scsiqpp)
+ adv_req_t **adv_reqpp)
{
+ u32 srb_tag = scp->request->tag;
adv_req_t *reqp;
ADV_SCSI_REQ_Q *scsiqp;
- int i;
int ret;
int use_sg;
+ dma_addr_t sense_addr;
/*
* Allocate an adv_req_t structure from the board to execute
* the command.
*/
- if (boardp->adv_reqp == NULL) {
+ reqp = &boardp->adv_reqp[srb_tag];
+ if (reqp->cmndp && reqp->cmndp != scp ) {
ASC_DBG(1, "no free adv_req_t\n");
ASC_STATS(scp->device->host, adv_build_noreq);
return ASC_BUSY;
- } else {
- reqp = boardp->adv_reqp;
- boardp->adv_reqp = reqp->next_reqp;
- reqp->next_reqp = NULL;
}
- /*
- * Get 32-byte aligned ADV_SCSI_REQ_Q and ADV_SG_BLOCK pointers.
- */
- scsiqp = (ADV_SCSI_REQ_Q *)ADV_32BALIGN(&reqp->scsi_req_q);
+ reqp->req_addr = boardp->adv_reqp_addr + (srb_tag * sizeof(adv_req_t));
+
+ scsiqp = &reqp->scsi_req_q;
/*
* Initialize the structure.
@@ -8119,14 +7767,15 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
scsiqp->cntl = scsiqp->scsi_cntl = scsiqp->done_status = 0;
/*
- * Set the ADV_SCSI_REQ_Q 'srb_ptr' to point to the adv_req_t structure.
+ * Set the srb_tag to the command tag.
*/
- scsiqp->srb_ptr = ADV_VADDR_TO_U32(reqp);
+ scsiqp->srb_tag = srb_tag;
/*
- * Set the adv_req_t 'cmndp' to point to the struct scsi_cmnd structure.
+ * Set 'host_scribble' to point to the adv_req_t structure.
*/
reqp->cmndp = scp;
+ scp->host_scribble = (void *)reqp;
/*
* Build the ADV_SCSI_REQ_Q request.
@@ -8135,28 +7784,38 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
/* Set CDB length and copy it to the request structure. */
scsiqp->cdb_len = scp->cmd_len;
/* Copy first 12 CDB bytes to cdb[]. */
- for (i = 0; i < scp->cmd_len && i < 12; i++) {
- scsiqp->cdb[i] = scp->cmnd[i];
- }
+ memcpy(scsiqp->cdb, scp->cmnd, scp->cmd_len < 12 ? scp->cmd_len : 12);
/* Copy last 4 CDB bytes, if present, to cdb16[]. */
- for (; i < scp->cmd_len; i++) {
- scsiqp->cdb16[i - 12] = scp->cmnd[i];
+ if (scp->cmd_len > 12) {
+ int cdb16_len = scp->cmd_len - 12;
+
+ memcpy(scsiqp->cdb16, &scp->cmnd[12], cdb16_len);
}
scsiqp->target_id = scp->device->id;
scsiqp->target_lun = scp->device->lun;
- scsiqp->sense_addr = cpu_to_le32(virt_to_bus(&scp->sense_buffer[0]));
- scsiqp->sense_len = SCSI_SENSE_BUFFERSIZE;
+ sense_addr = dma_map_single(boardp->dev, scp->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(boardp->dev, sense_addr)) {
+ ASC_DBG(1, "failed to map sense buffer\n");
+ ASC_STATS(scp->device->host, adv_build_noreq);
+ return ASC_BUSY;
+ }
+ scsiqp->sense_addr = cpu_to_le32(sense_addr);
+ scsiqp->sense_len = cpu_to_le32(SCSI_SENSE_BUFFERSIZE);
/* Build ADV_SCSI_REQ_Q */
use_sg = scsi_dma_map(scp);
- if (use_sg == 0) {
+ if (use_sg < 0) {
+ ASC_DBG(1, "failed to map SG list\n");
+ ASC_STATS(scp->device->host, adv_build_noreq);
+ return ASC_BUSY;
+ } else if (use_sg == 0) {
/* Zero-length transfer */
reqp->sgblkp = NULL;
scsiqp->data_cnt = 0;
- scsiqp->vdata_addr = NULL;
scsiqp->data_addr = 0;
scsiqp->sg_list_ptr = NULL;
@@ -8168,27 +7827,20 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
scp->device->host->sg_tablesize);
scsi_dma_unmap(scp);
scp->result = HOST_BYTE(DID_ERROR);
-
- /*
- * Free the 'adv_req_t' structure by adding it back
- * to the board free list.
- */
- reqp->next_reqp = boardp->adv_reqp;
- boardp->adv_reqp = reqp;
+ reqp->cmndp = NULL;
+ scp->host_scribble = NULL;
return ASC_ERROR;
}
scsiqp->data_cnt = cpu_to_le32(scsi_bufflen(scp));
- ret = adv_get_sglist(boardp, reqp, scp, use_sg);
+ ret = adv_get_sglist(boardp, reqp, scsiqp, scp, use_sg);
if (ret != ADV_SUCCESS) {
- /*
- * Free the adv_req_t structure by adding it back to
- * the board free list.
- */
- reqp->next_reqp = boardp->adv_reqp;
- boardp->adv_reqp = reqp;
+ scsi_dma_unmap(scp);
+ scp->result = HOST_BYTE(DID_ERROR);
+ reqp->cmndp = NULL;
+ scp->host_scribble = NULL;
return ret;
}
@@ -8201,7 +7853,7 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
ASC_DBG_PRT_CDB(1, scp->cmnd, scp->cmd_len);
- *adv_scsiqpp = scsiqp;
+ *adv_reqpp = reqp;
return ASC_NOERROR;
}
@@ -8358,8 +8010,8 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no)
int i;
ASC_SG_HEAD *sg_head;
ASC_SG_LIST_Q scsi_sg_q;
- ASC_DCNT saved_data_addr;
- ASC_DCNT saved_data_cnt;
+ __le32 saved_data_addr;
+ __le32 saved_data_cnt;
PortAddr iop_base;
ushort sg_list_dwords;
ushort sg_index;
@@ -8371,42 +8023,15 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no)
sg_head = scsiq->sg_head;
saved_data_addr = scsiq->q1.data_addr;
saved_data_cnt = scsiq->q1.data_cnt;
- scsiq->q1.data_addr = (ASC_PADDR) sg_head->sg_list[0].addr;
- scsiq->q1.data_cnt = (ASC_DCNT) sg_head->sg_list[0].bytes;
-#if CC_VERY_LONG_SG_LIST
+ scsiq->q1.data_addr = cpu_to_le32(sg_head->sg_list[0].addr);
+ scsiq->q1.data_cnt = cpu_to_le32(sg_head->sg_list[0].bytes);
/*
- * If sg_head->entry_cnt is greater than ASC_MAX_SG_LIST
- * then not all SG elements will fit in the allocated queues.
- * The rest of the SG elements will be copied when the RISC
- * completes the SG elements that fit and halts.
+ * Set sg_entry_cnt to be the number of SG elements that
+ * will fit in the allocated SG queues. It is minus 1, because
+ * the first SG element is handled above.
*/
- if (sg_head->entry_cnt > ASC_MAX_SG_LIST) {
- /*
- * Set sg_entry_cnt to be the number of SG elements that
- * will fit in the allocated SG queues. It is minus 1, because
- * the first SG element is handled above. ASC_MAX_SG_LIST is
- * already inflated by 1 to account for this. For example it
- * may be 50 which is 1 + 7 queues * 7 SG elements.
- */
- sg_entry_cnt = ASC_MAX_SG_LIST - 1;
+ sg_entry_cnt = sg_head->entry_cnt - 1;
- /*
- * Keep track of remaining number of SG elements that will
- * need to be handled from a_isr.c.
- */
- scsiq->remain_sg_entry_cnt =
- sg_head->entry_cnt - ASC_MAX_SG_LIST;
- } else {
-#endif /* CC_VERY_LONG_SG_LIST */
- /*
- * Set sg_entry_cnt to be the number of SG elements that
- * will fit in the allocated SG queues. It is minus 1, because
- * the first SG element is handled above.
- */
- sg_entry_cnt = sg_head->entry_cnt - 1;
-#if CC_VERY_LONG_SG_LIST
- }
-#endif /* CC_VERY_LONG_SG_LIST */
if (sg_entry_cnt != 0) {
scsiq->q1.cntl |= QC_SG_HEAD;
q_addr = ASC_QNO_TO_QADDR(q_no);
@@ -8431,21 +8056,7 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no)
ASC_SG_LIST_PER_Q - 1;
}
} else {
-#if CC_VERY_LONG_SG_LIST
- /*
- * This is the last SG queue in the list of
- * allocated SG queues. If there are more
- * SG elements than will fit in the allocated
- * queues, then set the QCSG_SG_XFER_MORE flag.
- */
- if (sg_head->entry_cnt > ASC_MAX_SG_LIST) {
- scsi_sg_q.cntl |= QCSG_SG_XFER_MORE;
- } else {
-#endif /* CC_VERY_LONG_SG_LIST */
- scsi_sg_q.cntl |= QCSG_SG_XFER_END;
-#if CC_VERY_LONG_SG_LIST
- }
-#endif /* CC_VERY_LONG_SG_LIST */
+ scsi_sg_q.cntl |= QCSG_SG_XFER_END;
sg_list_dwords = sg_entry_cnt << 1;
if (i == 0) {
scsi_sg_q.sg_list_cnt = sg_entry_cnt;
@@ -8550,9 +8161,9 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
PortAddr iop_base;
int sta;
int n_q_required;
- int disable_syn_offset_one_fix;
+ bool disable_syn_offset_one_fix;
int i;
- ASC_PADDR addr;
+ u32 addr;
ushort sg_entry_cnt = 0;
ushort sg_entry_cnt_minus_one = 0;
uchar target_ix;
@@ -8562,12 +8173,12 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
uchar scsi_cmd;
uchar disable_cmd;
ASC_SG_HEAD *sg_head;
- ASC_DCNT data_cnt;
+ unsigned long data_cnt;
iop_base = asc_dvc->iop_base;
sg_head = scsiq->sg_head;
if (asc_dvc->err_code != 0)
- return (ERR);
+ return ASC_ERROR;
scsiq->q1.q_no = 0;
if ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES) == 0) {
scsiq->q1.extra_bytes = 0;
@@ -8593,46 +8204,41 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
}
if (asc_dvc->in_critical_cnt != 0) {
AscSetLibErrorCode(asc_dvc, ASCQ_ERR_CRITICAL_RE_ENTRY);
- return (ERR);
+ return ASC_ERROR;
}
asc_dvc->in_critical_cnt++;
if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) {
if ((sg_entry_cnt = sg_head->entry_cnt) == 0) {
asc_dvc->in_critical_cnt--;
- return (ERR);
+ return ASC_ERROR;
}
-#if !CC_VERY_LONG_SG_LIST
if (sg_entry_cnt > ASC_MAX_SG_LIST) {
asc_dvc->in_critical_cnt--;
- return (ERR);
+ return ASC_ERROR;
}
-#endif /* !CC_VERY_LONG_SG_LIST */
if (sg_entry_cnt == 1) {
- scsiq->q1.data_addr =
- (ADV_PADDR)sg_head->sg_list[0].addr;
- scsiq->q1.data_cnt =
- (ADV_DCNT)sg_head->sg_list[0].bytes;
+ scsiq->q1.data_addr = cpu_to_le32(sg_head->sg_list[0].addr);
+ scsiq->q1.data_cnt = cpu_to_le32(sg_head->sg_list[0].bytes);
scsiq->q1.cntl &= ~(QC_SG_HEAD | QC_SG_SWAP_QUEUE);
}
sg_entry_cnt_minus_one = sg_entry_cnt - 1;
}
scsi_cmd = scsiq->cdbptr[0];
- disable_syn_offset_one_fix = FALSE;
+ disable_syn_offset_one_fix = false;
if ((asc_dvc->pci_fix_asyn_xfer & scsiq->q1.target_id) &&
!(asc_dvc->pci_fix_asyn_xfer_always & scsiq->q1.target_id)) {
if (scsiq->q1.cntl & QC_SG_HEAD) {
data_cnt = 0;
for (i = 0; i < sg_entry_cnt; i++) {
- data_cnt +=
- (ADV_DCNT)le32_to_cpu(sg_head->sg_list[i].
- bytes);
+ data_cnt += le32_to_cpu(sg_head->sg_list[i].
+ bytes);
}
} else {
data_cnt = le32_to_cpu(scsiq->q1.data_cnt);
}
if (data_cnt != 0UL) {
if (data_cnt < 512UL) {
- disable_syn_offset_one_fix = TRUE;
+ disable_syn_offset_one_fix = true;
} else {
for (i = 0; i < ASC_SYN_OFFSET_ONE_DISABLE_LIST;
i++) {
@@ -8643,7 +8249,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
}
if (scsi_cmd == disable_cmd) {
disable_syn_offset_one_fix =
- TRUE;
+ true;
break;
}
}
@@ -8662,12 +8268,11 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_IF_NOT_DWB) {
if ((scsi_cmd == READ_6) ||
(scsi_cmd == READ_10)) {
- addr =
- (ADV_PADDR)le32_to_cpu(sg_head->
+ addr = le32_to_cpu(sg_head->
sg_list
[sg_entry_cnt_minus_one].
addr) +
- (ADV_DCNT)le32_to_cpu(sg_head->
+ le32_to_cpu(sg_head->
sg_list
[sg_entry_cnt_minus_one].
bytes);
@@ -8688,8 +8293,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
sg_list
[sg_entry_cnt_minus_one].
bytes);
- data_cnt -=
- (ASC_DCNT) extra_bytes;
+ data_cnt -= extra_bytes;
sg_head->
sg_list
[sg_entry_cnt_minus_one].
@@ -8700,16 +8304,6 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
}
}
sg_head->entry_to_copy = sg_head->entry_cnt;
-#if CC_VERY_LONG_SG_LIST
- /*
- * Set the sg_entry_cnt to the maximum possible. The rest of
- * the SG elements will be copied when the RISC completes the
- * SG elements that fit and halts.
- */
- if (sg_entry_cnt > ASC_MAX_SG_LIST) {
- sg_entry_cnt = ASC_MAX_SG_LIST;
- }
-#endif /* CC_VERY_LONG_SG_LIST */
n_q_required = AscSgListToQueue(sg_entry_cnt);
if ((AscGetNumOfFreeQueue(asc_dvc, target_ix, n_q_required) >=
(uint) n_q_required)
@@ -8744,8 +8338,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
== 0) {
scsiq->q2.tag_code |=
ASC_TAG_FLAG_EXTRA_BYTES;
- data_cnt -= (ASC_DCNT)
- extra_bytes;
+ data_cnt -= extra_bytes;
scsiq->q1.data_cnt =
cpu_to_le32
(data_cnt);
@@ -8780,7 +8373,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
* If 'done_status' is not set to QD_DO_RETRY, then 'error_retry' will be
* set to SCSI_MAX_RETRY.
*
- * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the microcode
+ * Multi-byte fields in the ADV_SCSI_REQ_Q that are used by the microcode
* for DMA addresses or math operations are byte swapped to little-endian
* order.
*
@@ -8791,11 +8384,11 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
* ADV_ERROR(-1) - Invalid ADV_SCSI_REQ_Q request structure
* host IC error.
*/
-static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq)
+static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, adv_req_t *reqp)
{
AdvPortAddr iop_base;
- ADV_PADDR req_paddr;
ADV_CARR_T *new_carrp;
+ ADV_SCSI_REQ_Q *scsiq = &reqp->scsi_req_q;
/*
* The ADV_SCSI_REQ_Q 'target_id' field should never exceed ADV_MAX_TID.
@@ -8812,39 +8405,19 @@ static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq)
* Allocate a carrier ensuring at least one carrier always
* remains on the freelist and initialize fields.
*/
- if ((new_carrp = asc_dvc->carr_freelist) == NULL) {
+ new_carrp = adv_get_next_carrier(asc_dvc);
+ if (!new_carrp) {
+ ASC_DBG(1, "No free carriers\n");
return ADV_BUSY;
}
- asc_dvc->carr_freelist = (ADV_CARR_T *)
- ADV_U32_TO_VADDR(le32_to_cpu(new_carrp->next_vpa));
- asc_dvc->carr_pending_cnt++;
-
- /*
- * Set the carrier to be a stopper by setting 'next_vpa'
- * to the stopper value. The current stopper will be changed
- * below to point to the new stopper.
- */
- new_carrp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
- /*
- * Clear the ADV_SCSI_REQ_Q done flag.
- */
- scsiq->a_flag &= ~ADV_SCSIQ_DONE;
-
- req_paddr = virt_to_bus(scsiq);
- BUG_ON(req_paddr & 31);
- /* Wait for assertion before making little-endian */
- req_paddr = cpu_to_le32(req_paddr);
+ asc_dvc->carr_pending_cnt++;
/* Save virtual and physical address of ADV_SCSI_REQ_Q and carrier. */
- scsiq->scsiq_ptr = cpu_to_le32(ADV_VADDR_TO_U32(scsiq));
- scsiq->scsiq_rptr = req_paddr;
+ scsiq->scsiq_ptr = cpu_to_le32(scsiq->srb_tag);
+ scsiq->scsiq_rptr = cpu_to_le32(reqp->req_addr);
- scsiq->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->icq_sp));
- /*
- * Every ADV_CARR_T.carr_pa is byte swapped to little-endian
- * order during initialization.
- */
+ scsiq->carr_va = asc_dvc->icq_sp->carr_va;
scsiq->carr_pa = asc_dvc->icq_sp->carr_pa;
/*
@@ -8852,7 +8425,7 @@ static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq)
* the microcode. The newly allocated stopper will become the new
* stopper.
*/
- asc_dvc->icq_sp->areq_vpa = req_paddr;
+ asc_dvc->icq_sp->areq_vpa = scsiq->scsiq_rptr;
/*
* Set the 'next_vpa' pointer for the old stopper to be the
@@ -8907,11 +8480,10 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
ASC_DVC_VAR *asc_dvc = &boardp->dvc_var.asc_dvc_var;
struct asc_scsi_q asc_scsi_q;
- /* asc_build_req() can not return ASC_BUSY. */
ret = asc_build_req(boardp, scp, &asc_scsi_q);
- if (ret == ASC_ERROR) {
+ if (ret != ASC_NOERROR) {
ASC_STATS(scp->device->host, build_error);
- return ASC_ERROR;
+ return ret;
}
ret = AscExeScsiQueue(asc_dvc, &asc_scsi_q);
@@ -8919,9 +8491,9 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
err_code = asc_dvc->err_code;
} else {
ADV_DVC_VAR *adv_dvc = &boardp->dvc_var.adv_dvc_var;
- ADV_SCSI_REQ_Q *adv_scsiqp;
+ adv_req_t *adv_reqp;
- switch (adv_build_req(boardp, scp, &adv_scsiqp)) {
+ switch (adv_build_req(boardp, scp, &adv_reqp)) {
case ASC_NOERROR:
ASC_DBG(3, "adv_build_req ASC_NOERROR\n");
break;
@@ -8941,7 +8513,7 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
return ASC_ERROR;
}
- ret = AdvExeScsiQueue(adv_dvc, adv_scsiqp);
+ ret = AdvExeScsiQueue(adv_dvc, adv_reqp);
err_code = adv_dvc->err_code;
}
@@ -8956,6 +8528,7 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
ASC_DBG(1, "ExeScsiQueue() ASC_NOERROR\n");
break;
case ASC_BUSY:
+ ASC_DBG(1, "ExeScsiQueue() ASC_BUSY\n");
ASC_STATS(scp->device->host, exe_busy);
break;
case ASC_ERROR:
@@ -9122,7 +8695,7 @@ static int AscStopQueueExe(PortAddr iop_base)
return (0);
}
-static ASC_DCNT AscGetMaxDmaCount(ushort bus_type)
+static unsigned int AscGetMaxDmaCount(ushort bus_type)
{
if (bus_type & ASC_IS_ISA)
return ASC_MAX_ISA_DMA_COUNT;
@@ -9183,15 +8756,13 @@ static uchar AscSetIsaDmaSpeed(PortAddr iop_base, uchar speed_value)
}
#endif /* CONFIG_ISA */
-static ushort AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
+static void AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
{
int i;
PortAddr iop_base;
- ushort warn_code;
uchar chip_version;
iop_base = asc_dvc->iop_base;
- warn_code = 0;
asc_dvc->err_code = 0;
if ((asc_dvc->bus_type &
(ASC_IS_ISA | ASC_IS_PCI | ASC_IS_EISA | ASC_IS_VL)) == 0) {
@@ -9205,7 +8776,7 @@ static ushort AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
/* asc_dvc->init_state initialized in AscInitGetConfig(). */
asc_dvc->sdtr_done = 0;
asc_dvc->cur_total_qng = 0;
- asc_dvc->is_in_int = 0;
+ asc_dvc->is_in_int = false;
asc_dvc->in_critical_cnt = 0;
asc_dvc->last_q_shortage = 0;
asc_dvc->use_tagged_qng = 0;
@@ -9267,7 +8838,6 @@ static ushort AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
asc_dvc->scsiq_busy_tail[i] = (ASC_SCSI_Q *)0L;
asc_dvc->cfg->max_tag_qng[i] = ASC_MAX_INRAM_TAG_QNG;
}
- return warn_code;
}
static int AscWriteEEPCmdReg(PortAddr iop_base, uchar cmd_reg)
@@ -9385,7 +8955,7 @@ static int AscWriteEEPDataReg(PortAddr iop_base, ushort data_reg)
int retry;
retry = 0;
- while (TRUE) {
+ while (true) {
AscSetChipEEPData(iop_base, data_reg);
mdelay(1);
read_back = AscGetChipEEPData(iop_base);
@@ -9521,7 +9091,7 @@ static int AscSetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf,
int n_error;
retry = 0;
- while (TRUE) {
+ while (true) {
if ((n_error = AscSetEEPConfigOnce(iop_base, cfg_buf,
bus_type)) == 0) {
break;
@@ -9533,7 +9103,7 @@ static int AscSetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf,
return n_error;
}
-static ushort AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
+static int AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
{
ASCEEP_CONFIG eep_config_buf;
ASCEEP_CONFIG *eep_config;
@@ -9548,13 +9118,13 @@ static ushort AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
warn_code = 0;
AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0x00FE);
AscStopQueueExe(iop_base);
- if ((AscStopChip(iop_base) == FALSE) ||
+ if ((AscStopChip(iop_base)) ||
(AscGetChipScsiCtrl(iop_base) != 0)) {
asc_dvc->init_state |= ASC_INIT_RESET_SCSI_DONE;
AscResetChipAndScsiBus(asc_dvc);
mdelay(asc_dvc->scsi_reset_wait * 1000); /* XXX: msleep? */
}
- if (AscIsChipHalted(iop_base) == FALSE) {
+ if (!AscIsChipHalted(iop_base)) {
asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
return (warn_code);
}
@@ -9709,8 +9279,8 @@ static int AscInitGetConfig(struct Scsi_Host *shost)
return asc_dvc->err_code;
if (AscFindSignature(asc_dvc->iop_base)) {
- warn_code |= AscInitAscDvcVar(asc_dvc);
- warn_code |= AscInitFromEEP(asc_dvc);
+ AscInitAscDvcVar(asc_dvc);
+ warn_code = AscInitFromEEP(asc_dvc);
asc_dvc->init_state |= ASC_INIT_STATE_END_GET_CFG;
if (asc_dvc->scsi_reset_wait > ASC_MAX_SCSI_RESET_WAIT)
asc_dvc->scsi_reset_wait = ASC_MAX_SCSI_RESET_WAIT;
@@ -9866,6 +9436,7 @@ static int AscInitSetConfig(struct pci_dev *pdev, struct Scsi_Host *shost)
* on big-endian platforms so char fields read as words are actually being
* unswapped on big-endian platforms.
*/
+#ifdef CONFIG_PCI
static ADVEEP_3550_CONFIG Default_3550_EEPROM_Config = {
ADV_EEPROM_BIOS_ENABLE, /* cfg_lsw */
0x0000, /* cfg_msw */
@@ -10202,7 +9773,6 @@ static ADVEEP_38C1600_CONFIG ADVEEP_38C1600_Config_Field_IsChar = {
0 /* 63 reserved */
};
-#ifdef CONFIG_PCI
/*
* Wait for EEPROM command to complete
*/
@@ -11232,7 +10802,7 @@ static struct scsi_host_template advansys_template = {
.name = DRV_NAME,
.info = advansys_info,
.queuecommand = advansys_queuecommand,
- .eh_bus_reset_handler = advansys_reset,
+ .eh_host_reset_handler = advansys_reset,
.bios_param = advansys_biosparam,
.slave_configure = advansys_slave_configure,
/*
@@ -11240,7 +10810,7 @@ static struct scsi_host_template advansys_template = {
* must be set. The flag will be cleared in advansys_board_found
* for non-ISA adapters.
*/
- .unchecked_isa_dma = 1,
+ .unchecked_isa_dma = true,
/*
* All adapters controlled by this driver are capable of large
* scatter-gather lists. According to the mid-level SCSI documentation
@@ -11249,26 +10819,25 @@ static struct scsi_host_template advansys_template = {
* by enabling clustering, I/O throughput increases as well.
*/
.use_clustering = ENABLE_CLUSTERING,
+ .use_blk_tags = 1,
};
static int advansys_wide_init_chip(struct Scsi_Host *shost)
{
struct asc_board *board = shost_priv(shost);
struct adv_dvc_var *adv_dvc = &board->dvc_var.adv_dvc_var;
- int req_cnt = 0;
- adv_req_t *reqp = NULL;
- int sg_cnt = 0;
- adv_sgblk_t *sgp;
+ size_t sgblk_pool_size;
int warn_code, err_code;
/*
* Allocate buffer carrier structures. The total size
- * is about 4 KB, so allocate all at once.
+ * is about 8 KB, so allocate all at once.
*/
- adv_dvc->carrier_buf = kmalloc(ADV_CARRIER_BUFSIZE, GFP_KERNEL);
- ASC_DBG(1, "carrier_buf 0x%p\n", adv_dvc->carrier_buf);
+ adv_dvc->carrier = dma_alloc_coherent(board->dev,
+ ADV_CARRIER_BUFSIZE, &adv_dvc->carrier_addr, GFP_KERNEL);
+ ASC_DBG(1, "carrier 0x%p\n", adv_dvc->carrier);
- if (!adv_dvc->carrier_buf)
+ if (!adv_dvc->carrier)
goto kmalloc_failed;
/*
@@ -11276,54 +10845,34 @@ static int advansys_wide_init_chip(struct Scsi_Host *shost)
* board. The total size is about 16 KB, so allocate all at once.
* If the allocation fails decrement and try again.
*/
- for (req_cnt = adv_dvc->max_host_qng; req_cnt > 0; req_cnt--) {
- reqp = kmalloc(sizeof(adv_req_t) * req_cnt, GFP_KERNEL);
-
- ASC_DBG(1, "reqp 0x%p, req_cnt %d, bytes %lu\n", reqp, req_cnt,
- (ulong)sizeof(adv_req_t) * req_cnt);
-
- if (reqp)
- break;
+ board->adv_reqp_size = adv_dvc->max_host_qng * sizeof(adv_req_t);
+ if (board->adv_reqp_size & 0x1f) {
+ ASC_DBG(1, "unaligned reqp %lu bytes\n", sizeof(adv_req_t));
+ board->adv_reqp_size = ADV_32BALIGN(board->adv_reqp_size);
}
+ board->adv_reqp = dma_alloc_coherent(board->dev, board->adv_reqp_size,
+ &board->adv_reqp_addr, GFP_KERNEL);
- if (!reqp)
+ if (!board->adv_reqp)
goto kmalloc_failed;
- adv_dvc->orig_reqp = reqp;
+ ASC_DBG(1, "reqp 0x%p, req_cnt %d, bytes %lu\n", board->adv_reqp,
+ adv_dvc->max_host_qng, board->adv_reqp_size);
/*
* Allocate up to ADV_TOT_SG_BLOCK request structures for
* the Wide board. Each structure is about 136 bytes.
*/
- board->adv_sgblkp = NULL;
- for (sg_cnt = 0; sg_cnt < ADV_TOT_SG_BLOCK; sg_cnt++) {
- sgp = kmalloc(sizeof(adv_sgblk_t), GFP_KERNEL);
+ sgblk_pool_size = sizeof(adv_sgblk_t) * ADV_TOT_SG_BLOCK;
+ board->adv_sgblk_pool = dma_pool_create("adv_sgblk", board->dev,
+ sgblk_pool_size, 32, 0);
- if (!sgp)
- break;
-
- sgp->next_sgblkp = board->adv_sgblkp;
- board->adv_sgblkp = sgp;
-
- }
-
- ASC_DBG(1, "sg_cnt %d * %lu = %lu bytes\n", sg_cnt, sizeof(adv_sgblk_t),
- sizeof(adv_sgblk_t) * sg_cnt);
+ ASC_DBG(1, "sg_cnt %d * %lu = %lu bytes\n", ADV_TOT_SG_BLOCK,
+ sizeof(adv_sgblk_t), sgblk_pool_size);
- if (!board->adv_sgblkp)
+ if (!board->adv_sgblk_pool)
goto kmalloc_failed;
- /*
- * Point 'adv_reqp' to the request structures and
- * link them together.
- */
- req_cnt--;
- reqp[req_cnt].next_reqp = NULL;
- for (; req_cnt > 0; req_cnt--) {
- reqp[req_cnt - 1].next_reqp = &reqp[req_cnt];
- }
- board->adv_reqp = &reqp[0];
-
if (adv_dvc->chip_type == ADV_CHIP_ASC3550) {
ASC_DBG(2, "AdvInitAsc3550Driver()\n");
warn_code = AdvInitAsc3550Driver(adv_dvc);
@@ -11353,14 +10902,20 @@ static int advansys_wide_init_chip(struct Scsi_Host *shost)
static void advansys_wide_free_mem(struct asc_board *board)
{
struct adv_dvc_var *adv_dvc = &board->dvc_var.adv_dvc_var;
- kfree(adv_dvc->carrier_buf);
- adv_dvc->carrier_buf = NULL;
- kfree(adv_dvc->orig_reqp);
- adv_dvc->orig_reqp = board->adv_reqp = NULL;
- while (board->adv_sgblkp) {
- adv_sgblk_t *sgp = board->adv_sgblkp;
- board->adv_sgblkp = sgp->next_sgblkp;
- kfree(sgp);
+
+ if (adv_dvc->carrier) {
+ dma_free_coherent(board->dev, ADV_CARRIER_BUFSIZE,
+ adv_dvc->carrier, adv_dvc->carrier_addr);
+ adv_dvc->carrier = NULL;
+ }
+ if (board->adv_reqp) {
+ dma_free_coherent(board->dev, board->adv_reqp_size,
+ board->adv_reqp, board->adv_reqp_addr);
+ board->adv_reqp = NULL;
+ }
+ if (board->adv_sgblk_pool) {
+ dma_pool_destroy(board->adv_sgblk_pool);
+ board->adv_sgblk_pool = NULL;
}
}
@@ -11431,28 +10986,28 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop,
switch (asc_dvc_varp->bus_type) {
#ifdef CONFIG_ISA
case ASC_IS_ISA:
- shost->unchecked_isa_dma = TRUE;
+ shost->unchecked_isa_dma = true;
share_irq = 0;
break;
case ASC_IS_VL:
- shost->unchecked_isa_dma = FALSE;
+ shost->unchecked_isa_dma = false;
share_irq = 0;
break;
case ASC_IS_EISA:
- shost->unchecked_isa_dma = FALSE;
+ shost->unchecked_isa_dma = false;
share_irq = IRQF_SHARED;
break;
#endif /* CONFIG_ISA */
#ifdef CONFIG_PCI
case ASC_IS_PCI:
- shost->unchecked_isa_dma = FALSE;
+ shost->unchecked_isa_dma = false;
share_irq = IRQF_SHARED;
break;
#endif /* CONFIG_PCI */
default:
shost_printk(KERN_ERR, shost, "unknown adapter type: "
"%d\n", asc_dvc_varp->bus_type);
- shost->unchecked_isa_dma = TRUE;
+ shost->unchecked_isa_dma = false;
share_irq = 0;
break;
}
@@ -11471,7 +11026,7 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop,
* For Wide boards set PCI information before calling
* AdvInitGetConfig().
*/
- shost->unchecked_isa_dma = FALSE;
+ shost->unchecked_isa_dma = false;
share_irq = IRQF_SHARED;
ASC_DBG(2, "AdvInitGetConfig()\n");
@@ -11656,24 +11211,11 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop,
/* Set maximum number of queues the adapter can handle. */
shost->can_queue = adv_dvc_varp->max_host_qng;
}
-
- /*
- * Following v1.3.89, 'cmd_per_lun' is no longer needed
- * and should be set to zero.
- *
- * But because of a bug introduced in v1.3.89 if the driver is
- * compiled as a module and 'cmd_per_lun' is zero, the Mid-Level
- * SCSI function 'allocate_device' will panic. To allow the driver
- * to work as a module in these kernels set 'cmd_per_lun' to 1.
- *
- * Note: This is wrong. cmd_per_lun should be set to the depth
- * you want on untagged devices always.
- #ifdef MODULE
- */
- shost->cmd_per_lun = 1;
-/* #else
- shost->cmd_per_lun = 0;
-#endif */
+ ret = scsi_init_shared_tag_map(shost, shost->can_queue);
+ if (ret) {
+ shost_printk(KERN_ERR, shost, "init tag map failed\n");
+ goto err_free_dma;
+ }
/*
* Set the maximum number of scatter-gather elements the
@@ -11844,7 +11386,9 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop,
err_unmap:
if (boardp->ioremap_addr)
iounmap(boardp->ioremap_addr);
+#ifdef CONFIG_PCI
err_shost:
+#endif
return ret;
}
@@ -11927,6 +11471,7 @@ static int advansys_isa_probe(struct device *dev, unsigned int id)
board = shost_priv(shost);
board->irq = advansys_isa_irq_no(iop_base);
board->dev = dev;
+ board->shost = shost;
err = advansys_board_found(shost, iop_base, ASC_IS_ISA);
if (err)
@@ -12009,6 +11554,7 @@ static int advansys_vlb_probe(struct device *dev, unsigned int id)
board = shost_priv(shost);
board->irq = advansys_vlb_irq_no(iop_base);
board->dev = dev;
+ board->shost = shost;
err = advansys_board_found(shost, iop_base, ASC_IS_VL);
if (err)
@@ -12116,6 +11662,7 @@ static int advansys_eisa_probe(struct device *dev)
board = shost_priv(shost);
board->irq = irq;
board->dev = dev;
+ board->shost = shost;
err = advansys_board_found(shost, ioport, ASC_IS_EISA);
if (!err) {
@@ -12232,6 +11779,7 @@ static int advansys_pci_probe(struct pci_dev *pdev,
board = shost_priv(shost);
board->irq = pdev->irq;
board->dev = &pdev->dev;
+ board->shost = shost;
if (pdev->device == PCI_DEVICE_ID_ASP_ABP940UW ||
pdev->device == PCI_DEVICE_ID_38C0800_REV1 ||
diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c
index e31c460a1335..f44d0487236e 100644
--- a/drivers/scsi/aha152x.c
+++ b/drivers/scsi/aha152x.c
@@ -2922,7 +2922,6 @@ static struct scsi_host_template aha152x_driver_template = {
.can_queue = 1,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
.slave_alloc = aha152x_adjust_queue,
};
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index ec432763a29a..5b8b2937a3fe 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -375,9 +375,10 @@ static int aha1542_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
u8 lun = cmd->device->lun;
unsigned long flags;
int bufflen = scsi_bufflen(cmd);
- int mbo;
+ int mbo, sg_count;
struct mailbox *mb = aha1542->mb;
struct ccb *ccb = aha1542->ccb;
+ struct chain *cptr;
if (*cmd->cmnd == REQUEST_SENSE) {
/* Don't do the command - we have the sense data already */
@@ -397,6 +398,13 @@ static int aha1542_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
print_hex_dump_bytes("command: ", DUMP_PREFIX_NONE, cmd->cmnd, cmd->cmd_len);
}
#endif
+ if (bufflen) { /* allocate memory before taking host_lock */
+ sg_count = scsi_sg_count(cmd);
+ cptr = kmalloc(sizeof(*cptr) * sg_count, GFP_KERNEL | GFP_DMA);
+ if (!cptr)
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
/* Use the outgoing mailboxes in a round-robin fashion, because this
is how the host adapter will scan for them */
@@ -441,19 +449,10 @@ static int aha1542_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
if (bufflen) {
struct scatterlist *sg;
- struct chain *cptr;
- int i, sg_count = scsi_sg_count(cmd);
+ int i;
ccb[mbo].op = 2; /* SCSI Initiator Command w/scatter-gather */
- cmd->host_scribble = kmalloc(sizeof(*cptr)*sg_count,
- GFP_KERNEL | GFP_DMA);
- cptr = (struct chain *) cmd->host_scribble;
- if (cptr == NULL) {
- /* free the claimed mailbox slot */
- aha1542->int_cmds[mbo] = NULL;
- spin_unlock_irqrestore(sh->host_lock, flags);
- return SCSI_MLQUEUE_HOST_BUSY;
- }
+ cmd->host_scribble = (void *)cptr;
scsi_for_each_sg(cmd, sg, sg_count, i) {
any2scsi(cptr[i].dataptr, isa_page_to_bus(sg_page(sg))
+ sg->offset);
@@ -951,7 +950,6 @@ static struct scsi_host_template driver_template = {
.can_queue = AHA1542_MAILBOXES,
.this_id = 7,
.sg_tablesize = 16,
- .cmd_per_lun = 1,
.unchecked_isa_dma = 1,
.use_clustering = ENABLE_CLUSTERING,
};
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index 31ace4bef8fe..bad35ffc015d 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -544,7 +544,6 @@ static struct scsi_host_template aha1740_template = {
.can_queue = AHA1740_ECBS,
.this_id = 7,
.sg_tablesize = AHA1740_SCATTER,
- .cmd_per_lun = AHA1740_CMDLUN,
.use_clustering = ENABLE_CLUSTERING,
.eh_abort_handler = aha1740_eh_abort_handler,
};
diff --git a/drivers/scsi/aha1740.h b/drivers/scsi/aha1740.h
index af23fd6bd795..b0c5603461ca 100644
--- a/drivers/scsi/aha1740.h
+++ b/drivers/scsi/aha1740.h
@@ -149,6 +149,5 @@ struct ecb { /* Enhanced Control Block 6.1 */
#define AHA1740_ECBS 32
#define AHA1740_SCATTER 16
-#define AHA1740_CMDLUN 1
#endif
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c
index 02a2512b76a8..4b135cca42a1 100644
--- a/drivers/scsi/aic94xx/aic94xx_init.c
+++ b/drivers/scsi/aic94xx/aic94xx_init.c
@@ -65,7 +65,6 @@ static struct scsi_host_template aic94xx_sht = {
.change_queue_depth = sas_change_queue_depth,
.bios_param = sas_bios_param,
.can_queue = 1,
- .cmd_per_lun = 1,
.this_id = -1,
.sg_tablesize = SG_ALL,
.max_sectors = SCSI_DEFAULT_MAX_SECTORS,
diff --git a/drivers/scsi/arm/arxescsi.c b/drivers/scsi/arm/arxescsi.c
index 32d23212de48..3110736fd337 100644
--- a/drivers/scsi/arm/arxescsi.c
+++ b/drivers/scsi/arm/arxescsi.c
@@ -245,7 +245,6 @@ static struct scsi_host_template arxescsi_template = {
.can_queue = 0,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "arxescsi",
};
diff --git a/drivers/scsi/arm/cumana_2.c b/drivers/scsi/arm/cumana_2.c
index abc66f5263ec..faa1bee07c8a 100644
--- a/drivers/scsi/arm/cumana_2.c
+++ b/drivers/scsi/arm/cumana_2.c
@@ -367,7 +367,6 @@ static struct scsi_host_template cumanascsi2_template = {
.this_id = 7,
.sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS,
.dma_boundary = IOMD_DMA_BOUNDARY,
- .cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "cumanascsi2",
};
diff --git a/drivers/scsi/arm/eesox.c b/drivers/scsi/arm/eesox.c
index 5bf3c0d134b4..a8ad6880dd91 100644
--- a/drivers/scsi/arm/eesox.c
+++ b/drivers/scsi/arm/eesox.c
@@ -486,7 +486,6 @@ static struct scsi_host_template eesox_template = {
.this_id = 7,
.sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS,
.dma_boundary = IOMD_DMA_BOUNDARY,
- .cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "eesox",
};
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
index 0836433e3a2d..05301bc752ee 100644
--- a/drivers/scsi/atp870u.c
+++ b/drivers/scsi/atp870u.c
@@ -3158,7 +3158,6 @@ static struct scsi_host_template atp870u_template = {
.can_queue = qcnt /* can_queue */,
.this_id = 7 /* SCSI ID */,
.sg_tablesize = ATP870U_SCATTER /*SG_ALL*/ /*SG_NONE*/,
- .cmd_per_lun = ATP870U_CMDLUN /* commands per lun */,
.use_clustering = ENABLE_CLUSTERING,
.max_sectors = ATP870U_MAX_SECTORS,
};
diff --git a/drivers/scsi/atp870u.h b/drivers/scsi/atp870u.h
index 62bae64a01c1..5cf62566ad42 100644
--- a/drivers/scsi/atp870u.h
+++ b/drivers/scsi/atp870u.h
@@ -10,7 +10,6 @@
#define MAX_SENSE 14
#define qcnt 32
#define ATP870U_SCATTER 128
-#define ATP870U_CMDLUN 1
#define MAX_ADAPTER 8
#define MAX_SCSI_ID 16
diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h
index 81e83a65a193..32070099c333 100644
--- a/drivers/scsi/be2iscsi/be.h
+++ b/drivers/scsi/be2iscsi/be.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2014 Emulex
+ * Copyright (C) 2005 - 2015 Avago Technologies
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -8,9 +8,9 @@
* Public License is included in this distribution in the file called COPYING.
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@avagotech.com
*
- * Emulex
+ * Avago Technologies
* 3333 Susan Street
* Costa Mesa, CA 92626
*/
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index 1028760b8a22..185391a64d4b 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2014 Emulex
+ * Copyright (C) 2005 - 2015 Avago Technologies
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -8,9 +8,9 @@
* Public License is included in this distribution in the file called COPYING.
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@avagotech.com
*
- * Emulex
+ * Avago Technologies
* 3333 Susan Street
* Costa Mesa, CA 92626
*/
@@ -452,6 +452,7 @@ void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
((evt->port_link_status & ASYNC_EVENT_LOGICAL) &&
(evt->port_fault == BEISCSI_PHY_LINK_FAULT_NONE))) {
phba->state = BE_ADAPTER_LINK_UP | BE_ADAPTER_CHECK_BOOT;
+ phba->get_boot = BE_GET_BOOT_RETRIES;
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT,
@@ -480,6 +481,7 @@ int beiscsi_process_mcc(struct beiscsi_hba *phba)
case ASYNC_EVENT_NEW_ISCSI_CONN:
case ASYNC_EVENT_NEW_TCP_CONN:
phba->state |= BE_ADAPTER_CHECK_BOOT;
+ phba->get_boot = BE_GET_BOOT_RETRIES;
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG |
BEISCSI_LOG_MBOX,
@@ -488,6 +490,8 @@ int beiscsi_process_mcc(struct beiscsi_hba *phba)
compl->flags);
break;
default:
+ phba->state |= BE_ADAPTER_CHECK_BOOT;
+ phba->get_boot = BE_GET_BOOT_RETRIES;
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG |
BEISCSI_LOG_MBOX,
diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h
index 98897434bcb4..cdfbc5c19cf4 100644
--- a/drivers/scsi/be2iscsi/be_cmds.h
+++ b/drivers/scsi/be2iscsi/be_cmds.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2014 Emulex
+ * Copyright (C) 2005 - 2015 Avago Technologies
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -8,9 +8,9 @@
* Public License is included in this distribution in the file called COPYING.
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@avagotech.com
*
- * Emulex
+ * Avago Technologies
* 3333 Susan Street
* Costa Mesa, CA 92626
*/
@@ -304,6 +304,17 @@ struct mgmt_auth_method_format {
struct mgmt_chap_format chap;
} __packed;
+struct be_cmd_req_logout_fw_sess {
+ struct be_cmd_req_hdr hdr; /* dw[4] */
+ uint32_t session_handle;
+} __packed;
+
+struct be_cmd_resp_logout_fw_sess {
+ struct be_cmd_resp_hdr hdr; /* dw[4] */
+#define BEISCSI_MGMT_SESSION_CLOSE 0x20
+ uint32_t session_status;
+} __packed;
+
struct mgmt_conn_login_options {
u8 flags;
u8 header_digest;
@@ -1136,6 +1147,7 @@ struct be_cmd_get_all_if_id_req {
#define OPCODE_ISCSI_INI_CFG_GET_HBA_NAME 6
#define OPCODE_ISCSI_INI_CFG_SET_HBA_NAME 7
#define OPCODE_ISCSI_INI_SESSION_GET_A_SESSION 14
+#define OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET 24
#define OPCODE_ISCSI_INI_DRIVER_REOPEN_ALL_SESSIONS 36
#define OPCODE_ISCSI_INI_DRIVER_OFFLOAD_SESSION 41
#define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index b7391a3f9f0b..2f0700796842 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2014 Emulex
+ * Copyright (C) 2005 - 2015 Avago Technologies
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,12 +7,12 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com)
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@avagotech.com
*
- * Emulex
+ * Avago Technologies
* 3333 Susan Street
* Costa Mesa, CA 92626
*/
diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h
index e0b3b2d1f27a..0c84e1c0763a 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.h
+++ b/drivers/scsi/be2iscsi/be_iscsi.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2014 Emulex
+ * Copyright (C) 2005 - 2015 Avago Technologies
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,12 +7,12 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com)
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@avagotech.com
*
- * Emulex
+ * Avago Technologies
* 3333 Susan Street
* Costa Mesa, CA 92626
*/
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index 923a2b5a2439..7a6dbfbccec9 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2014 Emulex
+ * Copyright (C) 2005 - 2015 Avago Technologies
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,12 +7,12 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com)
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@avagotech.com
*
- * Emulex
+ * Avago Technologies
* 3333 Susan Street
* Costa Mesa, CA 92626
*/
@@ -50,7 +50,7 @@ static unsigned int enable_msix = 1;
MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR);
MODULE_VERSION(BUILD_STR);
-MODULE_AUTHOR("Emulex Corporation");
+MODULE_AUTHOR("Avago Technologies");
MODULE_LICENSE("GPL");
module_param(be_iopoll_budget, int, 0);
module_param(enable_msix, int, 0);
@@ -552,7 +552,7 @@ MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
static struct scsi_host_template beiscsi_sht = {
.module = THIS_MODULE,
- .name = "Emulex 10Gbe open-iscsi Initiator Driver",
+ .name = "Avago Technologies 10Gbe open-iscsi Initiator Driver",
.proc_name = DRV_NAME,
.queuecommand = iscsi_queuecommand,
.change_queue_depth = scsi_change_queue_depth,
@@ -668,14 +668,20 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev)
return ret;
}
+ ret = pci_request_regions(pcidev, DRV_NAME);
+ if (ret) {
+ dev_err(&pcidev->dev,
+ "beiscsi_enable_pci - request region failed\n");
+ goto pci_dev_disable;
+ }
+
pci_set_master(pcidev);
ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(64));
if (ret) {
ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n");
- pci_disable_device(pcidev);
- return ret;
+ goto pci_region_release;
} else {
ret = pci_set_consistent_dma_mask(pcidev,
DMA_BIT_MASK(32));
@@ -684,11 +690,17 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev)
ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64));
if (ret) {
dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n");
- pci_disable_device(pcidev);
- return ret;
+ goto pci_region_release;
}
}
return 0;
+
+pci_region_release:
+ pci_release_regions(pcidev);
+pci_dev_disable:
+ pci_disable_device(pcidev);
+
+ return ret;
}
static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
@@ -1356,8 +1368,10 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ)
conn->rxdata_octets += resid;
unmap:
- scsi_dma_unmap(io_task->scsi_cmnd);
- io_task->scsi_cmnd = NULL;
+ if (io_task->scsi_cmnd) {
+ scsi_dma_unmap(io_task->scsi_cmnd);
+ io_task->scsi_cmnd = NULL;
+ }
iscsi_complete_scsi_task(task, exp_cmdsn, max_cmdsn);
}
@@ -2037,11 +2051,16 @@ static void beiscsi_process_mcc_isr(struct beiscsi_hba *phba)
/* Interpret compl as a async link evt */
beiscsi_async_link_state_process(phba,
(struct be_async_event_link_state *) mcc_compl);
- else
+ else {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_MBOX,
"BM_%d : Unsupported Async Event, flags"
" = 0x%08x\n",
mcc_compl->flags);
+ if (phba->state & BE_ADAPTER_LINK_UP) {
+ phba->state |= BE_ADAPTER_CHECK_BOOT;
+ phba->get_boot = BE_GET_BOOT_RETRIES;
+ }
+ }
} else if (mcc_compl->flags & CQE_FLAGS_COMPLETED_MASK) {
be_mcc_compl_process_isr(&phba->ctrl, mcc_compl);
atomic_dec(&phba->ctrl.mcc_obj.q.used);
@@ -3678,14 +3697,16 @@ static void be_mcc_queues_destroy(struct beiscsi_hba *phba)
struct be_ctrl_info *ctrl = &phba->ctrl;
q = &phba->ctrl.mcc_obj.q;
- if (q->created)
+ if (q->created) {
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ);
- be_queue_free(phba, q);
+ be_queue_free(phba, q);
+ }
q = &phba->ctrl.mcc_obj.cq;
- if (q->created)
+ if (q->created) {
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
- be_queue_free(phba, q);
+ be_queue_free(phba, q);
+ }
}
static void hwi_cleanup(struct beiscsi_hba *phba)
@@ -3729,8 +3750,10 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
for (i = 0; i < (phba->num_cpus); i++) {
q = &phwi_context->be_cq[i];
- if (q->created)
+ if (q->created) {
+ be_queue_free(phba, q);
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+ }
}
be_mcc_queues_destroy(phba);
@@ -3740,8 +3763,10 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
eq_for_mcc = 0;
for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
q = &phwi_context->be_eq[i].q;
- if (q->created)
+ if (q->created) {
+ be_queue_free(phba, q);
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+ }
}
be_cmd_fw_uninit(ctrl);
}
@@ -4328,8 +4353,14 @@ static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
"BM_%d : No boot session\n");
+
+ if (ret == -ENXIO)
+ phba->get_boot = 0;
+
+
return ret;
}
+ phba->get_boot = 0;
nonemb_cmd.va = pci_zalloc_consistent(phba->ctrl.pdev,
sizeof(*session_resp),
&nonemb_cmd.dma);
@@ -4369,6 +4400,9 @@ static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
memcpy(&phba->boot_sess, &session_resp->session_info,
sizeof(struct mgmt_session_info));
+
+ beiscsi_logout_fw_sess(phba,
+ phba->boot_sess.session_handle);
ret = 0;
boot_freemem:
@@ -4580,11 +4614,13 @@ beiscsi_free_mgmt_task_handles(struct beiscsi_conn *beiscsi_conn,
spin_unlock_bh(&phba->mgmt_sgl_lock);
}
- if (io_task->mtask_addr)
+ if (io_task->mtask_addr) {
pci_unmap_single(phba->pcidev,
io_task->mtask_addr,
io_task->mtask_data_count,
PCI_DMA_TODEVICE);
+ io_task->mtask_addr = 0;
+ }
}
/**
@@ -5264,6 +5300,7 @@ static void beiscsi_remove(struct pci_dev *pcidev)
iscsi_host_free(phba->shost);
pci_disable_pcie_error_reporting(pcidev);
pci_set_drvdata(pcidev, NULL);
+ pci_release_regions(pcidev);
pci_disable_device(pcidev);
}
@@ -5374,8 +5411,14 @@ beiscsi_hw_health_check(struct work_struct *work)
be_eqd_update(phba);
if (phba->state & BE_ADAPTER_CHECK_BOOT) {
- phba->state &= ~BE_ADAPTER_CHECK_BOOT;
- be_check_boot_session(phba);
+ if ((phba->get_boot > 0) && (!phba->boot_kset)) {
+ phba->get_boot--;
+ if (!(phba->get_boot % BE_GET_BOOT_TO))
+ be_check_boot_session(phba);
+ } else {
+ phba->state &= ~BE_ADAPTER_CHECK_BOOT;
+ phba->get_boot = 0;
+ }
}
beiscsi_ue_detect(phba);
@@ -5738,6 +5781,7 @@ hba_free:
iscsi_host_free(phba->shost);
pci_set_drvdata(pcidev, NULL);
disable_pci:
+ pci_release_regions(pcidev);
pci_disable_device(pcidev);
return ret;
}
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index 7ee0ffc38514..b8c0c7819cb1 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2014 Emulex
+ * Copyright (C) 2005 - 2015 Avago Technologies
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,12 +7,12 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com)
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@avagotech.com
*
- * Emulex
+ * Avago Technologies
* 3333 Susan Street
* Costa Mesa, CA 92626
*/
@@ -36,8 +36,8 @@
#include <scsi/scsi_transport_iscsi.h>
#define DRV_NAME "be2iscsi"
-#define BUILD_STR "10.4.114.0"
-#define BE_NAME "Emulex OneConnect" \
+#define BUILD_STR "10.6.0.0"
+#define BE_NAME "Avago Technologies OneConnect" \
"Open-iSCSI Driver version" BUILD_STR
#define DRV_DESC BE_NAME " " "Driver"
@@ -109,6 +109,9 @@
#define BEISCSI_CLEAN_UNLOAD 0x01
#define BEISCSI_EEH_UNLOAD 0x02
+
+#define BE_GET_BOOT_RETRIES 45
+#define BE_GET_BOOT_TO 20
/**
* hardware needs the async PDU buffers to be posted in multiples of 8
* So have atleast 8 of them by default
@@ -413,6 +416,7 @@ struct beiscsi_hba {
} fw_config;
unsigned int state;
+ int get_boot;
bool fw_timeout;
bool ue_detected;
struct delayed_work beiscsi_hw_check_task;
diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c
index 681d4e8f003a..ca4016f20e76 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2014 Emulex
+ * Copyright (C) 2005 - 2015 Avago Technologies
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,12 +7,12 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com)
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@avagotech.com
*
- * Emulex
+ * Avago Technologies
* 3333 Susan Street
* Costa Mesa, CA 92626
*/
@@ -1707,3 +1707,72 @@ void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params,
(params->dw[offsetof(struct amap_beiscsi_offload_params,
exp_statsn) / 32] + 1));
}
+
+/**
+ * beiscsi_logout_fw_sess()- Firmware Session Logout
+ * @phba: Device priv structure instance
+ * @fw_sess_handle: FW session handle
+ *
+ * Logout from the FW established sessions.
+ * returns
+ * Success: 0
+ * Failure: Non-Zero Value
+ *
+ */
+int beiscsi_logout_fw_sess(struct beiscsi_hba *phba,
+ uint32_t fw_sess_handle)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_logout_fw_sess *req;
+ struct be_cmd_resp_logout_fw_sess *resp;
+ unsigned int tag;
+ int rc;
+
+ beiscsi_log(phba, KERN_INFO,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BG_%d : In bescsi_logout_fwboot_sess\n");
+
+ spin_lock(&ctrl->mbox_lock);
+ tag = alloc_mcc_tag(phba);
+ if (!tag) {
+ spin_unlock(&ctrl->mbox_lock);
+ beiscsi_log(phba, KERN_INFO,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BG_%d : MBX Tag Failure\n");
+ return -EINVAL;
+ }
+
+ wrb = wrb_from_mccq(phba);
+ req = embedded_payload(wrb);
+ wrb->tag0 |= tag;
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
+ OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET,
+ sizeof(struct be_cmd_req_logout_fw_sess));
+
+ /* Set the session handle */
+ req->session_handle = fw_sess_handle;
+ be_mcc_notify(phba);
+ spin_unlock(&ctrl->mbox_lock);
+
+ rc = beiscsi_mccq_compl(phba, tag, &wrb, NULL);
+ if (rc) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
+ "BG_%d : MBX CMD FW_SESSION_LOGOUT_TARGET Failed\n");
+ return -EBUSY;
+ }
+
+ resp = embedded_payload(wrb);
+ if (resp->session_status !=
+ BEISCSI_MGMT_SESSION_CLOSE) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
+ "BG_%d : FW_SESSION_LOGOUT_TARGET resp : 0x%x\n",
+ resp->session_status);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h
index bd81446936fc..b58a7decbd94 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.h
+++ b/drivers/scsi/be2iscsi/be_mgmt.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2014 Emulex
+ * Copyright (C) 2005 - 2015 Avago Technologies
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,12 +7,12 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com)
*
* Contact Information:
- * linux-drivers@emulex.com
+ * linux-drivers@avagotech.com
*
- * Emulex
+ * Avago Technologies
* 3333 Susan Street
* Costa Mesa, CA 92626
*/
@@ -338,4 +338,7 @@ void beiscsi_ue_detect(struct beiscsi_hba *phba);
int be_cmd_modify_eq_delay(struct beiscsi_hba *phba,
struct be_set_eqd *, int num);
+int beiscsi_logout_fw_sess(struct beiscsi_hba *phba,
+ uint32_t fw_sess_handle);
+
#endif
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index e53078d03309..72894378ffcf 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -1173,8 +1173,10 @@ static void bnx2i_cleanup_task(struct iscsi_task *task)
bnx2i_send_cmd_cleanup_req(hba, task->dd_data);
spin_unlock_bh(&conn->session->back_lock);
+ spin_unlock_bh(&conn->session->frwd_lock);
wait_for_completion_timeout(&bnx2i_conn->cmd_cleanup_cmpl,
msecs_to_jiffies(ISCSI_CMD_CLEANUP_TIMEOUT));
+ spin_lock_bh(&conn->session->frwd_lock);
spin_lock_bh(&conn->session->back_lock);
}
bnx2i_iscsi_unmap_sg_list(task->dd_data);
@@ -2093,7 +2095,8 @@ int bnx2i_hw_ep_disconnect(struct bnx2i_endpoint *bnx2i_ep)
else
/* wait for option-2 conn teardown */
wait_event_interruptible(bnx2i_ep->ofld_wait,
- bnx2i_ep->state != EP_STATE_DISCONN_START);
+ ((bnx2i_ep->state != EP_STATE_DISCONN_START)
+ && (bnx2i_ep->state != EP_STATE_TCP_FIN_RCVD)));
if (signal_pending(current))
flush_signals(current);
diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c
index 2e66f34ebb79..622bdabc8894 100644
--- a/drivers/scsi/csiostor/csio_hw.c
+++ b/drivers/scsi/csiostor/csio_hw.c
@@ -3928,6 +3928,7 @@ csio_hw_init(struct csio_hw *hw)
evt_entry = kzalloc(sizeof(struct csio_evt_msg), GFP_KERNEL);
if (!evt_entry) {
+ rv = -ENOMEM;
csio_err(hw, "Failed to initialize eventq");
goto err_evtq_cleanup;
}
diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
index 3db4c63978c5..0e2bee937fe8 100644
--- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
+++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
@@ -1,7 +1,7 @@
/*
* cxgb3i_offload.c: Chelsio S3xx iscsi offloaded tcp connection management
*
- * Copyright (C) 2003-2008 Chelsio Communications. All rights reserved.
+ * Copyright (C) 2003-2015 Chelsio Communications. All rights reserved.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -32,8 +32,8 @@ static unsigned int dbg_level;
#define DRV_MODULE_NAME "cxgb3i"
#define DRV_MODULE_DESC "Chelsio T3 iSCSI Driver"
-#define DRV_MODULE_VERSION "2.0.0"
-#define DRV_MODULE_RELDATE "Jun. 2010"
+#define DRV_MODULE_VERSION "2.0.1-ko"
+#define DRV_MODULE_RELDATE "Apr. 2015"
static char version[] =
DRV_MODULE_DESC " " DRV_MODULE_NAME
@@ -156,7 +156,7 @@ static int push_tx_frames(struct cxgbi_sock *csk, int req_completion);
static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
const struct l2t_entry *e)
{
- unsigned int wscale = cxgbi_sock_compute_wscale(cxgb3i_rcv_win);
+ unsigned int wscale = cxgbi_sock_compute_wscale(csk->rcv_win);
struct cpl_act_open_req *req = (struct cpl_act_open_req *)skb->head;
skb->priority = CPL_PRIORITY_SETUP;
@@ -172,7 +172,7 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
V_WND_SCALE(wscale) | V_MSS_IDX(csk->mss_idx) |
V_L2T_IDX(e->idx) | V_TX_CHANNEL(e->smt_idx));
req->opt0l = htonl(V_ULP_MODE(ULP2_MODE_ISCSI) |
- V_RCV_BUFSIZ(cxgb3i_rcv_win>>10));
+ V_RCV_BUFSIZ(csk->rcv_win >> 10));
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p,%u,0x%lx,%u, %pI4:%u-%pI4:%u, %u,%u,%u.\n",
@@ -369,7 +369,7 @@ static inline void make_tx_data_wr(struct cxgbi_sock *csk, struct sk_buff *skb,
req->flags |= htonl(V_TX_ACK_PAGES(2) | F_TX_INIT |
V_TX_CPU_IDX(csk->rss_qid));
/* sendbuffer is in units of 32KB. */
- req->param |= htonl(V_TX_SNDBUF(cxgb3i_snd_win >> 15));
+ req->param |= htonl(V_TX_SNDBUF(csk->snd_win >> 15));
cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT);
}
}
@@ -503,8 +503,8 @@ static int do_act_establish(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
csk, csk->state, csk->flags, csk->tid);
csk->copied_seq = csk->rcv_wup = csk->rcv_nxt = rcv_isn;
- if (cxgb3i_rcv_win > (M_RCV_BUFSIZ << 10))
- csk->rcv_wup -= cxgb3i_rcv_win - (M_RCV_BUFSIZ << 10);
+ if (csk->rcv_win > (M_RCV_BUFSIZ << 10))
+ csk->rcv_wup -= csk->rcv_win - (M_RCV_BUFSIZ << 10);
cxgbi_sock_established(csk, ntohl(req->snd_isn), ntohs(req->tcp_opt));
@@ -988,6 +988,8 @@ static int init_act_open(struct cxgbi_sock *csk)
goto rel_resource;
skb->sk = (struct sock *)csk;
set_arp_failure_handler(skb, act_open_arp_failure);
+ csk->snd_win = cxgb3i_snd_win;
+ csk->rcv_win = cxgb3i_rcv_win;
csk->wr_max_cred = csk->wr_cred = T3C_DATA(t3dev)->max_wrs - 1;
csk->wr_una_cred = 0;
@@ -1320,8 +1322,6 @@ static void cxgb3i_dev_open(struct t3cdev *t3dev)
cdev->nports = adapter->params.nports;
cdev->mtus = adapter->params.mtus;
cdev->nmtus = NMTUS;
- cdev->snd_win = cxgb3i_snd_win;
- cdev->rcv_win = cxgb3i_rcv_win;
cdev->rx_credit_thres = cxgb3i_rx_credit_thres;
cdev->skb_tx_rsvd = CXGB3I_TX_HEADER_LEN;
cdev->skb_rx_extra = sizeof(struct cpl_iscsi_hdr_norss);
diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h
index 20593fd69d8f..b0430c9359e7 100644
--- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h
+++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h
@@ -1,7 +1,7 @@
/*
* cxgb3i.h: Chelsio S3xx iSCSI driver.
*
- * Copyright (c) 2008 Chelsio Communications, Inc.
+ * Copyright (c) 2008-2015 Chelsio Communications, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
index dd00e5fe4a5e..de6feb8964c9 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
@@ -1,7 +1,7 @@
/*
* cxgb4i.c: Chelsio T4 iSCSI driver.
*
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -36,11 +36,12 @@ static unsigned int dbg_level;
#define DRV_MODULE_NAME "cxgb4i"
#define DRV_MODULE_DESC "Chelsio T4/T5 iSCSI Driver"
-#define DRV_MODULE_VERSION "0.9.4"
+#define DRV_MODULE_VERSION "0.9.5-ko"
+#define DRV_MODULE_RELDATE "Apr. 2015"
static char version[] =
DRV_MODULE_DESC " " DRV_MODULE_NAME
- " v" DRV_MODULE_VERSION "\n";
+ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("Chelsio Communications, Inc.");
MODULE_DESCRIPTION(DRV_MODULE_DESC);
@@ -50,11 +51,13 @@ MODULE_LICENSE("GPL");
module_param(dbg_level, uint, 0644);
MODULE_PARM_DESC(dbg_level, "Debug flag (default=0)");
-static int cxgb4i_rcv_win = 256 * 1024;
+#define CXGB4I_DEFAULT_10G_RCV_WIN (256 * 1024)
+static int cxgb4i_rcv_win = -1;
module_param(cxgb4i_rcv_win, int, 0644);
MODULE_PARM_DESC(cxgb4i_rcv_win, "TCP reveive window in bytes");
-static int cxgb4i_snd_win = 128 * 1024;
+#define CXGB4I_DEFAULT_10G_SND_WIN (128 * 1024)
+static int cxgb4i_snd_win = -1;
module_param(cxgb4i_snd_win, int, 0644);
MODULE_PARM_DESC(cxgb4i_snd_win, "TCP send window in bytes");
@@ -196,10 +199,10 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
TX_CHAN_V(csk->tx_chan) |
SMAC_SEL_V(csk->smac_idx) |
ULP_MODE_V(ULP_MODE_ISCSI) |
- RCV_BUFSIZ_V(cxgb4i_rcv_win >> 10);
+ RCV_BUFSIZ_V(csk->rcv_win >> 10);
+
opt2 = RX_CHANNEL_V(0) |
RSS_QUEUE_VALID_F |
- (RX_FC_DISABLE_F) |
RSS_QUEUE_V(csk->rss_qid);
if (is_t4(lldi->adapter_type)) {
@@ -228,6 +231,7 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
} else {
struct cpl_t5_act_open_req *req =
(struct cpl_t5_act_open_req *)skb->head;
+ u32 isn = (prandom_u32() & ~7UL) - 1;
INIT_TP_WR(req, 0);
OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
@@ -241,7 +245,10 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
cxgb4_select_ntuple(
csk->cdev->ports[csk->port_id],
csk->l2t)));
- opt2 |= 1 << 31;
+ req->rsvd = cpu_to_be32(isn);
+ opt2 |= T5_ISS_VALID;
+ opt2 |= T5_OPT_2_VALID_F;
+
req->opt2 = cpu_to_be32(opt2);
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
@@ -279,7 +286,7 @@ static void send_act_open_req6(struct cxgbi_sock *csk, struct sk_buff *skb,
TX_CHAN_V(csk->tx_chan) |
SMAC_SEL_V(csk->smac_idx) |
ULP_MODE_V(ULP_MODE_ISCSI) |
- RCV_BUFSIZ_V(cxgb4i_rcv_win >> 10);
+ RCV_BUFSIZ_V(csk->rcv_win >> 10);
opt2 = RX_CHANNEL_V(0) |
RSS_QUEUE_VALID_F |
@@ -544,7 +551,7 @@ static inline int send_tx_flowc_wr(struct cxgbi_sock *csk)
flowc->mnemval[5].mnemonic = FW_FLOWC_MNEM_RCVNXT;
flowc->mnemval[5].val = htonl(csk->rcv_nxt);
flowc->mnemval[6].mnemonic = FW_FLOWC_MNEM_SNDBUF;
- flowc->mnemval[6].val = htonl(cxgb4i_snd_win);
+ flowc->mnemval[6].val = htonl(csk->snd_win);
flowc->mnemval[7].mnemonic = FW_FLOWC_MNEM_MSS;
flowc->mnemval[7].val = htonl(csk->advmss);
flowc->mnemval[8].mnemonic = 0;
@@ -557,7 +564,7 @@ static inline int send_tx_flowc_wr(struct cxgbi_sock *csk)
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p, tid 0x%x, %u,%u,%u,%u,%u,%u,%u.\n",
csk, csk->tid, 0, csk->tx_chan, csk->rss_qid,
- csk->snd_nxt, csk->rcv_nxt, cxgb4i_snd_win,
+ csk->snd_nxt, csk->rcv_nxt, csk->snd_win,
csk->advmss);
cxgb4_ofld_send(csk->cdev->ports[csk->port_id], skb);
@@ -750,8 +757,8 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb)
* Causes the first RX_DATA_ACK to supply any Rx credits we couldn't
* pass through opt0.
*/
- if (cxgb4i_rcv_win > (RCV_BUFSIZ_MASK << 10))
- csk->rcv_wup -= cxgb4i_rcv_win - (RCV_BUFSIZ_MASK << 10);
+ if (csk->rcv_win > (RCV_BUFSIZ_MASK << 10))
+ csk->rcv_wup -= csk->rcv_win - (RCV_BUFSIZ_MASK << 10);
csk->advmss = lldi->mtus[TCPOPT_MSS_G(tcp_opt)] - 40;
if (TCPOPT_TSTAMP_G(tcp_opt))
@@ -1367,6 +1374,8 @@ static int init_act_open(struct cxgbi_sock *csk)
unsigned int step;
unsigned int size, size6;
int t4 = is_t4(lldi->adapter_type);
+ unsigned int linkspeed;
+ unsigned int rcv_winf, snd_winf;
log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
"csk 0x%p,%u,0x%lx,%u.\n",
@@ -1440,6 +1449,21 @@ static int init_act_open(struct cxgbi_sock *csk)
csk->txq_idx = cxgb4_port_idx(ndev) * step;
step = lldi->nrxq / lldi->nchan;
csk->rss_qid = lldi->rxq_ids[cxgb4_port_idx(ndev) * step];
+ linkspeed = ((struct port_info *)netdev_priv(ndev))->link_cfg.speed;
+ csk->snd_win = cxgb4i_snd_win;
+ csk->rcv_win = cxgb4i_rcv_win;
+ if (cxgb4i_rcv_win <= 0) {
+ csk->rcv_win = CXGB4I_DEFAULT_10G_RCV_WIN;
+ rcv_winf = linkspeed / SPEED_10000;
+ if (rcv_winf)
+ csk->rcv_win *= rcv_winf;
+ }
+ if (cxgb4i_snd_win <= 0) {
+ csk->snd_win = CXGB4I_DEFAULT_10G_SND_WIN;
+ snd_winf = linkspeed / SPEED_10000;
+ if (snd_winf)
+ csk->snd_win *= snd_winf;
+ }
csk->wr_cred = lldi->wr_cred -
DIV_ROUND_UP(sizeof(struct cpl_abort_req), 16);
csk->wr_max_cred = csk->wr_cred;
@@ -1758,8 +1782,6 @@ static void *t4_uld_add(const struct cxgb4_lld_info *lldi)
cdev->nports = lldi->nports;
cdev->mtus = lldi->mtus;
cdev->nmtus = NMTUS;
- cdev->snd_win = cxgb4i_snd_win;
- cdev->rcv_win = cxgb4i_rcv_win;
cdev->rx_credit_thres = cxgb4i_rx_credit_thres;
cdev->skb_tx_rsvd = CXGB4I_TX_HEADER_LEN;
cdev->skb_rx_extra = sizeof(struct cpl_iscsi_hdr);
diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h
index 1096026ba241..22dd8d670e4a 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h
@@ -1,7 +1,7 @@
/*
* cxgb4i.h: Chelsio T4 iSCSI driver.
*
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@
#define CXGB4I_TX_HEADER_LEN \
(sizeof(struct fw_ofld_tx_data_wr) + sizeof(struct sge_opaque_hdr))
+#define T5_ISS_VALID (1 << 18)
+
struct ulptx_idata {
__be32 cmd_more;
__be32 len;
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index eb58afcfb73b..1d42e4f88b96 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -1,7 +1,7 @@
/*
* libcxgbi.c: Chelsio common library for T3/T4 iSCSI driver.
*
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -38,8 +38,12 @@ static unsigned int dbg_level;
#define DRV_MODULE_NAME "libcxgbi"
#define DRV_MODULE_DESC "Chelsio iSCSI driver library"
-#define DRV_MODULE_VERSION "0.9.0"
-#define DRV_MODULE_RELDATE "Jun. 2010"
+#define DRV_MODULE_VERSION "0.9.1-ko"
+#define DRV_MODULE_RELDATE "Apr. 2015"
+
+static char version[] =
+ DRV_MODULE_DESC " " DRV_MODULE_NAME
+ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("Chelsio Communications, Inc.");
MODULE_DESCRIPTION(DRV_MODULE_DESC);
@@ -1126,11 +1130,11 @@ static int cxgbi_sock_send_pdus(struct cxgbi_sock *csk, struct sk_buff *skb)
goto out_err;
}
- if (csk->write_seq - csk->snd_una >= cdev->snd_win) {
+ if (csk->write_seq - csk->snd_una >= csk->snd_win) {
log_debug(1 << CXGBI_DBG_PDU_TX,
"csk 0x%p,%u,0x%lx,%u, FULL %u-%u >= %u.\n",
csk, csk->state, csk->flags, csk->tid, csk->write_seq,
- csk->snd_una, cdev->snd_win);
+ csk->snd_una, csk->snd_win);
err = -ENOBUFS;
goto out_err;
}
@@ -1885,7 +1889,7 @@ static void csk_return_rx_credits(struct cxgbi_sock *csk, int copied)
"csk 0x%p,%u,0x%lx,%u, seq %u, wup %u, thre %u, %u.\n",
csk, csk->state, csk->flags, csk->tid, csk->copied_seq,
csk->rcv_wup, cdev->rx_credit_thres,
- cdev->rcv_win);
+ csk->rcv_win);
if (csk->state != CTP_ESTABLISHED)
return;
@@ -1896,7 +1900,7 @@ static void csk_return_rx_credits(struct cxgbi_sock *csk, int copied)
if (unlikely(cdev->rx_credit_thres == 0))
return;
- must_send = credits + 16384 >= cdev->rcv_win;
+ must_send = credits + 16384 >= csk->rcv_win;
if (must_send || credits >= cdev->rx_credit_thres)
csk->rcv_wup += cdev->csk_send_rx_credits(csk, credits);
}
@@ -2913,6 +2917,8 @@ static int __init libcxgbi_init_module(void)
sw_tag_idx_bits = (__ilog2_u32(ISCSI_ITT_MASK)) + 1;
sw_tag_age_bits = (__ilog2_u32(ISCSI_AGE_MASK)) + 1;
+ pr_info("%s", version);
+
pr_info("tag itt 0x%x, %u bits, age 0x%x, %u bits.\n",
ISCSI_ITT_MASK, sw_tag_idx_bits,
ISCSI_AGE_MASK, sw_tag_age_bits);
diff --git a/drivers/scsi/cxgbi/libcxgbi.h b/drivers/scsi/cxgbi/libcxgbi.h
index aba1af720df6..b3e5bd1d5d9c 100644
--- a/drivers/scsi/cxgbi/libcxgbi.h
+++ b/drivers/scsi/cxgbi/libcxgbi.h
@@ -1,7 +1,7 @@
/*
* libcxgbi.h: Chelsio common library for T3/T4 iSCSI driver.
*
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -234,6 +234,8 @@ struct cxgbi_sock {
u32 snd_nxt;
u32 snd_una;
u32 write_seq;
+ u32 snd_win;
+ u32 rcv_win;
};
/*
@@ -540,8 +542,6 @@ struct cxgbi_device {
struct iscsi_transport *itp;
unsigned int pfvf;
- unsigned int snd_win;
- unsigned int rcv_win;
unsigned int rx_credit_thres;
unsigned int skb_tx_rsvd;
unsigned int skb_rx_extra; /* for msg coalesced mode */
diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c
index 2806cfbec2b9..f35ed53adaac 100644
--- a/drivers/scsi/dpt_i2o.c
+++ b/drivers/scsi/dpt_i2o.c
@@ -3562,7 +3562,6 @@ static struct scsi_host_template driver_template = {
.slave_configure = adpt_slave_configure,
.can_queue = MAX_TO_IOP_MESSAGES,
.this_id = 7,
- .cmd_per_lun = 1,
.use_clustering = ENABLE_CLUSTERING,
};
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
index fff682976c56..eefe14d453db 100644
--- a/drivers/scsi/fdomain.c
+++ b/drivers/scsi/fdomain.c
@@ -1764,7 +1764,6 @@ struct scsi_host_template fdomain_driver_template = {
.can_queue = 1,
.this_id = 6,
.sg_tablesize = 64,
- .cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
};
diff --git a/drivers/scsi/fnic/fnic_debugfs.c b/drivers/scsi/fnic/fnic_debugfs.c
index 5980c10c734d..d6498fabe628 100644
--- a/drivers/scsi/fnic/fnic_debugfs.c
+++ b/drivers/scsi/fnic/fnic_debugfs.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/debugfs.h>
+#include <linux/vmalloc.h>
#include "fnic.h"
static struct dentry *fnic_trace_debugfs_root;
diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c
index 65a9bde26974..4e15c4bf0795 100644
--- a/drivers/scsi/fnic/fnic_trace.c
+++ b/drivers/scsi/fnic/fnic_trace.c
@@ -21,6 +21,7 @@
#include <linux/spinlock.h>
#include <linux/kallsyms.h>
#include <linux/time.h>
+#include <linux/vmalloc.h>
#include "fnic_io.h"
#include "fnic.h"
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 8eab107b53fb..1dafeb43333b 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -43,6 +43,8 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dbg.h>
#include <linux/cciss_ioctl.h>
#include <linux/string.h>
#include <linux/bitmap.h>
@@ -56,7 +58,7 @@
#include "hpsa.h"
/* HPSA_DRIVER_VERSION must be 3 byte values (0-255) separated by '.' */
-#define HPSA_DRIVER_VERSION "3.4.4-1"
+#define HPSA_DRIVER_VERSION "3.4.10-0"
#define DRIVER_NAME "HP HPSA Driver (v " HPSA_DRIVER_VERSION ")"
#define HPSA "hpsa"
@@ -129,6 +131,7 @@ static const struct pci_device_id hpsa_pci_device_id[] = {
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CC},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CD},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CE},
+ {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0580},
{PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0076},
{PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0087},
{PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x007D},
@@ -186,6 +189,7 @@ static struct board_type products[] = {
{0x21CC103C, "Smart Array", &SA5_access},
{0x21CD103C, "Smart Array", &SA5_access},
{0x21CE103C, "Smart HBA", &SA5_access},
+ {0x05809005, "SmartHBA-SA", &SA5_access},
{0x00761590, "HP Storage P1224 Array Controller", &SA5_access},
{0x00871590, "HP Storage P1224e Array Controller", &SA5_access},
{0x007D1590, "HP Storage P1228 Array Controller", &SA5_access},
@@ -194,6 +198,10 @@ static struct board_type products[] = {
{0xFFFF103C, "Unknown Smart Array", &SA5_access},
};
+#define SCSI_CMD_BUSY ((struct scsi_cmnd *)&hpsa_cmd_busy)
+static const struct scsi_cmnd hpsa_cmd_busy;
+#define SCSI_CMD_IDLE ((struct scsi_cmnd *)&hpsa_cmd_idle)
+static const struct scsi_cmnd hpsa_cmd_idle;
static int number_of_controllers;
static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id);
@@ -207,6 +215,9 @@ static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd,
static void cmd_free(struct ctlr_info *h, struct CommandList *c);
static struct CommandList *cmd_alloc(struct ctlr_info *h);
+static void cmd_tagged_free(struct ctlr_info *h, struct CommandList *c);
+static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h,
+ struct scsi_cmnd *scmd);
static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
void *buff, size_t size, u16 page_code, unsigned char *scsi3addr,
int cmd_type);
@@ -222,6 +233,7 @@ static int hpsa_change_queue_depth(struct scsi_device *sdev, int qdepth);
static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd);
static int hpsa_eh_abort_handler(struct scsi_cmnd *scsicmd);
static int hpsa_slave_alloc(struct scsi_device *sdev);
+static int hpsa_slave_configure(struct scsi_device *sdev);
static void hpsa_slave_destroy(struct scsi_device *sdev);
static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno);
@@ -232,7 +244,8 @@ static void check_ioctl_unit_attention(struct ctlr_info *h,
/* performant mode helper functions */
static void calc_bucket_map(int *bucket, int num_buckets,
int nsgs, int min_blocks, u32 *bucket_map);
-static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h);
+static void hpsa_free_performant_mode(struct ctlr_info *h);
+static int hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h);
static inline u32 next_command(struct ctlr_info *h, u8 q);
static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr,
u32 *cfg_base_addr, u64 *cfg_base_addr_index,
@@ -252,6 +265,8 @@ static int hpsa_scsi_ioaccel_queue_command(struct ctlr_info *h,
struct CommandList *c, u32 ioaccel_handle, u8 *cdb, int cdb_len,
u8 *scsi3addr, struct hpsa_scsi_dev_t *phys_disk);
static void hpsa_command_resubmit_worker(struct work_struct *work);
+static u32 lockup_detected(struct ctlr_info *h);
+static int detect_controller_lockup(struct ctlr_info *h);
static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
{
@@ -265,40 +280,86 @@ static inline struct ctlr_info *shost_to_hba(struct Scsi_Host *sh)
return (struct ctlr_info *) *priv;
}
+static inline bool hpsa_is_cmd_idle(struct CommandList *c)
+{
+ return c->scsi_cmd == SCSI_CMD_IDLE;
+}
+
+static inline bool hpsa_is_pending_event(struct CommandList *c)
+{
+ return c->abort_pending || c->reset_pending;
+}
+
+/* extract sense key, asc, and ascq from sense data. -1 means invalid. */
+static void decode_sense_data(const u8 *sense_data, int sense_data_len,
+ u8 *sense_key, u8 *asc, u8 *ascq)
+{
+ struct scsi_sense_hdr sshdr;
+ bool rc;
+
+ *sense_key = -1;
+ *asc = -1;
+ *ascq = -1;
+
+ if (sense_data_len < 1)
+ return;
+
+ rc = scsi_normalize_sense(sense_data, sense_data_len, &sshdr);
+ if (rc) {
+ *sense_key = sshdr.sense_key;
+ *asc = sshdr.asc;
+ *ascq = sshdr.ascq;
+ }
+}
+
static int check_for_unit_attention(struct ctlr_info *h,
struct CommandList *c)
{
- if (c->err_info->SenseInfo[2] != UNIT_ATTENTION)
+ u8 sense_key, asc, ascq;
+ int sense_len;
+
+ if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo))
+ sense_len = sizeof(c->err_info->SenseInfo);
+ else
+ sense_len = c->err_info->SenseLen;
+
+ decode_sense_data(c->err_info->SenseInfo, sense_len,
+ &sense_key, &asc, &ascq);
+ if (sense_key != UNIT_ATTENTION || asc == -1)
return 0;
- switch (c->err_info->SenseInfo[12]) {
+ switch (asc) {
case STATE_CHANGED:
- dev_warn(&h->pdev->dev, HPSA "%d: a state change "
- "detected, command retried\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "%s: a state change detected, command retried\n",
+ h->devname);
break;
case LUN_FAILED:
dev_warn(&h->pdev->dev,
- HPSA "%d: LUN failure detected\n", h->ctlr);
+ "%s: LUN failure detected\n", h->devname);
break;
case REPORT_LUNS_CHANGED:
dev_warn(&h->pdev->dev,
- HPSA "%d: report LUN data changed\n", h->ctlr);
+ "%s: report LUN data changed\n", h->devname);
/*
* Note: this REPORT_LUNS_CHANGED condition only occurs on the external
* target (array) devices.
*/
break;
case POWER_OR_RESET:
- dev_warn(&h->pdev->dev, HPSA "%d: a power on "
- "or device reset detected\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "%s: a power on or device reset detected\n",
+ h->devname);
break;
case UNIT_ATTENTION_CLEARED:
- dev_warn(&h->pdev->dev, HPSA "%d: unit attention "
- "cleared by another initiator\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "%s: unit attention cleared by another initiator\n",
+ h->devname);
break;
default:
- dev_warn(&h->pdev->dev, HPSA "%d: unknown "
- "unit attention detected\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "%s: unknown unit attention detected\n",
+ h->devname);
break;
}
return 1;
@@ -314,6 +375,20 @@ static int check_for_busy(struct ctlr_info *h, struct CommandList *c)
return 1;
}
+static u32 lockup_detected(struct ctlr_info *h);
+static ssize_t host_show_lockup_detected(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ld;
+ struct ctlr_info *h;
+ struct Scsi_Host *shost = class_to_shost(dev);
+
+ h = shost_to_hba(shost);
+ ld = lockup_detected(h);
+
+ return sprintf(buf, "ld=%d\n", ld);
+}
+
static ssize_t host_store_hp_ssd_smart_path_status(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -425,7 +500,7 @@ static ssize_t host_show_hp_ssd_smart_path_status(struct device *dev,
/* List of controllers which cannot be hard reset on kexec with reset_devices */
static u32 unresettable_controller[] = {
0x324a103C, /* Smart Array P712m */
- 0x324b103C, /* SmartArray P711m */
+ 0x324b103C, /* Smart Array P711m */
0x3223103C, /* Smart Array P800 */
0x3234103C, /* Smart Array P400 */
0x3235103C, /* Smart Array P400i */
@@ -467,24 +542,32 @@ static u32 soft_unresettable_controller[] = {
0x409D0E11, /* Smart Array 6400 EM */
};
-static int ctlr_is_hard_resettable(u32 board_id)
+static u32 needs_abort_tags_swizzled[] = {
+ 0x323D103C, /* Smart Array P700m */
+ 0x324a103C, /* Smart Array P712m */
+ 0x324b103C, /* SmartArray P711m */
+};
+
+static int board_id_in_array(u32 a[], int nelems, u32 board_id)
{
int i;
- for (i = 0; i < ARRAY_SIZE(unresettable_controller); i++)
- if (unresettable_controller[i] == board_id)
- return 0;
- return 1;
+ for (i = 0; i < nelems; i++)
+ if (a[i] == board_id)
+ return 1;
+ return 0;
}
-static int ctlr_is_soft_resettable(u32 board_id)
+static int ctlr_is_hard_resettable(u32 board_id)
{
- int i;
+ return !board_id_in_array(unresettable_controller,
+ ARRAY_SIZE(unresettable_controller), board_id);
+}
- for (i = 0; i < ARRAY_SIZE(soft_unresettable_controller); i++)
- if (soft_unresettable_controller[i] == board_id)
- return 0;
- return 1;
+static int ctlr_is_soft_resettable(u32 board_id)
+{
+ return !board_id_in_array(soft_unresettable_controller,
+ ARRAY_SIZE(soft_unresettable_controller), board_id);
}
static int ctlr_is_resettable(u32 board_id)
@@ -493,6 +576,12 @@ static int ctlr_is_resettable(u32 board_id)
ctlr_is_soft_resettable(board_id);
}
+static int ctlr_needs_abort_tags_swizzled(u32 board_id)
+{
+ return board_id_in_array(needs_abort_tags_swizzled,
+ ARRAY_SIZE(needs_abort_tags_swizzled), board_id);
+}
+
static ssize_t host_show_resettable(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -647,12 +736,15 @@ static DEVICE_ATTR(transport_mode, S_IRUGO,
host_show_transport_mode, NULL);
static DEVICE_ATTR(resettable, S_IRUGO,
host_show_resettable, NULL);
+static DEVICE_ATTR(lockup_detected, S_IRUGO,
+ host_show_lockup_detected, NULL);
static struct device_attribute *hpsa_sdev_attrs[] = {
&dev_attr_raid_level,
&dev_attr_lunid,
&dev_attr_unique_id,
&dev_attr_hp_ssd_smart_path_enabled,
+ &dev_attr_lockup_detected,
NULL,
};
@@ -667,6 +759,9 @@ static struct device_attribute *hpsa_shost_attrs[] = {
NULL,
};
+#define HPSA_NRESERVED_CMDS (HPSA_CMDS_RESERVED_FOR_ABORTS + \
+ HPSA_CMDS_RESERVED_FOR_DRIVER + HPSA_MAX_CONCURRENT_PASSTHRUS)
+
static struct scsi_host_template hpsa_driver_template = {
.module = THIS_MODULE,
.name = HPSA,
@@ -681,6 +776,7 @@ static struct scsi_host_template hpsa_driver_template = {
.eh_device_reset_handler = hpsa_eh_device_reset_handler,
.ioctl = hpsa_ioctl,
.slave_alloc = hpsa_slave_alloc,
+ .slave_configure = hpsa_slave_configure,
.slave_destroy = hpsa_slave_destroy,
#ifdef CONFIG_COMPAT
.compat_ioctl = hpsa_compat_ioctl,
@@ -743,30 +839,43 @@ static inline u32 next_command(struct ctlr_info *h, u8 q)
* a separate special register for submitting commands.
*/
-/* set_performant_mode: Modify the tag for cciss performant
+/*
+ * set_performant_mode: Modify the tag for cciss performant
* set bit 0 for pull model, bits 3-1 for block fetch
* register number
*/
-static void set_performant_mode(struct ctlr_info *h, struct CommandList *c)
+#define DEFAULT_REPLY_QUEUE (-1)
+static void set_performant_mode(struct ctlr_info *h, struct CommandList *c,
+ int reply_queue)
{
if (likely(h->transMethod & CFGTBL_Trans_Performant)) {
c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
- if (likely(h->msix_vector > 0))
+ if (unlikely(!h->msix_vector))
+ return;
+ if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
c->Header.ReplyQueue =
raw_smp_processor_id() % h->nreply_queues;
+ else
+ c->Header.ReplyQueue = reply_queue % h->nreply_queues;
}
}
static void set_ioaccel1_performant_mode(struct ctlr_info *h,
- struct CommandList *c)
+ struct CommandList *c,
+ int reply_queue)
{
struct io_accel1_cmd *cp = &h->ioaccel_cmd_pool[c->cmdindex];
- /* Tell the controller to post the reply to the queue for this
+ /*
+ * Tell the controller to post the reply to the queue for this
* processor. This seems to give the best I/O throughput.
*/
- cp->ReplyQueue = smp_processor_id() % h->nreply_queues;
- /* Set the bits in the address sent down to include:
+ if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
+ cp->ReplyQueue = smp_processor_id() % h->nreply_queues;
+ else
+ cp->ReplyQueue = reply_queue % h->nreply_queues;
+ /*
+ * Set the bits in the address sent down to include:
* - performant mode bit (bit 0)
* - pull count (bits 1-3)
* - command type (bits 4-6)
@@ -775,20 +884,48 @@ static void set_ioaccel1_performant_mode(struct ctlr_info *h,
IOACCEL1_BUSADDR_CMDTYPE;
}
-static void set_ioaccel2_performant_mode(struct ctlr_info *h,
- struct CommandList *c)
+static void set_ioaccel2_tmf_performant_mode(struct ctlr_info *h,
+ struct CommandList *c,
+ int reply_queue)
{
- struct io_accel2_cmd *cp = &h->ioaccel2_cmd_pool[c->cmdindex];
+ struct hpsa_tmf_struct *cp = (struct hpsa_tmf_struct *)
+ &h->ioaccel2_cmd_pool[c->cmdindex];
/* Tell the controller to post the reply to the queue for this
* processor. This seems to give the best I/O throughput.
*/
- cp->reply_queue = smp_processor_id() % h->nreply_queues;
+ if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
+ cp->reply_queue = smp_processor_id() % h->nreply_queues;
+ else
+ cp->reply_queue = reply_queue % h->nreply_queues;
/* Set the bits in the address sent down to include:
* - performant mode bit not used in ioaccel mode 2
* - pull count (bits 0-3)
* - command type isn't needed for ioaccel2
*/
+ c->busaddr |= h->ioaccel2_blockFetchTable[0];
+}
+
+static void set_ioaccel2_performant_mode(struct ctlr_info *h,
+ struct CommandList *c,
+ int reply_queue)
+{
+ struct io_accel2_cmd *cp = &h->ioaccel2_cmd_pool[c->cmdindex];
+
+ /*
+ * Tell the controller to post the reply to the queue for this
+ * processor. This seems to give the best I/O throughput.
+ */
+ if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
+ cp->reply_queue = smp_processor_id() % h->nreply_queues;
+ else
+ cp->reply_queue = reply_queue % h->nreply_queues;
+ /*
+ * Set the bits in the address sent down to include:
+ * - performant mode bit not used in ioaccel mode 2
+ * - pull count (bits 0-3)
+ * - command type isn't needed for ioaccel2
+ */
c->busaddr |= (h->ioaccel2_blockFetchTable[cp->sg_count]);
}
@@ -821,26 +958,38 @@ static void dial_up_lockup_detection_on_fw_flash_complete(struct ctlr_info *h,
h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL;
}
-static void enqueue_cmd_and_start_io(struct ctlr_info *h,
- struct CommandList *c)
+static void __enqueue_cmd_and_start_io(struct ctlr_info *h,
+ struct CommandList *c, int reply_queue)
{
dial_down_lockup_detection_during_fw_flash(h, c);
atomic_inc(&h->commands_outstanding);
switch (c->cmd_type) {
case CMD_IOACCEL1:
- set_ioaccel1_performant_mode(h, c);
+ set_ioaccel1_performant_mode(h, c, reply_queue);
writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
break;
case CMD_IOACCEL2:
- set_ioaccel2_performant_mode(h, c);
+ set_ioaccel2_performant_mode(h, c, reply_queue);
+ writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32);
+ break;
+ case IOACCEL2_TMF:
+ set_ioaccel2_tmf_performant_mode(h, c, reply_queue);
writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32);
break;
default:
- set_performant_mode(h, c);
+ set_performant_mode(h, c, reply_queue);
h->access.submit_command(h, c);
}
}
+static void enqueue_cmd_and_start_io(struct ctlr_info *h, struct CommandList *c)
+{
+ if (unlikely(hpsa_is_pending_event(c)))
+ return finish_cmd(c);
+
+ __enqueue_cmd_and_start_io(h, c, DEFAULT_REPLY_QUEUE);
+}
+
static inline int is_hba_lunid(unsigned char scsi3addr[])
{
return memcmp(scsi3addr, RAID_CTLR_LUNID, 8) == 0;
@@ -881,6 +1030,23 @@ static int hpsa_find_target_lun(struct ctlr_info *h,
return !found;
}
+static inline void hpsa_show_dev_msg(const char *level, struct ctlr_info *h,
+ struct hpsa_scsi_dev_t *dev, char *description)
+{
+ dev_printk(level, &h->pdev->dev,
+ "scsi %d:%d:%d:%d: %s %s %.8s %.16s RAID-%s SSDSmartPathCap%c En%c Exp=%d\n",
+ h->scsi_host->host_no, dev->bus, dev->target, dev->lun,
+ description,
+ scsi_device_type(dev->devtype),
+ dev->vendor,
+ dev->model,
+ dev->raid_level > RAID_UNKNOWN ?
+ "RAID-?" : raid_label[dev->raid_level],
+ dev->offload_config ? '+' : '-',
+ dev->offload_enabled ? '+' : '-',
+ dev->expose_state);
+}
+
/* Add an entry into h->dev[] array. */
static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno,
struct hpsa_scsi_dev_t *device,
@@ -948,15 +1114,10 @@ lun_assigned:
h->ndevices++;
added[*nadded] = device;
(*nadded)++;
-
- /* initially, (before registering with scsi layer) we don't
- * know our hostno and we don't want to print anything first
- * time anyway (the scsi layer's inquiries will show that info)
- */
- /* if (hostno != -1) */
- dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d added.\n",
- scsi_device_type(device->devtype), hostno,
- device->bus, device->target, device->lun);
+ hpsa_show_dev_msg(KERN_INFO, h, device,
+ device->expose_state & HPSA_SCSI_ADD ? "added" : "masked");
+ device->offload_to_be_enabled = device->offload_enabled;
+ device->offload_enabled = 0;
return 0;
}
@@ -964,6 +1125,7 @@ lun_assigned:
static void hpsa_scsi_update_entry(struct ctlr_info *h, int hostno,
int entry, struct hpsa_scsi_dev_t *new_entry)
{
+ int offload_enabled;
/* assumes h->devlock is held */
BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES);
@@ -982,16 +1144,29 @@ static void hpsa_scsi_update_entry(struct ctlr_info *h, int hostno,
*/
h->dev[entry]->raid_map = new_entry->raid_map;
h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle;
- wmb(); /* ensure raid map updated prior to ->offload_enabled */
}
+ if (new_entry->hba_ioaccel_enabled) {
+ h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle;
+ wmb(); /* set ioaccel_handle *before* hba_ioaccel_enabled */
+ }
+ h->dev[entry]->hba_ioaccel_enabled = new_entry->hba_ioaccel_enabled;
h->dev[entry]->offload_config = new_entry->offload_config;
h->dev[entry]->offload_to_mirror = new_entry->offload_to_mirror;
- h->dev[entry]->offload_enabled = new_entry->offload_enabled;
h->dev[entry]->queue_depth = new_entry->queue_depth;
- dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d updated.\n",
- scsi_device_type(new_entry->devtype), hostno, new_entry->bus,
- new_entry->target, new_entry->lun);
+ /*
+ * We can turn off ioaccel offload now, but need to delay turning
+ * it on until we can update h->dev[entry]->phys_disk[], but we
+ * can't do that until all the devices are updated.
+ */
+ h->dev[entry]->offload_to_be_enabled = new_entry->offload_enabled;
+ if (!new_entry->offload_enabled)
+ h->dev[entry]->offload_enabled = 0;
+
+ offload_enabled = h->dev[entry]->offload_enabled;
+ h->dev[entry]->offload_enabled = h->dev[entry]->offload_to_be_enabled;
+ hpsa_show_dev_msg(KERN_INFO, h, h->dev[entry], "updated");
+ h->dev[entry]->offload_enabled = offload_enabled;
}
/* Replace an entry from h->dev[] array. */
@@ -1017,9 +1192,9 @@ static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno,
h->dev[entry] = new_entry;
added[*nadded] = new_entry;
(*nadded)++;
- dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d changed.\n",
- scsi_device_type(new_entry->devtype), hostno, new_entry->bus,
- new_entry->target, new_entry->lun);
+ hpsa_show_dev_msg(KERN_INFO, h, new_entry, "replaced");
+ new_entry->offload_to_be_enabled = new_entry->offload_enabled;
+ new_entry->offload_enabled = 0;
}
/* Remove an entry from h->dev[] array. */
@@ -1039,9 +1214,7 @@ static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry,
for (i = entry; i < h->ndevices-1; i++)
h->dev[i] = h->dev[i+1];
h->ndevices--;
- dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d removed.\n",
- scsi_device_type(sd->devtype), hostno, sd->bus, sd->target,
- sd->lun);
+ hpsa_show_dev_msg(KERN_INFO, h, sd, "removed");
}
#define SCSI3ADDR_EQ(a, b) ( \
@@ -1283,6 +1456,8 @@ static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h,
if (nraid_map_entries > RAID_MAP_MAX_ENTRIES)
nraid_map_entries = RAID_MAP_MAX_ENTRIES;
+ logical_drive->nphysical_disks = nraid_map_entries;
+
qdepth = 0;
for (i = 0; i < nraid_map_entries; i++) {
logical_drive->phys_disk[i] = NULL;
@@ -1312,7 +1487,8 @@ static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h,
*/
if (!logical_drive->phys_disk[i]) {
logical_drive->offload_enabled = 0;
- logical_drive->queue_depth = h->nr_cmds;
+ logical_drive->offload_to_be_enabled = 0;
+ logical_drive->queue_depth = 8;
}
}
if (nraid_map_entries)
@@ -1335,6 +1511,16 @@ static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h,
continue;
if (!is_logical_dev_addr_mode(dev[i]->scsi3addr))
continue;
+
+ /*
+ * If offload is currently enabled, the RAID map and
+ * phys_disk[] assignment *better* not be changing
+ * and since it isn't changing, we do not need to
+ * update it.
+ */
+ if (dev[i]->offload_enabled)
+ continue;
+
hpsa_figure_phys_disk_ptrs(h, dev, ndevices, dev[i]);
}
}
@@ -1411,9 +1597,7 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
*/
if (sd[i]->volume_offline) {
hpsa_show_volume_status(h, sd[i]);
- dev_info(&h->pdev->dev, "c%db%dt%dl%d: temporarily offline\n",
- h->scsi_host->host_no,
- sd[i]->bus, sd[i]->target, sd[i]->lun);
+ hpsa_show_dev_msg(KERN_INFO, h, sd[i], "offline");
continue;
}
@@ -1433,6 +1617,14 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
/* but if it does happen, we just ignore that device */
}
}
+ hpsa_update_log_drive_phys_drive_ptrs(h, h->dev, h->ndevices);
+
+ /* Now that h->dev[]->phys_disk[] is coherent, we can enable
+ * any logical drives that need it enabled.
+ */
+ for (i = 0; i < h->ndevices; i++)
+ h->dev[i]->offload_enabled = h->dev[i]->offload_to_be_enabled;
+
spin_unlock_irqrestore(&h->devlock, flags);
/* Monitor devices which are in one of several NOT READY states to be
@@ -1456,20 +1648,22 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
sh = h->scsi_host;
/* Notify scsi mid layer of any removed devices */
for (i = 0; i < nremoved; i++) {
- struct scsi_device *sdev =
- scsi_device_lookup(sh, removed[i]->bus,
- removed[i]->target, removed[i]->lun);
- if (sdev != NULL) {
- scsi_remove_device(sdev);
- scsi_device_put(sdev);
- } else {
- /* We don't expect to get here.
- * future cmds to this device will get selection
- * timeout as if the device was gone.
- */
- dev_warn(&h->pdev->dev, "didn't find c%db%dt%dl%d "
- " for removal.", hostno, removed[i]->bus,
- removed[i]->target, removed[i]->lun);
+ if (removed[i]->expose_state & HPSA_SCSI_ADD) {
+ struct scsi_device *sdev =
+ scsi_device_lookup(sh, removed[i]->bus,
+ removed[i]->target, removed[i]->lun);
+ if (sdev != NULL) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ } else {
+ /*
+ * We don't expect to get here.
+ * future cmds to this device will get selection
+ * timeout as if the device was gone.
+ */
+ hpsa_show_dev_msg(KERN_WARNING, h, removed[i],
+ "didn't find device for removal.");
+ }
}
kfree(removed[i]);
removed[i] = NULL;
@@ -1477,16 +1671,18 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
/* Notify scsi mid layer of any added devices */
for (i = 0; i < nadded; i++) {
+ if (!(added[i]->expose_state & HPSA_SCSI_ADD))
+ continue;
if (scsi_add_device(sh, added[i]->bus,
added[i]->target, added[i]->lun) == 0)
continue;
- dev_warn(&h->pdev->dev, "scsi_add_device c%db%dt%dl%d failed, "
- "device not added.\n", hostno, added[i]->bus,
- added[i]->target, added[i]->lun);
+ hpsa_show_dev_msg(KERN_WARNING, h, added[i],
+ "addition failed, device not added.");
/* now we have to remove it from h->dev,
* since it didn't get added to scsi mid layer
*/
fixup_botched_add(h, added[i]);
+ added[i] = NULL;
}
free_and_out:
@@ -1512,7 +1708,6 @@ static struct hpsa_scsi_dev_t *lookup_hpsa_scsi_dev(struct ctlr_info *h,
return NULL;
}
-/* link sdev->hostdata to our per-device structure. */
static int hpsa_slave_alloc(struct scsi_device *sdev)
{
struct hpsa_scsi_dev_t *sd;
@@ -1523,21 +1718,80 @@ static int hpsa_slave_alloc(struct scsi_device *sdev)
spin_lock_irqsave(&h->devlock, flags);
sd = lookup_hpsa_scsi_dev(h, sdev_channel(sdev),
sdev_id(sdev), sdev->lun);
- if (sd != NULL) {
- sdev->hostdata = sd;
- if (sd->queue_depth)
- scsi_change_queue_depth(sdev, sd->queue_depth);
+ if (likely(sd)) {
atomic_set(&sd->ioaccel_cmds_out, 0);
- }
+ sdev->hostdata = (sd->expose_state & HPSA_SCSI_ADD) ? sd : NULL;
+ } else
+ sdev->hostdata = NULL;
spin_unlock_irqrestore(&h->devlock, flags);
return 0;
}
+/* configure scsi device based on internal per-device structure */
+static int hpsa_slave_configure(struct scsi_device *sdev)
+{
+ struct hpsa_scsi_dev_t *sd;
+ int queue_depth;
+
+ sd = sdev->hostdata;
+ sdev->no_uld_attach = !sd || !(sd->expose_state & HPSA_ULD_ATTACH);
+
+ if (sd)
+ queue_depth = sd->queue_depth != 0 ?
+ sd->queue_depth : sdev->host->can_queue;
+ else
+ queue_depth = sdev->host->can_queue;
+
+ scsi_change_queue_depth(sdev, queue_depth);
+
+ return 0;
+}
+
static void hpsa_slave_destroy(struct scsi_device *sdev)
{
/* nothing to do. */
}
+static void hpsa_free_ioaccel2_sg_chain_blocks(struct ctlr_info *h)
+{
+ int i;
+
+ if (!h->ioaccel2_cmd_sg_list)
+ return;
+ for (i = 0; i < h->nr_cmds; i++) {
+ kfree(h->ioaccel2_cmd_sg_list[i]);
+ h->ioaccel2_cmd_sg_list[i] = NULL;
+ }
+ kfree(h->ioaccel2_cmd_sg_list);
+ h->ioaccel2_cmd_sg_list = NULL;
+}
+
+static int hpsa_allocate_ioaccel2_sg_chain_blocks(struct ctlr_info *h)
+{
+ int i;
+
+ if (h->chainsize <= 0)
+ return 0;
+
+ h->ioaccel2_cmd_sg_list =
+ kzalloc(sizeof(*h->ioaccel2_cmd_sg_list) * h->nr_cmds,
+ GFP_KERNEL);
+ if (!h->ioaccel2_cmd_sg_list)
+ return -ENOMEM;
+ for (i = 0; i < h->nr_cmds; i++) {
+ h->ioaccel2_cmd_sg_list[i] =
+ kmalloc(sizeof(*h->ioaccel2_cmd_sg_list[i]) *
+ h->maxsgentries, GFP_KERNEL);
+ if (!h->ioaccel2_cmd_sg_list[i])
+ goto clean;
+ }
+ return 0;
+
+clean:
+ hpsa_free_ioaccel2_sg_chain_blocks(h);
+ return -ENOMEM;
+}
+
static void hpsa_free_sg_chain_blocks(struct ctlr_info *h)
{
int i;
@@ -1552,7 +1806,7 @@ static void hpsa_free_sg_chain_blocks(struct ctlr_info *h)
h->cmd_sg_list = NULL;
}
-static int hpsa_allocate_sg_chain_blocks(struct ctlr_info *h)
+static int hpsa_alloc_sg_chain_blocks(struct ctlr_info *h)
{
int i;
@@ -1580,6 +1834,39 @@ clean:
return -ENOMEM;
}
+static int hpsa_map_ioaccel2_sg_chain_block(struct ctlr_info *h,
+ struct io_accel2_cmd *cp, struct CommandList *c)
+{
+ struct ioaccel2_sg_element *chain_block;
+ u64 temp64;
+ u32 chain_size;
+
+ chain_block = h->ioaccel2_cmd_sg_list[c->cmdindex];
+ chain_size = le32_to_cpu(cp->data_len);
+ temp64 = pci_map_single(h->pdev, chain_block, chain_size,
+ PCI_DMA_TODEVICE);
+ if (dma_mapping_error(&h->pdev->dev, temp64)) {
+ /* prevent subsequent unmapping */
+ cp->sg->address = 0;
+ return -1;
+ }
+ cp->sg->address = cpu_to_le64(temp64);
+ return 0;
+}
+
+static void hpsa_unmap_ioaccel2_sg_chain_block(struct ctlr_info *h,
+ struct io_accel2_cmd *cp)
+{
+ struct ioaccel2_sg_element *chain_sg;
+ u64 temp64;
+ u32 chain_size;
+
+ chain_sg = cp->sg;
+ temp64 = le64_to_cpu(chain_sg->address);
+ chain_size = le32_to_cpu(cp->data_len);
+ pci_unmap_single(h->pdev, temp64, chain_size, PCI_DMA_TODEVICE);
+}
+
static int hpsa_map_sg_chain_block(struct ctlr_info *h,
struct CommandList *c)
{
@@ -1629,6 +1916,7 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h,
{
int data_len;
int retry = 0;
+ u32 ioaccel2_resid = 0;
switch (c2->error_data.serv_response) {
case IOACCEL2_SERV_RESPONSE_COMPLETE:
@@ -1636,9 +1924,6 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h,
case IOACCEL2_STATUS_SR_TASK_COMP_GOOD:
break;
case IOACCEL2_STATUS_SR_TASK_COMP_CHK_COND:
- dev_warn(&h->pdev->dev,
- "%s: task complete with check condition.\n",
- "HP SSD Smart Path");
cmd->result |= SAM_STAT_CHECK_CONDITION;
if (c2->error_data.data_present !=
IOACCEL2_SENSE_DATA_PRESENT) {
@@ -1658,58 +1943,56 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h,
retry = 1;
break;
case IOACCEL2_STATUS_SR_TASK_COMP_BUSY:
- dev_warn(&h->pdev->dev,
- "%s: task complete with BUSY status.\n",
- "HP SSD Smart Path");
retry = 1;
break;
case IOACCEL2_STATUS_SR_TASK_COMP_RES_CON:
- dev_warn(&h->pdev->dev,
- "%s: task complete with reservation conflict.\n",
- "HP SSD Smart Path");
retry = 1;
break;
case IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL:
- /* Make scsi midlayer do unlimited retries */
- cmd->result = DID_IMM_RETRY << 16;
+ retry = 1;
break;
case IOACCEL2_STATUS_SR_TASK_COMP_ABORTED:
- dev_warn(&h->pdev->dev,
- "%s: task complete with aborted status.\n",
- "HP SSD Smart Path");
retry = 1;
break;
default:
- dev_warn(&h->pdev->dev,
- "%s: task complete with unrecognized status: 0x%02x\n",
- "HP SSD Smart Path", c2->error_data.status);
retry = 1;
break;
}
break;
case IOACCEL2_SERV_RESPONSE_FAILURE:
- /* don't expect to get here. */
- dev_warn(&h->pdev->dev,
- "unexpected delivery or target failure, status = 0x%02x\n",
- c2->error_data.status);
- retry = 1;
+ switch (c2->error_data.status) {
+ case IOACCEL2_STATUS_SR_IO_ERROR:
+ case IOACCEL2_STATUS_SR_IO_ABORTED:
+ case IOACCEL2_STATUS_SR_OVERRUN:
+ retry = 1;
+ break;
+ case IOACCEL2_STATUS_SR_UNDERRUN:
+ cmd->result = (DID_OK << 16); /* host byte */
+ cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */
+ ioaccel2_resid = get_unaligned_le32(
+ &c2->error_data.resid_cnt[0]);
+ scsi_set_resid(cmd, ioaccel2_resid);
+ break;
+ case IOACCEL2_STATUS_SR_NO_PATH_TO_DEVICE:
+ case IOACCEL2_STATUS_SR_INVALID_DEVICE:
+ case IOACCEL2_STATUS_SR_IOACCEL_DISABLED:
+ /* We will get an event from ctlr to trigger rescan */
+ retry = 1;
+ break;
+ default:
+ retry = 1;
+ }
break;
case IOACCEL2_SERV_RESPONSE_TMF_COMPLETE:
break;
case IOACCEL2_SERV_RESPONSE_TMF_SUCCESS:
break;
case IOACCEL2_SERV_RESPONSE_TMF_REJECTED:
- dev_warn(&h->pdev->dev, "task management function rejected.\n");
retry = 1;
break;
case IOACCEL2_SERV_RESPONSE_TMF_WRONG_LUN:
- dev_warn(&h->pdev->dev, "task management function invalid LUN\n");
break;
default:
- dev_warn(&h->pdev->dev,
- "%s: Unrecognized server response: 0x%02x\n",
- "HP SSD Smart Path",
- c2->error_data.serv_response);
retry = 1;
break;
}
@@ -1717,6 +2000,87 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h,
return retry; /* retry on raid path? */
}
+static void hpsa_cmd_resolve_events(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ bool do_wake = false;
+
+ /*
+ * Prevent the following race in the abort handler:
+ *
+ * 1. LLD is requested to abort a SCSI command
+ * 2. The SCSI command completes
+ * 3. The struct CommandList associated with step 2 is made available
+ * 4. New I/O request to LLD to another LUN re-uses struct CommandList
+ * 5. Abort handler follows scsi_cmnd->host_scribble and
+ * finds struct CommandList and tries to aborts it
+ * Now we have aborted the wrong command.
+ *
+ * Reset c->scsi_cmd here so that the abort or reset handler will know
+ * this command has completed. Then, check to see if the handler is
+ * waiting for this command, and, if so, wake it.
+ */
+ c->scsi_cmd = SCSI_CMD_IDLE;
+ mb(); /* Declare command idle before checking for pending events. */
+ if (c->abort_pending) {
+ do_wake = true;
+ c->abort_pending = false;
+ }
+ if (c->reset_pending) {
+ unsigned long flags;
+ struct hpsa_scsi_dev_t *dev;
+
+ /*
+ * There appears to be a reset pending; lock the lock and
+ * reconfirm. If so, then decrement the count of outstanding
+ * commands and wake the reset command if this is the last one.
+ */
+ spin_lock_irqsave(&h->lock, flags);
+ dev = c->reset_pending; /* Re-fetch under the lock. */
+ if (dev && atomic_dec_and_test(&dev->reset_cmds_out))
+ do_wake = true;
+ c->reset_pending = NULL;
+ spin_unlock_irqrestore(&h->lock, flags);
+ }
+
+ if (do_wake)
+ wake_up_all(&h->event_sync_wait_queue);
+}
+
+static void hpsa_cmd_resolve_and_free(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ hpsa_cmd_resolve_events(h, c);
+ cmd_tagged_free(h, c);
+}
+
+static void hpsa_cmd_free_and_done(struct ctlr_info *h,
+ struct CommandList *c, struct scsi_cmnd *cmd)
+{
+ hpsa_cmd_resolve_and_free(h, c);
+ cmd->scsi_done(cmd);
+}
+
+static void hpsa_retry_cmd(struct ctlr_info *h, struct CommandList *c)
+{
+ INIT_WORK(&c->work, hpsa_command_resubmit_worker);
+ queue_work_on(raw_smp_processor_id(), h->resubmit_wq, &c->work);
+}
+
+static void hpsa_set_scsi_cmd_aborted(struct scsi_cmnd *cmd)
+{
+ cmd->result = DID_ABORT << 16;
+}
+
+static void hpsa_cmd_abort_and_free(struct ctlr_info *h, struct CommandList *c,
+ struct scsi_cmnd *cmd)
+{
+ hpsa_set_scsi_cmd_aborted(cmd);
+ dev_warn(&h->pdev->dev, "CDB %16phN was aborted with status 0x%x\n",
+ c->Request.CDB, c->err_info->ScsiStatus);
+ hpsa_cmd_resolve_and_free(h, c);
+}
+
static void process_ioaccel2_completion(struct ctlr_info *h,
struct CommandList *c, struct scsi_cmnd *cmd,
struct hpsa_scsi_dev_t *dev)
@@ -1725,13 +2089,11 @@ static void process_ioaccel2_completion(struct ctlr_info *h,
/* check for good status */
if (likely(c2->error_data.serv_response == 0 &&
- c2->error_data.status == 0)) {
- cmd_free(h, c);
- cmd->scsi_done(cmd);
- return;
- }
+ c2->error_data.status == 0))
+ return hpsa_cmd_free_and_done(h, c, cmd);
- /* Any RAID offload error results in retry which will use
+ /*
+ * Any RAID offload error results in retry which will use
* the normal I/O path so the controller can handle whatever's
* wrong.
*/
@@ -1741,19 +2103,42 @@ static void process_ioaccel2_completion(struct ctlr_info *h,
if (c2->error_data.status ==
IOACCEL2_STATUS_SR_IOACCEL_DISABLED)
dev->offload_enabled = 0;
- goto retry_cmd;
+
+ return hpsa_retry_cmd(h, c);
}
if (handle_ioaccel_mode2_error(h, c, cmd, c2))
- goto retry_cmd;
+ return hpsa_retry_cmd(h, c);
- cmd_free(h, c);
- cmd->scsi_done(cmd);
- return;
+ return hpsa_cmd_free_and_done(h, c, cmd);
+}
-retry_cmd:
- INIT_WORK(&c->work, hpsa_command_resubmit_worker);
- queue_work_on(raw_smp_processor_id(), h->resubmit_wq, &c->work);
+/* Returns 0 on success, < 0 otherwise. */
+static int hpsa_evaluate_tmf_status(struct ctlr_info *h,
+ struct CommandList *cp)
+{
+ u8 tmf_status = cp->err_info->ScsiStatus;
+
+ switch (tmf_status) {
+ case CISS_TMF_COMPLETE:
+ /*
+ * CISS_TMF_COMPLETE never happens, instead,
+ * ei->CommandStatus == 0 for this case.
+ */
+ case CISS_TMF_SUCCESS:
+ return 0;
+ case CISS_TMF_INVALID_FRAME:
+ case CISS_TMF_NOT_SUPPORTED:
+ case CISS_TMF_FAILED:
+ case CISS_TMF_WRONG_LUN:
+ case CISS_TMF_OVERLAPPED_TAG:
+ break;
+ default:
+ dev_warn(&h->pdev->dev, "Unknown TMF status: 0x%02x\n",
+ tmf_status);
+ break;
+ }
+ return -tmf_status;
}
static void complete_scsi_command(struct CommandList *cp)
@@ -1762,51 +2147,58 @@ static void complete_scsi_command(struct CommandList *cp)
struct ctlr_info *h;
struct ErrorInfo *ei;
struct hpsa_scsi_dev_t *dev;
+ struct io_accel2_cmd *c2;
- unsigned char sense_key;
- unsigned char asc; /* additional sense code */
- unsigned char ascq; /* additional sense code qualifier */
+ u8 sense_key;
+ u8 asc; /* additional sense code */
+ u8 ascq; /* additional sense code qualifier */
unsigned long sense_data_size;
ei = cp->err_info;
cmd = cp->scsi_cmd;
h = cp->h;
dev = cmd->device->hostdata;
+ c2 = &h->ioaccel2_cmd_pool[cp->cmdindex];
scsi_dma_unmap(cmd); /* undo the DMA mappings */
if ((cp->cmd_type == CMD_SCSI) &&
(le16_to_cpu(cp->Header.SGTotal) > h->max_cmd_sg_entries))
hpsa_unmap_sg_chain_block(h, cp);
+ if ((cp->cmd_type == CMD_IOACCEL2) &&
+ (c2->sg[0].chain_indicator == IOACCEL2_CHAIN))
+ hpsa_unmap_ioaccel2_sg_chain_block(h, c2);
+
cmd->result = (DID_OK << 16); /* host byte */
cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */
if (cp->cmd_type == CMD_IOACCEL2 || cp->cmd_type == CMD_IOACCEL1)
atomic_dec(&cp->phys_disk->ioaccel_cmds_out);
- if (cp->cmd_type == CMD_IOACCEL2)
- return process_ioaccel2_completion(h, cp, cmd, dev);
-
- cmd->result |= ei->ScsiStatus;
+ /*
+ * We check for lockup status here as it may be set for
+ * CMD_SCSI, CMD_IOACCEL1 and CMD_IOACCEL2 commands by
+ * fail_all_oustanding_cmds()
+ */
+ if (unlikely(ei->CommandStatus == CMD_CTLR_LOCKUP)) {
+ /* DID_NO_CONNECT will prevent a retry */
+ cmd->result = DID_NO_CONNECT << 16;
+ return hpsa_cmd_free_and_done(h, cp, cmd);
+ }
- scsi_set_resid(cmd, ei->ResidualCnt);
- if (ei->CommandStatus == 0) {
- if (cp->cmd_type == CMD_IOACCEL1)
- atomic_dec(&cp->phys_disk->ioaccel_cmds_out);
- cmd_free(h, cp);
- cmd->scsi_done(cmd);
- return;
+ if ((unlikely(hpsa_is_pending_event(cp)))) {
+ if (cp->reset_pending)
+ return hpsa_cmd_resolve_and_free(h, cp);
+ if (cp->abort_pending)
+ return hpsa_cmd_abort_and_free(h, cp, cmd);
}
- /* copy the sense data */
- if (SCSI_SENSE_BUFFERSIZE < sizeof(ei->SenseInfo))
- sense_data_size = SCSI_SENSE_BUFFERSIZE;
- else
- sense_data_size = sizeof(ei->SenseInfo);
- if (ei->SenseLen < sense_data_size)
- sense_data_size = ei->SenseLen;
+ if (cp->cmd_type == CMD_IOACCEL2)
+ return process_ioaccel2_completion(h, cp, cmd, dev);
- memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size);
+ scsi_set_resid(cmd, ei->ResidualCnt);
+ if (ei->CommandStatus == 0)
+ return hpsa_cmd_free_and_done(h, cp, cmd);
/* For I/O accelerator commands, copy over some fields to the normal
* CISS header used below for error handling.
@@ -1828,10 +2220,7 @@ static void complete_scsi_command(struct CommandList *cp)
if (is_logical_dev_addr_mode(dev->scsi3addr)) {
if (ei->CommandStatus == CMD_IOACCEL_DISABLED)
dev->offload_enabled = 0;
- INIT_WORK(&cp->work, hpsa_command_resubmit_worker);
- queue_work_on(raw_smp_processor_id(),
- h->resubmit_wq, &cp->work);
- return;
+ return hpsa_retry_cmd(h, cp);
}
}
@@ -1839,14 +2228,18 @@ static void complete_scsi_command(struct CommandList *cp)
switch (ei->CommandStatus) {
case CMD_TARGET_STATUS:
- if (ei->ScsiStatus) {
- /* Get sense key */
- sense_key = 0xf & ei->SenseInfo[2];
- /* Get additional sense code */
- asc = ei->SenseInfo[12];
- /* Get addition sense code qualifier */
- ascq = ei->SenseInfo[13];
- }
+ cmd->result |= ei->ScsiStatus;
+ /* copy the sense data */
+ if (SCSI_SENSE_BUFFERSIZE < sizeof(ei->SenseInfo))
+ sense_data_size = SCSI_SENSE_BUFFERSIZE;
+ else
+ sense_data_size = sizeof(ei->SenseInfo);
+ if (ei->SenseLen < sense_data_size)
+ sense_data_size = ei->SenseLen;
+ memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size);
+ if (ei->ScsiStatus)
+ decode_sense_data(ei->SenseInfo, sense_data_size,
+ &sense_key, &asc, &ascq);
if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION) {
if (sense_key == ABORTED_COMMAND) {
cmd->result |= DID_SOFT_ERROR << 16;
@@ -1918,10 +2311,8 @@ static void complete_scsi_command(struct CommandList *cp)
cp->Request.CDB);
break;
case CMD_ABORTED:
- cmd->result = DID_ABORT << 16;
- dev_warn(&h->pdev->dev, "CDB %16phN was aborted with status 0x%x\n",
- cp->Request.CDB, ei->ScsiStatus);
- break;
+ /* Return now to avoid calling scsi_done(). */
+ return hpsa_cmd_abort_and_free(h, cp, cmd);
case CMD_ABORT_FAILED:
cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev, "CDB %16phN : abort failed\n",
@@ -1941,6 +2332,10 @@ static void complete_scsi_command(struct CommandList *cp)
cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev, "Command unabortable\n");
break;
+ case CMD_TMF_STATUS:
+ if (hpsa_evaluate_tmf_status(h, cp)) /* TMF failed? */
+ cmd->result = DID_ERROR << 16;
+ break;
case CMD_IOACCEL_DISABLED:
/* This only handles the direct pass-through case since RAID
* offload is handled above. Just attempt a retry.
@@ -1954,8 +2349,8 @@ static void complete_scsi_command(struct CommandList *cp)
dev_warn(&h->pdev->dev, "cp %p returned unknown status %x\n",
cp, ei->CommandStatus);
}
- cmd_free(h, cp);
- cmd->scsi_done(cmd);
+
+ return hpsa_cmd_free_and_done(h, cp, cmd);
}
static void hpsa_pci_unmap(struct pci_dev *pdev,
@@ -1998,14 +2393,36 @@ static int hpsa_map_one(struct pci_dev *pdev,
return 0;
}
-static inline void hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h,
- struct CommandList *c)
+#define NO_TIMEOUT ((unsigned long) -1)
+#define DEFAULT_TIMEOUT 30000 /* milliseconds */
+static int hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h,
+ struct CommandList *c, int reply_queue, unsigned long timeout_msecs)
{
DECLARE_COMPLETION_ONSTACK(wait);
c->waiting = &wait;
- enqueue_cmd_and_start_io(h, c);
- wait_for_completion(&wait);
+ __enqueue_cmd_and_start_io(h, c, reply_queue);
+ if (timeout_msecs == NO_TIMEOUT) {
+ /* TODO: get rid of this no-timeout thing */
+ wait_for_completion_io(&wait);
+ return IO_OK;
+ }
+ if (!wait_for_completion_io_timeout(&wait,
+ msecs_to_jiffies(timeout_msecs))) {
+ dev_warn(&h->pdev->dev, "Command timed out.\n");
+ return -ETIMEDOUT;
+ }
+ return IO_OK;
+}
+
+static int hpsa_scsi_do_simple_cmd(struct ctlr_info *h, struct CommandList *c,
+ int reply_queue, unsigned long timeout_msecs)
+{
+ if (unlikely(lockup_detected(h))) {
+ c->err_info->CommandStatus = CMD_CTLR_LOCKUP;
+ return IO_OK;
+ }
+ return hpsa_scsi_do_simple_cmd_core(h, c, reply_queue, timeout_msecs);
}
static u32 lockup_detected(struct ctlr_info *h)
@@ -2020,25 +2437,19 @@ static u32 lockup_detected(struct ctlr_info *h)
return rc;
}
-static void hpsa_scsi_do_simple_cmd_core_if_no_lockup(struct ctlr_info *h,
- struct CommandList *c)
-{
- /* If controller lockup detected, fake a hardware error. */
- if (unlikely(lockup_detected(h)))
- c->err_info->CommandStatus = CMD_HARDWARE_ERR;
- else
- hpsa_scsi_do_simple_cmd_core(h, c);
-}
-
#define MAX_DRIVER_CMD_RETRIES 25
-static void hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h,
- struct CommandList *c, int data_direction)
+static int hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h,
+ struct CommandList *c, int data_direction, unsigned long timeout_msecs)
{
int backoff_time = 10, retry_count = 0;
+ int rc;
do {
memset(c->err_info, 0, sizeof(*c->err_info));
- hpsa_scsi_do_simple_cmd_core(h, c);
+ rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE,
+ timeout_msecs);
+ if (rc)
+ break;
retry_count++;
if (retry_count > 3) {
msleep(backoff_time);
@@ -2049,6 +2460,9 @@ static void hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h,
check_for_busy(h, c)) &&
retry_count <= MAX_DRIVER_CMD_RETRIES);
hpsa_pci_unmap(h->pdev, c, 1, data_direction);
+ if (retry_count > MAX_DRIVER_CMD_RETRIES)
+ rc = -EIO;
+ return rc;
}
static void hpsa_print_cmd(struct ctlr_info *h, char *txt,
@@ -2072,16 +2486,23 @@ static void hpsa_scsi_interpret_error(struct ctlr_info *h,
{
const struct ErrorInfo *ei = cp->err_info;
struct device *d = &cp->h->pdev->dev;
- const u8 *sd = ei->SenseInfo;
+ u8 sense_key, asc, ascq;
+ int sense_len;
switch (ei->CommandStatus) {
case CMD_TARGET_STATUS:
+ if (ei->SenseLen > sizeof(ei->SenseInfo))
+ sense_len = sizeof(ei->SenseInfo);
+ else
+ sense_len = ei->SenseLen;
+ decode_sense_data(ei->SenseInfo, sense_len,
+ &sense_key, &asc, &ascq);
hpsa_print_cmd(h, "SCSI status", cp);
if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION)
- dev_warn(d, "SCSI Status = 02, Sense key = %02x, ASC = %02x, ASCQ = %02x\n",
- sd[2] & 0x0f, sd[12], sd[13]);
+ dev_warn(d, "SCSI Status = 02, Sense key = 0x%02x, ASC = 0x%02x, ASCQ = 0x%02x\n",
+ sense_key, asc, ascq);
else
- dev_warn(d, "SCSI Status = %02x\n", ei->ScsiStatus);
+ dev_warn(d, "SCSI Status = 0x%02x\n", ei->ScsiStatus);
if (ei->ScsiStatus == 0)
dev_warn(d, "SCSI status is abnormally zero. "
"(probably indicates selection timeout "
@@ -2125,6 +2546,9 @@ static void hpsa_scsi_interpret_error(struct ctlr_info *h,
case CMD_UNABORTABLE:
hpsa_print_cmd(h, "unabortable", cp);
break;
+ case CMD_CTLR_LOCKUP:
+ hpsa_print_cmd(h, "controller lockup detected", cp);
+ break;
default:
hpsa_print_cmd(h, "unknown status", cp);
dev_warn(d, "Unknown command status %x\n",
@@ -2142,17 +2566,15 @@ static int hpsa_scsi_do_inquiry(struct ctlr_info *h, unsigned char *scsi3addr,
c = cmd_alloc(h);
- if (c == NULL) {
- dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
- return -ENOMEM;
- }
-
if (fill_cmd(c, HPSA_INQUIRY, h, buf, bufsize,
page, scsi3addr, TYPE_CMD)) {
rc = -1;
goto out;
}
- hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+ PCI_DMA_FROMDEVICE, NO_TIMEOUT);
+ if (rc)
+ goto out;
ei = c->err_info;
if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
hpsa_scsi_interpret_error(h, c);
@@ -2172,17 +2594,15 @@ static int hpsa_bmic_ctrl_mode_sense(struct ctlr_info *h,
struct ErrorInfo *ei;
c = cmd_alloc(h);
- if (c == NULL) { /* trouble... */
- dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
- return -ENOMEM;
- }
-
if (fill_cmd(c, BMIC_SENSE_CONTROLLER_PARAMETERS, h, buf, bufsize,
page, scsi3addr, TYPE_CMD)) {
rc = -1;
goto out;
}
- hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+ PCI_DMA_FROMDEVICE, NO_TIMEOUT);
+ if (rc)
+ goto out;
ei = c->err_info;
if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
hpsa_scsi_interpret_error(h, c);
@@ -2191,10 +2611,10 @@ static int hpsa_bmic_ctrl_mode_sense(struct ctlr_info *h,
out:
cmd_free(h, c);
return rc;
- }
+}
static int hpsa_send_reset(struct ctlr_info *h, unsigned char *scsi3addr,
- u8 reset_type)
+ u8 reset_type, int reply_queue)
{
int rc = IO_OK;
struct CommandList *c;
@@ -2202,16 +2622,16 @@ static int hpsa_send_reset(struct ctlr_info *h, unsigned char *scsi3addr,
c = cmd_alloc(h);
- if (c == NULL) { /* trouble... */
- dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
- return -ENOMEM;
- }
/* fill_cmd can't fail here, no data buffer to map. */
(void) fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0,
scsi3addr, TYPE_MSG);
c->Request.CDB[1] = reset_type; /* fill_cmd defaults to LUN reset */
- hpsa_scsi_do_simple_cmd_core(h, c);
+ rc = hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT);
+ if (rc) {
+ dev_warn(&h->pdev->dev, "Failed to send reset command\n");
+ goto out;
+ }
/* no unmap needed here because no data xfer. */
ei = c->err_info;
@@ -2219,10 +2639,129 @@ static int hpsa_send_reset(struct ctlr_info *h, unsigned char *scsi3addr,
hpsa_scsi_interpret_error(h, c);
rc = -1;
}
+out:
cmd_free(h, c);
return rc;
}
+static bool hpsa_cmd_dev_match(struct ctlr_info *h, struct CommandList *c,
+ struct hpsa_scsi_dev_t *dev,
+ unsigned char *scsi3addr)
+{
+ int i;
+ bool match = false;
+ struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+ struct hpsa_tmf_struct *ac = (struct hpsa_tmf_struct *) c2;
+
+ if (hpsa_is_cmd_idle(c))
+ return false;
+
+ switch (c->cmd_type) {
+ case CMD_SCSI:
+ case CMD_IOCTL_PEND:
+ match = !memcmp(scsi3addr, &c->Header.LUN.LunAddrBytes,
+ sizeof(c->Header.LUN.LunAddrBytes));
+ break;
+
+ case CMD_IOACCEL1:
+ case CMD_IOACCEL2:
+ if (c->phys_disk == dev) {
+ /* HBA mode match */
+ match = true;
+ } else {
+ /* Possible RAID mode -- check each phys dev. */
+ /* FIXME: Do we need to take out a lock here? If
+ * so, we could just call hpsa_get_pdisk_of_ioaccel2()
+ * instead. */
+ for (i = 0; i < dev->nphysical_disks && !match; i++) {
+ /* FIXME: an alternate test might be
+ *
+ * match = dev->phys_disk[i]->ioaccel_handle
+ * == c2->scsi_nexus; */
+ match = dev->phys_disk[i] == c->phys_disk;
+ }
+ }
+ break;
+
+ case IOACCEL2_TMF:
+ for (i = 0; i < dev->nphysical_disks && !match; i++) {
+ match = dev->phys_disk[i]->ioaccel_handle ==
+ le32_to_cpu(ac->it_nexus);
+ }
+ break;
+
+ case 0: /* The command is in the middle of being initialized. */
+ match = false;
+ break;
+
+ default:
+ dev_err(&h->pdev->dev, "unexpected cmd_type: %d\n",
+ c->cmd_type);
+ BUG();
+ }
+
+ return match;
+}
+
+static int hpsa_do_reset(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev,
+ unsigned char *scsi3addr, u8 reset_type, int reply_queue)
+{
+ int i;
+ int rc = 0;
+
+ /* We can really only handle one reset at a time */
+ if (mutex_lock_interruptible(&h->reset_mutex) == -EINTR) {
+ dev_warn(&h->pdev->dev, "concurrent reset wait interrupted.\n");
+ return -EINTR;
+ }
+
+ BUG_ON(atomic_read(&dev->reset_cmds_out) != 0);
+
+ for (i = 0; i < h->nr_cmds; i++) {
+ struct CommandList *c = h->cmd_pool + i;
+ int refcount = atomic_inc_return(&c->refcount);
+
+ if (refcount > 1 && hpsa_cmd_dev_match(h, c, dev, scsi3addr)) {
+ unsigned long flags;
+
+ /*
+ * Mark the target command as having a reset pending,
+ * then lock a lock so that the command cannot complete
+ * while we're considering it. If the command is not
+ * idle then count it; otherwise revoke the event.
+ */
+ c->reset_pending = dev;
+ spin_lock_irqsave(&h->lock, flags); /* Implied MB */
+ if (!hpsa_is_cmd_idle(c))
+ atomic_inc(&dev->reset_cmds_out);
+ else
+ c->reset_pending = NULL;
+ spin_unlock_irqrestore(&h->lock, flags);
+ }
+
+ cmd_free(h, c);
+ }
+
+ rc = hpsa_send_reset(h, scsi3addr, reset_type, reply_queue);
+ if (!rc)
+ wait_event(h->event_sync_wait_queue,
+ atomic_read(&dev->reset_cmds_out) == 0 ||
+ lockup_detected(h));
+
+ if (unlikely(lockup_detected(h))) {
+ dev_warn(&h->pdev->dev,
+ "Controller lockup detected during reset wait\n");
+ mutex_unlock(&h->reset_mutex);
+ rc = -ENODEV;
+ }
+
+ if (unlikely(rc))
+ atomic_set(&dev->reset_cmds_out, 0);
+
+ mutex_unlock(&h->reset_mutex);
+ return rc;
+}
+
static void hpsa_get_raid_level(struct ctlr_info *h,
unsigned char *scsi3addr, unsigned char *raid_level)
{
@@ -2328,23 +2867,23 @@ static int hpsa_get_raid_map(struct ctlr_info *h,
struct ErrorInfo *ei;
c = cmd_alloc(h);
- if (c == NULL) {
- dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
- return -ENOMEM;
- }
+
if (fill_cmd(c, HPSA_GET_RAID_MAP, h, &this_device->raid_map,
sizeof(this_device->raid_map), 0,
scsi3addr, TYPE_CMD)) {
- dev_warn(&h->pdev->dev, "Out of memory in hpsa_get_raid_map()\n");
+ dev_warn(&h->pdev->dev, "hpsa_get_raid_map fill_cmd failed\n");
cmd_free(h, c);
- return -ENOMEM;
+ return -1;
}
- hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+ PCI_DMA_FROMDEVICE, NO_TIMEOUT);
+ if (rc)
+ goto out;
ei = c->err_info;
if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
hpsa_scsi_interpret_error(h, c);
- cmd_free(h, c);
- return -1;
+ rc = -1;
+ goto out;
}
cmd_free(h, c);
@@ -2356,6 +2895,9 @@ static int hpsa_get_raid_map(struct ctlr_info *h,
}
hpsa_debug_map_buff(h, rc, &this_device->raid_map);
return rc;
+out:
+ cmd_free(h, c);
+ return rc;
}
static int hpsa_bmic_id_physical_device(struct ctlr_info *h,
@@ -2375,7 +2917,8 @@ static int hpsa_bmic_id_physical_device(struct ctlr_info *h,
c->Request.CDB[2] = bmic_device_index & 0xff;
c->Request.CDB[9] = (bmic_device_index >> 8) & 0xff;
- hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+ hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE,
+ NO_TIMEOUT);
ei = c->err_info;
if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
hpsa_scsi_interpret_error(h, c);
@@ -2438,6 +2981,7 @@ static void hpsa_get_ioaccel_status(struct ctlr_info *h,
this_device->offload_config = 0;
this_device->offload_enabled = 0;
+ this_device->offload_to_be_enabled = 0;
buf = kzalloc(64, GFP_KERNEL);
if (!buf)
@@ -2461,6 +3005,7 @@ static void hpsa_get_ioaccel_status(struct ctlr_info *h,
if (hpsa_get_raid_map(h, scsi3addr, this_device))
this_device->offload_enabled = 0;
}
+ this_device->offload_to_be_enabled = this_device->offload_enabled;
out:
kfree(buf);
return;
@@ -2495,10 +3040,7 @@ static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical,
struct ErrorInfo *ei;
c = cmd_alloc(h);
- if (c == NULL) { /* trouble... */
- dev_err(&h->pdev->dev, "cmd_alloc returned NULL!\n");
- return -1;
- }
+
/* address the controller */
memset(scsi3addr, 0, sizeof(scsi3addr));
if (fill_cmd(c, logical ? HPSA_REPORT_LOG : HPSA_REPORT_PHYS, h,
@@ -2508,7 +3050,10 @@ static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical,
}
if (extended_response)
c->Request.CDB[1] = extended_response;
- hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+ PCI_DMA_FROMDEVICE, NO_TIMEOUT);
+ if (rc)
+ goto out;
ei = c->err_info;
if (ei->CommandStatus != 0 &&
ei->CommandStatus != CMD_DATA_UNDERRUN) {
@@ -2600,8 +3145,10 @@ static int hpsa_volume_offline(struct ctlr_info *h,
unsigned char scsi3addr[])
{
struct CommandList *c;
- unsigned char *sense, sense_key, asc, ascq;
- int ldstat = 0;
+ unsigned char *sense;
+ u8 sense_key, asc, ascq;
+ int sense_len;
+ int rc, ldstat = 0;
u16 cmd_status;
u8 scsi_status;
#define ASC_LUN_NOT_READY 0x04
@@ -2609,14 +3156,19 @@ static int hpsa_volume_offline(struct ctlr_info *h,
#define ASCQ_LUN_NOT_READY_INITIALIZING_CMD_REQ 0x02
c = cmd_alloc(h);
- if (!c)
- return 0;
+
(void) fill_cmd(c, TEST_UNIT_READY, h, NULL, 0, 0, scsi3addr, TYPE_CMD);
- hpsa_scsi_do_simple_cmd_core(h, c);
+ rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT);
+ if (rc) {
+ cmd_free(h, c);
+ return 0;
+ }
sense = c->err_info->SenseInfo;
- sense_key = sense[2];
- asc = sense[12];
- ascq = sense[13];
+ if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo))
+ sense_len = sizeof(c->err_info->SenseInfo);
+ else
+ sense_len = c->err_info->SenseLen;
+ decode_sense_data(sense, sense_len, &sense_key, &asc, &ascq);
cmd_status = c->err_info->CommandStatus;
scsi_status = c->err_info->ScsiStatus;
cmd_free(h, c);
@@ -2656,6 +3208,52 @@ static int hpsa_volume_offline(struct ctlr_info *h,
return 0;
}
+/*
+ * Find out if a logical device supports aborts by simply trying one.
+ * Smart Array may claim not to support aborts on logical drives, but
+ * if a MSA2000 * is connected, the drives on that will be presented
+ * by the Smart Array as logical drives, and aborts may be sent to
+ * those devices successfully. So the simplest way to find out is
+ * to simply try an abort and see how the device responds.
+ */
+static int hpsa_device_supports_aborts(struct ctlr_info *h,
+ unsigned char *scsi3addr)
+{
+ struct CommandList *c;
+ struct ErrorInfo *ei;
+ int rc = 0;
+
+ u64 tag = (u64) -1; /* bogus tag */
+
+ /* Assume that physical devices support aborts */
+ if (!is_logical_dev_addr_mode(scsi3addr))
+ return 1;
+
+ c = cmd_alloc(h);
+
+ (void) fill_cmd(c, HPSA_ABORT_MSG, h, &tag, 0, 0, scsi3addr, TYPE_MSG);
+ (void) hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT);
+ /* no unmap needed here because no data xfer. */
+ ei = c->err_info;
+ switch (ei->CommandStatus) {
+ case CMD_INVALID:
+ rc = 0;
+ break;
+ case CMD_UNABORTABLE:
+ case CMD_ABORT_FAILED:
+ rc = 1;
+ break;
+ case CMD_TMF_STATUS:
+ rc = hpsa_evaluate_tmf_status(h, c);
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ cmd_free(h, c);
+ return rc;
+}
+
static int hpsa_update_device_info(struct ctlr_info *h,
unsigned char scsi3addr[], struct hpsa_scsi_dev_t *this_device,
unsigned char *is_OBDR_device)
@@ -2708,6 +3306,8 @@ static int hpsa_update_device_info(struct ctlr_info *h,
this_device->raid_level = RAID_UNKNOWN;
this_device->offload_config = 0;
this_device->offload_enabled = 0;
+ this_device->offload_to_be_enabled = 0;
+ this_device->hba_ioaccel_enabled = 0;
this_device->volume_offline = 0;
this_device->queue_depth = h->nr_cmds;
}
@@ -2721,7 +3321,6 @@ static int hpsa_update_device_info(struct ctlr_info *h,
strncmp(obdr_sig, OBDR_TAPE_SIG,
OBDR_SIG_LEN) == 0);
}
-
kfree(inq_buff);
return 0;
@@ -2730,6 +3329,31 @@ bail_out:
return 1;
}
+static void hpsa_update_device_supports_aborts(struct ctlr_info *h,
+ struct hpsa_scsi_dev_t *dev, u8 *scsi3addr)
+{
+ unsigned long flags;
+ int rc, entry;
+ /*
+ * See if this device supports aborts. If we already know
+ * the device, we already know if it supports aborts, otherwise
+ * we have to find out if it supports aborts by trying one.
+ */
+ spin_lock_irqsave(&h->devlock, flags);
+ rc = hpsa_scsi_find_entry(dev, h->dev, h->ndevices, &entry);
+ if ((rc == DEVICE_SAME || rc == DEVICE_UPDATED) &&
+ entry >= 0 && entry < h->ndevices) {
+ dev->supports_aborts = h->dev[entry]->supports_aborts;
+ spin_unlock_irqrestore(&h->devlock, flags);
+ } else {
+ spin_unlock_irqrestore(&h->devlock, flags);
+ dev->supports_aborts =
+ hpsa_device_supports_aborts(h, scsi3addr);
+ if (dev->supports_aborts < 0)
+ dev->supports_aborts = 0;
+ }
+}
+
static unsigned char *ext_target_model[] = {
"MSA2012",
"MSA2024",
@@ -2835,6 +3459,7 @@ static int add_ext_target_dev(struct ctlr_info *h,
(*n_ext_target_devs)++;
hpsa_set_bus_target_lun(this_device,
tmpdevice->bus, tmpdevice->target, 0);
+ hpsa_update_device_supports_aborts(h, this_device, scsi3addr);
set_bit(tmpdevice->target, lunzerobits);
return 1;
}
@@ -2850,88 +3475,23 @@ static int add_ext_target_dev(struct ctlr_info *h,
static int hpsa_get_pdisk_of_ioaccel2(struct ctlr_info *h,
struct CommandList *ioaccel2_cmd_to_abort, unsigned char *scsi3addr)
{
- struct ReportExtendedLUNdata *physicals = NULL;
- int responsesize = 24; /* size of physical extended response */
- int reportsize = sizeof(*physicals) + HPSA_MAX_PHYS_LUN * responsesize;
- u32 nphysicals = 0; /* number of reported physical devs */
- int found = 0; /* found match (1) or not (0) */
- u32 find; /* handle we need to match */
+ struct io_accel2_cmd *c2 =
+ &h->ioaccel2_cmd_pool[ioaccel2_cmd_to_abort->cmdindex];
+ unsigned long flags;
int i;
- struct scsi_cmnd *scmd; /* scsi command within request being aborted */
- struct hpsa_scsi_dev_t *d; /* device of request being aborted */
- struct io_accel2_cmd *c2a; /* ioaccel2 command to abort */
- __le32 it_nexus; /* 4 byte device handle for the ioaccel2 cmd */
- __le32 scsi_nexus; /* 4 byte device handle for the ioaccel2 cmd */
-
- if (ioaccel2_cmd_to_abort->cmd_type != CMD_IOACCEL2)
- return 0; /* no match */
-
- /* point to the ioaccel2 device handle */
- c2a = &h->ioaccel2_cmd_pool[ioaccel2_cmd_to_abort->cmdindex];
- if (c2a == NULL)
- return 0; /* no match */
-
- scmd = (struct scsi_cmnd *) ioaccel2_cmd_to_abort->scsi_cmd;
- if (scmd == NULL)
- return 0; /* no match */
-
- d = scmd->device->hostdata;
- if (d == NULL)
- return 0; /* no match */
-
- it_nexus = cpu_to_le32(d->ioaccel_handle);
- scsi_nexus = c2a->scsi_nexus;
- find = le32_to_cpu(c2a->scsi_nexus);
-
- if (h->raid_offload_debug > 0)
- dev_info(&h->pdev->dev,
- "%s: scsi_nexus:0x%08x device id: 0x%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
- __func__, scsi_nexus,
- d->device_id[0], d->device_id[1], d->device_id[2],
- d->device_id[3], d->device_id[4], d->device_id[5],
- d->device_id[6], d->device_id[7], d->device_id[8],
- d->device_id[9], d->device_id[10], d->device_id[11],
- d->device_id[12], d->device_id[13], d->device_id[14],
- d->device_id[15]);
-
- /* Get the list of physical devices */
- physicals = kzalloc(reportsize, GFP_KERNEL);
- if (physicals == NULL)
- return 0;
- if (hpsa_scsi_do_report_phys_luns(h, physicals, reportsize)) {
- dev_err(&h->pdev->dev,
- "Can't lookup %s device handle: report physical LUNs failed.\n",
- "HP SSD Smart Path");
- kfree(physicals);
- return 0;
- }
- nphysicals = be32_to_cpu(*((__be32 *)physicals->LUNListLength)) /
- responsesize;
-
- /* find ioaccel2 handle in list of physicals: */
- for (i = 0; i < nphysicals; i++) {
- struct ext_report_lun_entry *entry = &physicals->LUN[i];
-
- /* handle is in bytes 28-31 of each lun */
- if (entry->ioaccel_handle != find)
- continue; /* didn't match */
- found = 1;
- memcpy(scsi3addr, entry->lunid, 8);
- if (h->raid_offload_debug > 0)
- dev_info(&h->pdev->dev,
- "%s: Searched h=0x%08x, Found h=0x%08x, scsiaddr 0x%8phN\n",
- __func__, find,
- entry->ioaccel_handle, scsi3addr);
- break; /* found it */
- }
-
- kfree(physicals);
- if (found)
- return 1;
- else
- return 0;
+ spin_lock_irqsave(&h->devlock, flags);
+ for (i = 0; i < h->ndevices; i++)
+ if (h->dev[i]->ioaccel_handle == le32_to_cpu(c2->scsi_nexus)) {
+ memcpy(scsi3addr, h->dev[i]->scsi3addr,
+ sizeof(h->dev[i]->scsi3addr));
+ spin_unlock_irqrestore(&h->devlock, flags);
+ return 1;
+ }
+ spin_unlock_irqrestore(&h->devlock, flags);
+ return 0;
}
+
/*
* Do CISS_REPORT_PHYS and CISS_REPORT_LOG. Data is returned in physdev,
* logdev. The number of luns in physdev and logdev are returned in
@@ -3036,6 +3596,8 @@ static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h,
(struct ext_report_lun_entry *) lunaddrbytes;
dev->ioaccel_handle = rle->ioaccel_handle;
+ if (PHYS_IOACCEL(lunaddrbytes) && dev->ioaccel_handle)
+ dev->hba_ioaccel_enabled = 1;
memset(id_phys, 0, sizeof(*id_phys));
rc = hpsa_bmic_id_physical_device(h, lunaddrbytes,
GET_BMIC_DRIVE_NUMBER(lunaddrbytes), id_phys,
@@ -3050,6 +3612,7 @@ static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h,
else
dev->queue_depth = DRIVE_QUEUE_DEPTH; /* conservative */
atomic_set(&dev->ioaccel_cmds_out, 0);
+ atomic_set(&dev->reset_cmds_out, 0);
}
static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
@@ -3142,16 +3705,19 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
/* Figure out where the LUN ID info is coming from */
lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position,
i, nphysicals, nlogicals, physdev_list, logdev_list);
- /* skip masked physical devices. */
- if (lunaddrbytes[3] & 0xC0 &&
- i < nphysicals + (raid_ctlr_position == 0))
- continue;
+
+ /* skip masked non-disk devices */
+ if (MASKED_DEVICE(lunaddrbytes))
+ if (i < nphysicals + (raid_ctlr_position == 0) &&
+ NON_DISK_PHYS_DEV(lunaddrbytes))
+ continue;
/* Get device type, vendor, model, device id */
if (hpsa_update_device_info(h, lunaddrbytes, tmpdevice,
&is_OBDR))
continue; /* skip it if we can't talk to it. */
figure_bus_target_lun(h, lunaddrbytes, tmpdevice);
+ hpsa_update_device_supports_aborts(h, tmpdevice, lunaddrbytes);
this_device = currentsd[ncurrent];
/*
@@ -3170,6 +3736,18 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
*this_device = *tmpdevice;
+ /* do not expose masked devices */
+ if (MASKED_DEVICE(lunaddrbytes) &&
+ i < nphysicals + (raid_ctlr_position == 0)) {
+ if (h->hba_mode_enabled)
+ dev_warn(&h->pdev->dev,
+ "Masked physical device detected\n");
+ this_device->expose_state = HPSA_DO_NOT_EXPOSE;
+ } else {
+ this_device->expose_state =
+ HPSA_SG_ATTACH | HPSA_ULD_ATTACH;
+ }
+
switch (this_device->devtype) {
case TYPE_ROM:
/* We don't *really* support actual CD-ROM devices,
@@ -3183,34 +3761,31 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
ncurrent++;
break;
case TYPE_DISK:
- if (h->hba_mode_enabled) {
- /* never use raid mapper in HBA mode */
- this_device->offload_enabled = 0;
- ncurrent++;
- break;
- } else if (h->acciopath_status) {
- if (i >= nphysicals) {
- ncurrent++;
- break;
- }
- } else {
- if (i < nphysicals)
- break;
+ if (i >= nphysicals) {
ncurrent++;
break;
}
- if (h->transMethod & CFGTBL_Trans_io_accel1 ||
- h->transMethod & CFGTBL_Trans_io_accel2) {
- hpsa_get_ioaccel_drive_info(h, this_device,
- lunaddrbytes, id_phys);
- atomic_set(&this_device->ioaccel_cmds_out, 0);
- ncurrent++;
- }
+
+ if (h->hba_mode_enabled)
+ /* never use raid mapper in HBA mode */
+ this_device->offload_enabled = 0;
+ else if (!(h->transMethod & CFGTBL_Trans_io_accel1 ||
+ h->transMethod & CFGTBL_Trans_io_accel2))
+ break;
+
+ hpsa_get_ioaccel_drive_info(h, this_device,
+ lunaddrbytes, id_phys);
+ atomic_set(&this_device->ioaccel_cmds_out, 0);
+ ncurrent++;
break;
case TYPE_TAPE:
case TYPE_MEDIUM_CHANGER:
ncurrent++;
break;
+ case TYPE_ENCLOSURE:
+ if (h->hba_mode_enabled)
+ ncurrent++;
+ break;
case TYPE_RAID:
/* Only present the Smartarray HBA as a RAID controller.
* If it's a RAID controller other than the HBA itself
@@ -3227,7 +3802,6 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
if (ncurrent >= HPSA_MAX_DEVICES)
break;
}
- hpsa_update_log_drive_phys_drive_ptrs(h, currentsd, ncurrent);
adjust_hpsa_scsi_table(h, hostno, currentsd, ncurrent);
out:
kfree(tmpdevice);
@@ -3260,7 +3834,7 @@ static int hpsa_scatter_gather(struct ctlr_info *h,
struct scsi_cmnd *cmd)
{
struct scatterlist *sg;
- int use_sg, i, sg_index, chained;
+ int use_sg, i, sg_limit, chained, last_sg;
struct SGDescriptor *curr_sg;
BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
@@ -3272,22 +3846,39 @@ static int hpsa_scatter_gather(struct ctlr_info *h,
if (!use_sg)
goto sglist_finished;
+ /*
+ * If the number of entries is greater than the max for a single list,
+ * then we have a chained list; we will set up all but one entry in the
+ * first list (the last entry is saved for link information);
+ * otherwise, we don't have a chained list and we'll set up at each of
+ * the entries in the one list.
+ */
curr_sg = cp->SG;
- chained = 0;
- sg_index = 0;
- scsi_for_each_sg(cmd, sg, use_sg, i) {
- if (i == h->max_cmd_sg_entries - 1 &&
- use_sg > h->max_cmd_sg_entries) {
- chained = 1;
- curr_sg = h->cmd_sg_list[cp->cmdindex];
- sg_index = 0;
- }
+ chained = use_sg > h->max_cmd_sg_entries;
+ sg_limit = chained ? h->max_cmd_sg_entries - 1 : use_sg;
+ last_sg = scsi_sg_count(cmd) - 1;
+ scsi_for_each_sg(cmd, sg, sg_limit, i) {
hpsa_set_sg_descriptor(curr_sg, sg);
curr_sg++;
}
+ if (chained) {
+ /*
+ * Continue with the chained list. Set curr_sg to the chained
+ * list. Modify the limit to the total count less the entries
+ * we've already set up. Resume the scan at the list entry
+ * where the previous loop left off.
+ */
+ curr_sg = h->cmd_sg_list[cp->cmdindex];
+ sg_limit = use_sg - sg_limit;
+ for_each_sg(sg, sg, sg_limit, i) {
+ hpsa_set_sg_descriptor(curr_sg, sg);
+ curr_sg++;
+ }
+ }
+
/* Back the pointer up to the last entry and mark it as "last". */
- (--curr_sg)->Ext = cpu_to_le32(HPSA_SG_LAST);
+ (curr_sg - 1)->Ext = cpu_to_le32(HPSA_SG_LAST);
if (use_sg + chained > h->maxSG)
h->maxSG = use_sg + chained;
@@ -3530,10 +4121,7 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
u32 len;
u32 total_len = 0;
- if (scsi_sg_count(cmd) > h->ioaccel_maxsg) {
- atomic_dec(&phys_disk->ioaccel_cmds_out);
- return IO_ACCEL_INELIGIBLE;
- }
+ BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
if (fixup_ioaccel_cdb(cdb, &cdb_len)) {
atomic_dec(&phys_disk->ioaccel_cmds_out);
@@ -3556,8 +4144,19 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
}
if (use_sg) {
- BUG_ON(use_sg > IOACCEL2_MAXSGENTRIES);
curr_sg = cp->sg;
+ if (use_sg > h->ioaccel_maxsg) {
+ addr64 = le64_to_cpu(
+ h->ioaccel2_cmd_sg_list[c->cmdindex]->address);
+ curr_sg->address = cpu_to_le64(addr64);
+ curr_sg->length = 0;
+ curr_sg->reserved[0] = 0;
+ curr_sg->reserved[1] = 0;
+ curr_sg->reserved[2] = 0;
+ curr_sg->chain_indicator = 0x80;
+
+ curr_sg = h->ioaccel2_cmd_sg_list[c->cmdindex];
+ }
scsi_for_each_sg(cmd, sg, use_sg, i) {
addr64 = (u64) sg_dma_address(sg);
len = sg_dma_len(sg);
@@ -3602,14 +4201,22 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
cp->Tag = cpu_to_le32(c->cmdindex << DIRECT_LOOKUP_SHIFT);
memcpy(cp->cdb, cdb, sizeof(cp->cdb));
- /* fill in sg elements */
- cp->sg_count = (u8) use_sg;
-
cp->data_len = cpu_to_le32(total_len);
cp->err_ptr = cpu_to_le64(c->busaddr +
offsetof(struct io_accel2_cmd, error_data));
cp->err_len = cpu_to_le32(sizeof(cp->error_data));
+ /* fill in sg elements */
+ if (use_sg > h->ioaccel_maxsg) {
+ cp->sg_count = 1;
+ if (hpsa_map_ioaccel2_sg_chain_block(h, cp, c)) {
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ scsi_dma_unmap(cmd);
+ return -1;
+ }
+ } else
+ cp->sg_count = (u8) use_sg;
+
enqueue_cmd_and_start_io(h, c);
return 0;
}
@@ -3992,7 +4599,11 @@ static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h,
dev->phys_disk[map_index]);
}
-/* Submit commands down the "normal" RAID stack path */
+/*
+ * Submit commands down the "normal" RAID stack path
+ * All callers to hpsa_ciss_submit must check lockup_detected
+ * beforehand, before (opt.) and after calling cmd_alloc
+ */
static int hpsa_ciss_submit(struct ctlr_info *h,
struct CommandList *c, struct scsi_cmnd *cmd,
unsigned char scsi3addr[])
@@ -4007,7 +4618,6 @@ static int hpsa_ciss_submit(struct ctlr_info *h,
/* Fill in the request block... */
c->Request.Timeout = 0;
- memset(c->Request.CDB, 0, sizeof(c->Request.CDB));
BUG_ON(cmd->cmd_len > sizeof(c->Request.CDB));
c->Request.CDBLen = cmd->cmd_len;
memcpy(c->Request.CDB, cmd->cmnd, cmd->cmd_len);
@@ -4050,7 +4660,7 @@ static int hpsa_ciss_submit(struct ctlr_info *h,
}
if (hpsa_scatter_gather(h, c, cmd) < 0) { /* Fill SG list */
- cmd_free(h, c);
+ hpsa_cmd_resolve_and_free(h, c);
return SCSI_MLQUEUE_HOST_BUSY;
}
enqueue_cmd_and_start_io(h, c);
@@ -4058,25 +4668,125 @@ static int hpsa_ciss_submit(struct ctlr_info *h,
return 0;
}
+static void hpsa_cmd_init(struct ctlr_info *h, int index,
+ struct CommandList *c)
+{
+ dma_addr_t cmd_dma_handle, err_dma_handle;
+
+ /* Zero out all of commandlist except the last field, refcount */
+ memset(c, 0, offsetof(struct CommandList, refcount));
+ c->Header.tag = cpu_to_le64((u64) (index << DIRECT_LOOKUP_SHIFT));
+ cmd_dma_handle = h->cmd_pool_dhandle + index * sizeof(*c);
+ c->err_info = h->errinfo_pool + index;
+ memset(c->err_info, 0, sizeof(*c->err_info));
+ err_dma_handle = h->errinfo_pool_dhandle
+ + index * sizeof(*c->err_info);
+ c->cmdindex = index;
+ c->busaddr = (u32) cmd_dma_handle;
+ c->ErrDesc.Addr = cpu_to_le64((u64) err_dma_handle);
+ c->ErrDesc.Len = cpu_to_le32((u32) sizeof(*c->err_info));
+ c->h = h;
+ c->scsi_cmd = SCSI_CMD_IDLE;
+}
+
+static void hpsa_preinitialize_commands(struct ctlr_info *h)
+{
+ int i;
+
+ for (i = 0; i < h->nr_cmds; i++) {
+ struct CommandList *c = h->cmd_pool + i;
+
+ hpsa_cmd_init(h, i, c);
+ atomic_set(&c->refcount, 0);
+ }
+}
+
+static inline void hpsa_cmd_partial_init(struct ctlr_info *h, int index,
+ struct CommandList *c)
+{
+ dma_addr_t cmd_dma_handle = h->cmd_pool_dhandle + index * sizeof(*c);
+
+ BUG_ON(c->cmdindex != index);
+
+ memset(c->Request.CDB, 0, sizeof(c->Request.CDB));
+ memset(c->err_info, 0, sizeof(*c->err_info));
+ c->busaddr = (u32) cmd_dma_handle;
+}
+
+static int hpsa_ioaccel_submit(struct ctlr_info *h,
+ struct CommandList *c, struct scsi_cmnd *cmd,
+ unsigned char *scsi3addr)
+{
+ struct hpsa_scsi_dev_t *dev = cmd->device->hostdata;
+ int rc = IO_ACCEL_INELIGIBLE;
+
+ cmd->host_scribble = (unsigned char *) c;
+
+ if (dev->offload_enabled) {
+ hpsa_cmd_init(h, c->cmdindex, c);
+ c->cmd_type = CMD_SCSI;
+ c->scsi_cmd = cmd;
+ rc = hpsa_scsi_ioaccel_raid_map(h, c);
+ if (rc < 0) /* scsi_dma_map failed. */
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ } else if (dev->hba_ioaccel_enabled) {
+ hpsa_cmd_init(h, c->cmdindex, c);
+ c->cmd_type = CMD_SCSI;
+ c->scsi_cmd = cmd;
+ rc = hpsa_scsi_ioaccel_direct_map(h, c);
+ if (rc < 0) /* scsi_dma_map failed. */
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ }
+ return rc;
+}
+
static void hpsa_command_resubmit_worker(struct work_struct *work)
{
struct scsi_cmnd *cmd;
struct hpsa_scsi_dev_t *dev;
- struct CommandList *c =
- container_of(work, struct CommandList, work);
+ struct CommandList *c = container_of(work, struct CommandList, work);
cmd = c->scsi_cmd;
dev = cmd->device->hostdata;
if (!dev) {
cmd->result = DID_NO_CONNECT << 16;
- cmd->scsi_done(cmd);
- return;
+ return hpsa_cmd_free_and_done(c->h, c, cmd);
+ }
+ if (c->reset_pending)
+ return hpsa_cmd_resolve_and_free(c->h, c);
+ if (c->abort_pending)
+ return hpsa_cmd_abort_and_free(c->h, c, cmd);
+ if (c->cmd_type == CMD_IOACCEL2) {
+ struct ctlr_info *h = c->h;
+ struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+ int rc;
+
+ if (c2->error_data.serv_response ==
+ IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL) {
+ rc = hpsa_ioaccel_submit(h, c, cmd, dev->scsi3addr);
+ if (rc == 0)
+ return;
+ if (rc == SCSI_MLQUEUE_HOST_BUSY) {
+ /*
+ * If we get here, it means dma mapping failed.
+ * Try again via scsi mid layer, which will
+ * then get SCSI_MLQUEUE_HOST_BUSY.
+ */
+ cmd->result = DID_IMM_RETRY << 16;
+ return hpsa_cmd_free_and_done(h, c, cmd);
+ }
+ /* else, fall thru and resubmit down CISS path */
+ }
}
+ hpsa_cmd_partial_init(c->h, c->cmdindex, c);
if (hpsa_ciss_submit(c->h, c, cmd, dev->scsi3addr)) {
/*
* If we get here, it means dma mapping failed. Try
* again via scsi mid layer, which will then get
* SCSI_MLQUEUE_HOST_BUSY.
+ *
+ * hpsa_ciss_submit will have already freed c
+ * if it encountered a dma mapping failure.
*/
cmd->result = DID_IMM_RETRY << 16;
cmd->scsi_done(cmd);
@@ -4094,30 +4804,24 @@ static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
/* Get the ptr to our adapter structure out of cmd->host. */
h = sdev_to_hba(cmd->device);
+
+ BUG_ON(cmd->request->tag < 0);
+
dev = cmd->device->hostdata;
if (!dev) {
cmd->result = DID_NO_CONNECT << 16;
cmd->scsi_done(cmd);
return 0;
}
+
memcpy(scsi3addr, dev->scsi3addr, sizeof(scsi3addr));
if (unlikely(lockup_detected(h))) {
- cmd->result = DID_ERROR << 16;
- cmd->scsi_done(cmd);
- return 0;
- }
- c = cmd_alloc(h);
- if (c == NULL) { /* trouble... */
- dev_err(&h->pdev->dev, "cmd_alloc returned NULL!\n");
- return SCSI_MLQUEUE_HOST_BUSY;
- }
- if (unlikely(lockup_detected(h))) {
- cmd->result = DID_ERROR << 16;
- cmd_free(h, c);
+ cmd->result = DID_NO_CONNECT << 16;
cmd->scsi_done(cmd);
return 0;
}
+ c = cmd_tagged_alloc(h, cmd);
/*
* Call alternate submit routine for I/O accelerated commands.
@@ -4126,27 +4830,12 @@ static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
if (likely(cmd->retries == 0 &&
cmd->request->cmd_type == REQ_TYPE_FS &&
h->acciopath_status)) {
-
- cmd->host_scribble = (unsigned char *) c;
- c->cmd_type = CMD_SCSI;
- c->scsi_cmd = cmd;
-
- if (dev->offload_enabled) {
- rc = hpsa_scsi_ioaccel_raid_map(h, c);
- if (rc == 0)
- return 0; /* Sent on ioaccel path */
- if (rc < 0) { /* scsi_dma_map failed. */
- cmd_free(h, c);
- return SCSI_MLQUEUE_HOST_BUSY;
- }
- } else if (dev->ioaccel_handle) {
- rc = hpsa_scsi_ioaccel_direct_map(h, c);
- if (rc == 0)
- return 0; /* Sent on direct map path */
- if (rc < 0) { /* scsi_dma_map failed. */
- cmd_free(h, c);
- return SCSI_MLQUEUE_HOST_BUSY;
- }
+ rc = hpsa_ioaccel_submit(h, c, cmd, scsi3addr);
+ if (rc == 0)
+ return 0;
+ if (rc == SCSI_MLQUEUE_HOST_BUSY) {
+ hpsa_cmd_resolve_and_free(h, c);
+ return SCSI_MLQUEUE_HOST_BUSY;
}
}
return hpsa_ciss_submit(h, c, cmd, scsi3addr);
@@ -4228,22 +4917,16 @@ static int hpsa_scan_finished(struct Scsi_Host *sh,
return finished;
}
-static void hpsa_unregister_scsi(struct ctlr_info *h)
-{
- /* we are being forcibly unloaded, and may not refuse. */
- scsi_remove_host(h->scsi_host);
- scsi_host_put(h->scsi_host);
- h->scsi_host = NULL;
-}
-
-static int hpsa_register_scsi(struct ctlr_info *h)
+static int hpsa_scsi_host_alloc(struct ctlr_info *h)
{
struct Scsi_Host *sh;
int error;
sh = scsi_host_alloc(&hpsa_driver_template, sizeof(h));
- if (sh == NULL)
- goto fail;
+ if (sh == NULL) {
+ dev_err(&h->pdev->dev, "scsi_host_alloc failed\n");
+ return -ENOMEM;
+ }
sh->io_port = 0;
sh->n_io_port = 0;
@@ -4252,80 +4935,156 @@ static int hpsa_register_scsi(struct ctlr_info *h)
sh->max_cmd_len = MAX_COMMAND_SIZE;
sh->max_lun = HPSA_MAX_LUN;
sh->max_id = HPSA_MAX_LUN;
- sh->can_queue = h->nr_cmds -
- HPSA_CMDS_RESERVED_FOR_ABORTS -
- HPSA_CMDS_RESERVED_FOR_DRIVER -
- HPSA_MAX_CONCURRENT_PASSTHRUS;
+ sh->can_queue = h->nr_cmds - HPSA_NRESERVED_CMDS;
sh->cmd_per_lun = sh->can_queue;
sh->sg_tablesize = h->maxsgentries;
- h->scsi_host = sh;
sh->hostdata[0] = (unsigned long) h;
sh->irq = h->intr[h->intr_mode];
sh->unique_id = sh->irq;
- error = scsi_add_host(sh, &h->pdev->dev);
- if (error)
- goto fail_host_put;
- scsi_scan_host(sh);
+ error = scsi_init_shared_tag_map(sh, sh->can_queue);
+ if (error) {
+ dev_err(&h->pdev->dev,
+ "%s: scsi_init_shared_tag_map failed for controller %d\n",
+ __func__, h->ctlr);
+ scsi_host_put(sh);
+ return error;
+ }
+ h->scsi_host = sh;
return 0;
+}
- fail_host_put:
- dev_err(&h->pdev->dev, "%s: scsi_add_host"
- " failed for controller %d\n", __func__, h->ctlr);
- scsi_host_put(sh);
- return error;
- fail:
- dev_err(&h->pdev->dev, "%s: scsi_host_alloc"
- " failed for controller %d\n", __func__, h->ctlr);
- return -ENOMEM;
+static int hpsa_scsi_add_host(struct ctlr_info *h)
+{
+ int rv;
+
+ rv = scsi_add_host(h->scsi_host, &h->pdev->dev);
+ if (rv) {
+ dev_err(&h->pdev->dev, "scsi_add_host failed\n");
+ return rv;
+ }
+ scsi_scan_host(h->scsi_host);
+ return 0;
}
-static int wait_for_device_to_become_ready(struct ctlr_info *h,
- unsigned char lunaddr[])
+/*
+ * The block layer has already gone to the trouble of picking out a unique,
+ * small-integer tag for this request. We use an offset from that value as
+ * an index to select our command block. (The offset allows us to reserve the
+ * low-numbered entries for our own uses.)
+ */
+static int hpsa_get_cmd_index(struct scsi_cmnd *scmd)
+{
+ int idx = scmd->request->tag;
+
+ if (idx < 0)
+ return idx;
+
+ /* Offset to leave space for internal cmds. */
+ return idx += HPSA_NRESERVED_CMDS;
+}
+
+/*
+ * Send a TEST_UNIT_READY command to the specified LUN using the specified
+ * reply queue; returns zero if the unit is ready, and non-zero otherwise.
+ */
+static int hpsa_send_test_unit_ready(struct ctlr_info *h,
+ struct CommandList *c, unsigned char lunaddr[],
+ int reply_queue)
+{
+ int rc;
+
+ /* Send the Test Unit Ready, fill_cmd can't fail, no mapping */
+ (void) fill_cmd(c, TEST_UNIT_READY, h,
+ NULL, 0, 0, lunaddr, TYPE_CMD);
+ rc = hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT);
+ if (rc)
+ return rc;
+ /* no unmap needed here because no data xfer. */
+
+ /* Check if the unit is already ready. */
+ if (c->err_info->CommandStatus == CMD_SUCCESS)
+ return 0;
+
+ /*
+ * The first command sent after reset will receive "unit attention" to
+ * indicate that the LUN has been reset...this is actually what we're
+ * looking for (but, success is good too).
+ */
+ if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
+ c->err_info->ScsiStatus == SAM_STAT_CHECK_CONDITION &&
+ (c->err_info->SenseInfo[2] == NO_SENSE ||
+ c->err_info->SenseInfo[2] == UNIT_ATTENTION))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Wait for a TEST_UNIT_READY command to complete, retrying as necessary;
+ * returns zero when the unit is ready, and non-zero when giving up.
+ */
+static int hpsa_wait_for_test_unit_ready(struct ctlr_info *h,
+ struct CommandList *c,
+ unsigned char lunaddr[], int reply_queue)
{
int rc;
int count = 0;
int waittime = 1; /* seconds */
- struct CommandList *c;
-
- c = cmd_alloc(h);
- if (!c) {
- dev_warn(&h->pdev->dev, "out of memory in "
- "wait_for_device_to_become_ready.\n");
- return IO_ERROR;
- }
/* Send test unit ready until device ready, or give up. */
- while (count < HPSA_TUR_RETRY_LIMIT) {
+ for (count = 0; count < HPSA_TUR_RETRY_LIMIT; count++) {
- /* Wait for a bit. do this first, because if we send
+ /*
+ * Wait for a bit. do this first, because if we send
* the TUR right away, the reset will just abort it.
*/
msleep(1000 * waittime);
- count++;
- rc = 0; /* Device ready. */
+
+ rc = hpsa_send_test_unit_ready(h, c, lunaddr, reply_queue);
+ if (!rc)
+ break;
/* Increase wait time with each try, up to a point. */
if (waittime < HPSA_MAX_WAIT_INTERVAL_SECS)
- waittime = waittime * 2;
+ waittime *= 2;
- /* Send the Test Unit Ready, fill_cmd can't fail, no mapping */
- (void) fill_cmd(c, TEST_UNIT_READY, h,
- NULL, 0, 0, lunaddr, TYPE_CMD);
- hpsa_scsi_do_simple_cmd_core(h, c);
- /* no unmap needed here because no data xfer. */
+ dev_warn(&h->pdev->dev,
+ "waiting %d secs for device to become ready.\n",
+ waittime);
+ }
- if (c->err_info->CommandStatus == CMD_SUCCESS)
- break;
+ return rc;
+}
- if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
- c->err_info->ScsiStatus == SAM_STAT_CHECK_CONDITION &&
- (c->err_info->SenseInfo[2] == NO_SENSE ||
- c->err_info->SenseInfo[2] == UNIT_ATTENTION))
- break;
+static int wait_for_device_to_become_ready(struct ctlr_info *h,
+ unsigned char lunaddr[],
+ int reply_queue)
+{
+ int first_queue;
+ int last_queue;
+ int rq;
+ int rc = 0;
+ struct CommandList *c;
- dev_warn(&h->pdev->dev, "waiting %d secs "
- "for device to become ready.\n", waittime);
- rc = 1; /* device not ready. */
+ c = cmd_alloc(h);
+
+ /*
+ * If no specific reply queue was requested, then send the TUR
+ * repeatedly, requesting a reply on each reply queue; otherwise execute
+ * the loop exactly once using only the specified queue.
+ */
+ if (reply_queue == DEFAULT_REPLY_QUEUE) {
+ first_queue = 0;
+ last_queue = h->nreply_queues - 1;
+ } else {
+ first_queue = reply_queue;
+ last_queue = reply_queue;
+ }
+
+ for (rq = first_queue; rq <= last_queue; rq++) {
+ rc = hpsa_wait_for_test_unit_ready(h, c, lunaddr, rq);
+ if (rc)
+ break;
}
if (rc)
@@ -4345,6 +5104,7 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
int rc;
struct ctlr_info *h;
struct hpsa_scsi_dev_t *dev;
+ char msg[40];
/* find the controller to which the command to be aborted was sent */
h = sdev_to_hba(scsicmd->device);
@@ -4356,19 +5116,38 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
dev = scsicmd->device->hostdata;
if (!dev) {
- dev_err(&h->pdev->dev, "hpsa_eh_device_reset_handler: "
- "device lookup failed.\n");
+ dev_err(&h->pdev->dev, "%s: device lookup failed\n", __func__);
return FAILED;
}
- dev_warn(&h->pdev->dev, "resetting device %d:%d:%d:%d\n",
- h->scsi_host->host_no, dev->bus, dev->target, dev->lun);
- /* send a reset to the SCSI LUN which the command was sent to */
- rc = hpsa_send_reset(h, dev->scsi3addr, HPSA_RESET_TYPE_LUN);
- if (rc == 0 && wait_for_device_to_become_ready(h, dev->scsi3addr) == 0)
+
+ /* if controller locked up, we can guarantee command won't complete */
+ if (lockup_detected(h)) {
+ sprintf(msg, "cmd %d RESET FAILED, lockup detected",
+ hpsa_get_cmd_index(scsicmd));
+ hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+ return FAILED;
+ }
+
+ /* this reset request might be the result of a lockup; check */
+ if (detect_controller_lockup(h)) {
+ sprintf(msg, "cmd %d RESET FAILED, new lockup detected",
+ hpsa_get_cmd_index(scsicmd));
+ hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+ return FAILED;
+ }
+
+ /* Do not attempt on controller */
+ if (is_hba_lunid(dev->scsi3addr))
return SUCCESS;
- dev_warn(&h->pdev->dev, "resetting device failed.\n");
- return FAILED;
+ hpsa_show_dev_msg(KERN_WARNING, h, dev, "resetting");
+
+ /* send a reset to the SCSI LUN which the command was sent to */
+ rc = hpsa_do_reset(h, dev, dev->scsi3addr, HPSA_RESET_TYPE_LUN,
+ DEFAULT_REPLY_QUEUE);
+ sprintf(msg, "reset %s", rc == 0 ? "completed successfully" : "failed");
+ hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+ return rc == 0 ? SUCCESS : FAILED;
}
static void swizzle_abort_tag(u8 *tag)
@@ -4412,7 +5191,7 @@ static void hpsa_get_tag(struct ctlr_info *h,
}
static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
- struct CommandList *abort, int swizzle)
+ struct CommandList *abort, int reply_queue)
{
int rc = IO_OK;
struct CommandList *c;
@@ -4420,19 +5199,15 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
__le32 tagupper, taglower;
c = cmd_alloc(h);
- if (c == NULL) { /* trouble... */
- dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
- return -ENOMEM;
- }
/* fill_cmd can't fail here, no buffer to map */
- (void) fill_cmd(c, HPSA_ABORT_MSG, h, abort,
+ (void) fill_cmd(c, HPSA_ABORT_MSG, h, &abort->Header.tag,
0, 0, scsi3addr, TYPE_MSG);
- if (swizzle)
+ if (h->needs_abort_tags_swizzled)
swizzle_abort_tag(&c->Request.CDB[4]);
- hpsa_scsi_do_simple_cmd_core(h, c);
+ (void) hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT);
hpsa_get_tag(h, abort, &taglower, &tagupper);
- dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: do_simple_cmd_core completed.\n",
+ dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: do_simple_cmd(abort) completed.\n",
__func__, tagupper, taglower);
/* no unmap needed here because no data xfer. */
@@ -4440,6 +5215,9 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
switch (ei->CommandStatus) {
case CMD_SUCCESS:
break;
+ case CMD_TMF_STATUS:
+ rc = hpsa_evaluate_tmf_status(h, c);
+ break;
case CMD_UNABORTABLE: /* Very common, don't make noise. */
rc = -1;
break;
@@ -4456,6 +5234,48 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
return rc;
}
+static void setup_ioaccel2_abort_cmd(struct CommandList *c, struct ctlr_info *h,
+ struct CommandList *command_to_abort, int reply_queue)
+{
+ struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+ struct hpsa_tmf_struct *ac = (struct hpsa_tmf_struct *) c2;
+ struct io_accel2_cmd *c2a =
+ &h->ioaccel2_cmd_pool[command_to_abort->cmdindex];
+ struct scsi_cmnd *scmd = command_to_abort->scsi_cmd;
+ struct hpsa_scsi_dev_t *dev = scmd->device->hostdata;
+
+ /*
+ * We're overlaying struct hpsa_tmf_struct on top of something which
+ * was allocated as a struct io_accel2_cmd, so we better be sure it
+ * actually fits, and doesn't overrun the error info space.
+ */
+ BUILD_BUG_ON(sizeof(struct hpsa_tmf_struct) >
+ sizeof(struct io_accel2_cmd));
+ BUG_ON(offsetof(struct io_accel2_cmd, error_data) <
+ offsetof(struct hpsa_tmf_struct, error_len) +
+ sizeof(ac->error_len));
+
+ c->cmd_type = IOACCEL2_TMF;
+ c->scsi_cmd = SCSI_CMD_BUSY;
+
+ /* Adjust the DMA address to point to the accelerated command buffer */
+ c->busaddr = (u32) h->ioaccel2_cmd_pool_dhandle +
+ (c->cmdindex * sizeof(struct io_accel2_cmd));
+ BUG_ON(c->busaddr & 0x0000007F);
+
+ memset(ac, 0, sizeof(*c2)); /* yes this is correct */
+ ac->iu_type = IOACCEL2_IU_TMF_TYPE;
+ ac->reply_queue = reply_queue;
+ ac->tmf = IOACCEL2_TMF_ABORT;
+ ac->it_nexus = cpu_to_le32(dev->ioaccel_handle);
+ memset(ac->lun_id, 0, sizeof(ac->lun_id));
+ ac->tag = cpu_to_le64(c->cmdindex << DIRECT_LOOKUP_SHIFT);
+ ac->abort_tag = cpu_to_le64(le32_to_cpu(c2a->Tag));
+ ac->error_ptr = cpu_to_le64(c->busaddr +
+ offsetof(struct io_accel2_cmd, error_data));
+ ac->error_len = cpu_to_le32(sizeof(c2->error_data));
+}
+
/* ioaccel2 path firmware cannot handle abort task requests.
* Change abort requests to physical target reset, and send to the
* address of the physical disk used for the ioaccel 2 command.
@@ -4464,7 +5284,7 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
*/
static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
- unsigned char *scsi3addr, struct CommandList *abort)
+ unsigned char *scsi3addr, struct CommandList *abort, int reply_queue)
{
int rc = IO_OK;
struct scsi_cmnd *scmd; /* scsi command within request being aborted */
@@ -4483,8 +5303,9 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
if (h->raid_offload_debug > 0)
dev_info(&h->pdev->dev,
- "Reset as abort: Abort requested on C%d:B%d:T%d:L%d scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ "scsi %d:%d:%d:%d %s scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
h->scsi_host->host_no, dev->bus, dev->target, dev->lun,
+ "Reset as abort",
scsi3addr[0], scsi3addr[1], scsi3addr[2], scsi3addr[3],
scsi3addr[4], scsi3addr[5], scsi3addr[6], scsi3addr[7]);
@@ -4506,7 +5327,7 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
"Reset as abort: Resetting physical device at scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
psa[0], psa[1], psa[2], psa[3],
psa[4], psa[5], psa[6], psa[7]);
- rc = hpsa_send_reset(h, psa, HPSA_RESET_TYPE_TARGET);
+ rc = hpsa_do_reset(h, dev, psa, HPSA_RESET_TYPE_TARGET, reply_queue);
if (rc != 0) {
dev_warn(&h->pdev->dev,
"Reset as abort: Failed on physical device at scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
@@ -4516,7 +5337,7 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
}
/* wait for device to recover */
- if (wait_for_device_to_become_ready(h, psa) != 0) {
+ if (wait_for_device_to_become_ready(h, psa, reply_queue) != 0) {
dev_warn(&h->pdev->dev,
"Reset as abort: Failed: Device never recovered from reset: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
psa[0], psa[1], psa[2], psa[3],
@@ -4533,25 +5354,94 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
return rc; /* success */
}
-/* Some Smart Arrays need the abort tag swizzled, and some don't. It's hard to
- * tell which kind we're dealing with, so we send the abort both ways. There
- * shouldn't be any collisions between swizzled and unswizzled tags due to the
- * way we construct our tags but we check anyway in case the assumptions which
- * make this true someday become false.
- */
+static int hpsa_send_abort_ioaccel2(struct ctlr_info *h,
+ struct CommandList *abort, int reply_queue)
+{
+ int rc = IO_OK;
+ struct CommandList *c;
+ __le32 taglower, tagupper;
+ struct hpsa_scsi_dev_t *dev;
+ struct io_accel2_cmd *c2;
+
+ dev = abort->scsi_cmd->device->hostdata;
+ if (!dev->offload_enabled && !dev->hba_ioaccel_enabled)
+ return -1;
+
+ c = cmd_alloc(h);
+ setup_ioaccel2_abort_cmd(c, h, abort, reply_queue);
+ c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+ (void) hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT);
+ hpsa_get_tag(h, abort, &taglower, &tagupper);
+ dev_dbg(&h->pdev->dev,
+ "%s: Tag:0x%08x:%08x: do_simple_cmd(ioaccel2 abort) completed.\n",
+ __func__, tagupper, taglower);
+ /* no unmap needed here because no data xfer. */
+
+ dev_dbg(&h->pdev->dev,
+ "%s: Tag:0x%08x:%08x: abort service response = 0x%02x.\n",
+ __func__, tagupper, taglower, c2->error_data.serv_response);
+ switch (c2->error_data.serv_response) {
+ case IOACCEL2_SERV_RESPONSE_TMF_COMPLETE:
+ case IOACCEL2_SERV_RESPONSE_TMF_SUCCESS:
+ rc = 0;
+ break;
+ case IOACCEL2_SERV_RESPONSE_TMF_REJECTED:
+ case IOACCEL2_SERV_RESPONSE_FAILURE:
+ case IOACCEL2_SERV_RESPONSE_TMF_WRONG_LUN:
+ rc = -1;
+ break;
+ default:
+ dev_warn(&h->pdev->dev,
+ "%s: Tag:0x%08x:%08x: unknown abort service response 0x%02x\n",
+ __func__, tagupper, taglower,
+ c2->error_data.serv_response);
+ rc = -1;
+ }
+ cmd_free(h, c);
+ dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: Finished.\n", __func__,
+ tagupper, taglower);
+ return rc;
+}
+
static int hpsa_send_abort_both_ways(struct ctlr_info *h,
- unsigned char *scsi3addr, struct CommandList *abort)
+ unsigned char *scsi3addr, struct CommandList *abort, int reply_queue)
{
- /* ioccelerator mode 2 commands should be aborted via the
+ /*
+ * ioccelerator mode 2 commands should be aborted via the
* accelerated path, since RAID path is unaware of these commands,
- * but underlying firmware can't handle abort TMF.
- * Change abort to physical device reset.
+ * but not all underlying firmware can handle abort TMF.
+ * Change abort to physical device reset when abort TMF is unsupported.
*/
- if (abort->cmd_type == CMD_IOACCEL2)
- return hpsa_send_reset_as_abort_ioaccel2(h, scsi3addr, abort);
+ if (abort->cmd_type == CMD_IOACCEL2) {
+ if (HPSATMF_IOACCEL_ENABLED & h->TMFSupportFlags)
+ return hpsa_send_abort_ioaccel2(h, abort,
+ reply_queue);
+ else
+ return hpsa_send_reset_as_abort_ioaccel2(h, scsi3addr,
+ abort, reply_queue);
+ }
+ return hpsa_send_abort(h, scsi3addr, abort, reply_queue);
+}
- return hpsa_send_abort(h, scsi3addr, abort, 0) &&
- hpsa_send_abort(h, scsi3addr, abort, 1);
+/* Find out which reply queue a command was meant to return on */
+static int hpsa_extract_reply_queue(struct ctlr_info *h,
+ struct CommandList *c)
+{
+ if (c->cmd_type == CMD_IOACCEL2)
+ return h->ioaccel2_cmd_pool[c->cmdindex].reply_queue;
+ return c->Header.ReplyQueue;
+}
+
+/*
+ * Limit concurrency of abort commands to prevent
+ * over-subscription of commands
+ */
+static inline int wait_for_available_abort_cmd(struct ctlr_info *h)
+{
+#define ABORT_CMD_WAIT_MSECS 5000
+ return !wait_event_timeout(h->abort_cmd_wait_queue,
+ atomic_dec_if_positive(&h->abort_cmds_available) >= 0,
+ msecs_to_jiffies(ABORT_CMD_WAIT_MSECS));
}
/* Send an abort for the specified command.
@@ -4561,7 +5451,7 @@ static int hpsa_send_abort_both_ways(struct ctlr_info *h,
static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
{
- int i, rc;
+ int rc;
struct ctlr_info *h;
struct hpsa_scsi_dev_t *dev;
struct CommandList *abort; /* pointer to command to be aborted */
@@ -4569,27 +5459,19 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
char msg[256]; /* For debug messaging. */
int ml = 0;
__le32 tagupper, taglower;
- int refcount;
+ int refcount, reply_queue;
- /* Find the controller of the command to be aborted */
- h = sdev_to_hba(sc->device);
- if (WARN(h == NULL,
- "ABORT REQUEST FAILED, Controller lookup failed.\n"))
+ if (sc == NULL)
return FAILED;
- if (lockup_detected(h))
+ if (sc->device == NULL)
return FAILED;
- /* Check that controller supports some kind of task abort */
- if (!(HPSATMF_PHYS_TASK_ABORT & h->TMFSupportFlags) &&
- !(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags))
+ /* Find the controller of the command to be aborted */
+ h = sdev_to_hba(sc->device);
+ if (h == NULL)
return FAILED;
- memset(msg, 0, sizeof(msg));
- ml += sprintf(msg+ml, "ABORT REQUEST on C%d:B%d:T%d:L%llu ",
- h->scsi_host->host_no, sc->device->channel,
- sc->device->id, sc->device->lun);
-
/* Find the device of the command to be aborted */
dev = sc->device->hostdata;
if (!dev) {
@@ -4598,6 +5480,31 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
return FAILED;
}
+ /* If controller locked up, we can guarantee command won't complete */
+ if (lockup_detected(h)) {
+ hpsa_show_dev_msg(KERN_WARNING, h, dev,
+ "ABORT FAILED, lockup detected");
+ return FAILED;
+ }
+
+ /* This is a good time to check if controller lockup has occurred */
+ if (detect_controller_lockup(h)) {
+ hpsa_show_dev_msg(KERN_WARNING, h, dev,
+ "ABORT FAILED, new lockup detected");
+ return FAILED;
+ }
+
+ /* Check that controller supports some kind of task abort */
+ if (!(HPSATMF_PHYS_TASK_ABORT & h->TMFSupportFlags) &&
+ !(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags))
+ return FAILED;
+
+ memset(msg, 0, sizeof(msg));
+ ml += sprintf(msg+ml, "scsi %d:%d:%d:%llu %s %p",
+ h->scsi_host->host_no, sc->device->channel,
+ sc->device->id, sc->device->lun,
+ "Aborting command", sc);
+
/* Get SCSI command to be aborted */
abort = (struct CommandList *) sc->host_scribble;
if (abort == NULL) {
@@ -4609,50 +5516,115 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
cmd_free(h, abort);
return SUCCESS;
}
+
+ /* Don't bother trying the abort if we know it won't work. */
+ if (abort->cmd_type != CMD_IOACCEL2 &&
+ abort->cmd_type != CMD_IOACCEL1 && !dev->supports_aborts) {
+ cmd_free(h, abort);
+ return FAILED;
+ }
+
+ /*
+ * Check that we're aborting the right command.
+ * It's possible the CommandList already completed and got re-used.
+ */
+ if (abort->scsi_cmd != sc) {
+ cmd_free(h, abort);
+ return SUCCESS;
+ }
+
+ abort->abort_pending = true;
hpsa_get_tag(h, abort, &taglower, &tagupper);
+ reply_queue = hpsa_extract_reply_queue(h, abort);
ml += sprintf(msg+ml, "Tag:0x%08x:%08x ", tagupper, taglower);
as = abort->scsi_cmd;
if (as != NULL)
- ml += sprintf(msg+ml, "Command:0x%x SN:0x%lx ",
- as->cmnd[0], as->serial_number);
- dev_dbg(&h->pdev->dev, "%s\n", msg);
- dev_warn(&h->pdev->dev, "Abort request on C%d:B%d:T%d:L%d\n",
- h->scsi_host->host_no, dev->bus, dev->target, dev->lun);
+ ml += sprintf(msg+ml,
+ "CDBLen: %d CDB: 0x%02x%02x... SN: 0x%lx ",
+ as->cmd_len, as->cmnd[0], as->cmnd[1],
+ as->serial_number);
+ dev_warn(&h->pdev->dev, "%s BEING SENT\n", msg);
+ hpsa_show_dev_msg(KERN_WARNING, h, dev, "Aborting command");
+
/*
* Command is in flight, or possibly already completed
* by the firmware (but not to the scsi mid layer) but we can't
* distinguish which. Send the abort down.
*/
- rc = hpsa_send_abort_both_ways(h, dev->scsi3addr, abort);
+ if (wait_for_available_abort_cmd(h)) {
+ dev_warn(&h->pdev->dev,
+ "%s FAILED, timeout waiting for an abort command to become available.\n",
+ msg);
+ cmd_free(h, abort);
+ return FAILED;
+ }
+ rc = hpsa_send_abort_both_ways(h, dev->scsi3addr, abort, reply_queue);
+ atomic_inc(&h->abort_cmds_available);
+ wake_up_all(&h->abort_cmd_wait_queue);
if (rc != 0) {
- dev_dbg(&h->pdev->dev, "%s Request FAILED.\n", msg);
- dev_warn(&h->pdev->dev, "FAILED abort on device C%d:B%d:T%d:L%d\n",
- h->scsi_host->host_no,
- dev->bus, dev->target, dev->lun);
+ dev_warn(&h->pdev->dev, "%s SENT, FAILED\n", msg);
+ hpsa_show_dev_msg(KERN_WARNING, h, dev,
+ "FAILED to abort command");
cmd_free(h, abort);
return FAILED;
}
- dev_info(&h->pdev->dev, "%s REQUEST SUCCEEDED.\n", msg);
+ dev_info(&h->pdev->dev, "%s SENT, SUCCESS\n", msg);
+ wait_event(h->event_sync_wait_queue,
+ abort->scsi_cmd != sc || lockup_detected(h));
+ cmd_free(h, abort);
+ return !lockup_detected(h) ? SUCCESS : FAILED;
+}
- /* If the abort(s) above completed and actually aborted the
- * command, then the command to be aborted should already be
- * completed. If not, wait around a bit more to see if they
- * manage to complete normally.
- */
-#define ABORT_COMPLETE_WAIT_SECS 30
- for (i = 0; i < ABORT_COMPLETE_WAIT_SECS * 10; i++) {
- refcount = atomic_read(&abort->refcount);
- if (refcount < 2) {
- cmd_free(h, abort);
- return SUCCESS;
- } else {
- msleep(100);
- }
+/*
+ * For operations with an associated SCSI command, a command block is allocated
+ * at init, and managed by cmd_tagged_alloc() and cmd_tagged_free() using the
+ * block request tag as an index into a table of entries. cmd_tagged_free() is
+ * the complement, although cmd_free() may be called instead.
+ */
+static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h,
+ struct scsi_cmnd *scmd)
+{
+ int idx = hpsa_get_cmd_index(scmd);
+ struct CommandList *c = h->cmd_pool + idx;
+
+ if (idx < HPSA_NRESERVED_CMDS || idx >= h->nr_cmds) {
+ dev_err(&h->pdev->dev, "Bad block tag: %d not in [%d..%d]\n",
+ idx, HPSA_NRESERVED_CMDS, h->nr_cmds - 1);
+ /* The index value comes from the block layer, so if it's out of
+ * bounds, it's probably not our bug.
+ */
+ BUG();
}
- dev_warn(&h->pdev->dev, "%s FAILED. Aborted command has not completed after %d seconds.\n",
- msg, ABORT_COMPLETE_WAIT_SECS);
- cmd_free(h, abort);
- return FAILED;
+
+ atomic_inc(&c->refcount);
+ if (unlikely(!hpsa_is_cmd_idle(c))) {
+ /*
+ * We expect that the SCSI layer will hand us a unique tag
+ * value. Thus, there should never be a collision here between
+ * two requests...because if the selected command isn't idle
+ * then someone is going to be very disappointed.
+ */
+ dev_err(&h->pdev->dev,
+ "tag collision (tag=%d) in cmd_tagged_alloc().\n",
+ idx);
+ if (c->scsi_cmd != NULL)
+ scsi_print_command(c->scsi_cmd);
+ scsi_print_command(scmd);
+ }
+
+ hpsa_cmd_partial_init(h, idx, c);
+ return c;
+}
+
+static void cmd_tagged_free(struct ctlr_info *h, struct CommandList *c)
+{
+ /*
+ * Release our reference to the block. We don't need to do anything
+ * else to free it, because it is accessed by index. (There's no point
+ * in checking the result of the decrement, since we cannot guarantee
+ * that there isn't a concurrent abort which is also accessing it.)
+ */
+ (void)atomic_dec(&c->refcount);
}
/*
@@ -4660,16 +5632,15 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
* and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
* which ones are free or in use. Lock must be held when calling this.
* cmd_free() is the complement.
+ * This function never gives up and returns NULL. If it hangs,
+ * another thread must call cmd_free() to free some tags.
*/
static struct CommandList *cmd_alloc(struct ctlr_info *h)
{
struct CommandList *c;
- int i;
- union u64bit temp64;
- dma_addr_t cmd_dma_handle, err_dma_handle;
- int refcount;
- unsigned long offset;
+ int refcount, i;
+ int offset = 0;
/*
* There is some *extremely* small but non-zero chance that that
@@ -4681,12 +5652,20 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h)
* very unlucky thread might be starved anyway, never able to
* beat the other threads. In reality, this happens so
* infrequently as to be indistinguishable from never.
+ *
+ * Note that we start allocating commands before the SCSI host structure
+ * is initialized. Since the search starts at bit zero, this
+ * all works, since we have at least one command structure available;
+ * however, it means that the structures with the low indexes have to be
+ * reserved for driver-initiated requests, while requests from the block
+ * layer will use the higher indexes.
*/
- offset = h->last_allocation; /* benignly racy */
for (;;) {
- i = find_next_zero_bit(h->cmd_pool_bits, h->nr_cmds, offset);
- if (unlikely(i == h->nr_cmds)) {
+ i = find_next_zero_bit(h->cmd_pool_bits,
+ HPSA_NRESERVED_CMDS,
+ offset);
+ if (unlikely(i >= HPSA_NRESERVED_CMDS)) {
offset = 0;
continue;
}
@@ -4694,35 +5673,23 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h)
refcount = atomic_inc_return(&c->refcount);
if (unlikely(refcount > 1)) {
cmd_free(h, c); /* already in use */
- offset = (i + 1) % h->nr_cmds;
+ offset = (i + 1) % HPSA_NRESERVED_CMDS;
continue;
}
set_bit(i & (BITS_PER_LONG - 1),
h->cmd_pool_bits + (i / BITS_PER_LONG));
break; /* it's ours now. */
}
- h->last_allocation = i; /* benignly racy */
-
- /* Zero out all of commandlist except the last field, refcount */
- memset(c, 0, offsetof(struct CommandList, refcount));
- c->Header.tag = cpu_to_le64((u64) (i << DIRECT_LOOKUP_SHIFT));
- cmd_dma_handle = h->cmd_pool_dhandle + i * sizeof(*c);
- c->err_info = h->errinfo_pool + i;
- memset(c->err_info, 0, sizeof(*c->err_info));
- err_dma_handle = h->errinfo_pool_dhandle
- + i * sizeof(*c->err_info);
-
- c->cmdindex = i;
-
- c->busaddr = (u32) cmd_dma_handle;
- temp64.val = (u64) err_dma_handle;
- c->ErrDesc.Addr = cpu_to_le64((u64) err_dma_handle);
- c->ErrDesc.Len = cpu_to_le32((u32) sizeof(*c->err_info));
-
- c->h = h;
+ hpsa_cmd_partial_init(h, i, c);
return c;
}
+/*
+ * This is the complementary operation to cmd_alloc(). Note, however, in some
+ * corner cases it may also be used to free blocks allocated by
+ * cmd_tagged_alloc() in which case the ref-count decrement does the trick and
+ * the clear-bit is harmless.
+ */
static void cmd_free(struct ctlr_info *h, struct CommandList *c)
{
if (atomic_dec_and_test(&c->refcount)) {
@@ -4900,7 +5867,7 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
if (iocommand.buf_size > 0) {
buff = kmalloc(iocommand.buf_size, GFP_KERNEL);
if (buff == NULL)
- return -EFAULT;
+ return -ENOMEM;
if (iocommand.Request.Type.Direction & XFER_WRITE) {
/* Copy the data into the buffer we created */
if (copy_from_user(buff, iocommand.buf,
@@ -4913,12 +5880,10 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
}
}
c = cmd_alloc(h);
- if (c == NULL) {
- rc = -ENOMEM;
- goto out_kfree;
- }
+
/* Fill in the command type */
c->cmd_type = CMD_IOCTL_PEND;
+ c->scsi_cmd = SCSI_CMD_BUSY;
/* Fill in Command Header */
c->Header.ReplyQueue = 0; /* unused in simple mode */
if (iocommand.buf_size > 0) { /* buffer to fill */
@@ -4948,10 +5913,14 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
c->SG[0].Len = cpu_to_le32(iocommand.buf_size);
c->SG[0].Ext = cpu_to_le32(HPSA_SG_LAST); /* not chaining */
}
- hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c);
+ rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT);
if (iocommand.buf_size > 0)
hpsa_pci_unmap(h->pdev, c, 1, PCI_DMA_BIDIRECTIONAL);
check_ioctl_unit_attention(h, c);
+ if (rc) {
+ rc = -EIO;
+ goto out;
+ }
/* Copy the error information out */
memcpy(&iocommand.error_info, c->err_info,
@@ -5048,11 +6017,9 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
sg_used++;
}
c = cmd_alloc(h);
- if (c == NULL) {
- status = -ENOMEM;
- goto cleanup1;
- }
+
c->cmd_type = CMD_IOCTL_PEND;
+ c->scsi_cmd = SCSI_CMD_BUSY;
c->Header.ReplyQueue = 0;
c->Header.SGList = (u8) sg_used;
c->Header.SGTotal = cpu_to_le16(sg_used);
@@ -5078,10 +6045,15 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
}
c->SG[--i].Ext = cpu_to_le32(HPSA_SG_LAST);
}
- hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c);
+ status = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT);
if (sg_used)
hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL);
check_ioctl_unit_attention(h, c);
+ if (status) {
+ status = -EIO;
+ goto cleanup0;
+ }
+
/* Copy the error information out */
memcpy(&ioc->error_info, c->err_info, sizeof(ioc->error_info));
if (copy_to_user(argp, ioc, sizeof(*ioc))) {
@@ -5163,14 +6135,13 @@ static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
}
}
-static int hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr,
+static void hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr,
u8 reset_type)
{
struct CommandList *c;
c = cmd_alloc(h);
- if (!c)
- return -ENOMEM;
+
/* fill_cmd can't fail here, no data buffer to map */
(void) fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0,
RAID_CTLR_LUNID, TYPE_MSG);
@@ -5181,7 +6152,7 @@ static int hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr,
* the command either. This is the last command we will send before
* re-initializing everything, so it doesn't matter and won't leak.
*/
- return 0;
+ return;
}
static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
@@ -5189,9 +6160,10 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
int cmd_type)
{
int pci_dir = XFER_NONE;
- struct CommandList *a; /* for commands to be aborted */
+ u64 tag; /* for commands to be aborted */
c->cmd_type = CMD_IOCTL_PEND;
+ c->scsi_cmd = SCSI_CMD_BUSY;
c->Header.ReplyQueue = 0;
if (buff != NULL && size > 0) {
c->Header.SGList = 1;
@@ -5305,10 +6277,10 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
c->Request.CDB[7] = 0x00;
break;
case HPSA_ABORT_MSG:
- a = buff; /* point to command to be aborted */
+ memcpy(&tag, buff, sizeof(tag));
dev_dbg(&h->pdev->dev,
- "Abort Tag:0x%016llx request Tag:0x%016llx",
- a->Header.tag, c->Header.tag);
+ "Abort Tag:0x%016llx using rqst Tag:0x%016llx",
+ tag, c->Header.tag);
c->Request.CDBLen = 16;
c->Request.type_attr_dir =
TYPE_ATTR_DIR(cmd_type,
@@ -5319,8 +6291,7 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
c->Request.CDB[2] = 0x00; /* reserved */
c->Request.CDB[3] = 0x00; /* reserved */
/* Tag to abort goes in CDB[4]-CDB[11] */
- memcpy(&c->Request.CDB[4], &a->Header.tag,
- sizeof(a->Header.tag));
+ memcpy(&c->Request.CDB[4], &tag, sizeof(tag));
c->Request.CDB[12] = 0x00; /* reserved */
c->Request.CDB[13] = 0x00; /* reserved */
c->Request.CDB[14] = 0x00; /* reserved */
@@ -5399,7 +6370,7 @@ static inline void finish_cmd(struct CommandList *c)
if (likely(c->cmd_type == CMD_IOACCEL1 || c->cmd_type == CMD_SCSI
|| c->cmd_type == CMD_IOACCEL2))
complete_scsi_command(c);
- else if (c->cmd_type == CMD_IOCTL_PEND)
+ else if (c->cmd_type == CMD_IOCTL_PEND || c->cmd_type == IOACCEL2_TMF)
complete(c->waiting);
}
@@ -5733,7 +6704,7 @@ static int controller_reset_failed(struct CfgTable __iomem *cfgtable)
/* This does a hard reset of the controller using PCI power management
* states or the using the doorbell register.
*/
-static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
+static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev, u32 board_id)
{
u64 cfg_offset;
u32 cfg_base_addr;
@@ -5744,7 +6715,6 @@ static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
int rc;
struct CfgTable __iomem *cfgtable;
u32 use_doorbell;
- u32 board_id;
u16 command_register;
/* For controllers as old as the P600, this is very nearly
@@ -5760,11 +6730,6 @@ static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
* using the doorbell register.
*/
- rc = hpsa_lookup_board_id(pdev, &board_id);
- if (rc < 0) {
- dev_warn(&pdev->dev, "Board ID not found\n");
- return rc;
- }
if (!ctlr_is_resettable(board_id)) {
dev_warn(&pdev->dev, "Controller not resettable\n");
return -ENODEV;
@@ -5930,10 +6895,22 @@ static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr)
return -1;
}
+static void hpsa_disable_interrupt_mode(struct ctlr_info *h)
+{
+ if (h->msix_vector) {
+ if (h->pdev->msix_enabled)
+ pci_disable_msix(h->pdev);
+ h->msix_vector = 0;
+ } else if (h->msi_vector) {
+ if (h->pdev->msi_enabled)
+ pci_disable_msi(h->pdev);
+ h->msi_vector = 0;
+ }
+}
+
/* If MSI/MSI-X is supported by the kernel we will try to enable it on
* controllers that are capable. If not, we use legacy INTx mode.
*/
-
static void hpsa_interrupt_mode(struct ctlr_info *h)
{
#ifdef CONFIG_PCI_MSI
@@ -6064,6 +7041,21 @@ static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr,
return 0;
}
+static void hpsa_free_cfgtables(struct ctlr_info *h)
+{
+ if (h->transtable) {
+ iounmap(h->transtable);
+ h->transtable = NULL;
+ }
+ if (h->cfgtable) {
+ iounmap(h->cfgtable);
+ h->cfgtable = NULL;
+ }
+}
+
+/* Find and map CISS config table and transfer table
++ * several items must be unmapped (freed) later
++ * */
static int hpsa_find_cfgtables(struct ctlr_info *h)
{
u64 cfg_offset;
@@ -6090,25 +7082,31 @@ static int hpsa_find_cfgtables(struct ctlr_info *h)
h->transtable = remap_pci_mem(pci_resource_start(h->pdev,
cfg_base_addr_index)+cfg_offset+trans_offset,
sizeof(*h->transtable));
- if (!h->transtable)
+ if (!h->transtable) {
+ dev_err(&h->pdev->dev, "Failed mapping transfer table\n");
+ hpsa_free_cfgtables(h);
return -ENOMEM;
+ }
return 0;
}
static void hpsa_get_max_perf_mode_cmds(struct ctlr_info *h)
{
- h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
+#define MIN_MAX_COMMANDS 16
+ BUILD_BUG_ON(MIN_MAX_COMMANDS <= HPSA_NRESERVED_CMDS);
+
+ h->max_commands = readl(&h->cfgtable->MaxPerformantModeCommands);
/* Limit commands in memory limited kdump scenario. */
if (reset_devices && h->max_commands > 32)
h->max_commands = 32;
- if (h->max_commands < 16) {
- dev_warn(&h->pdev->dev, "Controller reports "
- "max supported commands of %d, an obvious lie. "
- "Using 16. Ensure that firmware is up to date.\n",
- h->max_commands);
- h->max_commands = 16;
+ if (h->max_commands < MIN_MAX_COMMANDS) {
+ dev_warn(&h->pdev->dev,
+ "Controller reports max supported commands of %d Using %d instead. Ensure that firmware is up to date.\n",
+ h->max_commands,
+ MIN_MAX_COMMANDS);
+ h->max_commands = MIN_MAX_COMMANDS;
}
}
@@ -6153,6 +7151,8 @@ static void hpsa_find_board_params(struct ctlr_info *h)
dev_warn(&h->pdev->dev, "Physical aborts not supported\n");
if (!(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags))
dev_warn(&h->pdev->dev, "Logical aborts not supported\n");
+ if (!(HPSATMF_IOACCEL_ENABLED & h->TMFSupportFlags))
+ dev_warn(&h->pdev->dev, "HP SSD Smart Path aborts not supported\n");
}
static inline bool hpsa_CISS_signature_present(struct ctlr_info *h)
@@ -6222,6 +7222,8 @@ static int hpsa_wait_for_mode_change_ack(struct ctlr_info *h)
* as we enter this code.)
*/
for (i = 0; i < MAX_MODE_CHANGE_WAIT; i++) {
+ if (h->remove_in_progress)
+ goto done;
spin_lock_irqsave(&h->lock, flags);
doorbell_value = readl(h->vaddr + SA5_DOORBELL);
spin_unlock_irqrestore(&h->lock, flags);
@@ -6262,6 +7264,22 @@ error:
return -ENODEV;
}
+/* free items allocated or mapped by hpsa_pci_init */
+static void hpsa_free_pci_init(struct ctlr_info *h)
+{
+ hpsa_free_cfgtables(h); /* pci_init 4 */
+ iounmap(h->vaddr); /* pci_init 3 */
+ h->vaddr = NULL;
+ hpsa_disable_interrupt_mode(h); /* pci_init 2 */
+ /*
+ * call pci_disable_device before pci_release_regions per
+ * Documentation/PCI/pci.txt
+ */
+ pci_disable_device(h->pdev); /* pci_init 1 */
+ pci_release_regions(h->pdev); /* pci_init 2 */
+}
+
+/* several items must be freed later */
static int hpsa_pci_init(struct ctlr_info *h)
{
int prod_index, err;
@@ -6272,19 +7290,24 @@ static int hpsa_pci_init(struct ctlr_info *h)
h->product_name = products[prod_index].product_name;
h->access = *(products[prod_index].access);
+ h->needs_abort_tags_swizzled =
+ ctlr_needs_abort_tags_swizzled(h->board_id);
+
pci_disable_link_state(h->pdev, PCIE_LINK_STATE_L0S |
PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
err = pci_enable_device(h->pdev);
if (err) {
- dev_warn(&h->pdev->dev, "unable to enable PCI device\n");
+ dev_err(&h->pdev->dev, "failed to enable PCI device\n");
+ pci_disable_device(h->pdev);
return err;
}
err = pci_request_regions(h->pdev, HPSA);
if (err) {
dev_err(&h->pdev->dev,
- "cannot obtain PCI resources, aborting\n");
+ "failed to obtain PCI resources\n");
+ pci_disable_device(h->pdev);
return err;
}
@@ -6293,38 +7316,43 @@ static int hpsa_pci_init(struct ctlr_info *h)
hpsa_interrupt_mode(h);
err = hpsa_pci_find_memory_BAR(h->pdev, &h->paddr);
if (err)
- goto err_out_free_res;
+ goto clean2; /* intmode+region, pci */
h->vaddr = remap_pci_mem(h->paddr, 0x250);
if (!h->vaddr) {
+ dev_err(&h->pdev->dev, "failed to remap PCI mem\n");
err = -ENOMEM;
- goto err_out_free_res;
+ goto clean2; /* intmode+region, pci */
}
err = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY);
if (err)
- goto err_out_free_res;
+ goto clean3; /* vaddr, intmode+region, pci */
err = hpsa_find_cfgtables(h);
if (err)
- goto err_out_free_res;
+ goto clean3; /* vaddr, intmode+region, pci */
hpsa_find_board_params(h);
if (!hpsa_CISS_signature_present(h)) {
err = -ENODEV;
- goto err_out_free_res;
+ goto clean4; /* cfgtables, vaddr, intmode+region, pci */
}
hpsa_set_driver_support_bits(h);
hpsa_p600_dma_prefetch_quirk(h);
err = hpsa_enter_simple_mode(h);
if (err)
- goto err_out_free_res;
+ goto clean4; /* cfgtables, vaddr, intmode+region, pci */
return 0;
-err_out_free_res:
- if (h->transtable)
- iounmap(h->transtable);
- if (h->cfgtable)
- iounmap(h->cfgtable);
- if (h->vaddr)
- iounmap(h->vaddr);
+clean4: /* cfgtables, vaddr, intmode+region, pci */
+ hpsa_free_cfgtables(h);
+clean3: /* vaddr, intmode+region, pci */
+ iounmap(h->vaddr);
+ h->vaddr = NULL;
+clean2: /* intmode+region, pci */
+ hpsa_disable_interrupt_mode(h);
+ /*
+ * call pci_disable_device before pci_release_regions per
+ * Documentation/PCI/pci.txt
+ */
pci_disable_device(h->pdev);
pci_release_regions(h->pdev);
return err;
@@ -6346,7 +7374,7 @@ static void hpsa_hba_inquiry(struct ctlr_info *h)
}
}
-static int hpsa_init_reset_devices(struct pci_dev *pdev)
+static int hpsa_init_reset_devices(struct pci_dev *pdev, u32 board_id)
{
int rc, i;
void __iomem *vaddr;
@@ -6382,7 +7410,7 @@ static int hpsa_init_reset_devices(struct pci_dev *pdev)
iounmap(vaddr);
/* Reset the controller with a PCI power-cycle or via doorbell */
- rc = hpsa_kdump_hard_reset_controller(pdev);
+ rc = hpsa_kdump_hard_reset_controller(pdev, board_id);
/* -ENOTSUPP here means we cannot reset the controller
* but it's already (and still) up and running in
@@ -6408,7 +7436,29 @@ out_disable:
return rc;
}
-static int hpsa_allocate_cmd_pool(struct ctlr_info *h)
+static void hpsa_free_cmd_pool(struct ctlr_info *h)
+{
+ kfree(h->cmd_pool_bits);
+ h->cmd_pool_bits = NULL;
+ if (h->cmd_pool) {
+ pci_free_consistent(h->pdev,
+ h->nr_cmds * sizeof(struct CommandList),
+ h->cmd_pool,
+ h->cmd_pool_dhandle);
+ h->cmd_pool = NULL;
+ h->cmd_pool_dhandle = 0;
+ }
+ if (h->errinfo_pool) {
+ pci_free_consistent(h->pdev,
+ h->nr_cmds * sizeof(struct ErrorInfo),
+ h->errinfo_pool,
+ h->errinfo_pool_dhandle);
+ h->errinfo_pool = NULL;
+ h->errinfo_pool_dhandle = 0;
+ }
+}
+
+static int hpsa_alloc_cmd_pool(struct ctlr_info *h)
{
h->cmd_pool_bits = kzalloc(
DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG) *
@@ -6425,34 +7475,13 @@ static int hpsa_allocate_cmd_pool(struct ctlr_info *h)
dev_err(&h->pdev->dev, "out of memory in %s", __func__);
goto clean_up;
}
+ hpsa_preinitialize_commands(h);
return 0;
clean_up:
hpsa_free_cmd_pool(h);
return -ENOMEM;
}
-static void hpsa_free_cmd_pool(struct ctlr_info *h)
-{
- kfree(h->cmd_pool_bits);
- if (h->cmd_pool)
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct CommandList),
- h->cmd_pool, h->cmd_pool_dhandle);
- if (h->ioaccel2_cmd_pool)
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool),
- h->ioaccel2_cmd_pool, h->ioaccel2_cmd_pool_dhandle);
- if (h->errinfo_pool)
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct ErrorInfo),
- h->errinfo_pool,
- h->errinfo_pool_dhandle);
- if (h->ioaccel_cmd_pool)
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct io_accel1_cmd),
- h->ioaccel_cmd_pool, h->ioaccel_cmd_pool_dhandle);
-}
-
static void hpsa_irq_affinity_hints(struct ctlr_info *h)
{
int i, cpu;
@@ -6474,12 +7503,14 @@ static void hpsa_free_irqs(struct ctlr_info *h)
i = h->intr_mode;
irq_set_affinity_hint(h->intr[i], NULL);
free_irq(h->intr[i], &h->q[i]);
+ h->q[i] = 0;
return;
}
for (i = 0; i < h->msix_vector; i++) {
irq_set_affinity_hint(h->intr[i], NULL);
free_irq(h->intr[i], &h->q[i]);
+ h->q[i] = 0;
}
for (; i < MAX_REPLY_QUEUES; i++)
h->q[i] = 0;
@@ -6502,8 +7533,9 @@ static int hpsa_request_irqs(struct ctlr_info *h,
if (h->intr_mode == PERF_MODE_INT && h->msix_vector > 0) {
/* If performant mode and MSI-X, use multiple reply queues */
for (i = 0; i < h->msix_vector; i++) {
+ sprintf(h->intrname[i], "%s-msix%d", h->devname, i);
rc = request_irq(h->intr[i], msixhandler,
- 0, h->devname,
+ 0, h->intrname[i],
&h->q[i]);
if (rc) {
int j;
@@ -6524,18 +7556,30 @@ static int hpsa_request_irqs(struct ctlr_info *h,
} else {
/* Use single reply pool */
if (h->msix_vector > 0 || h->msi_vector) {
+ if (h->msix_vector)
+ sprintf(h->intrname[h->intr_mode],
+ "%s-msix", h->devname);
+ else
+ sprintf(h->intrname[h->intr_mode],
+ "%s-msi", h->devname);
rc = request_irq(h->intr[h->intr_mode],
- msixhandler, 0, h->devname,
+ msixhandler, 0,
+ h->intrname[h->intr_mode],
&h->q[h->intr_mode]);
} else {
+ sprintf(h->intrname[h->intr_mode],
+ "%s-intx", h->devname);
rc = request_irq(h->intr[h->intr_mode],
- intxhandler, IRQF_SHARED, h->devname,
+ intxhandler, IRQF_SHARED,
+ h->intrname[h->intr_mode],
&h->q[h->intr_mode]);
}
+ irq_set_affinity_hint(h->intr[h->intr_mode], NULL);
}
if (rc) {
- dev_err(&h->pdev->dev, "unable to get irq %d for %s\n",
+ dev_err(&h->pdev->dev, "failed to get irq %d for %s\n",
h->intr[h->intr_mode], h->devname);
+ hpsa_free_irqs(h);
return -ENODEV;
}
return 0;
@@ -6543,42 +7587,27 @@ static int hpsa_request_irqs(struct ctlr_info *h,
static int hpsa_kdump_soft_reset(struct ctlr_info *h)
{
- if (hpsa_send_host_reset(h, RAID_CTLR_LUNID,
- HPSA_RESET_TYPE_CONTROLLER)) {
- dev_warn(&h->pdev->dev, "Resetting array controller failed.\n");
- return -EIO;
- }
+ int rc;
+ hpsa_send_host_reset(h, RAID_CTLR_LUNID, HPSA_RESET_TYPE_CONTROLLER);
dev_info(&h->pdev->dev, "Waiting for board to soft reset.\n");
- if (hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY)) {
+ rc = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY);
+ if (rc) {
dev_warn(&h->pdev->dev, "Soft reset had no effect.\n");
- return -1;
+ return rc;
}
dev_info(&h->pdev->dev, "Board reset, awaiting READY status.\n");
- if (hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY)) {
+ rc = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY);
+ if (rc) {
dev_warn(&h->pdev->dev, "Board failed to become ready "
"after soft reset.\n");
- return -1;
+ return rc;
}
return 0;
}
-static void hpsa_free_irqs_and_disable_msix(struct ctlr_info *h)
-{
- hpsa_free_irqs(h);
-#ifdef CONFIG_PCI_MSI
- if (h->msix_vector) {
- if (h->pdev->msix_enabled)
- pci_disable_msix(h->pdev);
- } else if (h->msi_vector) {
- if (h->pdev->msi_enabled)
- pci_disable_msi(h->pdev);
- }
-#endif /* CONFIG_PCI_MSI */
-}
-
static void hpsa_free_reply_queues(struct ctlr_info *h)
{
int i;
@@ -6586,30 +7615,36 @@ static void hpsa_free_reply_queues(struct ctlr_info *h)
for (i = 0; i < h->nreply_queues; i++) {
if (!h->reply_queue[i].head)
continue;
- pci_free_consistent(h->pdev, h->reply_queue_size,
- h->reply_queue[i].head, h->reply_queue[i].busaddr);
+ pci_free_consistent(h->pdev,
+ h->reply_queue_size,
+ h->reply_queue[i].head,
+ h->reply_queue[i].busaddr);
h->reply_queue[i].head = NULL;
h->reply_queue[i].busaddr = 0;
}
+ h->reply_queue_size = 0;
}
static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h)
{
- hpsa_free_irqs_and_disable_msix(h);
- hpsa_free_sg_chain_blocks(h);
- hpsa_free_cmd_pool(h);
- kfree(h->ioaccel1_blockFetchTable);
- kfree(h->blockFetchTable);
- hpsa_free_reply_queues(h);
- if (h->vaddr)
- iounmap(h->vaddr);
- if (h->transtable)
- iounmap(h->transtable);
- if (h->cfgtable)
- iounmap(h->cfgtable);
- pci_disable_device(h->pdev);
- pci_release_regions(h->pdev);
- kfree(h);
+ hpsa_free_performant_mode(h); /* init_one 7 */
+ hpsa_free_sg_chain_blocks(h); /* init_one 6 */
+ hpsa_free_cmd_pool(h); /* init_one 5 */
+ hpsa_free_irqs(h); /* init_one 4 */
+ scsi_host_put(h->scsi_host); /* init_one 3 */
+ h->scsi_host = NULL; /* init_one 3 */
+ hpsa_free_pci_init(h); /* init_one 2_5 */
+ free_percpu(h->lockup_detected); /* init_one 2 */
+ h->lockup_detected = NULL; /* init_one 2 */
+ if (h->resubmit_wq) {
+ destroy_workqueue(h->resubmit_wq); /* init_one 1 */
+ h->resubmit_wq = NULL;
+ }
+ if (h->rescan_ctlr_wq) {
+ destroy_workqueue(h->rescan_ctlr_wq);
+ h->rescan_ctlr_wq = NULL;
+ }
+ kfree(h); /* init_one 1 */
}
/* Called when controller lockup detected. */
@@ -6617,17 +7652,22 @@ static void fail_all_outstanding_cmds(struct ctlr_info *h)
{
int i, refcount;
struct CommandList *c;
+ int failcount = 0;
flush_workqueue(h->resubmit_wq); /* ensure all cmds are fully built */
for (i = 0; i < h->nr_cmds; i++) {
c = h->cmd_pool + i;
refcount = atomic_inc_return(&c->refcount);
if (refcount > 1) {
- c->err_info->CommandStatus = CMD_HARDWARE_ERR;
+ c->err_info->CommandStatus = CMD_CTLR_LOCKUP;
finish_cmd(c);
+ atomic_dec(&h->commands_outstanding);
+ failcount++;
}
cmd_free(h, c);
}
+ dev_warn(&h->pdev->dev,
+ "failed %d commands in fail_all\n", failcount);
}
static void set_lockup_detected_for_all_cpus(struct ctlr_info *h, u32 value)
@@ -6653,18 +7693,19 @@ static void controller_lockup_detected(struct ctlr_info *h)
if (!lockup_detected) {
/* no heartbeat, but controller gave us a zero. */
dev_warn(&h->pdev->dev,
- "lockup detected but scratchpad register is zero\n");
+ "lockup detected after %d but scratchpad register is zero\n",
+ h->heartbeat_sample_interval / HZ);
lockup_detected = 0xffffffff;
}
set_lockup_detected_for_all_cpus(h, lockup_detected);
spin_unlock_irqrestore(&h->lock, flags);
- dev_warn(&h->pdev->dev, "Controller lockup detected: 0x%08x\n",
- lockup_detected);
+ dev_warn(&h->pdev->dev, "Controller lockup detected: 0x%08x after %d\n",
+ lockup_detected, h->heartbeat_sample_interval / HZ);
pci_disable_device(h->pdev);
fail_all_outstanding_cmds(h);
}
-static void detect_controller_lockup(struct ctlr_info *h)
+static int detect_controller_lockup(struct ctlr_info *h)
{
u64 now;
u32 heartbeat;
@@ -6674,7 +7715,7 @@ static void detect_controller_lockup(struct ctlr_info *h)
/* If we've received an interrupt recently, we're ok. */
if (time_after64(h->last_intr_timestamp +
(h->heartbeat_sample_interval), now))
- return;
+ return false;
/*
* If we've already checked the heartbeat recently, we're ok.
@@ -6683,7 +7724,7 @@ static void detect_controller_lockup(struct ctlr_info *h)
*/
if (time_after64(h->last_heartbeat_timestamp +
(h->heartbeat_sample_interval), now))
- return;
+ return false;
/* If heartbeat has not changed since we last looked, we're not ok. */
spin_lock_irqsave(&h->lock, flags);
@@ -6691,12 +7732,13 @@ static void detect_controller_lockup(struct ctlr_info *h)
spin_unlock_irqrestore(&h->lock, flags);
if (h->last_heartbeat == heartbeat) {
controller_lockup_detected(h);
- return;
+ return true;
}
/* We're ok. */
h->last_heartbeat = heartbeat;
h->last_heartbeat_timestamp = now;
+ return false;
}
static void hpsa_ack_ctlr_events(struct ctlr_info *h)
@@ -6843,11 +7885,18 @@ static int hpsa_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
struct ctlr_info *h;
int try_soft_reset = 0;
unsigned long flags;
+ u32 board_id;
if (number_of_controllers == 0)
printk(KERN_INFO DRIVER_NAME "\n");
- rc = hpsa_init_reset_devices(pdev);
+ rc = hpsa_lookup_board_id(pdev, &board_id);
+ if (rc < 0) {
+ dev_warn(&pdev->dev, "Board ID not found\n");
+ return rc;
+ }
+
+ rc = hpsa_init_reset_devices(pdev, board_id);
if (rc) {
if (rc != -ENOTSUPP)
return rc;
@@ -6868,42 +7917,41 @@ reinit_after_soft_reset:
*/
BUILD_BUG_ON(sizeof(struct CommandList) % COMMANDLIST_ALIGNMENT);
h = kzalloc(sizeof(*h), GFP_KERNEL);
- if (!h)
+ if (!h) {
+ dev_err(&pdev->dev, "Failed to allocate controller head\n");
return -ENOMEM;
+ }
h->pdev = pdev;
+
h->intr_mode = hpsa_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT;
INIT_LIST_HEAD(&h->offline_device_list);
spin_lock_init(&h->lock);
spin_lock_init(&h->offline_device_lock);
spin_lock_init(&h->scan_lock);
atomic_set(&h->passthru_cmds_avail, HPSA_MAX_CONCURRENT_PASSTHRUS);
-
- h->rescan_ctlr_wq = hpsa_create_controller_wq(h, "rescan");
- if (!h->rescan_ctlr_wq) {
- rc = -ENOMEM;
- goto clean1;
- }
-
- h->resubmit_wq = hpsa_create_controller_wq(h, "resubmit");
- if (!h->resubmit_wq) {
- rc = -ENOMEM;
- goto clean1;
- }
+ atomic_set(&h->abort_cmds_available, HPSA_CMDS_RESERVED_FOR_ABORTS);
/* Allocate and clear per-cpu variable lockup_detected */
h->lockup_detected = alloc_percpu(u32);
if (!h->lockup_detected) {
+ dev_err(&h->pdev->dev, "Failed to allocate lockup detector\n");
rc = -ENOMEM;
- goto clean1;
+ goto clean1; /* aer/h */
}
set_lockup_detected_for_all_cpus(h, 0);
rc = hpsa_pci_init(h);
- if (rc != 0)
- goto clean1;
+ if (rc)
+ goto clean2; /* lu, aer/h */
+
+ /* relies on h-> settings made by hpsa_pci_init, including
+ * interrupt_mode h->intr */
+ rc = hpsa_scsi_host_alloc(h);
+ if (rc)
+ goto clean2_5; /* pci, lu, aer/h */
- sprintf(h->devname, HPSA "%d", number_of_controllers);
+ sprintf(h->devname, HPSA "%d", h->scsi_host->host_no);
h->ctlr = number_of_controllers;
number_of_controllers++;
@@ -6917,34 +7965,57 @@ reinit_after_soft_reset:
dac = 0;
} else {
dev_err(&pdev->dev, "no suitable DMA available\n");
- goto clean1;
+ goto clean3; /* shost, pci, lu, aer/h */
}
}
/* make sure the board interrupts are off */
h->access.set_intr_mask(h, HPSA_INTR_OFF);
- if (hpsa_request_irqs(h, do_hpsa_intr_msi, do_hpsa_intr_intx))
- goto clean2;
- dev_info(&pdev->dev, "%s: <0x%x> at IRQ %d%s using DAC\n",
- h->devname, pdev->device,
- h->intr[h->intr_mode], dac ? "" : " not");
- rc = hpsa_allocate_cmd_pool(h);
+ rc = hpsa_request_irqs(h, do_hpsa_intr_msi, do_hpsa_intr_intx);
+ if (rc)
+ goto clean3; /* shost, pci, lu, aer/h */
+ rc = hpsa_alloc_cmd_pool(h);
if (rc)
- goto clean2_and_free_irqs;
- if (hpsa_allocate_sg_chain_blocks(h))
- goto clean4;
+ goto clean4; /* irq, shost, pci, lu, aer/h */
+ rc = hpsa_alloc_sg_chain_blocks(h);
+ if (rc)
+ goto clean5; /* cmd, irq, shost, pci, lu, aer/h */
init_waitqueue_head(&h->scan_wait_queue);
+ init_waitqueue_head(&h->abort_cmd_wait_queue);
+ init_waitqueue_head(&h->event_sync_wait_queue);
+ mutex_init(&h->reset_mutex);
h->scan_finished = 1; /* no scan currently in progress */
pci_set_drvdata(pdev, h);
h->ndevices = 0;
h->hba_mode_enabled = 0;
- h->scsi_host = NULL;
+
spin_lock_init(&h->devlock);
- hpsa_put_ctlr_into_performant_mode(h);
+ rc = hpsa_put_ctlr_into_performant_mode(h);
+ if (rc)
+ goto clean6; /* sg, cmd, irq, shost, pci, lu, aer/h */
+
+ /* hook into SCSI subsystem */
+ rc = hpsa_scsi_add_host(h);
+ if (rc)
+ goto clean7; /* perf, sg, cmd, irq, shost, pci, lu, aer/h */
+
+ /* create the resubmit workqueue */
+ h->rescan_ctlr_wq = hpsa_create_controller_wq(h, "rescan");
+ if (!h->rescan_ctlr_wq) {
+ rc = -ENOMEM;
+ goto clean7;
+ }
- /* At this point, the controller is ready to take commands.
+ h->resubmit_wq = hpsa_create_controller_wq(h, "resubmit");
+ if (!h->resubmit_wq) {
+ rc = -ENOMEM;
+ goto clean7; /* aer/h */
+ }
+
+ /*
+ * At this point, the controller is ready to take commands.
* Now, if reset_devices and the hard reset didn't work, try
* the soft reset and see if that works.
*/
@@ -6966,13 +8037,24 @@ reinit_after_soft_reset:
if (rc) {
dev_warn(&h->pdev->dev,
"Failed to request_irq after soft reset.\n");
- goto clean4;
+ /*
+ * cannot goto clean7 or free_irqs will be called
+ * again. Instead, do its work
+ */
+ hpsa_free_performant_mode(h); /* clean7 */
+ hpsa_free_sg_chain_blocks(h); /* clean6 */
+ hpsa_free_cmd_pool(h); /* clean5 */
+ /*
+ * skip hpsa_free_irqs(h) clean4 since that
+ * was just called before request_irqs failed
+ */
+ goto clean3;
}
rc = hpsa_kdump_soft_reset(h);
if (rc)
/* Neither hard nor soft reset worked, we're hosed. */
- goto clean4;
+ goto clean9;
dev_info(&h->pdev->dev, "Board READY.\n");
dev_info(&h->pdev->dev,
@@ -6993,21 +8075,20 @@ reinit_after_soft_reset:
hpsa_undo_allocations_after_kdump_soft_reset(h);
try_soft_reset = 0;
if (rc)
- /* don't go to clean4, we already unallocated */
+ /* don't goto clean, we already unallocated */
return -ENODEV;
goto reinit_after_soft_reset;
}
- /* Enable Accelerated IO path at driver layer */
- h->acciopath_status = 1;
+ /* Enable Accelerated IO path at driver layer */
+ h->acciopath_status = 1;
/* Turn the interrupts on so we can service requests */
h->access.set_intr_mask(h, HPSA_INTR_ON);
hpsa_hba_inquiry(h);
- hpsa_register_scsi(h); /* hook ourselves into SCSI subsystem */
/* Monitor the controller for firmware lockups */
h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL;
@@ -7019,19 +8100,36 @@ reinit_after_soft_reset:
h->heartbeat_sample_interval);
return 0;
-clean4:
+clean9: /* wq, sh, perf, sg, cmd, irq, shost, pci, lu, aer/h */
+ kfree(h->hba_inquiry_data);
+clean7: /* perf, sg, cmd, irq, shost, pci, lu, aer/h */
+ hpsa_free_performant_mode(h);
+ h->access.set_intr_mask(h, HPSA_INTR_OFF);
+clean6: /* sg, cmd, irq, pci, lockup, wq/aer/h */
hpsa_free_sg_chain_blocks(h);
+clean5: /* cmd, irq, shost, pci, lu, aer/h */
hpsa_free_cmd_pool(h);
-clean2_and_free_irqs:
+clean4: /* irq, shost, pci, lu, aer/h */
hpsa_free_irqs(h);
-clean2:
-clean1:
- if (h->resubmit_wq)
+clean3: /* shost, pci, lu, aer/h */
+ scsi_host_put(h->scsi_host);
+ h->scsi_host = NULL;
+clean2_5: /* pci, lu, aer/h */
+ hpsa_free_pci_init(h);
+clean2: /* lu, aer/h */
+ if (h->lockup_detected) {
+ free_percpu(h->lockup_detected);
+ h->lockup_detected = NULL;
+ }
+clean1: /* wq/aer/h */
+ if (h->resubmit_wq) {
destroy_workqueue(h->resubmit_wq);
- if (h->rescan_ctlr_wq)
+ h->resubmit_wq = NULL;
+ }
+ if (h->rescan_ctlr_wq) {
destroy_workqueue(h->rescan_ctlr_wq);
- if (h->lockup_detected)
- free_percpu(h->lockup_detected);
+ h->rescan_ctlr_wq = NULL;
+ }
kfree(h);
return rc;
}
@@ -7040,8 +8138,8 @@ static void hpsa_flush_cache(struct ctlr_info *h)
{
char *flush_buf;
struct CommandList *c;
+ int rc;
- /* Don't bother trying to flush the cache if locked up */
if (unlikely(lockup_detected(h)))
return;
flush_buf = kzalloc(4, GFP_KERNEL);
@@ -7049,21 +8147,20 @@ static void hpsa_flush_cache(struct ctlr_info *h)
return;
c = cmd_alloc(h);
- if (!c) {
- dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
- goto out_of_memory;
- }
+
if (fill_cmd(c, HPSA_CACHE_FLUSH, h, flush_buf, 4, 0,
RAID_CTLR_LUNID, TYPE_CMD)) {
goto out;
}
- hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_TODEVICE);
+ rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+ PCI_DMA_TODEVICE, NO_TIMEOUT);
+ if (rc)
+ goto out;
if (c->err_info->CommandStatus != 0)
out:
dev_warn(&h->pdev->dev,
"error flushing cache on controller\n");
cmd_free(h, c);
-out_of_memory:
kfree(flush_buf);
}
@@ -7078,15 +8175,18 @@ static void hpsa_shutdown(struct pci_dev *pdev)
*/
hpsa_flush_cache(h);
h->access.set_intr_mask(h, HPSA_INTR_OFF);
- hpsa_free_irqs_and_disable_msix(h);
+ hpsa_free_irqs(h); /* init_one 4 */
+ hpsa_disable_interrupt_mode(h); /* pci_init 2 */
}
static void hpsa_free_device_info(struct ctlr_info *h)
{
int i;
- for (i = 0; i < h->ndevices; i++)
+ for (i = 0; i < h->ndevices; i++) {
kfree(h->dev[i]);
+ h->dev[i] = NULL;
+ }
}
static void hpsa_remove_one(struct pci_dev *pdev)
@@ -7108,29 +8208,34 @@ static void hpsa_remove_one(struct pci_dev *pdev)
cancel_delayed_work_sync(&h->rescan_ctlr_work);
destroy_workqueue(h->rescan_ctlr_wq);
destroy_workqueue(h->resubmit_wq);
- hpsa_unregister_scsi(h); /* unhook from SCSI subsystem */
+
+ /* includes hpsa_free_irqs - init_one 4 */
+ /* includes hpsa_disable_interrupt_mode - pci_init 2 */
hpsa_shutdown(pdev);
- iounmap(h->vaddr);
- iounmap(h->transtable);
- iounmap(h->cfgtable);
- hpsa_free_device_info(h);
- hpsa_free_sg_chain_blocks(h);
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct CommandList),
- h->cmd_pool, h->cmd_pool_dhandle);
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct ErrorInfo),
- h->errinfo_pool, h->errinfo_pool_dhandle);
- hpsa_free_reply_queues(h);
- kfree(h->cmd_pool_bits);
- kfree(h->blockFetchTable);
- kfree(h->ioaccel1_blockFetchTable);
- kfree(h->ioaccel2_blockFetchTable);
- kfree(h->hba_inquiry_data);
- pci_disable_device(pdev);
- pci_release_regions(pdev);
- free_percpu(h->lockup_detected);
- kfree(h);
+
+ hpsa_free_device_info(h); /* scan */
+
+ kfree(h->hba_inquiry_data); /* init_one 10 */
+ h->hba_inquiry_data = NULL; /* init_one 10 */
+ if (h->scsi_host)
+ scsi_remove_host(h->scsi_host); /* init_one 8 */
+ hpsa_free_ioaccel2_sg_chain_blocks(h);
+ hpsa_free_performant_mode(h); /* init_one 7 */
+ hpsa_free_sg_chain_blocks(h); /* init_one 6 */
+ hpsa_free_cmd_pool(h); /* init_one 5 */
+
+ /* hpsa_free_irqs already called via hpsa_shutdown init_one 4 */
+
+ scsi_host_put(h->scsi_host); /* init_one 3 */
+ h->scsi_host = NULL; /* init_one 3 */
+
+ /* includes hpsa_disable_interrupt_mode - pci_init 2 */
+ hpsa_free_pci_init(h); /* init_one 2.5 */
+
+ free_percpu(h->lockup_detected); /* init_one 2 */
+ h->lockup_detected = NULL; /* init_one 2 */
+ /* (void) pci_disable_pcie_error_reporting(pdev); */ /* init_one 1 */
+ kfree(h); /* init_one 1 */
}
static int hpsa_suspend(__attribute__((unused)) struct pci_dev *pdev,
@@ -7188,7 +8293,10 @@ static void calc_bucket_map(int bucket[], int num_buckets,
}
}
-/* return -ENODEV or other reason on error, 0 on success */
+/*
+ * return -ENODEV on err, 0 on success (or no action)
+ * allocates numerous items that must be freed later
+ */
static int hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
{
int i;
@@ -7370,7 +8478,23 @@ static int hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
return 0;
}
-static int hpsa_alloc_ioaccel_cmd_and_bft(struct ctlr_info *h)
+/* Free ioaccel1 mode command blocks and block fetch table */
+static void hpsa_free_ioaccel1_cmd_and_bft(struct ctlr_info *h)
+{
+ if (h->ioaccel_cmd_pool) {
+ pci_free_consistent(h->pdev,
+ h->nr_cmds * sizeof(*h->ioaccel_cmd_pool),
+ h->ioaccel_cmd_pool,
+ h->ioaccel_cmd_pool_dhandle);
+ h->ioaccel_cmd_pool = NULL;
+ h->ioaccel_cmd_pool_dhandle = 0;
+ }
+ kfree(h->ioaccel1_blockFetchTable);
+ h->ioaccel1_blockFetchTable = NULL;
+}
+
+/* Allocate ioaccel1 mode command blocks and block fetch table */
+static int hpsa_alloc_ioaccel1_cmd_and_bft(struct ctlr_info *h)
{
h->ioaccel_maxsg =
readl(&(h->cfgtable->io_accel_max_embedded_sg_count));
@@ -7401,16 +8525,32 @@ static int hpsa_alloc_ioaccel_cmd_and_bft(struct ctlr_info *h)
return 0;
clean_up:
- if (h->ioaccel_cmd_pool)
+ hpsa_free_ioaccel1_cmd_and_bft(h);
+ return -ENOMEM;
+}
+
+/* Free ioaccel2 mode command blocks and block fetch table */
+static void hpsa_free_ioaccel2_cmd_and_bft(struct ctlr_info *h)
+{
+ hpsa_free_ioaccel2_sg_chain_blocks(h);
+
+ if (h->ioaccel2_cmd_pool) {
pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(*h->ioaccel_cmd_pool),
- h->ioaccel_cmd_pool, h->ioaccel_cmd_pool_dhandle);
- kfree(h->ioaccel1_blockFetchTable);
- return 1;
+ h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool),
+ h->ioaccel2_cmd_pool,
+ h->ioaccel2_cmd_pool_dhandle);
+ h->ioaccel2_cmd_pool = NULL;
+ h->ioaccel2_cmd_pool_dhandle = 0;
+ }
+ kfree(h->ioaccel2_blockFetchTable);
+ h->ioaccel2_blockFetchTable = NULL;
}
-static int ioaccel2_alloc_cmds_and_bft(struct ctlr_info *h)
+/* Allocate ioaccel2 mode command blocks and block fetch table */
+static int hpsa_alloc_ioaccel2_cmd_and_bft(struct ctlr_info *h)
{
+ int rc;
+
/* Allocate ioaccel2 mode command blocks and block fetch table */
h->ioaccel_maxsg =
@@ -7430,7 +8570,13 @@ static int ioaccel2_alloc_cmds_and_bft(struct ctlr_info *h)
sizeof(u32)), GFP_KERNEL);
if ((h->ioaccel2_cmd_pool == NULL) ||
- (h->ioaccel2_blockFetchTable == NULL))
+ (h->ioaccel2_blockFetchTable == NULL)) {
+ rc = -ENOMEM;
+ goto clean_up;
+ }
+
+ rc = hpsa_allocate_ioaccel2_sg_chain_blocks(h);
+ if (rc)
goto clean_up;
memset(h->ioaccel2_cmd_pool, 0,
@@ -7438,41 +8584,50 @@ static int ioaccel2_alloc_cmds_and_bft(struct ctlr_info *h)
return 0;
clean_up:
- if (h->ioaccel2_cmd_pool)
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool),
- h->ioaccel2_cmd_pool, h->ioaccel2_cmd_pool_dhandle);
- kfree(h->ioaccel2_blockFetchTable);
- return 1;
+ hpsa_free_ioaccel2_cmd_and_bft(h);
+ return rc;
}
-static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
+/* Free items allocated by hpsa_put_ctlr_into_performant_mode */
+static void hpsa_free_performant_mode(struct ctlr_info *h)
+{
+ kfree(h->blockFetchTable);
+ h->blockFetchTable = NULL;
+ hpsa_free_reply_queues(h);
+ hpsa_free_ioaccel1_cmd_and_bft(h);
+ hpsa_free_ioaccel2_cmd_and_bft(h);
+}
+
+/* return -ENODEV on error, 0 on success (or no action)
+ * allocates numerous items that must be freed later
+ */
+static int hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
{
u32 trans_support;
unsigned long transMethod = CFGTBL_Trans_Performant |
CFGTBL_Trans_use_short_tags;
- int i;
+ int i, rc;
if (hpsa_simple_mode)
- return;
+ return 0;
trans_support = readl(&(h->cfgtable->TransportSupport));
if (!(trans_support & PERFORMANT_MODE))
- return;
+ return 0;
/* Check for I/O accelerator mode support */
if (trans_support & CFGTBL_Trans_io_accel1) {
transMethod |= CFGTBL_Trans_io_accel1 |
CFGTBL_Trans_enable_directed_msix;
- if (hpsa_alloc_ioaccel_cmd_and_bft(h))
- goto clean_up;
- } else {
- if (trans_support & CFGTBL_Trans_io_accel2) {
- transMethod |= CFGTBL_Trans_io_accel2 |
+ rc = hpsa_alloc_ioaccel1_cmd_and_bft(h);
+ if (rc)
+ return rc;
+ } else if (trans_support & CFGTBL_Trans_io_accel2) {
+ transMethod |= CFGTBL_Trans_io_accel2 |
CFGTBL_Trans_enable_directed_msix;
- if (ioaccel2_alloc_cmds_and_bft(h))
- goto clean_up;
- }
+ rc = hpsa_alloc_ioaccel2_cmd_and_bft(h);
+ if (rc)
+ return rc;
}
h->nreply_queues = h->msix_vector > 0 ? h->msix_vector : 1;
@@ -7484,8 +8639,10 @@ static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
h->reply_queue[i].head = pci_alloc_consistent(h->pdev,
h->reply_queue_size,
&(h->reply_queue[i].busaddr));
- if (!h->reply_queue[i].head)
- goto clean_up;
+ if (!h->reply_queue[i].head) {
+ rc = -ENOMEM;
+ goto clean1; /* rq, ioaccel */
+ }
h->reply_queue[i].size = h->max_commands;
h->reply_queue[i].wraparound = 1; /* spec: init to 1 */
h->reply_queue[i].current_entry = 0;
@@ -7494,15 +8651,24 @@ static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
/* Need a block fetch table for performant mode */
h->blockFetchTable = kmalloc(((SG_ENTRIES_IN_CMD + 1) *
sizeof(u32)), GFP_KERNEL);
- if (!h->blockFetchTable)
- goto clean_up;
+ if (!h->blockFetchTable) {
+ rc = -ENOMEM;
+ goto clean1; /* rq, ioaccel */
+ }
- hpsa_enter_performant_mode(h, trans_support);
- return;
+ rc = hpsa_enter_performant_mode(h, trans_support);
+ if (rc)
+ goto clean2; /* bft, rq, ioaccel */
+ return 0;
-clean_up:
- hpsa_free_reply_queues(h);
+clean2: /* bft, rq, ioaccel */
kfree(h->blockFetchTable);
+ h->blockFetchTable = NULL;
+clean1: /* rq, ioaccel */
+ hpsa_free_reply_queues(h);
+ hpsa_free_ioaccel1_cmd_and_bft(h);
+ hpsa_free_ioaccel2_cmd_and_bft(h);
+ return rc;
}
static int is_accelerated_cmd(struct CommandList *c)
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index 657713050349..6ee4da6b1153 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -47,6 +47,7 @@ struct hpsa_scsi_dev_t {
unsigned char raid_level; /* from inquiry page 0xC1 */
unsigned char volume_offline; /* discovered via TUR or VPD */
u16 queue_depth; /* max queue_depth for this device */
+ atomic_t reset_cmds_out; /* Count of commands to-be affected */
atomic_t ioaccel_cmds_out; /* Only used for physical devices
* counts commands sent to physical
* device via "ioaccel" path.
@@ -54,6 +55,8 @@ struct hpsa_scsi_dev_t {
u32 ioaccel_handle;
int offload_config; /* I/O accel RAID offload configured */
int offload_enabled; /* I/O accel RAID offload enabled */
+ int offload_to_be_enabled;
+ int hba_ioaccel_enabled;
int offload_to_mirror; /* Send next I/O accelerator RAID
* offload request to mirror drive
*/
@@ -68,6 +71,13 @@ struct hpsa_scsi_dev_t {
* devices in order to honor physical device queue depth limits.
*/
struct hpsa_scsi_dev_t *phys_disk[RAID_MAP_MAX_ENTRIES];
+ int nphysical_disks;
+ int supports_aborts;
+#define HPSA_DO_NOT_EXPOSE 0x0
+#define HPSA_SG_ATTACH 0x1
+#define HPSA_ULD_ATTACH 0x2
+#define HPSA_SCSI_ADD (HPSA_SG_ATTACH | HPSA_ULD_ATTACH)
+ u8 expose_state;
};
struct reply_queue_buffer {
@@ -133,7 +143,6 @@ struct ctlr_info {
struct CfgTable __iomem *cfgtable;
int interrupts_enabled;
int max_commands;
- int last_allocation;
atomic_t commands_outstanding;
# define PERF_MODE_INT 0
# define DOORBELL_INT 1
@@ -154,6 +163,7 @@ struct ctlr_info {
u8 max_cmd_sg_entries;
int chainsize;
struct SGDescriptor **cmd_sg_list;
+ struct ioaccel2_sg_element **ioaccel2_cmd_sg_list;
/* pointers to command and error info pool */
struct CommandList *cmd_pool;
@@ -211,6 +221,7 @@ struct ctlr_info {
int remove_in_progress;
/* Address of h->q[x] is passed to intr handler to know which queue */
u8 q[MAX_REPLY_QUEUES];
+ char intrname[MAX_REPLY_QUEUES][16]; /* "hpsa0-msix00" names */
u32 TMFSupportFlags; /* cache what task mgmt funcs are supported. */
#define HPSATMF_BITS_SUPPORTED (1 << 0)
#define HPSATMF_PHYS_LUN_RESET (1 << 1)
@@ -222,6 +233,7 @@ struct ctlr_info {
#define HPSATMF_PHYS_QRY_TASK (1 << 7)
#define HPSATMF_PHYS_QRY_TSET (1 << 8)
#define HPSATMF_PHYS_QRY_ASYNC (1 << 9)
+#define HPSATMF_IOACCEL_ENABLED (1 << 15)
#define HPSATMF_MASK_SUPPORTED (1 << 16)
#define HPSATMF_LOG_LUN_RESET (1 << 17)
#define HPSATMF_LOG_NEX_RESET (1 << 18)
@@ -251,8 +263,13 @@ struct ctlr_info {
struct list_head offline_device_list;
int acciopath_status;
int raid_offload_debug;
+ int needs_abort_tags_swizzled;
struct workqueue_struct *resubmit_wq;
struct workqueue_struct *rescan_ctlr_wq;
+ atomic_t abort_cmds_available;
+ wait_queue_head_t abort_cmd_wait_queue;
+ wait_queue_head_t event_sync_wait_queue;
+ struct mutex reset_mutex;
};
struct offline_device_entry {
diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
index 3a621c74b76f..c601622cc98e 100644
--- a/drivers/scsi/hpsa_cmd.h
+++ b/drivers/scsi/hpsa_cmd.h
@@ -42,8 +42,22 @@
#define CMD_UNSOLICITED_ABORT 0x000A
#define CMD_TIMEOUT 0x000B
#define CMD_UNABORTABLE 0x000C
+#define CMD_TMF_STATUS 0x000D
#define CMD_IOACCEL_DISABLED 0x000E
+#define CMD_CTLR_LOCKUP 0xffff
+/* Note: CMD_CTLR_LOCKUP is not a value defined by the CISS spec
+ * it is a value defined by the driver that commands can be marked
+ * with when a controller lockup has been detected by the driver
+ */
+/* TMF function status values */
+#define CISS_TMF_COMPLETE 0x00
+#define CISS_TMF_INVALID_FRAME 0x02
+#define CISS_TMF_NOT_SUPPORTED 0x04
+#define CISS_TMF_FAILED 0x05
+#define CISS_TMF_SUCCESS 0x08
+#define CISS_TMF_WRONG_LUN 0x09
+#define CISS_TMF_OVERLAPPED_TAG 0x0a
/* Unit Attentions ASC's as defined for the MSA2012sa */
#define POWER_OR_RESET 0x29
@@ -240,6 +254,7 @@ struct ReportLUNdata {
struct ext_report_lun_entry {
u8 lunid[8];
+#define MASKED_DEVICE(x) ((x)[3] & 0xC0)
#define GET_BMIC_BUS(lunid) ((lunid)[7] & 0x3F)
#define GET_BMIC_LEVEL_TWO_TARGET(lunid) ((lunid)[6])
#define GET_BMIC_DRIVE_NUMBER(lunid) (((GET_BMIC_BUS((lunid)) - 1) << 8) + \
@@ -247,6 +262,8 @@ struct ext_report_lun_entry {
u8 wwid[8];
u8 device_type;
u8 device_flags;
+#define NON_DISK_PHYS_DEV(x) ((x)[17] & 0x01)
+#define PHYS_IOACCEL(x) ((x)[17] & 0x08)
u8 lun_count; /* multi-lun device, how many luns */
u8 redundant_paths;
u32 ioaccel_handle; /* ioaccel1 only uses lower 16 bits */
@@ -379,6 +396,7 @@ struct ErrorInfo {
#define CMD_SCSI 0x03
#define CMD_IOACCEL1 0x04
#define CMD_IOACCEL2 0x05
+#define IOACCEL2_TMF 0x06
#define DIRECT_LOOKUP_SHIFT 4
#define DIRECT_LOOKUP_MASK (~((1 << DIRECT_LOOKUP_SHIFT) - 1))
@@ -421,7 +439,10 @@ struct CommandList {
* not used.
*/
struct hpsa_scsi_dev_t *phys_disk;
- atomic_t refcount; /* Must be last to avoid memset in cmd_alloc */
+
+ int abort_pending;
+ struct hpsa_scsi_dev_t *reset_pending;
+ atomic_t refcount; /* Must be last to avoid memset in hpsa_cmd_init() */
} __aligned(COMMANDLIST_ALIGNMENT);
/* Max S/G elements in I/O accelerator command */
@@ -515,6 +536,12 @@ struct io_accel2_scsi_response {
#define IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL 0x28
#define IOACCEL2_STATUS_SR_TASK_COMP_ABORTED 0x40
#define IOACCEL2_STATUS_SR_IOACCEL_DISABLED 0x0E
+#define IOACCEL2_STATUS_SR_IO_ERROR 0x01
+#define IOACCEL2_STATUS_SR_IO_ABORTED 0x02
+#define IOACCEL2_STATUS_SR_NO_PATH_TO_DEVICE 0x03
+#define IOACCEL2_STATUS_SR_INVALID_DEVICE 0x04
+#define IOACCEL2_STATUS_SR_UNDERRUN 0x51
+#define IOACCEL2_STATUS_SR_OVERRUN 0x75
u8 data_present; /* low 2 bits */
#define IOACCEL2_NO_DATAPRESENT 0x000
#define IOACCEL2_RESPONSE_DATAPRESENT 0x001
@@ -567,6 +594,7 @@ struct io_accel2_cmd {
#define IOACCEL2_DIR_NO_DATA 0x00
#define IOACCEL2_DIR_DATA_IN 0x01
#define IOACCEL2_DIR_DATA_OUT 0x02
+#define IOACCEL2_TMF_ABORT 0x01
/*
* SCSI Task Management Request format for Accelerator Mode 2
*/
@@ -575,13 +603,13 @@ struct hpsa_tmf_struct {
u8 reply_queue; /* Reply Queue ID */
u8 tmf; /* Task Management Function */
u8 reserved1; /* byte 3 Reserved */
- u32 it_nexus; /* SCSI I-T Nexus */
+ __le32 it_nexus; /* SCSI I-T Nexus */
u8 lun_id[8]; /* LUN ID for TMF request */
__le64 tag; /* cciss tag associated w/ request */
__le64 abort_tag; /* cciss tag of SCSI cmd or TMF to abort */
__le64 error_ptr; /* Error Pointer */
__le32 error_len; /* Error Length */
-};
+} __aligned(IOACCEL2_COMMANDLIST_ALIGNMENT);
/* Configuration Table Structure */
struct HostWrite {
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index acea5d6eebd0..6a41c36b16b0 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -1053,7 +1053,7 @@ static int ibmvscsi_queuecommand_lck(struct scsi_cmnd *cmnd,
memset(srp_cmd, 0x00, SRP_MAX_IU_LEN);
srp_cmd->opcode = SRP_CMD;
memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(srp_cmd->cdb));
- srp_cmd->lun = cpu_to_be64(((u64)lun) << 48);
+ int_to_scsilun(lun, &srp_cmd->lun);
if (!map_data_for_srp_cmd(cmnd, evt_struct, srp_cmd, hostdata->dev)) {
if (!firmware_has_feature(FW_FEATURE_CMO))
@@ -1529,7 +1529,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
/* Set up an abort SRP command */
memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt));
tsk_mgmt->opcode = SRP_TSK_MGMT;
- tsk_mgmt->lun = cpu_to_be64(((u64) lun) << 48);
+ int_to_scsilun(lun, &tsk_mgmt->lun);
tsk_mgmt->tsk_mgmt_func = SRP_TSK_ABORT_TASK;
tsk_mgmt->task_tag = (u64) found_evt;
@@ -1652,7 +1652,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
/* Set up a lun reset SRP command */
memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt));
tsk_mgmt->opcode = SRP_TSK_MGMT;
- tsk_mgmt->lun = cpu_to_be64(((u64) lun) << 48);
+ int_to_scsilun(lun, &tsk_mgmt->lun);
tsk_mgmt->tsk_mgmt_func = SRP_TSK_LUN_RESET;
evt->sync_srp = &srp_rsp;
diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c
index 89a8266560d0..4e1a632ccf16 100644
--- a/drivers/scsi/imm.c
+++ b/drivers/scsi/imm.c
@@ -1109,7 +1109,6 @@ static struct scsi_host_template imm_template = {
.bios_param = imm_biosparam,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = ENABLE_CLUSTERING,
.can_queue = 1,
.slave_alloc = imm_adjust_queue,
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
index e5dae7b54d9a..6a926bae76b2 100644
--- a/drivers/scsi/initio.c
+++ b/drivers/scsi/initio.c
@@ -2833,7 +2833,6 @@ static struct scsi_host_template initio_template = {
.can_queue = MAX_TARGETS * i91u_MAXQUEUE,
.this_id = 1,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = ENABLE_CLUSTERING,
};
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
index 47412cf4eaac..73790a1d0969 100644
--- a/drivers/scsi/ipr.h
+++ b/drivers/scsi/ipr.h
@@ -272,7 +272,7 @@
#define IPR_RUNTIME_RESET 0x40000000
#define IPR_IPL_INIT_MIN_STAGE_TIME 5
-#define IPR_IPL_INIT_DEFAULT_STAGE_TIME 15
+#define IPR_IPL_INIT_DEFAULT_STAGE_TIME 30
#define IPR_IPL_INIT_STAGE_UNKNOWN 0x0
#define IPR_IPL_INIT_STAGE_TRANSOP 0xB0000000
#define IPR_IPL_INIT_STAGE_MASK 0xff000000
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
index 7542f11d3fcd..02cb76fd4420 100644
--- a/drivers/scsi/ips.c
+++ b/drivers/scsi/ips.c
@@ -206,10 +206,6 @@ module_param(ips, charp, 0);
#define IPS_VERSION_HIGH IPS_VER_MAJOR_STRING "." IPS_VER_MINOR_STRING
#define IPS_VERSION_LOW "." IPS_VER_BUILD_STRING " "
-#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
-#warning "This driver has only been tested on the x86/ia64/x86_64 platforms"
-#endif
-
#define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \
DMA_NONE == scb->scsi_cmd->sc_data_direction) ? \
PCI_DMA_BIDIRECTIONAL : \
@@ -6788,6 +6784,11 @@ ips_remove_device(struct pci_dev *pci_dev)
static int __init
ips_module_init(void)
{
+#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
+ printk(KERN_ERR "ips: This driver has only been tested on the x86/ia64/x86_64 platforms\n");
+ add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+#endif
+
if (pci_register_driver(&ips_pci_driver) < 0)
return -ENODEV;
ips_driver_template.module = THIS_MODULE;
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
index cd41b63a2f10..0dfcabe3ca7c 100644
--- a/drivers/scsi/isci/init.c
+++ b/drivers/scsi/isci/init.c
@@ -160,7 +160,6 @@ static struct scsi_host_template isci_sht = {
.change_queue_depth = sas_change_queue_depth,
.bios_param = sas_bios_param,
.can_queue = ISCI_CAN_QUEUE_VAL,
- .cmd_per_lun = 1,
.this_id = -1,
.sg_tablesize = SG_ALL,
.max_sectors = SCSI_DEFAULT_MAX_SECTORS,
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 9b81a34d7449..a5a56fa31e70 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -230,6 +230,8 @@ struct lpfc_stats {
uint32_t elsRcvRRQ;
uint32_t elsRcvRTV;
uint32_t elsRcvECHO;
+ uint32_t elsRcvLCB;
+ uint32_t elsRcvRDP;
uint32_t elsXmitFLOGI;
uint32_t elsXmitFDISC;
uint32_t elsXmitPLOGI;
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 587e3e962f2b..b0e6fe46448d 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -498,3 +498,5 @@ bool lpfc_disable_oas_lun(struct lpfc_hba *, struct lpfc_name *,
bool lpfc_find_next_oas_lun(struct lpfc_hba *, struct lpfc_name *,
struct lpfc_name *, uint64_t *, struct lpfc_name *,
struct lpfc_name *, uint64_t *, uint32_t *);
+int lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox);
+void lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb);
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 513edcb0c2da..25aa9b98d53a 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -710,7 +710,7 @@ lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
@@ -760,7 +760,7 @@ out:
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
@@ -810,7 +810,7 @@ out:
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
@@ -852,7 +852,7 @@ out:
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
@@ -894,7 +894,7 @@ out:
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
@@ -1115,7 +1115,7 @@ lpfc_debugfs_dif_err_release(struct inode *inode, struct file *file)
* returns a pointer to that log in the private_data field in @file.
*
* Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
* error value.
**/
static int
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h
index 6977027979be..361f5b3d9d93 100644
--- a/drivers/scsi/lpfc/lpfc_disc.h
+++ b/drivers/scsi/lpfc/lpfc_disc.h
@@ -79,7 +79,6 @@ struct lpfc_nodelist {
struct lpfc_name nlp_portname;
struct lpfc_name nlp_nodename;
uint32_t nlp_flag; /* entry flags */
- uint32_t nlp_add_flag; /* additional flags */
uint32_t nlp_DID; /* FC D_ID of entry */
uint32_t nlp_last_elscmd; /* Last ELS cmd sent */
uint16_t nlp_type;
@@ -147,6 +146,7 @@ struct lpfc_node_rrq {
#define NLP_LOGO_ACC 0x00100000 /* Process LOGO after ACC completes */
#define NLP_TGT_NO_SCSIID 0x00200000 /* good PRLI but no binding for scsid */
#define NLP_ISSUE_LOGO 0x00400000 /* waiting to issue a LOGO */
+#define NLP_IN_DEV_LOSS 0x00800000 /* devloss in progress */
#define NLP_ACC_REGLOGIN 0x01000000 /* Issue Reg Login after successful
ACC */
#define NLP_NPR_ADISC 0x02000000 /* Issue ADISC when dq'ed from
@@ -158,8 +158,6 @@ struct lpfc_node_rrq {
#define NLP_FIRSTBURST 0x40000000 /* Target supports FirstBurst */
#define NLP_RPI_REGISTERED 0x80000000 /* nlp_rpi is valid */
-/* Defines for nlp_add_flag (uint32) */
-#define NLP_IN_DEV_LOSS 0x00000001 /* Dev Loss processing in progress */
/* ndlp usage management macros */
#define NLP_CHK_NODE_ACT(ndlp) (((ndlp)->nlp_usg_map \
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 851e8efe364e..36bf58ba750a 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1509,12 +1509,14 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
struct lpfc_nodelist *ndlp)
{
struct lpfc_vport *vport = ndlp->vport;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_nodelist *new_ndlp;
struct lpfc_rport_data *rdata;
struct fc_rport *rport;
struct serv_parm *sp;
uint8_t name[sizeof(struct lpfc_name)];
- uint32_t rc, keepDID = 0;
+ uint32_t rc, keepDID = 0, keep_nlp_flag = 0;
+ uint16_t keep_nlp_state;
int put_node;
int put_rport;
unsigned long *active_rrqs_xri_bitmap = NULL;
@@ -1603,11 +1605,14 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
ndlp->active_rrqs_xri_bitmap,
phba->cfg_rrq_xri_bitmap_sz);
- if (ndlp->nlp_flag & NLP_NPR_2B_DISC)
- new_ndlp->nlp_flag |= NLP_NPR_2B_DISC;
- ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+ spin_lock_irq(shost->host_lock);
+ keep_nlp_flag = new_ndlp->nlp_flag;
+ new_ndlp->nlp_flag = ndlp->nlp_flag;
+ ndlp->nlp_flag = keep_nlp_flag;
+ spin_unlock_irq(shost->host_lock);
- /* Set state will put new_ndlp on to node list if not already done */
+ /* Set nlp_states accordingly */
+ keep_nlp_state = new_ndlp->nlp_state;
lpfc_nlp_set_state(vport, new_ndlp, ndlp->nlp_state);
/* Move this back to NPR state */
@@ -1624,8 +1629,9 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
if (rport) {
rdata = rport->dd_data;
if (rdata->pnode == ndlp) {
- lpfc_nlp_put(ndlp);
+ /* break the link before dropping the ref */
ndlp->rport = NULL;
+ lpfc_nlp_put(ndlp);
rdata->pnode = lpfc_nlp_get(new_ndlp);
new_ndlp->rport = rport;
}
@@ -1648,7 +1654,9 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
memcpy(ndlp->active_rrqs_xri_bitmap,
active_rrqs_xri_bitmap,
phba->cfg_rrq_xri_bitmap_sz);
- lpfc_drop_node(vport, ndlp);
+
+ if (!NLP_CHK_NODE_ACT(ndlp))
+ lpfc_drop_node(vport, ndlp);
}
else {
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
@@ -1665,20 +1673,13 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
active_rrqs_xri_bitmap,
phba->cfg_rrq_xri_bitmap_sz);
- /* Since we are swapping the ndlp passed in with the new one
- * and the did has already been swapped, copy over state.
- * The new WWNs are already in new_ndlp since thats what
- * we looked it up by in the begining of this routine.
- */
- new_ndlp->nlp_state = ndlp->nlp_state;
-
- /* Since we are switching over to the new_ndlp, the old
- * ndlp should be put in the NPR state, unless we have
- * already started re-discovery on it.
+ /* Since we are switching over to the new_ndlp,
+ * reset the old ndlp state
*/
if ((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) ||
(ndlp->nlp_state == NLP_STE_MAPPED_NODE))
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+ keep_nlp_state = NLP_STE_NPR_NODE;
+ lpfc_nlp_set_state(vport, ndlp, keep_nlp_state);
/* Fix up the rport accordingly */
rport = ndlp->rport;
@@ -3667,15 +3668,6 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
* At this point, the driver is done so release the IOCB
*/
lpfc_els_free_iocb(phba, cmdiocb);
-
- /*
- * Remove the ndlp reference if it's a fabric node that has
- * sent us an unsolicted LOGO.
- */
- if (ndlp->nlp_type & NLP_FABRIC)
- lpfc_nlp_put(ndlp);
-
- return;
}
/**
@@ -4020,7 +4012,9 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
ndlp->nlp_rpi, vport->fc_flag);
if (ndlp->nlp_flag & NLP_LOGO_ACC) {
spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= ~NLP_LOGO_ACC;
+ if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED ||
+ ndlp->nlp_flag & NLP_REG_LOGIN_SEND))
+ ndlp->nlp_flag &= ~NLP_LOGO_ACC;
spin_unlock_irq(shost->host_lock);
elsiocb->iocb_cmpl = lpfc_cmpl_els_logo_acc;
} else {
@@ -4587,16 +4581,16 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport)
if (!NLP_CHK_NODE_ACT(ndlp))
continue;
if (ndlp->nlp_state == NLP_STE_NPR_NODE &&
- (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 &&
- (ndlp->nlp_flag & NLP_DELAY_TMO) == 0 &&
- (ndlp->nlp_flag & NLP_NPR_ADISC) == 0) {
+ (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 &&
+ (ndlp->nlp_flag & NLP_DELAY_TMO) == 0 &&
+ (ndlp->nlp_flag & NLP_NPR_ADISC) == 0) {
ndlp->nlp_prev_state = ndlp->nlp_state;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
sentplogi++;
vport->num_disc_nodes++;
if (vport->num_disc_nodes >=
- vport->cfg_discovery_threads) {
+ vport->cfg_discovery_threads) {
spin_lock_irq(shost->host_lock);
vport->fc_flag |= FC_NLP_MORE;
spin_unlock_irq(shost->host_lock);
@@ -4615,6 +4609,660 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport)
return sentplogi;
}
+void
+lpfc_rdp_res_link_service(struct fc_rdp_link_service_desc *desc,
+ uint32_t word0)
+{
+
+ desc->tag = cpu_to_be32(RDP_LINK_SERVICE_DESC_TAG);
+ desc->payload.els_req = word0;
+ desc->length = cpu_to_be32(sizeof(desc->payload));
+}
+
+void
+lpfc_rdp_res_sfp_desc(struct fc_rdp_sfp_desc *desc,
+ uint8_t *page_a0, uint8_t *page_a2)
+{
+ uint16_t wavelength;
+ uint16_t temperature;
+ uint16_t rx_power;
+ uint16_t tx_bias;
+ uint16_t tx_power;
+ uint16_t vcc;
+ uint16_t flag = 0;
+ struct sff_trasnceiver_codes_byte4 *trasn_code_byte4;
+ struct sff_trasnceiver_codes_byte5 *trasn_code_byte5;
+
+ desc->tag = cpu_to_be32(RDP_SFP_DESC_TAG);
+
+ trasn_code_byte4 = (struct sff_trasnceiver_codes_byte4 *)
+ &page_a0[SSF_TRANSCEIVER_CODE_B4];
+ trasn_code_byte5 = (struct sff_trasnceiver_codes_byte5 *)
+ &page_a0[SSF_TRANSCEIVER_CODE_B5];
+
+ if ((trasn_code_byte4->fc_sw_laser) ||
+ (trasn_code_byte5->fc_sw_laser_sl) ||
+ (trasn_code_byte5->fc_sw_laser_sn)) { /* check if its short WL */
+ flag |= (SFP_FLAG_PT_SWLASER << SFP_FLAG_PT_SHIFT);
+ } else if (trasn_code_byte4->fc_lw_laser) {
+ wavelength = (page_a0[SSF_WAVELENGTH_B1] << 8) |
+ page_a0[SSF_WAVELENGTH_B0];
+ if (wavelength == SFP_WAVELENGTH_LC1310)
+ flag |= SFP_FLAG_PT_LWLASER_LC1310 << SFP_FLAG_PT_SHIFT;
+ if (wavelength == SFP_WAVELENGTH_LL1550)
+ flag |= SFP_FLAG_PT_LWLASER_LL1550 << SFP_FLAG_PT_SHIFT;
+ }
+ /* check if its SFP+ */
+ flag |= ((page_a0[SSF_IDENTIFIER] == SFF_PG0_IDENT_SFP) ?
+ SFP_FLAG_CT_SFP_PLUS : SFP_FLAG_CT_UNKNOWN)
+ << SFP_FLAG_CT_SHIFT;
+
+ /* check if its OPTICAL */
+ flag |= ((page_a0[SSF_CONNECTOR] == SFF_PG0_CONNECTOR_LC) ?
+ SFP_FLAG_IS_OPTICAL_PORT : 0)
+ << SFP_FLAG_IS_OPTICAL_SHIFT;
+
+ temperature = (page_a2[SFF_TEMPERATURE_B1] << 8 |
+ page_a2[SFF_TEMPERATURE_B0]);
+ vcc = (page_a2[SFF_VCC_B1] << 8 |
+ page_a2[SFF_VCC_B0]);
+ tx_power = (page_a2[SFF_TXPOWER_B1] << 8 |
+ page_a2[SFF_TXPOWER_B0]);
+ tx_bias = (page_a2[SFF_TX_BIAS_CURRENT_B1] << 8 |
+ page_a2[SFF_TX_BIAS_CURRENT_B0]);
+ rx_power = (page_a2[SFF_RXPOWER_B1] << 8 |
+ page_a2[SFF_RXPOWER_B0]);
+ desc->sfp_info.temperature = cpu_to_be16(temperature);
+ desc->sfp_info.rx_power = cpu_to_be16(rx_power);
+ desc->sfp_info.tx_bias = cpu_to_be16(tx_bias);
+ desc->sfp_info.tx_power = cpu_to_be16(tx_power);
+ desc->sfp_info.vcc = cpu_to_be16(vcc);
+
+ desc->sfp_info.flags = cpu_to_be16(flag);
+ desc->length = cpu_to_be32(sizeof(desc->sfp_info));
+}
+
+void
+lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc,
+ READ_LNK_VAR *stat)
+{
+ uint32_t type;
+
+ desc->tag = cpu_to_be32(RDP_LINK_ERROR_STATUS_DESC_TAG);
+
+ type = VN_PT_PHY_PF_PORT << VN_PT_PHY_SHIFT;
+
+ desc->info.port_type = cpu_to_be32(type);
+
+ desc->info.link_status.link_failure_cnt =
+ cpu_to_be32(stat->linkFailureCnt);
+ desc->info.link_status.loss_of_synch_cnt =
+ cpu_to_be32(stat->lossSyncCnt);
+ desc->info.link_status.loss_of_signal_cnt =
+ cpu_to_be32(stat->lossSignalCnt);
+ desc->info.link_status.primitive_seq_proto_err =
+ cpu_to_be32(stat->primSeqErrCnt);
+ desc->info.link_status.invalid_trans_word =
+ cpu_to_be32(stat->invalidXmitWord);
+ desc->info.link_status.invalid_crc_cnt = cpu_to_be32(stat->crcCnt);
+
+ desc->length = cpu_to_be32(sizeof(desc->info));
+}
+
+void
+lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba)
+{
+ uint16_t rdp_cap = 0;
+ uint16_t rdp_speed;
+
+ desc->tag = cpu_to_be32(RDP_PORT_SPEED_DESC_TAG);
+
+ switch (phba->sli4_hba.link_state.speed) {
+ case LPFC_FC_LA_SPEED_1G:
+ rdp_speed = RDP_PS_1GB;
+ break;
+ case LPFC_FC_LA_SPEED_2G:
+ rdp_speed = RDP_PS_2GB;
+ break;
+ case LPFC_FC_LA_SPEED_4G:
+ rdp_speed = RDP_PS_4GB;
+ break;
+ case LPFC_FC_LA_SPEED_8G:
+ rdp_speed = RDP_PS_8GB;
+ break;
+ case LPFC_FC_LA_SPEED_10G:
+ rdp_speed = RDP_PS_10GB;
+ break;
+ case LPFC_FC_LA_SPEED_16G:
+ rdp_speed = RDP_PS_16GB;
+ break;
+ case LPFC_FC_LA_SPEED_32G:
+ rdp_speed = RDP_PS_32GB;
+ break;
+ default:
+ rdp_speed = RDP_PS_UNKNOWN;
+ break;
+ }
+
+ desc->info.port_speed.speed = cpu_to_be16(rdp_speed);
+
+ if (phba->lmt & LMT_16Gb)
+ rdp_cap |= RDP_PS_16GB;
+ if (phba->lmt & LMT_10Gb)
+ rdp_cap |= RDP_PS_10GB;
+ if (phba->lmt & LMT_8Gb)
+ rdp_cap |= RDP_PS_8GB;
+ if (phba->lmt & LMT_4Gb)
+ rdp_cap |= RDP_PS_4GB;
+ if (phba->lmt & LMT_2Gb)
+ rdp_cap |= RDP_PS_2GB;
+ if (phba->lmt & LMT_1Gb)
+ rdp_cap |= RDP_PS_1GB;
+
+ if (rdp_cap == 0)
+ rdp_cap = RDP_CAP_UNKNOWN;
+
+ desc->info.port_speed.capabilities = cpu_to_be16(rdp_cap);
+ desc->length = cpu_to_be32(sizeof(desc->info));
+}
+
+void
+lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc,
+ struct lpfc_hba *phba)
+{
+
+ desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG);
+
+ memcpy(desc->port_names.wwnn, phba->wwnn,
+ sizeof(desc->port_names.wwnn));
+
+ memcpy(desc->port_names.wwpn, &phba->wwpn,
+ sizeof(desc->port_names.wwpn));
+
+ desc->length = cpu_to_be32(sizeof(desc->port_names));
+}
+
+void
+lpfc_rdp_res_attach_port_names(struct fc_rdp_port_name_desc *desc,
+ struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+{
+
+ desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG);
+ if (vport->fc_flag & FC_FABRIC) {
+ memcpy(desc->port_names.wwnn, &vport->fabric_nodename,
+ sizeof(desc->port_names.wwnn));
+
+ memcpy(desc->port_names.wwpn, &vport->fabric_portname,
+ sizeof(desc->port_names.wwpn));
+ } else { /* Point to Point */
+ memcpy(desc->port_names.wwnn, &ndlp->nlp_nodename,
+ sizeof(desc->port_names.wwnn));
+
+ memcpy(desc->port_names.wwnn, &ndlp->nlp_portname,
+ sizeof(desc->port_names.wwpn));
+ }
+
+ desc->length = cpu_to_be32(sizeof(desc->port_names));
+}
+
+void
+lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context,
+ int status)
+{
+ struct lpfc_nodelist *ndlp = rdp_context->ndlp;
+ struct lpfc_vport *vport = ndlp->vport;
+ struct lpfc_iocbq *elsiocb;
+ IOCB_t *icmd;
+ uint8_t *pcmd;
+ struct ls_rjt *stat;
+ struct fc_rdp_res_frame *rdp_res;
+ uint32_t cmdsize;
+ int rc;
+
+ if (status != SUCCESS)
+ goto error;
+ cmdsize = sizeof(struct fc_rdp_res_frame);
+
+ elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize,
+ lpfc_max_els_tries, rdp_context->ndlp,
+ rdp_context->ndlp->nlp_DID, ELS_CMD_ACC);
+ lpfc_nlp_put(ndlp);
+ if (!elsiocb)
+ goto free_rdp_context;
+
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = rdp_context->rx_id;
+ icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "2171 Xmit RDP response tag x%x xri x%x, "
+ "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x",
+ elsiocb->iotag, elsiocb->iocb.ulpContext,
+ ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
+ ndlp->nlp_rpi);
+ rdp_res = (struct fc_rdp_res_frame *)
+ (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+ memset(pcmd, 0, sizeof(struct fc_rdp_res_frame));
+ *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+
+ /* For RDP payload */
+ lpfc_rdp_res_link_service(&rdp_res->link_service_desc, ELS_CMD_RDP);
+
+ lpfc_rdp_res_sfp_desc(&rdp_res->sfp_desc,
+ rdp_context->page_a0, rdp_context->page_a2);
+ lpfc_rdp_res_speed(&rdp_res->portspeed_desc, phba);
+ lpfc_rdp_res_link_error(&rdp_res->link_error_desc,
+ &rdp_context->link_stat);
+ lpfc_rdp_res_diag_port_names(&rdp_res->diag_port_names_desc, phba);
+ lpfc_rdp_res_attach_port_names(&rdp_res->attached_port_names_desc,
+ vport, ndlp);
+ rdp_res->length = cpu_to_be32(RDP_DESC_PAYLOAD_SIZE);
+
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+
+ phba->fc_stat.elsXmitACC++;
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR)
+ lpfc_els_free_iocb(phba, elsiocb);
+
+ kfree(rdp_context);
+
+ return;
+error:
+ cmdsize = 2 * sizeof(uint32_t);
+ elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, lpfc_max_els_tries,
+ ndlp, ndlp->nlp_DID, ELS_CMD_LS_RJT);
+ lpfc_nlp_put(ndlp);
+ if (!elsiocb)
+ goto free_rdp_context;
+
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = rdp_context->rx_id;
+ icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id;
+ pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+ *((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT;
+ stat = (struct ls_rjt *)(pcmd + sizeof(uint32_t));
+ stat->un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+
+ phba->fc_stat.elsXmitLSRJT++;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+
+ if (rc == IOCB_ERROR)
+ lpfc_els_free_iocb(phba, elsiocb);
+free_rdp_context:
+ kfree(rdp_context);
+}
+
+int
+lpfc_get_rdp_info(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context)
+{
+ LPFC_MBOXQ_t *mbox = NULL;
+ int rc;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_ELS,
+ "7105 failed to allocate mailbox memory");
+ return 1;
+ }
+
+ if (lpfc_sli4_dump_page_a0(phba, mbox))
+ goto prep_mbox_fail;
+ mbox->vport = rdp_context->ndlp->vport;
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a0;
+ mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED)
+ goto issue_mbox_fail;
+
+ return 0;
+
+prep_mbox_fail:
+issue_mbox_fail:
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 1;
+}
+
+/*
+ * lpfc_els_rcv_rdp - Process an unsolicited RDP ELS.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * This routine processes an unsolicited RDP(Read Diagnostic Parameters)
+ * IOCB. First, the payload of the unsolicited RDP is checked.
+ * Then it will (1) send MBX_DUMP_MEMORY, Embedded DMP_LMSD sub command TYPE-3
+ * for Page A0, (2) send MBX_DUMP_MEMORY, DMP_LMSD for Page A2,
+ * (3) send MBX_READ_LNK_STAT to get link stat, (4) Call lpfc_els_rdp_cmpl
+ * gather all data and send RDP response.
+ *
+ * Return code
+ * 0 - Sent the acc response
+ * 1 - Sent the reject response.
+ */
+static int
+lpfc_els_rcv_rdp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_nodelist *ndlp)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_dmabuf *pcmd;
+ uint8_t rjt_err, rjt_expl = LSEXP_NOTHING_MORE;
+ struct fc_rdp_req_frame *rdp_req;
+ struct lpfc_rdp_context *rdp_context;
+ IOCB_t *cmd = NULL;
+ struct ls_rjt stat;
+
+ if (phba->sli_rev < LPFC_SLI_REV4 ||
+ (bf_get(lpfc_sli_intf_if_type,
+ &phba->sli4_hba.sli_intf) !=
+ LPFC_SLI_INTF_IF_TYPE_2)) {
+ rjt_err = LSRJT_UNABLE_TPC;
+ rjt_expl = LSEXP_REQ_UNSUPPORTED;
+ goto error;
+ }
+
+ if (phba->sli_rev < LPFC_SLI_REV4 || (phba->hba_flag & HBA_FCOE_MODE)) {
+ rjt_err = LSRJT_UNABLE_TPC;
+ rjt_expl = LSEXP_REQ_UNSUPPORTED;
+ goto error;
+ }
+
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ rdp_req = (struct fc_rdp_req_frame *) pcmd->virt;
+
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "2422 ELS RDP Request "
+ "dec len %d tag x%x port_id %d len %d\n",
+ be32_to_cpu(rdp_req->rdp_des_length),
+ be32_to_cpu(rdp_req->nport_id_desc.tag),
+ be32_to_cpu(rdp_req->nport_id_desc.nport_id),
+ be32_to_cpu(rdp_req->nport_id_desc.length));
+
+ if (sizeof(struct fc_rdp_nport_desc) !=
+ be32_to_cpu(rdp_req->rdp_des_length))
+ goto rjt_logerr;
+ if (RDP_N_PORT_DESC_TAG != be32_to_cpu(rdp_req->nport_id_desc.tag))
+ goto rjt_logerr;
+ if (RDP_NPORT_ID_SIZE !=
+ be32_to_cpu(rdp_req->nport_id_desc.length))
+ goto rjt_logerr;
+ rdp_context = kmalloc(sizeof(struct lpfc_rdp_context), GFP_KERNEL);
+ if (!rdp_context) {
+ rjt_err = LSRJT_UNABLE_TPC;
+ goto error;
+ }
+
+ memset(rdp_context, 0, sizeof(struct lpfc_rdp_context));
+ cmd = &cmdiocb->iocb;
+ rdp_context->ndlp = lpfc_nlp_get(ndlp);
+ rdp_context->ox_id = cmd->unsli3.rcvsli3.ox_id;
+ rdp_context->rx_id = cmd->ulpContext;
+ rdp_context->cmpl = lpfc_els_rdp_cmpl;
+ if (lpfc_get_rdp_info(phba, rdp_context)) {
+ lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_ELS,
+ "2423 Unable to send mailbox");
+ kfree(rdp_context);
+ rjt_err = LSRJT_UNABLE_TPC;
+ lpfc_nlp_put(ndlp);
+ goto error;
+ }
+
+ return 0;
+
+rjt_logerr:
+ rjt_err = LSRJT_LOGICAL_ERR;
+
+error:
+ memset(&stat, 0, sizeof(stat));
+ stat.un.b.lsRjtRsnCode = rjt_err;
+ stat.un.b.lsRjtRsnCodeExp = rjt_expl;
+ lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+ return 1;
+}
+
+
+static void
+lpfc_els_lcb_rsp(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ MAILBOX_t *mb;
+ IOCB_t *icmd;
+ uint8_t *pcmd;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_nodelist *ndlp;
+ struct ls_rjt *stat;
+ union lpfc_sli4_cfg_shdr *shdr;
+ struct lpfc_lcb_context *lcb_context;
+ struct fc_lcb_res_frame *lcb_res;
+ uint32_t cmdsize, shdr_status, shdr_add_status;
+ int rc;
+
+ mb = &pmb->u.mb;
+ lcb_context = (struct lpfc_lcb_context *)pmb->context1;
+ ndlp = lcb_context->ndlp;
+ pmb->context1 = NULL;
+ pmb->context2 = NULL;
+
+ shdr = (union lpfc_sli4_cfg_shdr *)
+ &pmb->u.mqe.un.beacon_config.header.cfg_shdr;
+ shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_MBOX,
+ "0194 SET_BEACON_CONFIG mailbox "
+ "completed with status x%x add_status x%x,"
+ " mbx status x%x\n",
+ shdr_status, shdr_add_status, mb->mbxStatus);
+
+ if (mb->mbxStatus && !(shdr_status &&
+ shdr_add_status == ADD_STATUS_OPERATION_ALREADY_ACTIVE)) {
+ mempool_free(pmb, phba->mbox_mem_pool);
+ goto error;
+ }
+
+ mempool_free(pmb, phba->mbox_mem_pool);
+ cmdsize = sizeof(struct fc_lcb_res_frame);
+ elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize,
+ lpfc_max_els_tries, ndlp,
+ ndlp->nlp_DID, ELS_CMD_ACC);
+
+ /* Decrement the ndlp reference count from previous mbox command */
+ lpfc_nlp_put(ndlp);
+
+ if (!elsiocb)
+ goto free_lcb_context;
+
+ lcb_res = (struct fc_lcb_res_frame *)
+ (((struct lpfc_dmabuf *)elsiocb->context2)->virt);
+
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = lcb_context->rx_id;
+ icmd->unsli3.rcvsli3.ox_id = lcb_context->ox_id;
+
+ pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
+ *((uint32_t *)(pcmd)) = ELS_CMD_ACC;
+ lcb_res->lcb_sub_command = lcb_context->sub_command;
+ lcb_res->lcb_type = lcb_context->type;
+ lcb_res->lcb_frequency = lcb_context->frequency;
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ phba->fc_stat.elsXmitACC++;
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR)
+ lpfc_els_free_iocb(phba, elsiocb);
+
+ kfree(lcb_context);
+ return;
+
+error:
+ cmdsize = sizeof(struct fc_lcb_res_frame);
+ elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize,
+ lpfc_max_els_tries, ndlp,
+ ndlp->nlp_DID, ELS_CMD_LS_RJT);
+ lpfc_nlp_put(ndlp);
+ if (!elsiocb)
+ goto free_lcb_context;
+
+ icmd = &elsiocb->iocb;
+ icmd->ulpContext = lcb_context->rx_id;
+ icmd->unsli3.rcvsli3.ox_id = lcb_context->ox_id;
+ pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
+
+ *((uint32_t *)(pcmd)) = ELS_CMD_LS_RJT;
+ stat = (struct ls_rjt *)(pcmd + sizeof(uint32_t));
+ stat->un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+
+ elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ phba->fc_stat.elsXmitLSRJT++;
+ rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+ if (rc == IOCB_ERROR)
+ lpfc_els_free_iocb(phba, elsiocb);
+free_lcb_context:
+ kfree(lcb_context);
+}
+
+static int
+lpfc_sli4_set_beacon(struct lpfc_vport *vport,
+ struct lpfc_lcb_context *lcb_context,
+ uint32_t beacon_state)
+{
+ struct lpfc_hba *phba = vport->phba;
+ LPFC_MBOXQ_t *mbox = NULL;
+ uint32_t len;
+ int rc;
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ return 1;
+
+ len = sizeof(struct lpfc_mbx_set_beacon_config) -
+ sizeof(struct lpfc_sli4_cfg_mhdr);
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_SET_BEACON_CONFIG, len,
+ LPFC_SLI4_MBX_EMBED);
+ mbox->context1 = (void *)lcb_context;
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_els_lcb_rsp;
+ bf_set(lpfc_mbx_set_beacon_port_num, &mbox->u.mqe.un.beacon_config,
+ phba->sli4_hba.physical_port);
+ bf_set(lpfc_mbx_set_beacon_state, &mbox->u.mqe.un.beacon_config,
+ beacon_state);
+ bf_set(lpfc_mbx_set_beacon_port_type, &mbox->u.mqe.un.beacon_config, 1);
+ bf_set(lpfc_mbx_set_beacon_duration, &mbox->u.mqe.un.beacon_config, 0);
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * lpfc_els_rcv_lcb - Process an unsolicited LCB
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * This routine processes an unsolicited LCB(LINK CABLE BEACON) IOCB.
+ * First, the payload of the unsolicited LCB is checked.
+ * Then based on Subcommand beacon will either turn on or off.
+ *
+ * Return code
+ * 0 - Sent the acc response
+ * 1 - Sent the reject response.
+ **/
+static int
+lpfc_els_rcv_lcb(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_nodelist *ndlp)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_dmabuf *pcmd;
+ IOCB_t *icmd;
+ uint8_t *lp;
+ struct fc_lcb_request_frame *beacon;
+ struct lpfc_lcb_context *lcb_context;
+ uint8_t state, rjt_err;
+ struct ls_rjt stat;
+
+ icmd = &cmdiocb->iocb;
+ pcmd = (struct lpfc_dmabuf *)cmdiocb->context2;
+ lp = (uint8_t *)pcmd->virt;
+ beacon = (struct fc_lcb_request_frame *)pcmd->virt;
+
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "0192 ELS LCB Data x%x x%x x%x x%x sub x%x "
+ "type x%x frequency %x duration x%x\n",
+ lp[0], lp[1], lp[2],
+ beacon->lcb_command,
+ beacon->lcb_sub_command,
+ beacon->lcb_type,
+ beacon->lcb_frequency,
+ be16_to_cpu(beacon->lcb_duration));
+
+ if (phba->sli_rev < LPFC_SLI_REV4 ||
+ (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ LPFC_SLI_INTF_IF_TYPE_2)) {
+ rjt_err = LSRJT_CMD_UNSUPPORTED;
+ goto rjt;
+ }
+ lcb_context = kmalloc(sizeof(struct lpfc_lcb_context), GFP_KERNEL);
+
+ if (phba->hba_flag & HBA_FCOE_MODE) {
+ rjt_err = LSRJT_CMD_UNSUPPORTED;
+ goto rjt;
+ }
+ if (beacon->lcb_frequency == 0) {
+ rjt_err = LSRJT_CMD_UNSUPPORTED;
+ goto rjt;
+ }
+ if ((beacon->lcb_type != LPFC_LCB_GREEN) &&
+ (beacon->lcb_type != LPFC_LCB_AMBER)) {
+ rjt_err = LSRJT_CMD_UNSUPPORTED;
+ goto rjt;
+ }
+ if ((beacon->lcb_sub_command != LPFC_LCB_ON) &&
+ (beacon->lcb_sub_command != LPFC_LCB_OFF)) {
+ rjt_err = LSRJT_CMD_UNSUPPORTED;
+ goto rjt;
+ }
+ if ((beacon->lcb_sub_command == LPFC_LCB_ON) &&
+ (beacon->lcb_type != LPFC_LCB_GREEN) &&
+ (beacon->lcb_type != LPFC_LCB_AMBER)) {
+ rjt_err = LSRJT_CMD_UNSUPPORTED;
+ goto rjt;
+ }
+ if (be16_to_cpu(beacon->lcb_duration) != 0) {
+ rjt_err = LSRJT_CMD_UNSUPPORTED;
+ goto rjt;
+ }
+
+ state = (beacon->lcb_sub_command == LPFC_LCB_ON) ? 1 : 0;
+ lcb_context->sub_command = beacon->lcb_sub_command;
+ lcb_context->type = beacon->lcb_type;
+ lcb_context->frequency = beacon->lcb_frequency;
+ lcb_context->ox_id = cmdiocb->iocb.unsli3.rcvsli3.ox_id;
+ lcb_context->rx_id = cmdiocb->iocb.ulpContext;
+ lcb_context->ndlp = lpfc_nlp_get(ndlp);
+ if (lpfc_sli4_set_beacon(vport, lcb_context, state)) {
+ lpfc_printf_vlog(ndlp->vport, KERN_ERR,
+ LOG_ELS, "0193 failed to send mail box");
+ lpfc_nlp_put(ndlp);
+ rjt_err = LSRJT_UNABLE_TPC;
+ goto rjt;
+ }
+ return 0;
+rjt:
+ memset(&stat, 0, sizeof(stat));
+ stat.un.b.lsRjtRsnCode = rjt_err;
+ lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+ return 1;
+}
+
+
/**
* lpfc_els_flush_rscn - Clean up any rscn activities with a vport
* @vport: pointer to a host virtual N_Port data structure.
@@ -6706,8 +7354,13 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
* Do not process any unsolicited ELS commands
* if the ndlp is in DEV_LOSS
*/
- if (ndlp->nlp_add_flag & NLP_IN_DEV_LOSS)
+ shost = lpfc_shost_from_vport(vport);
+ spin_lock_irq(shost->host_lock);
+ if (ndlp->nlp_flag & NLP_IN_DEV_LOSS) {
+ spin_unlock_irq(shost->host_lock);
goto dropit;
+ }
+ spin_unlock_irq(shost->host_lock);
elsiocb->context1 = lpfc_nlp_get(ndlp);
elsiocb->vport = vport;
@@ -6751,7 +7404,6 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
rjt_exp = LSEXP_NOTHING_MORE;
break;
}
- shost = lpfc_shost_from_vport(vport);
if (vport->port_state < LPFC_DISC_AUTH) {
if (!(phba->pport->fc_flag & FC_PT2PT) ||
(phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
@@ -6821,6 +7473,14 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
lpfc_disc_state_machine(vport, ndlp, elsiocb, NLP_EVT_RCV_PRLO);
break;
+ case ELS_CMD_LCB:
+ phba->fc_stat.elsRcvLCB++;
+ lpfc_els_rcv_lcb(vport, elsiocb, ndlp);
+ break;
+ case ELS_CMD_RDP:
+ phba->fc_stat.elsRcvRDP++;
+ lpfc_els_rcv_rdp(vport, elsiocb, ndlp);
+ break;
case ELS_CMD_RSCN:
phba->fc_stat.elsRcvRSCN++;
lpfc_els_rcv_rscn(vport, elsiocb, ndlp);
@@ -7586,7 +8246,8 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
lpfc_do_scr_ns_plogi(phba, vport);
goto out;
fdisc_failed:
- lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+ if (vport->fc_vport->vport_state != FC_VPORT_NO_FABRIC_RSCS)
+ lpfc_vport_set_state(vport, FC_VPORT_FAILED);
/* Cancel discovery timer */
lpfc_can_disctmo(vport);
lpfc_nlp_put(ndlp);
@@ -7739,8 +8400,10 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
if (irsp->ulpStatus == IOSTAT_SUCCESS) {
spin_lock_irq(shost->host_lock);
+ vport->fc_flag &= ~FC_NDISC_ACTIVE;
vport->fc_flag &= ~FC_FABRIC;
spin_unlock_irq(shost->host_lock);
+ lpfc_can_disctmo(vport);
}
}
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 2500f15d437f..ce96d5bf8ae7 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -106,6 +106,7 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
struct lpfc_rport_data *rdata;
struct lpfc_nodelist * ndlp;
struct lpfc_vport *vport;
+ struct Scsi_Host *shost;
struct lpfc_hba *phba;
struct lpfc_work_evt *evtp;
int put_node;
@@ -146,48 +147,32 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
if (ndlp->nlp_state == NLP_STE_MAPPED_NODE)
return;
- if (ndlp->nlp_type & NLP_FABRIC) {
-
- /* If the WWPN of the rport and ndlp don't match, ignore it */
- if (rport->port_name != wwn_to_u64(ndlp->nlp_portname.u.wwn)) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
- "6789 rport name %lx != node port name %lx",
- (unsigned long)rport->port_name,
- (unsigned long)wwn_to_u64(
- ndlp->nlp_portname.u.wwn));
- put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
- rdata->pnode = NULL;
- ndlp->rport = NULL;
- if (put_node)
- lpfc_nlp_put(ndlp);
- put_device(&rport->dev);
- return;
- }
-
- put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
- rdata->pnode = NULL;
- ndlp->rport = NULL;
- if (put_node)
- lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
- return;
- }
+ if (rport->port_name != wwn_to_u64(ndlp->nlp_portname.u.wwn))
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
+ "6789 rport name %llx != node port name %llx",
+ rport->port_name,
+ wwn_to_u64(ndlp->nlp_portname.u.wwn));
evtp = &ndlp->dev_loss_evt;
- if (!list_empty(&evtp->evt_listp))
+ if (!list_empty(&evtp->evt_listp)) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
+ "6790 rport name %llx dev_loss_evt pending",
+ rport->port_name);
return;
+ }
- evtp->evt_arg1 = lpfc_nlp_get(ndlp);
- ndlp->nlp_add_flag |= NLP_IN_DEV_LOSS;
+ shost = lpfc_shost_from_vport(vport);
+ spin_lock_irq(shost->host_lock);
+ ndlp->nlp_flag |= NLP_IN_DEV_LOSS;
+ spin_unlock_irq(shost->host_lock);
- spin_lock_irq(&phba->hbalock);
/* We need to hold the node by incrementing the reference
* count until this queued work is done
*/
+ evtp->evt_arg1 = lpfc_nlp_get(ndlp);
+
+ spin_lock_irq(&phba->hbalock);
if (evtp->evt_arg1) {
evtp->evt = LPFC_EVT_DEV_LOSS;
list_add_tail(&evtp->evt_listp, &phba->work_list);
@@ -215,22 +200,24 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
struct fc_rport *rport;
struct lpfc_vport *vport;
struct lpfc_hba *phba;
+ struct Scsi_Host *shost;
uint8_t *name;
int put_node;
- int put_rport;
int warn_on = 0;
int fcf_inuse = 0;
rport = ndlp->rport;
+ vport = ndlp->vport;
+ shost = lpfc_shost_from_vport(vport);
+
+ spin_lock_irq(shost->host_lock);
+ ndlp->nlp_flag &= ~NLP_IN_DEV_LOSS;
+ spin_unlock_irq(shost->host_lock);
- if (!rport) {
- ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
+ if (!rport)
return fcf_inuse;
- }
- rdata = rport->dd_data;
name = (uint8_t *) &ndlp->nlp_portname;
- vport = ndlp->vport;
phba = vport->phba;
if (phba->sli_rev == LPFC_SLI_REV4)
@@ -244,6 +231,13 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
"3182 dev_loss_tmo_handler x%06x, rport %p flg x%x\n",
ndlp->nlp_DID, ndlp->rport, ndlp->nlp_flag);
+ /*
+ * lpfc_nlp_remove if reached with dangling rport drops the
+ * reference. To make sure that does not happen clear rport
+ * pointer in ndlp before lpfc_nlp_put.
+ */
+ rdata = rport->dd_data;
+
/* Don't defer this if we are in the process of deleting the vport
* or unloading the driver. The unload will cleanup the node
* appropriately we just need to cleanup the ndlp rport info here.
@@ -256,14 +250,12 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
ndlp->nlp_sid, 0, LPFC_CTX_TGT);
}
put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
rdata->pnode = NULL;
ndlp->rport = NULL;
- ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
if (put_node)
lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
+ put_device(&rport->dev);
+
return fcf_inuse;
}
@@ -275,28 +267,21 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
*name, *(name+1), *(name+2), *(name+3),
*(name+4), *(name+5), *(name+6), *(name+7),
ndlp->nlp_DID);
- ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
return fcf_inuse;
}
- if (ndlp->nlp_type & NLP_FABRIC) {
- /* We will clean up these Nodes in linkup */
- put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
- rdata->pnode = NULL;
- ndlp->rport = NULL;
- ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
- if (put_node)
- lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
+ put_node = rdata->pnode != NULL;
+ rdata->pnode = NULL;
+ ndlp->rport = NULL;
+ if (put_node)
+ lpfc_nlp_put(ndlp);
+ put_device(&rport->dev);
+
+ if (ndlp->nlp_type & NLP_FABRIC)
return fcf_inuse;
- }
if (ndlp->nlp_sid != NLP_NO_SID) {
warn_on = 1;
- /* flush the target */
- ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
ndlp->nlp_sid, 0, LPFC_CTX_TGT);
}
@@ -321,16 +306,6 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
ndlp->nlp_state, ndlp->nlp_rpi);
}
- put_node = rdata->pnode != NULL;
- put_rport = ndlp->rport != NULL;
- rdata->pnode = NULL;
- ndlp->rport = NULL;
- ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
- if (put_node)
- lpfc_nlp_put(ndlp);
- if (put_rport)
- put_device(&rport->dev);
-
if (!(vport->load_flag & FC_UNLOADING) &&
!(ndlp->nlp_flag & NLP_DELAY_TMO) &&
!(ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
@@ -1802,7 +1777,7 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
dma_addr_t phys_addr;
struct lpfc_mbx_sge sge;
struct lpfc_mbx_read_fcf_tbl *read_fcf;
- uint32_t shdr_status, shdr_add_status;
+ uint32_t shdr_status, shdr_add_status, if_type;
union lpfc_sli4_cfg_shdr *shdr;
struct fcf_record *new_fcf_record;
@@ -1823,9 +1798,11 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
lpfc_sli_pcimem_bcopy(shdr, shdr,
sizeof(union lpfc_sli4_cfg_shdr));
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+ if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
if (shdr_status || shdr_add_status) {
- if (shdr_status == STATUS_FCF_TABLE_EMPTY)
+ if (shdr_status == STATUS_FCF_TABLE_EMPTY ||
+ if_type == LPFC_SLI_INTF_IF_TYPE_2)
lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
"2726 READ_FCF_RECORD Indicates empty "
"FCF table.\n");
@@ -3868,11 +3845,11 @@ out:
if (vport->port_state < LPFC_VPORT_READY) {
/* Link up discovery requires Fabric registration. */
- lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, 0); /* Do this first! */
lpfc_ns_cmd(vport, SLI_CTNS_RNN_ID, 0, 0);
lpfc_ns_cmd(vport, SLI_CTNS_RSNN_NN, 0, 0);
lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0);
lpfc_ns_cmd(vport, SLI_CTNS_RFT_ID, 0, 0);
+ lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, 0);
/* Issue SCR just before NameServer GID_FT Query */
lpfc_issue_els_scr(vport, SCR_DID, 0);
@@ -3918,9 +3895,17 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
* registered port, drop the reference that we took the last time we
* registered the port.
*/
- if (ndlp->rport && ndlp->rport->dd_data &&
- ((struct lpfc_rport_data *) ndlp->rport->dd_data)->pnode == ndlp)
- lpfc_nlp_put(ndlp);
+ rport = ndlp->rport;
+ if (rport) {
+ rdata = rport->dd_data;
+ /* break the link before dropping the ref */
+ ndlp->rport = NULL;
+ if (rdata && rdata->pnode == ndlp)
+ lpfc_nlp_put(ndlp);
+ rdata->pnode = NULL;
+ /* drop reference for earlier registeration */
+ put_device(&rport->dev);
+ }
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
"rport add: did:x%x flg:x%x type x%x",
@@ -4296,9 +4281,9 @@ lpfc_drop_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
if (vport->phba->sli_rev == LPFC_SLI_REV4) {
lpfc_cleanup_vports_rrqs(vport, ndlp);
lpfc_unreg_rpi(vport, ndlp);
- } else {
- lpfc_nlp_put(ndlp);
}
+
+ lpfc_nlp_put(ndlp);
return;
}
@@ -4510,7 +4495,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
struct lpfc_hba *phba = vport->phba;
LPFC_MBOXQ_t *mbox;
- int rc;
+ int rc, acc_plogi = 1;
uint16_t rpi;
if (ndlp->nlp_flag & NLP_RPI_REGISTERED ||
@@ -4543,14 +4528,20 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
mbox->context1 = lpfc_nlp_get(ndlp);
mbox->mbox_cmpl =
lpfc_sli4_unreg_rpi_cmpl_clr;
+ /*
+ * accept PLOGIs after unreg_rpi_cmpl
+ */
+ acc_plogi = 0;
} else
mbox->mbox_cmpl =
lpfc_sli_def_mbox_cmpl;
}
rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED)
+ if (rc == MBX_NOT_FINISHED) {
mempool_free(mbox, phba->mbox_mem_pool);
+ acc_plogi = 1;
+ }
}
lpfc_no_rpi(phba, ndlp);
@@ -4558,8 +4549,11 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
ndlp->nlp_rpi = 0;
ndlp->nlp_flag &= ~NLP_RPI_REGISTERED;
ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+ if (acc_plogi)
+ ndlp->nlp_flag &= ~NLP_LOGO_ACC;
return 1;
}
+ ndlp->nlp_flag &= ~NLP_LOGO_ACC;
return 0;
}
@@ -4761,6 +4755,7 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_rport_data *rdata;
+ struct fc_rport *rport;
LPFC_MBOXQ_t *mbox;
int rc;
@@ -4798,14 +4793,24 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
lpfc_cleanup_node(vport, ndlp);
/*
- * We can get here with a non-NULL ndlp->rport because when we
- * unregister a rport we don't break the rport/node linkage. So if we
- * do, make sure we don't leaving any dangling pointers behind.
+ * ndlp->rport must be set to NULL before it reaches here
+ * i.e. break rport/node link before doing lpfc_nlp_put for
+ * registered rport and then drop the reference of rport.
*/
if (ndlp->rport) {
- rdata = ndlp->rport->dd_data;
+ /*
+ * extra lpfc_nlp_put dropped the reference of ndlp
+ * for registered rport so need to cleanup rport
+ */
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE,
+ "0940 removed node x%p DID x%x "
+ " rport not null %p\n",
+ ndlp, ndlp->nlp_DID, ndlp->rport);
+ rport = ndlp->rport;
+ rdata = rport->dd_data;
rdata->pnode = NULL;
ndlp->rport = NULL;
+ put_device(&rport->dev);
}
}
@@ -4833,9 +4838,19 @@ lpfc_matchdid(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (matchdid.un.b.id == ndlpdid.un.b.id) {
if ((mydid.un.b.domain == matchdid.un.b.domain) &&
(mydid.un.b.area == matchdid.un.b.area)) {
+ /* This code is supposed to match the ID
+ * for a private loop device that is
+ * connect to fl_port. But we need to
+ * check that the port did not just go
+ * from pt2pt to fabric or we could end
+ * up matching ndlp->nlp_DID 000001 to
+ * fabric DID 0x20101
+ */
if ((ndlpdid.un.b.domain == 0) &&
(ndlpdid.un.b.area == 0)) {
- if (ndlpdid.un.b.id)
+ if (ndlpdid.un.b.id &&
+ vport->phba->fc_topology ==
+ LPFC_TOPOLOGY_LOOP)
return 1;
}
return 0;
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index 37beb9dc1311..892c5257d87c 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -543,6 +543,7 @@ struct fc_vft_header {
#define ELS_CMD_TEST 0x11000000
#define ELS_CMD_RRQ 0x12000000
#define ELS_CMD_REC 0x13000000
+#define ELS_CMD_RDP 0x18000000
#define ELS_CMD_PRLI 0x20100014
#define ELS_CMD_PRLO 0x21100014
#define ELS_CMD_PRLO_ACC 0x02100014
@@ -558,6 +559,7 @@ struct fc_vft_header {
#define ELS_CMD_SCR 0x62000000
#define ELS_CMD_RNID 0x78000000
#define ELS_CMD_LIRR 0x7A000000
+#define ELS_CMD_LCB 0x81000000
#else /* __LITTLE_ENDIAN_BITFIELD */
#define ELS_CMD_MASK 0xffff
#define ELS_RSP_MASK 0xff
@@ -580,6 +582,7 @@ struct fc_vft_header {
#define ELS_CMD_TEST 0x11
#define ELS_CMD_RRQ 0x12
#define ELS_CMD_REC 0x13
+#define ELS_CMD_RDP 0x18
#define ELS_CMD_PRLI 0x14001020
#define ELS_CMD_PRLO 0x14001021
#define ELS_CMD_PRLO_ACC 0x14001002
@@ -595,6 +598,7 @@ struct fc_vft_header {
#define ELS_CMD_SCR 0x62
#define ELS_CMD_RNID 0x78
#define ELS_CMD_LIRR 0x7A
+#define ELS_CMD_LCB 0x81
#endif
/*
@@ -1010,6 +1014,198 @@ typedef struct _ELS_PKT { /* Structure is in Big Endian format */
} un;
} ELS_PKT;
+/*
+ * Link Cable Beacon (LCB) ELS Frame
+ */
+
+struct fc_lcb_request_frame {
+ uint32_t lcb_command; /* ELS command opcode (0x81) */
+ uint8_t lcb_sub_command;/* LCB Payload Word 1, bit 24:31 */
+#define LPFC_LCB_ON 0x1
+#define LPFC_LCB_OFF 0x2
+ uint8_t reserved[3];
+
+ uint8_t lcb_type; /* LCB Payload Word 2, bit 24:31 */
+#define LPFC_LCB_GREEN 0x1
+#define LPFC_LCB_AMBER 0x2
+ uint8_t lcb_frequency; /* LCB Payload Word 2, bit 16:23 */
+ uint16_t lcb_duration; /* LCB Payload Word 2, bit 15:0 */
+};
+
+/*
+ * Link Cable Beacon (LCB) ELS Response Frame
+ */
+struct fc_lcb_res_frame {
+ uint32_t lcb_ls_acc; /* Acceptance of LCB request (0x02) */
+ uint8_t lcb_sub_command;/* LCB Payload Word 1, bit 24:31 */
+ uint8_t reserved[3];
+ uint8_t lcb_type; /* LCB Payload Word 2, bit 24:31 */
+ uint8_t lcb_frequency; /* LCB Payload Word 2, bit 16:23 */
+ uint16_t lcb_duration; /* LCB Payload Word 2, bit 15:0 */
+};
+
+/*
+ * Read Diagnostic Parameters (RDP) ELS frame.
+ */
+#define SFF_PG0_IDENT_SFP 0x3
+
+#define SFP_FLAG_PT_OPTICAL 0x0
+#define SFP_FLAG_PT_SWLASER 0x01
+#define SFP_FLAG_PT_LWLASER_LC1310 0x02
+#define SFP_FLAG_PT_LWLASER_LL1550 0x03
+#define SFP_FLAG_PT_MASK 0x0F
+#define SFP_FLAG_PT_SHIFT 0
+
+#define SFP_FLAG_IS_OPTICAL_PORT 0x01
+#define SFP_FLAG_IS_OPTICAL_MASK 0x010
+#define SFP_FLAG_IS_OPTICAL_SHIFT 4
+
+#define SFP_FLAG_IS_DESC_VALID 0x01
+#define SFP_FLAG_IS_DESC_VALID_MASK 0x020
+#define SFP_FLAG_IS_DESC_VALID_SHIFT 5
+
+#define SFP_FLAG_CT_UNKNOWN 0x0
+#define SFP_FLAG_CT_SFP_PLUS 0x01
+#define SFP_FLAG_CT_MASK 0x3C
+#define SFP_FLAG_CT_SHIFT 6
+
+struct fc_rdp_port_name_info {
+ uint8_t wwnn[8];
+ uint8_t wwpn[8];
+};
+
+
+/*
+ * Link Error Status Block Structure (FC-FS-3) for RDP
+ * This similar to RPS ELS
+ */
+struct fc_link_status {
+ uint32_t link_failure_cnt;
+ uint32_t loss_of_synch_cnt;
+ uint32_t loss_of_signal_cnt;
+ uint32_t primitive_seq_proto_err;
+ uint32_t invalid_trans_word;
+ uint32_t invalid_crc_cnt;
+
+};
+
+#define RDP_PORT_NAMES_DESC_TAG 0x00010003
+struct fc_rdp_port_name_desc {
+ uint32_t tag; /* 0001 0003h */
+ uint32_t length; /* set to size of payload struct */
+ struct fc_rdp_port_name_info port_names;
+};
+
+
+struct fc_rdp_link_error_status_payload_info {
+ struct fc_link_status link_status; /* 24 bytes */
+ uint32_t port_type; /* bits 31-30 only */
+};
+
+#define RDP_LINK_ERROR_STATUS_DESC_TAG 0x00010002
+struct fc_rdp_link_error_status_desc {
+ uint32_t tag; /* 0001 0002h */
+ uint32_t length; /* set to size of payload struct */
+ struct fc_rdp_link_error_status_payload_info info;
+};
+
+#define VN_PT_PHY_UNKNOWN 0x00
+#define VN_PT_PHY_PF_PORT 0x01
+#define VN_PT_PHY_ETH_MAC 0x10
+#define VN_PT_PHY_SHIFT 30
+
+#define RDP_PS_1GB 0x8000
+#define RDP_PS_2GB 0x4000
+#define RDP_PS_4GB 0x2000
+#define RDP_PS_10GB 0x1000
+#define RDP_PS_8GB 0x0800
+#define RDP_PS_16GB 0x0400
+#define RDP_PS_32GB 0x0200
+
+#define RDP_CAP_UNKNOWN 0x0001
+#define RDP_PS_UNKNOWN 0x0002
+#define RDP_PS_NOT_ESTABLISHED 0x0001
+
+struct fc_rdp_port_speed {
+ uint16_t capabilities;
+ uint16_t speed;
+};
+
+struct fc_rdp_port_speed_info {
+ struct fc_rdp_port_speed port_speed;
+};
+
+#define RDP_PORT_SPEED_DESC_TAG 0x00010001
+struct fc_rdp_port_speed_desc {
+ uint32_t tag; /* 00010001h */
+ uint32_t length; /* set to size of payload struct */
+ struct fc_rdp_port_speed_info info;
+};
+
+#define RDP_NPORT_ID_SIZE 4
+#define RDP_N_PORT_DESC_TAG 0x00000003
+struct fc_rdp_nport_desc {
+ uint32_t tag; /* 0000 0003h, big endian */
+ uint32_t length; /* size of RDP_N_PORT_ID struct */
+ uint32_t nport_id : 12;
+ uint32_t reserved : 8;
+};
+
+
+struct fc_rdp_link_service_info {
+ uint32_t els_req; /* Request payload word 0 value.*/
+};
+
+#define RDP_LINK_SERVICE_DESC_TAG 0x00000001
+struct fc_rdp_link_service_desc {
+ uint32_t tag; /* Descriptor tag 1 */
+ uint32_t length; /* set to size of payload struct. */
+ struct fc_rdp_link_service_info payload;
+ /* must be ELS req Word 0(0x18) */
+};
+
+struct fc_rdp_sfp_info {
+ uint16_t temperature;
+ uint16_t vcc;
+ uint16_t tx_bias;
+ uint16_t tx_power;
+ uint16_t rx_power;
+ uint16_t flags;
+};
+
+#define RDP_SFP_DESC_TAG 0x00010000
+struct fc_rdp_sfp_desc {
+ uint32_t tag;
+ uint32_t length; /* set to size of sfp_info struct */
+ struct fc_rdp_sfp_info sfp_info;
+};
+
+struct fc_rdp_req_frame {
+ uint32_t rdp_command; /* ELS command opcode (0x18)*/
+ uint32_t rdp_des_length; /* RDP Payload Word 1 */
+ struct fc_rdp_nport_desc nport_id_desc; /* RDP Payload Word 2 - 4 */
+};
+
+
+struct fc_rdp_res_frame {
+ uint32_t reply_sequence; /* FC word0 LS_ACC or LS_RJT */
+ uint32_t length; /* FC Word 1 */
+ struct fc_rdp_link_service_desc link_service_desc; /* Word 2 -4 */
+ struct fc_rdp_sfp_desc sfp_desc; /* Word 5 -9 */
+ struct fc_rdp_port_speed_desc portspeed_desc; /* Word 10-12 */
+ struct fc_rdp_link_error_status_desc link_error_desc; /* Word 13-21 */
+ struct fc_rdp_port_name_desc diag_port_names_desc; /* Word 22-27 */
+ struct fc_rdp_port_name_desc attached_port_names_desc;/* Word 28-33 */
+};
+
+
+#define RDP_DESC_PAYLOAD_SIZE (sizeof(struct fc_rdp_link_service_desc) \
+ + sizeof(struct fc_rdp_sfp_desc) \
+ + sizeof(struct fc_rdp_port_speed_desc) \
+ + sizeof(struct fc_rdp_link_error_status_desc) \
+ + (sizeof(struct fc_rdp_port_name_desc) * 2))
+
+
/******** FDMI ********/
/* lpfc_sli_ct_request defines the CT_IU preamble for FDMI commands */
@@ -1587,6 +1783,11 @@ typedef struct { /* FireFly BIU registers */
#define TEMPERATURE_OFFSET 0xB0 /* Slim offset for critical temperature event */
/*
+ * return code Fail
+ */
+#define FAILURE 1
+
+/*
* Begin Structure Definitions for Mailbox Commands
*/
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 1813c45946f4..33ec4fa39ccb 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -291,7 +291,7 @@ struct sli4_bls_rsp {
struct lpfc_eqe {
uint32_t word0;
#define lpfc_eqe_resource_id_SHIFT 16
-#define lpfc_eqe_resource_id_MASK 0x000000FF
+#define lpfc_eqe_resource_id_MASK 0x0000FFFF
#define lpfc_eqe_resource_id_WORD word0
#define lpfc_eqe_minor_code_SHIFT 4
#define lpfc_eqe_minor_code_MASK 0x00000FFF
@@ -914,6 +914,8 @@ struct mbox_header {
#define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D
#define LPFC_MBOX_OPCODE_SET_PHYSICAL_LINK_CONFIG 0x3E
#define LPFC_MBOX_OPCODE_SET_BOOT_CONFIG 0x43
+#define LPFC_MBOX_OPCODE_SET_BEACON_CONFIG 0x45
+#define LPFC_MBOX_OPCODE_GET_BEACON_CONFIG 0x46
#define LPFC_MBOX_OPCODE_GET_PORT_NAME 0x4D
#define LPFC_MBOX_OPCODE_MQ_CREATE_EXT 0x5A
#define LPFC_MBOX_OPCODE_GET_VPD_DATA 0x5B
@@ -1479,6 +1481,26 @@ struct lpfc_mbx_query_fw_config {
} rsp;
};
+struct lpfc_mbx_set_beacon_config {
+ struct mbox_header header;
+ uint32_t word4;
+#define lpfc_mbx_set_beacon_port_num_SHIFT 0
+#define lpfc_mbx_set_beacon_port_num_MASK 0x0000003F
+#define lpfc_mbx_set_beacon_port_num_WORD word4
+#define lpfc_mbx_set_beacon_port_type_SHIFT 6
+#define lpfc_mbx_set_beacon_port_type_MASK 0x00000003
+#define lpfc_mbx_set_beacon_port_type_WORD word4
+#define lpfc_mbx_set_beacon_state_SHIFT 8
+#define lpfc_mbx_set_beacon_state_MASK 0x000000FF
+#define lpfc_mbx_set_beacon_state_WORD word4
+#define lpfc_mbx_set_beacon_duration_SHIFT 16
+#define lpfc_mbx_set_beacon_duration_MASK 0x000000FF
+#define lpfc_mbx_set_beacon_duration_WORD word4
+#define lpfc_mbx_set_beacon_status_duration_SHIFT 24
+#define lpfc_mbx_set_beacon_status_duration_MASK 0x000000FF
+#define lpfc_mbx_set_beacon_status_duration_WORD word4
+};
+
struct lpfc_id_range {
uint32_t word5;
#define lpfc_mbx_rsrc_id_word4_0_SHIFT 0
@@ -1921,6 +1943,12 @@ struct lpfc_mbx_redisc_fcf_tbl {
#define STATUS_FCF_IN_USE 0x3a
#define STATUS_FCF_TABLE_EMPTY 0x43
+/*
+ * Additional status field for embedded SLI_CONFIG mailbox
+ * command.
+ */
+#define ADD_STATUS_OPERATION_ALREADY_ACTIVE 0x67
+
struct lpfc_mbx_sli4_config {
struct mbox_header header;
};
@@ -2433,6 +2461,205 @@ struct lpfc_mbx_supp_pages {
#define LPFC_SLI4_PARAMETERS 2
};
+struct lpfc_mbx_memory_dump_type3 {
+ uint32_t word1;
+#define lpfc_mbx_memory_dump_type3_type_SHIFT 0
+#define lpfc_mbx_memory_dump_type3_type_MASK 0x0000000f
+#define lpfc_mbx_memory_dump_type3_type_WORD word1
+#define lpfc_mbx_memory_dump_type3_link_SHIFT 24
+#define lpfc_mbx_memory_dump_type3_link_MASK 0x000000ff
+#define lpfc_mbx_memory_dump_type3_link_WORD word1
+ uint32_t word2;
+#define lpfc_mbx_memory_dump_type3_page_no_SHIFT 0
+#define lpfc_mbx_memory_dump_type3_page_no_MASK 0x0000ffff
+#define lpfc_mbx_memory_dump_type3_page_no_WORD word2
+#define lpfc_mbx_memory_dump_type3_offset_SHIFT 16
+#define lpfc_mbx_memory_dump_type3_offset_MASK 0x0000ffff
+#define lpfc_mbx_memory_dump_type3_offset_WORD word2
+ uint32_t word3;
+#define lpfc_mbx_memory_dump_type3_length_SHIFT 0
+#define lpfc_mbx_memory_dump_type3_length_MASK 0x00ffffff
+#define lpfc_mbx_memory_dump_type3_length_WORD word3
+ uint32_t addr_lo;
+ uint32_t addr_hi;
+ uint32_t return_len;
+};
+
+#define DMP_PAGE_A0 0xa0
+#define DMP_PAGE_A2 0xa2
+#define DMP_SFF_PAGE_A0_SIZE 256
+#define DMP_SFF_PAGE_A2_SIZE 256
+
+#define SFP_WAVELENGTH_LC1310 1310
+#define SFP_WAVELENGTH_LL1550 1550
+
+
+/*
+ * * SFF-8472 TABLE 3.4
+ * */
+#define SFF_PG0_CONNECTOR_UNKNOWN 0x00 /* Unknown */
+#define SFF_PG0_CONNECTOR_SC 0x01 /* SC */
+#define SFF_PG0_CONNECTOR_FC_COPPER1 0x02 /* FC style 1 copper connector */
+#define SFF_PG0_CONNECTOR_FC_COPPER2 0x03 /* FC style 2 copper connector */
+#define SFF_PG0_CONNECTOR_BNC 0x04 /* BNC / TNC */
+#define SFF_PG0_CONNECTOR__FC_COAX 0x05 /* FC coaxial headers */
+#define SFF_PG0_CONNECTOR_FIBERJACK 0x06 /* FiberJack */
+#define SFF_PG0_CONNECTOR_LC 0x07 /* LC */
+#define SFF_PG0_CONNECTOR_MT 0x08 /* MT - RJ */
+#define SFF_PG0_CONNECTOR_MU 0x09 /* MU */
+#define SFF_PG0_CONNECTOR_SF 0x0A /* SG */
+#define SFF_PG0_CONNECTOR_OPTICAL_PIGTAIL 0x0B /* Optical pigtail */
+#define SFF_PG0_CONNECTOR_OPTICAL_PARALLEL 0x0C /* MPO Parallel Optic */
+#define SFF_PG0_CONNECTOR_HSSDC_II 0x20 /* HSSDC II */
+#define SFF_PG0_CONNECTOR_COPPER_PIGTAIL 0x21 /* Copper pigtail */
+#define SFF_PG0_CONNECTOR_RJ45 0x22 /* RJ45 */
+
+/* SFF-8472 Table 3.1 Diagnostics: Data Fields Address/Page A0 */
+
+#define SSF_IDENTIFIER 0
+#define SSF_EXT_IDENTIFIER 1
+#define SSF_CONNECTOR 2
+#define SSF_TRANSCEIVER_CODE_B0 3
+#define SSF_TRANSCEIVER_CODE_B1 4
+#define SSF_TRANSCEIVER_CODE_B2 5
+#define SSF_TRANSCEIVER_CODE_B3 6
+#define SSF_TRANSCEIVER_CODE_B4 7
+#define SSF_TRANSCEIVER_CODE_B5 8
+#define SSF_TRANSCEIVER_CODE_B6 9
+#define SSF_TRANSCEIVER_CODE_B7 10
+#define SSF_ENCODING 11
+#define SSF_BR_NOMINAL 12
+#define SSF_RATE_IDENTIFIER 13
+#define SSF_LENGTH_9UM_KM 14
+#define SSF_LENGTH_9UM 15
+#define SSF_LENGTH_50UM_OM2 16
+#define SSF_LENGTH_62UM_OM1 17
+#define SFF_LENGTH_COPPER 18
+#define SSF_LENGTH_50UM_OM3 19
+#define SSF_VENDOR_NAME 20
+#define SSF_VENDOR_OUI 36
+#define SSF_VENDOR_PN 40
+#define SSF_VENDOR_REV 56
+#define SSF_WAVELENGTH_B1 60
+#define SSF_WAVELENGTH_B0 61
+#define SSF_CC_BASE 63
+#define SSF_OPTIONS_B1 64
+#define SSF_OPTIONS_B0 65
+#define SSF_BR_MAX 66
+#define SSF_BR_MIN 67
+#define SSF_VENDOR_SN 68
+#define SSF_DATE_CODE 84
+#define SSF_MONITORING_TYPEDIAGNOSTIC 92
+#define SSF_ENHANCED_OPTIONS 93
+#define SFF_8472_COMPLIANCE 94
+#define SSF_CC_EXT 95
+#define SSF_A0_VENDOR_SPECIFIC 96
+
+/* SFF-8472 Table 3.1a Diagnostics: Data Fields Address/Page A2 */
+
+#define SSF_AW_THRESHOLDS 0
+#define SSF_EXT_CAL_CONSTANTS 56
+#define SSF_CC_DMI 95
+#define SFF_TEMPERATURE_B1 96
+#define SFF_TEMPERATURE_B0 97
+#define SFF_VCC_B1 98
+#define SFF_VCC_B0 99
+#define SFF_TX_BIAS_CURRENT_B1 100
+#define SFF_TX_BIAS_CURRENT_B0 101
+#define SFF_TXPOWER_B1 102
+#define SFF_TXPOWER_B0 103
+#define SFF_RXPOWER_B1 104
+#define SFF_RXPOWER_B0 105
+#define SSF_STATUS_CONTROL 110
+#define SSF_ALARM_FLAGS_B1 112
+#define SSF_ALARM_FLAGS_B0 113
+#define SSF_WARNING_FLAGS_B1 116
+#define SSF_WARNING_FLAGS_B0 117
+#define SSF_EXT_TATUS_CONTROL_B1 118
+#define SSF_EXT_TATUS_CONTROL_B0 119
+#define SSF_A2_VENDOR_SPECIFIC 120
+#define SSF_USER_EEPROM 128
+#define SSF_VENDOR_CONTROL 148
+
+
+/*
+ * Tranceiver codes Fibre Channel SFF-8472
+ * Table 3.5.
+ */
+
+struct sff_trasnceiver_codes_byte0 {
+ uint8_t inifiband:4;
+ uint8_t teng_ethernet:4;
+};
+
+struct sff_trasnceiver_codes_byte1 {
+ uint8_t sonet:6;
+ uint8_t escon:2;
+};
+
+struct sff_trasnceiver_codes_byte2 {
+ uint8_t soNet:8;
+};
+
+struct sff_trasnceiver_codes_byte3 {
+ uint8_t ethernet:8;
+};
+
+struct sff_trasnceiver_codes_byte4 {
+ uint8_t fc_el_lo:1;
+ uint8_t fc_lw_laser:1;
+ uint8_t fc_sw_laser:1;
+ uint8_t fc_md_distance:1;
+ uint8_t fc_lg_distance:1;
+ uint8_t fc_int_distance:1;
+ uint8_t fc_short_distance:1;
+ uint8_t fc_vld_distance:1;
+};
+
+struct sff_trasnceiver_codes_byte5 {
+ uint8_t reserved1:1;
+ uint8_t reserved2:1;
+ uint8_t fc_sfp_active:1; /* Active cable */
+ uint8_t fc_sfp_passive:1; /* Passive cable */
+ uint8_t fc_lw_laser:1; /* Longwave laser */
+ uint8_t fc_sw_laser_sl:1;
+ uint8_t fc_sw_laser_sn:1;
+ uint8_t fc_el_hi:1; /* Electrical enclosure high bit */
+};
+
+struct sff_trasnceiver_codes_byte6 {
+ uint8_t fc_tm_sm:1; /* Single Mode */
+ uint8_t reserved:1;
+ uint8_t fc_tm_m6:1; /* Multimode, 62.5um (M6) */
+ uint8_t fc_tm_tv:1; /* Video Coax (TV) */
+ uint8_t fc_tm_mi:1; /* Miniature Coax (MI) */
+ uint8_t fc_tm_tp:1; /* Twisted Pair (TP) */
+ uint8_t fc_tm_tw:1; /* Twin Axial Pair */
+};
+
+struct sff_trasnceiver_codes_byte7 {
+ uint8_t fc_sp_100MB:1; /* 100 MB/sec */
+ uint8_t reserve:1;
+ uint8_t fc_sp_200mb:1; /* 200 MB/sec */
+ uint8_t fc_sp_3200MB:1; /* 3200 MB/sec */
+ uint8_t fc_sp_400MB:1; /* 400 MB/sec */
+ uint8_t fc_sp_1600MB:1; /* 1600 MB/sec */
+ uint8_t fc_sp_800MB:1; /* 800 MB/sec */
+ uint8_t fc_sp_1200MB:1; /* 1200 MB/sec */
+};
+
+/* User writable non-volatile memory, SFF-8472 Table 3.20 */
+struct user_eeprom {
+ uint8_t vendor_name[16];
+ uint8_t vendor_oui[3];
+ uint8_t vendor_pn[816];
+ uint8_t vendor_rev[4];
+ uint8_t vendor_sn[16];
+ uint8_t datecode[6];
+ uint8_t lot_code[2];
+ uint8_t reserved191[57];
+};
+
struct lpfc_mbx_pc_sli4_params {
uint32_t word1;
#define qs_SHIFT 0
@@ -3021,6 +3248,7 @@ struct lpfc_mqe {
struct lpfc_mbx_request_features req_ftrs;
struct lpfc_mbx_post_hdr_tmpl hdr_tmpl;
struct lpfc_mbx_query_fw_config query_fw_cfg;
+ struct lpfc_mbx_set_beacon_config beacon_config;
struct lpfc_mbx_supp_pages supp_pages;
struct lpfc_mbx_pc_sli4_params sli4_params;
struct lpfc_mbx_get_sli4_parameters get_sli4_parameters;
@@ -3031,6 +3259,7 @@ struct lpfc_mqe {
struct lpfc_mbx_get_prof_cfg get_prof_cfg;
struct lpfc_mbx_wr_object wr_object;
struct lpfc_mbx_get_port_name get_port_name;
+ struct lpfc_mbx_memory_dump_type3 mem_dump_type3;
struct lpfc_mbx_nop nop;
} un;
};
@@ -3041,8 +3270,8 @@ struct lpfc_mcqe {
#define lpfc_mcqe_status_MASK 0x0000FFFF
#define lpfc_mcqe_status_WORD word0
#define lpfc_mcqe_ext_status_SHIFT 16
-#define lpfc_mcqe_ext_status_MASK 0x0000FFFF
-#define lpfc_mcqe_ext_status_WORD word0
+#define lpfc_mcqe_ext_status_MASK 0x0000FFFF
+#define lpfc_mcqe_ext_status_WORD word0
uint32_t mcqe_tag0;
uint32_t mcqe_tag1;
uint32_t trailer;
@@ -3176,6 +3405,7 @@ struct lpfc_acqe_fc_la {
#define LPFC_FC_LA_SPEED_8G 0x8
#define LPFC_FC_LA_SPEED_10G 0xA
#define LPFC_FC_LA_SPEED_16G 0x10
+#define LPFC_FC_LA_SPEED_32G 0x20
#define lpfc_acqe_fc_la_topology_SHIFT 16
#define lpfc_acqe_fc_la_topology_MASK 0x000000FF
#define lpfc_acqe_fc_la_topology_WORD word0
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index e8c8c1ecc1f5..f962118da8ed 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -3303,6 +3303,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
shost->max_lun = vport->cfg_max_luns;
shost->this_id = -1;
shost->max_cmd_len = 16;
+ shost->nr_hw_queues = phba->cfg_fcp_io_channel;
if (phba->sli_rev == LPFC_SLI_REV4) {
shost->dma_boundary =
phba->sli4_hba.pc_sli4_params.sge_supp_len-1;
@@ -4483,7 +4484,13 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
lpfc_destroy_vport_work_array(phba, vports);
}
- if (active_vlink_present) {
+ /*
+ * Don't re-instantiate if vport is marked for deletion.
+ * If we are here first then vport_delete is going to wait
+ * for discovery to complete.
+ */
+ if (!(vport->load_flag & FC_UNLOADING) &&
+ active_vlink_present) {
/*
* If there are other active VLinks present,
* re-instantiate the Vlink using FDISC.
@@ -7500,6 +7507,8 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
mboxq->u.mqe.un.query_fw_cfg.rsp.function_mode;
phba->sli4_hba.ulp0_mode = mboxq->u.mqe.un.query_fw_cfg.rsp.ulp0_mode;
phba->sli4_hba.ulp1_mode = mboxq->u.mqe.un.query_fw_cfg.rsp.ulp1_mode;
+ phba->sli4_hba.physical_port =
+ mboxq->u.mqe.un.query_fw_cfg.rsp.physical_port;
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"3251 QUERY_FW_CFG: func_mode:x%x, ulp0_mode:x%x, "
"ulp1_mode:x%x\n", phba->sli4_hba.fw_func_mode,
@@ -8367,7 +8376,7 @@ lpfc_sli_enable_msix(struct lpfc_hba *phba)
/* vector-0 is associated to slow-path handler */
rc = request_irq(phba->msix_entries[0].vector,
- &lpfc_sli_sp_intr_handler, IRQF_SHARED,
+ &lpfc_sli_sp_intr_handler, 0,
LPFC_SP_DRIVER_HANDLER_NAME, phba);
if (rc) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -8378,7 +8387,7 @@ lpfc_sli_enable_msix(struct lpfc_hba *phba)
/* vector-1 is associated to fast-path handler */
rc = request_irq(phba->msix_entries[1].vector,
- &lpfc_sli_fp_intr_handler, IRQF_SHARED,
+ &lpfc_sli_fp_intr_handler, 0,
LPFC_FP_DRIVER_HANDLER_NAME, phba);
if (rc) {
@@ -8487,7 +8496,7 @@ lpfc_sli_enable_msi(struct lpfc_hba *phba)
}
rc = request_irq(phba->pcidev->irq, lpfc_sli_intr_handler,
- IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ 0, LPFC_DRIVER_NAME, phba);
if (rc) {
pci_disable_msi(phba->pcidev);
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -8944,13 +8953,13 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba)
if (phba->cfg_fof && (index == (vectors - 1)))
rc = request_irq(
phba->sli4_hba.msix_entries[index].vector,
- &lpfc_sli4_fof_intr_handler, IRQF_SHARED,
+ &lpfc_sli4_fof_intr_handler, 0,
(char *)&phba->sli4_hba.handler_name[index],
&phba->sli4_hba.fcp_eq_hdl[index]);
else
rc = request_irq(
phba->sli4_hba.msix_entries[index].vector,
- &lpfc_sli4_hba_intr_handler, IRQF_SHARED,
+ &lpfc_sli4_hba_intr_handler, 0,
(char *)&phba->sli4_hba.handler_name[index],
&phba->sli4_hba.fcp_eq_hdl[index]);
if (rc) {
@@ -8972,7 +8981,8 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba)
phba->cfg_fcp_io_channel = vectors;
}
- lpfc_sli4_set_affinity(phba, vectors);
+ if (!shost_use_blk_mq(lpfc_shost_from_vport(phba->pport)))
+ lpfc_sli4_set_affinity(phba, vectors);
return rc;
cfg_fail_out:
@@ -9050,7 +9060,7 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba)
}
rc = request_irq(phba->pcidev->irq, lpfc_sli4_intr_handler,
- IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ 0, LPFC_DRIVER_NAME, phba);
if (rc) {
pci_disable_msi(phba->pcidev);
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 816f596cda60..eb627724417e 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -2255,6 +2255,158 @@ lpfc_sli4_dump_cfg_rg23(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
return 0;
}
+void
+lpfc_mbx_cmpl_rdp_link_stat(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ MAILBOX_t *mb;
+ int rc = FAILURE;
+ struct lpfc_rdp_context *rdp_context =
+ (struct lpfc_rdp_context *)(mboxq->context2);
+
+ mb = &mboxq->u.mb;
+ if (mb->mbxStatus)
+ goto mbx_failed;
+
+ memcpy(&rdp_context->link_stat, &mb->un.varRdLnk, sizeof(READ_LNK_VAR));
+
+ rc = SUCCESS;
+
+mbx_failed:
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ rdp_context->cmpl(phba, rdp_context, rc);
+}
+
+void
+lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+ struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) mbox->context1;
+ struct lpfc_rdp_context *rdp_context =
+ (struct lpfc_rdp_context *)(mbox->context2);
+
+ if (bf_get(lpfc_mqe_status, &mbox->u.mqe))
+ goto error;
+
+ lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a2,
+ DMP_SFF_PAGE_A2_SIZE);
+
+ /* We don't need dma buffer for link stat. */
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+
+ memset(mbox, 0, sizeof(*mbox));
+ lpfc_read_lnk_stat(phba, mbox);
+ mbox->vport = rdp_context->ndlp->vport;
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_link_stat;
+ mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
+ if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) == MBX_NOT_FINISHED)
+ goto error;
+
+ return;
+
+error:
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ rdp_context->cmpl(phba, rdp_context, FAILURE);
+}
+
+void
+lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+ int rc;
+ struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (mbox->context1);
+ struct lpfc_rdp_context *rdp_context =
+ (struct lpfc_rdp_context *)(mbox->context2);
+
+ if (bf_get(lpfc_mqe_status, &mbox->u.mqe))
+ goto error;
+
+ lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a0,
+ DMP_SFF_PAGE_A0_SIZE);
+
+ memset(mbox, 0, sizeof(*mbox));
+
+ memset(mp->virt, 0, DMP_SFF_PAGE_A2_SIZE);
+ INIT_LIST_HEAD(&mp->list);
+
+ /* save address for completion */
+ mbox->context1 = mp;
+ mbox->vport = rdp_context->ndlp->vport;
+
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY);
+ bf_set(lpfc_mbx_memory_dump_type3_type,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD);
+ bf_set(lpfc_mbx_memory_dump_type3_link,
+ &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port);
+ bf_set(lpfc_mbx_memory_dump_type3_page_no,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A2);
+ bf_set(lpfc_mbx_memory_dump_type3_length,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A2_SIZE);
+ mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+ mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a2;
+ mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED)
+ goto error;
+
+ return;
+
+error:
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ lpfc_sli4_mbox_cmd_free(phba, mbox);
+ rdp_context->cmpl(phba, rdp_context, FAILURE);
+}
+
+
+/*
+ * lpfc_sli4_dump_sfp_pagea0 - Dump sli4 read SFP Diagnostic.
+ * @phba: pointer to the hba structure containing.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ *
+ * This function create a SLI4 dump mailbox command to dump configure
+ * type 3 page 0xA0.
+ */
+int
+lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+{
+ struct lpfc_dmabuf *mp = NULL;
+
+ memset(mbox, 0, sizeof(*mbox));
+
+ mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (mp)
+ mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+ if (!mp || !mp->virt) {
+ kfree(mp);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
+ "3569 dump type 3 page 0xA0 allocation failed\n");
+ return 1;
+ }
+
+ memset(mp->virt, 0, LPFC_BPL_SIZE);
+ INIT_LIST_HEAD(&mp->list);
+
+ bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY);
+ /* save address for completion */
+ mbox->context1 = mp;
+
+ bf_set(lpfc_mbx_memory_dump_type3_type,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD);
+ bf_set(lpfc_mbx_memory_dump_type3_link,
+ &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port);
+ bf_set(lpfc_mbx_memory_dump_type3_page_no,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A0);
+ bf_set(lpfc_mbx_memory_dump_type3_length,
+ &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A0_SIZE);
+ mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+ mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+
+ return 0;
+}
+
/**
* lpfc_reg_fcfi - Initialize the REG_FCFI mailbox command
* @phba: pointer to the hba structure containing the FCF index and RQ ID.
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 4cb9882af157..af3b38aba65e 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -661,7 +661,13 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
lpfc_destroy_vport_work_array(phba, vports);
}
- if (active_vlink_present) {
+ /*
+ * Don't re-instantiate if vport is marked for deletion.
+ * If we are here first then vport_delete is going to wait
+ * for discovery to complete.
+ */
+ if (!(vport->load_flag & FC_UNLOADING) &&
+ active_vlink_present) {
/*
* If there are other active VLinks present,
* re-instantiate the Vlink using FDISC.
@@ -1868,7 +1874,7 @@ lpfc_rcv_logo_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)arg;
spin_lock_irq(shost->host_lock);
- ndlp->nlp_flag &= NLP_LOGO_ACC;
+ ndlp->nlp_flag |= NLP_LOGO_ACC;
spin_unlock_irq(shost->host_lock);
lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
return ndlp->nlp_state;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index cb73cf9e9ba5..e5eb40d2c512 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -1130,25 +1130,6 @@ lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
}
/**
- * lpfc_fcpcmd_to_iocb - copy the fcp_cmd data into the IOCB
- * @data: A pointer to the immediate command data portion of the IOCB.
- * @fcp_cmnd: The FCP Command that is provided by the SCSI layer.
- *
- * The routine copies the entire FCP command from @fcp_cmnd to @data while
- * byte swapping the data to big endian format for transmission on the wire.
- **/
-static void
-lpfc_fcpcmd_to_iocb(uint8_t *data, struct fcp_cmnd *fcp_cmnd)
-{
- int i, j;
-
- for (i = 0, j = 0; i < sizeof(struct fcp_cmnd);
- i += sizeof(uint32_t), j++) {
- ((uint32_t *)data)[j] = cpu_to_be32(((uint32_t *)fcp_cmnd)[j]);
- }
-}
-
-/**
* lpfc_scsi_prep_dma_buf_s3 - DMA mapping for scsi buffer to SLI3 IF spec
* @phba: The Hba for which this call is being executed.
* @lpfc_cmd: The scsi buffer which is going to be mapped.
@@ -1283,7 +1264,6 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* we need to set word 4 of IOCB here
*/
iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd);
- lpfc_fcpcmd_to_iocb(iocb_cmd->unsli3.fcp_ext.icd, fcp_cmnd);
return 0;
}
@@ -3277,7 +3257,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
*/
nseg = scsi_dma_map(scsi_cmnd);
- if (unlikely(!nseg))
+ if (unlikely(nseg <= 0))
return 1;
sgl += 1;
/* clear the last flag in the fcp_rsp map entry */
@@ -3866,6 +3846,49 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
}
/**
+ * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution
+ * @phba: Pointer to HBA context object.
+ *
+ * This routine performs a roundrobin SCSI command to SLI4 FCP WQ index
+ * distribution. This is called by __lpfc_sli_issue_iocb_s4() with the hbalock
+ * held.
+ * If scsi-mq is enabled, get the default block layer mapping of software queues
+ * to hardware queues. This information is saved in request tag.
+ *
+ * Return: index into SLI4 fast-path FCP queue index.
+ **/
+int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba,
+ struct lpfc_scsi_buf *lpfc_cmd)
+{
+ struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
+ struct lpfc_vector_map_info *cpup;
+ int chann, cpu;
+ uint32_t tag;
+ uint16_t hwq;
+
+ if (shost_use_blk_mq(cmnd->device->host)) {
+ tag = blk_mq_unique_tag(cmnd->request);
+ hwq = blk_mq_unique_tag_to_hwq(tag);
+
+ return hwq;
+ }
+
+ if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU
+ && phba->cfg_fcp_io_channel > 1) {
+ cpu = smp_processor_id();
+ if (cpu < phba->sli4_hba.num_present_cpu) {
+ cpup = phba->sli4_hba.cpu_map;
+ cpup += cpu;
+ return cpup->channel_id;
+ }
+ }
+ chann = atomic_add_return(1, &phba->fcp_qidx);
+ chann = (chann % phba->cfg_fcp_io_channel);
+ return chann;
+}
+
+
+/**
* lpfc_scsi_cmd_iocb_cmpl - Scsi cmnd IOCB completion routine
* @phba: The Hba for which this call is being executed.
* @pIocbIn: The command IOCBQ for the scsi cmnd.
@@ -4147,6 +4170,24 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
}
/**
+ * lpfc_fcpcmd_to_iocb - copy the fcp_cmd data into the IOCB
+ * @data: A pointer to the immediate command data portion of the IOCB.
+ * @fcp_cmnd: The FCP Command that is provided by the SCSI layer.
+ *
+ * The routine copies the entire FCP command from @fcp_cmnd to @data while
+ * byte swapping the data to big endian format for transmission on the wire.
+ **/
+static void
+lpfc_fcpcmd_to_iocb(uint8_t *data, struct fcp_cmnd *fcp_cmnd)
+{
+ int i, j;
+ for (i = 0, j = 0; i < sizeof(struct fcp_cmnd);
+ i += sizeof(uint32_t), j++) {
+ ((uint32_t *)data)[j] = cpu_to_be32(((uint32_t *)fcp_cmnd)[j]);
+ }
+}
+
+/**
* lpfc_scsi_prep_cmnd - Wrapper func for convert scsi cmnd to FCP info unit
* @vport: The virtual port for which this call is being executed.
* @lpfc_cmd: The scsi command which needs to send.
@@ -4225,6 +4266,9 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
fcp_cmnd->fcpCntl3 = 0;
phba->fc4ControlRequests++;
}
+ if (phba->sli_rev == 3 &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED))
+ lpfc_fcpcmd_to_iocb(iocb_cmd->unsli3.fcp_ext.icd, fcp_cmnd);
/*
* Finish initializing those IOCB fields that are independent
* of the scsi_cmnd request_buffer
@@ -4536,7 +4580,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
if (lpfc_cmd == NULL) {
lpfc_rampdown_queue_depth(phba);
- lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_MISC,
"0707 driver's buffer pool is empty, "
"IO busied\n");
goto out_host_busy;
@@ -4967,13 +5011,16 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata,
iocbq, iocbqrsp, lpfc_cmd->timeout);
if ((status != IOCB_SUCCESS) ||
(iocbqrsp->iocb.ulpStatus != IOSTAT_SUCCESS)) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
- "0727 TMF %s to TGT %d LUN %llu failed (%d, %d) "
- "iocb_flag x%x\n",
- lpfc_taskmgmt_name(task_mgmt_cmd),
- tgt_id, lun_id, iocbqrsp->iocb.ulpStatus,
- iocbqrsp->iocb.un.ulpWord[4],
- iocbq->iocb_flag);
+ if (status != IOCB_SUCCESS ||
+ iocbqrsp->iocb.ulpStatus != IOSTAT_FCP_RSP_ERROR)
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "0727 TMF %s to TGT %d LUN %llu "
+ "failed (%d, %d) iocb_flag x%x\n",
+ lpfc_taskmgmt_name(task_mgmt_cmd),
+ tgt_id, lun_id,
+ iocbqrsp->iocb.ulpStatus,
+ iocbqrsp->iocb.un.ulpWord[4],
+ iocbq->iocb_flag);
/* if ulpStatus != IOCB_SUCCESS, then status == IOCB_SUCCESS */
if (status == IOCB_SUCCESS) {
if (iocbqrsp->iocb.ulpStatus == IOSTAT_FCP_RSP_ERROR)
@@ -4987,7 +5034,6 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata,
} else {
ret = FAILED;
}
- lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
} else
ret = SUCCESS;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index 474e30cdee6e..18b9260ccfac 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -184,3 +184,6 @@ struct lpfc_scsi_buf {
#define FIND_FIRST_OAS_LUN 0
#define NO_MORE_OAS_LUN -1
#define NOT_OAS_ENABLED_LUN NO_MORE_OAS_LUN
+
+int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba,
+ struct lpfc_scsi_buf *lpfc_cmd);
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 56f73682d4bd..4feb9312a447 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -2249,7 +2249,7 @@ lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
vport->vpi, ndlp->nlp_rpi,
ndlp->nlp_DID,
ndlp->nlp_usg_map, ndlp);
-
+ ndlp->nlp_flag &= ~NLP_LOGO_ACC;
lpfc_nlp_put(ndlp);
}
}
@@ -8138,36 +8138,6 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
}
/**
- * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution
- * @phba: Pointer to HBA context object.
- *
- * This routine performs a roundrobin SCSI command to SLI4 FCP WQ index
- * distribution. This is called by __lpfc_sli_issue_iocb_s4() with the hbalock
- * held.
- *
- * Return: index into SLI4 fast-path FCP queue index.
- **/
-static inline int
-lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba)
-{
- struct lpfc_vector_map_info *cpup;
- int chann, cpu;
-
- if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU
- && phba->cfg_fcp_io_channel > 1) {
- cpu = smp_processor_id();
- if (cpu < phba->sli4_hba.num_present_cpu) {
- cpup = phba->sli4_hba.cpu_map;
- cpup += cpu;
- return cpup->channel_id;
- }
- }
- chann = atomic_add_return(1, &phba->fcp_qidx);
- chann = (chann % phba->cfg_fcp_io_channel);
- return chann;
-}
-
-/**
* lpfc_sli_iocb2wqe - Convert the IOCB to a work queue entry.
* @phba: Pointer to HBA context object.
* @piocb: Pointer to command iocb.
@@ -8792,32 +8762,44 @@ lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
return 0;
}
+/**
+ * lpfc_sli_calc_ring - Calculates which ring to use
+ * @phba: Pointer to HBA context object.
+ * @ring_number: Initial ring
+ * @piocb: Pointer to command iocb.
+ *
+ * For SLI4, FCP IO can deferred to one fo many WQs, based on
+ * fcp_wqidx, thus we need to calculate the corresponding ring.
+ * Since ABORTS must go on the same WQ of the command they are
+ * aborting, we use command's fcp_wqidx.
+ */
int
lpfc_sli_calc_ring(struct lpfc_hba *phba, uint32_t ring_number,
struct lpfc_iocbq *piocb)
{
- uint32_t idx;
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ return ring_number;
- if (phba->sli_rev == LPFC_SLI_REV4) {
- if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) {
+ if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) {
+ if (!(phba->cfg_fof) ||
+ (!(piocb->iocb_flag & LPFC_IO_FOF))) {
+ if (unlikely(!phba->sli4_hba.fcp_wq))
+ return LPFC_HBA_ERROR;
/*
- * fcp_wqidx should already be setup based on what
- * completion queue we want to use.
+ * for abort iocb fcp_wqidx should already
+ * be setup based on what work queue we used.
*/
- if (!(phba->cfg_fof) ||
- (!(piocb->iocb_flag & LPFC_IO_FOF))) {
- if (unlikely(!phba->sli4_hba.fcp_wq))
- return LPFC_HBA_ERROR;
- idx = lpfc_sli4_scmd_to_wqidx_distr(phba);
- piocb->fcp_wqidx = idx;
- ring_number = MAX_SLI3_CONFIGURED_RINGS + idx;
- } else {
- if (unlikely(!phba->sli4_hba.oas_wq))
- return LPFC_HBA_ERROR;
- idx = 0;
- piocb->fcp_wqidx = idx;
- ring_number = LPFC_FCP_OAS_RING;
- }
+ if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX))
+ piocb->fcp_wqidx =
+ lpfc_sli4_scmd_to_wqidx_distr(phba,
+ piocb->context1);
+ ring_number = MAX_SLI3_CONFIGURED_RINGS +
+ piocb->fcp_wqidx;
+ } else {
+ if (unlikely(!phba->sli4_hba.oas_wq))
+ return LPFC_HBA_ERROR;
+ piocb->fcp_wqidx = 0;
+ ring_number = LPFC_FCP_OAS_RING;
}
}
return ring_number;
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 6eca3b8124d3..d1a5b057c6f3 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -602,6 +602,7 @@ struct lpfc_sli4_hba {
struct lpfc_iov iov;
spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */
spinlock_t abts_sgl_list_lock; /* list of aborted els IOs */
+ uint32_t physical_port;
/* CPU to vector mapping information */
struct lpfc_vector_map_info *cpu_map;
@@ -651,6 +652,26 @@ struct lpfc_rsrc_blks {
uint16_t rsrc_used;
};
+struct lpfc_rdp_context {
+ struct lpfc_nodelist *ndlp;
+ uint16_t ox_id;
+ uint16_t rx_id;
+ READ_LNK_VAR link_stat;
+ uint8_t page_a0[DMP_SFF_PAGE_A0_SIZE];
+ uint8_t page_a2[DMP_SFF_PAGE_A2_SIZE];
+ void (*cmpl)(struct lpfc_hba *, struct lpfc_rdp_context*, int);
+};
+
+struct lpfc_lcb_context {
+ uint8_t sub_command;
+ uint8_t type;
+ uint8_t frequency;
+ uint16_t ox_id;
+ uint16_t rx_id;
+ struct lpfc_nodelist *ndlp;
+};
+
+
/*
* SLI4 specific function prototypes
*/
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index c37bb9f91c3b..6258d3d7722a 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "10.5.0.0."
+#define LPFC_DRIVER_VERSION "10.7.0.0."
#define LPFC_DRIVER_NAME "lpfc"
/* Used for SLI 2/3 */
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index a87ee33f4f2a..769012663a8f 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -567,8 +567,8 @@ int
lpfc_vport_delete(struct fc_vport *fc_vport)
{
struct lpfc_nodelist *ndlp = NULL;
- struct Scsi_Host *shost = (struct Scsi_Host *) fc_vport->shost;
struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
long timeout;
bool ns_ndlp_referenced = false;
@@ -645,8 +645,8 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
}
/* Remove FC host and then SCSI host with the vport */
- fc_remove_host(lpfc_shost_from_vport(vport));
- scsi_remove_host(lpfc_shost_from_vport(vport));
+ fc_remove_host(shost);
+ scsi_remove_host(shost);
ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
@@ -772,7 +772,8 @@ skip_logo:
* Completion of unreg_vpi (lpfc_mbx_cmpl_unreg_vpi)
* does the scsi_host_put() to release the vport.
*/
- if (lpfc_mbx_unreg_vpi(vport))
+ if (!(vport->vpi_state & LPFC_VPI_REGISTERED) ||
+ lpfc_mbx_unreg_vpi(vport))
scsi_host_put(shost);
} else
scsi_host_put(shost);
diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c
index 0adb2e015597..141226631429 100644
--- a/drivers/scsi/mac53c94.c
+++ b/drivers/scsi/mac53c94.c
@@ -403,7 +403,6 @@ static struct scsi_host_template mac53c94_template = {
.can_queue = 1,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
};
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index 14e5c7cea929..20c37541963f 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -35,7 +35,8 @@
/*
* MegaRAID SAS Driver meta data
*/
-#define MEGASAS_VERSION "06.806.08.00-rc1"
+#define MEGASAS_VERSION "06.807.10.00-rc1"
+#define MEGASAS_RELDATE "March 6, 2015"
/*
* Device IDs
@@ -153,6 +154,9 @@
#define MFI_FRAME_DIR_BOTH 0x0018
#define MFI_FRAME_IEEE 0x0020
+/* Driver internal */
+#define DRV_DCMD_POLLED_MODE 0x1
+
/*
* Definition for cmd_status
*/
@@ -408,7 +412,7 @@ enum MR_PD_STATE {
* defines the physical drive address structure
*/
struct MR_PD_ADDRESS {
- u16 deviceId;
+ __le16 deviceId;
u16 enclDeviceId;
union {
@@ -433,8 +437,8 @@ struct MR_PD_ADDRESS {
* defines the physical drive list structure
*/
struct MR_PD_LIST {
- u32 size;
- u32 count;
+ __le32 size;
+ __le32 count;
struct MR_PD_ADDRESS addr[1];
} __packed;
@@ -451,28 +455,28 @@ union MR_LD_REF {
struct {
u8 targetId;
u8 reserved;
- u16 seqNum;
+ __le16 seqNum;
};
- u32 ref;
+ __le32 ref;
} __packed;
/*
* defines the logical drive list structure
*/
struct MR_LD_LIST {
- u32 ldCount;
- u32 reserved;
+ __le32 ldCount;
+ __le32 reserved;
struct {
union MR_LD_REF ref;
u8 state;
u8 reserved[3];
- u64 size;
+ __le64 size;
} ldList[MAX_LOGICAL_DRIVES_EXT];
} __packed;
struct MR_LD_TARGETID_LIST {
- u32 size;
- u32 count;
+ __le32 size;
+ __le32 count;
u8 pad[3];
u8 targetId[MAX_LOGICAL_DRIVES_EXT];
};
@@ -553,7 +557,7 @@ struct megasas_ctrl_prop {
} OnOffProperties;
u8 autoSnapVDSpace;
u8 viewSpace;
- u16 spinDownTime;
+ __le16 spinDownTime;
u8 reserved[24];
} __packed;
@@ -567,10 +571,10 @@ struct megasas_ctrl_info {
*/
struct {
- u16 vendor_id;
- u16 device_id;
- u16 sub_vendor_id;
- u16 sub_device_id;
+ __le16 vendor_id;
+ __le16 device_id;
+ __le16 sub_vendor_id;
+ __le16 sub_device_id;
u8 reserved[24];
} __attribute__ ((packed)) pci;
@@ -611,8 +615,8 @@ struct megasas_ctrl_info {
/*
* List of components residing in flash. All str are null terminated
*/
- u32 image_check_word;
- u32 image_component_count;
+ __le32 image_check_word;
+ __le32 image_component_count;
struct {
@@ -629,7 +633,7 @@ struct megasas_ctrl_info {
* empty if a flash operation has not occurred. All stings are null
* terminated
*/
- u32 pending_image_component_count;
+ __le32 pending_image_component_count;
struct {
@@ -662,39 +666,39 @@ struct megasas_ctrl_info {
} __attribute__ ((packed)) hw_present;
- u32 current_fw_time;
+ __le32 current_fw_time;
/*
* Maximum data transfer sizes
*/
- u16 max_concurrent_cmds;
- u16 max_sge_count;
- u32 max_request_size;
+ __le16 max_concurrent_cmds;
+ __le16 max_sge_count;
+ __le32 max_request_size;
/*
* Logical and physical device counts
*/
- u16 ld_present_count;
- u16 ld_degraded_count;
- u16 ld_offline_count;
+ __le16 ld_present_count;
+ __le16 ld_degraded_count;
+ __le16 ld_offline_count;
- u16 pd_present_count;
- u16 pd_disk_present_count;
- u16 pd_disk_pred_failure_count;
- u16 pd_disk_failed_count;
+ __le16 pd_present_count;
+ __le16 pd_disk_present_count;
+ __le16 pd_disk_pred_failure_count;
+ __le16 pd_disk_failed_count;
/*
* Memory size information
*/
- u16 nvram_size;
- u16 memory_size;
- u16 flash_size;
+ __le16 nvram_size;
+ __le16 memory_size;
+ __le16 flash_size;
/*
* Error counters
*/
- u16 mem_correctable_error_count;
- u16 mem_uncorrectable_error_count;
+ __le16 mem_correctable_error_count;
+ __le16 mem_uncorrectable_error_count;
/*
* Cluster information
@@ -705,7 +709,7 @@ struct megasas_ctrl_info {
/*
* Additional max data transfer sizes
*/
- u16 max_strips_per_io;
+ __le16 max_strips_per_io;
/*
* Controller capabilities structures
@@ -805,7 +809,7 @@ struct megasas_ctrl_info {
* deviceInterface.portAddr, and the rest shall be
* populated in deviceInterfacePortAddr2.
*/
- u64 deviceInterfacePortAddr2[8]; /*6a0h */
+ __le64 deviceInterfacePortAddr2[8]; /*6a0h */
u8 reserved3[128]; /*6e0h */
struct { /*760h */
@@ -842,26 +846,26 @@ struct megasas_ctrl_info {
u16 reserved[6];
} pdsForRaidLevels;
- u16 maxPds; /*780h */
- u16 maxDedHSPs; /*782h */
- u16 maxGlobalHSPs; /*784h */
- u16 ddfSize; /*786h */
+ __le16 maxPds; /*780h */
+ __le16 maxDedHSPs; /*782h */
+ __le16 maxGlobalHSP; /*784h */
+ __le16 ddfSize; /*786h */
u8 maxLdsPerArray; /*788h */
u8 partitionsInDDF; /*789h */
u8 lockKeyBinding; /*78ah */
u8 maxPITsPerLd; /*78bh */
u8 maxViewsPerLd; /*78ch */
u8 maxTargetId; /*78dh */
- u16 maxBvlVdSize; /*78eh */
+ __le16 maxBvlVdSize; /*78eh */
- u16 maxConfigurableSSCSize; /*790h */
- u16 currentSSCsize; /*792h */
+ __le16 maxConfigurableSSCSize; /*790h */
+ __le16 currentSSCsize; /*792h */
char expanderFwVersion[12]; /*794h */
- u16 PFKTrialTimeRemaining; /*7A0h */
+ __le16 PFKTrialTimeRemaining; /*7A0h */
- u16 cacheMemorySize; /*7A2h */
+ __le16 cacheMemorySize; /*7A2h */
struct { /*7A4h */
#if defined(__BIG_ENDIAN_BITFIELD)
@@ -931,7 +935,7 @@ struct megasas_ctrl_info {
u8 temperatureROC; /*7C9h */
u8 temperatureCtrl; /*7CAh */
u8 reserved4; /*7CBh */
- u16 maxConfigurablePds; /*7CCh */
+ __le16 maxConfigurablePds; /*7CCh */
u8 reserved5[2]; /*0x7CDh */
@@ -1042,11 +1046,6 @@ struct megasas_ctrl_info {
#define VD_EXT_DEBUG 0
-enum MR_MFI_MPT_PTHR_FLAGS {
- MFI_MPT_DETACHED = 0,
- MFI_LIST_ADDED = 1,
- MFI_MPT_ATTACHED = 2,
-};
enum MR_SCSI_CMD_TYPE {
READ_WRITE_LDIO = 0,
@@ -1084,6 +1083,7 @@ enum MR_SCSI_CMD_TYPE {
#define MEGASAS_SKINNY_INT_CMDS 5
#define MEGASAS_FUSION_INTERNAL_CMDS 5
#define MEGASAS_FUSION_IOCTL_CMDS 3
+#define MEGASAS_MFI_IOCTL_CMDS 27
#define MEGASAS_MAX_MSIX_QUEUES 128
/*
@@ -1172,22 +1172,22 @@ struct megasas_register_set {
struct megasas_sge32 {
- u32 phys_addr;
- u32 length;
+ __le32 phys_addr;
+ __le32 length;
} __attribute__ ((packed));
struct megasas_sge64 {
- u64 phys_addr;
- u32 length;
+ __le64 phys_addr;
+ __le32 length;
} __attribute__ ((packed));
struct megasas_sge_skinny {
- u64 phys_addr;
- u32 length;
- u32 flag;
+ __le64 phys_addr;
+ __le32 length;
+ __le32 flag;
} __packed;
union megasas_sgl {
@@ -1210,12 +1210,12 @@ struct megasas_header {
u8 cdb_len; /*06h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
- u32 data_xferlen; /*14h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
+ __le32 data_xferlen; /*14h */
} __attribute__ ((packed));
@@ -1248,7 +1248,7 @@ typedef union _MFI_CAPABILITIES {
u32 reserved:25;
#endif
} mfi_capabilities;
- u32 reg;
+ __le32 reg;
} MFI_CAPABILITIES;
struct megasas_init_frame {
@@ -1260,33 +1260,35 @@ struct megasas_init_frame {
u8 reserved_1; /*03h */
MFI_CAPABILITIES driver_operations; /*04h*/
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
-
- u16 flags; /*10h */
- u16 reserved_3; /*12h */
- u32 data_xfer_len; /*14h */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u32 queue_info_new_phys_addr_lo; /*18h */
- u32 queue_info_new_phys_addr_hi; /*1Ch */
- u32 queue_info_old_phys_addr_lo; /*20h */
- u32 queue_info_old_phys_addr_hi; /*24h */
+ __le16 flags; /*10h */
+ __le16 reserved_3; /*12h */
+ __le32 data_xfer_len; /*14h */
- u32 reserved_4[6]; /*28h */
+ __le32 queue_info_new_phys_addr_lo; /*18h */
+ __le32 queue_info_new_phys_addr_hi; /*1Ch */
+ __le32 queue_info_old_phys_addr_lo; /*20h */
+ __le32 queue_info_old_phys_addr_hi; /*24h */
+ __le32 reserved_4[2]; /*28h */
+ __le32 system_info_lo; /*30h */
+ __le32 system_info_hi; /*34h */
+ __le32 reserved_5[2]; /*38h */
} __attribute__ ((packed));
struct megasas_init_queue_info {
- u32 init_flags; /*00h */
- u32 reply_queue_entries; /*04h */
+ __le32 init_flags; /*00h */
+ __le32 reply_queue_entries; /*04h */
- u32 reply_queue_start_phys_addr_lo; /*08h */
- u32 reply_queue_start_phys_addr_hi; /*0Ch */
- u32 producer_index_phys_addr_lo; /*10h */
- u32 producer_index_phys_addr_hi; /*14h */
- u32 consumer_index_phys_addr_lo; /*18h */
- u32 consumer_index_phys_addr_hi; /*1Ch */
+ __le32 reply_queue_start_phys_addr_lo; /*08h */
+ __le32 reply_queue_start_phys_addr_hi; /*0Ch */
+ __le32 producer_index_phys_addr_lo; /*10h */
+ __le32 producer_index_phys_addr_hi; /*14h */
+ __le32 consumer_index_phys_addr_lo; /*18h */
+ __le32 consumer_index_phys_addr_hi; /*1Ch */
} __attribute__ ((packed));
@@ -1302,18 +1304,18 @@ struct megasas_io_frame {
u8 reserved_0; /*06h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
- u32 lba_count; /*14h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
+ __le32 lba_count; /*14h */
- u32 sense_buf_phys_addr_lo; /*18h */
- u32 sense_buf_phys_addr_hi; /*1Ch */
+ __le32 sense_buf_phys_addr_lo; /*18h */
+ __le32 sense_buf_phys_addr_hi; /*1Ch */
- u32 start_lba_lo; /*20h */
- u32 start_lba_hi; /*24h */
+ __le32 start_lba_lo; /*20h */
+ __le32 start_lba_hi; /*24h */
union megasas_sgl sgl; /*28h */
@@ -1331,15 +1333,15 @@ struct megasas_pthru_frame {
u8 cdb_len; /*06h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
- u32 data_xfer_len; /*14h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
+ __le32 data_xfer_len; /*14h */
- u32 sense_buf_phys_addr_lo; /*18h */
- u32 sense_buf_phys_addr_hi; /*1Ch */
+ __le32 sense_buf_phys_addr_lo; /*18h */
+ __le32 sense_buf_phys_addr_hi; /*1Ch */
u8 cdb[16]; /*20h */
union megasas_sgl sgl; /*30h */
@@ -1354,19 +1356,19 @@ struct megasas_dcmd_frame {
u8 reserved_1[4]; /*03h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
- u32 data_xfer_len; /*14h */
- u32 opcode; /*18h */
+ __le32 data_xfer_len; /*14h */
+ __le32 opcode; /*18h */
union { /*1Ch */
u8 b[12];
- u16 s[6];
- u32 w[3];
+ __le16 s[6];
+ __le32 w[3];
} mbox;
union megasas_sgl sgl; /*28h */
@@ -1380,22 +1382,22 @@ struct megasas_abort_frame {
u8 cmd_status; /*02h */
u8 reserved_1; /*03h */
- u32 reserved_2; /*04h */
+ __le32 reserved_2; /*04h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 reserved_3; /*12h */
- u32 reserved_4; /*14h */
+ __le16 flags; /*10h */
+ __le16 reserved_3; /*12h */
+ __le32 reserved_4; /*14h */
- u32 abort_context; /*18h */
- u32 pad_1; /*1Ch */
+ __le32 abort_context; /*18h */
+ __le32 pad_1; /*1Ch */
- u32 abort_mfi_phys_addr_lo; /*20h */
- u32 abort_mfi_phys_addr_hi; /*24h */
+ __le32 abort_mfi_phys_addr_lo; /*20h */
+ __le32 abort_mfi_phys_addr_hi; /*24h */
- u32 reserved_5[6]; /*28h */
+ __le32 reserved_5[6]; /*28h */
} __attribute__ ((packed));
@@ -1409,14 +1411,14 @@ struct megasas_smp_frame {
u8 reserved_2[3]; /*04h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
- u32 data_xfer_len; /*14h */
- u64 sas_addr; /*18h */
+ __le32 data_xfer_len; /*14h */
+ __le64 sas_addr; /*18h */
union {
struct megasas_sge32 sge32[2]; /* [0]: resp [1]: req */
@@ -1436,16 +1438,16 @@ struct megasas_stp_frame {
u8 reserved_3[2]; /*05h */
u8 sge_count; /*07h */
- u32 context; /*08h */
- u32 pad_0; /*0Ch */
+ __le32 context; /*08h */
+ __le32 pad_0; /*0Ch */
- u16 flags; /*10h */
- u16 timeout; /*12h */
+ __le16 flags; /*10h */
+ __le16 timeout; /*12h */
- u32 data_xfer_len; /*14h */
+ __le32 data_xfer_len; /*14h */
- u16 fis[10]; /*18h */
- u32 stp_flags;
+ __le16 fis[10]; /*18h */
+ __le32 stp_flags;
union {
struct megasas_sge32 sge32[2]; /* [0]: resp [1]: data */
@@ -1489,18 +1491,18 @@ union megasas_evt_class_locale {
} __attribute__ ((packed));
struct megasas_evt_log_info {
- u32 newest_seq_num;
- u32 oldest_seq_num;
- u32 clear_seq_num;
- u32 shutdown_seq_num;
- u32 boot_seq_num;
+ __le32 newest_seq_num;
+ __le32 oldest_seq_num;
+ __le32 clear_seq_num;
+ __le32 shutdown_seq_num;
+ __le32 boot_seq_num;
} __attribute__ ((packed));
struct megasas_progress {
- u16 progress;
- u16 elapsed_seconds;
+ __le16 progress;
+ __le16 elapsed_seconds;
} __attribute__ ((packed));
@@ -1521,9 +1523,9 @@ struct megasas_evtarg_pd {
struct megasas_evt_detail {
- u32 seq_num;
- u32 time_stamp;
- u32 code;
+ __le32 seq_num;
+ __le32 time_stamp;
+ __le32 code;
union megasas_evt_class_locale cl;
u8 arg_type;
u8 reserved1[15];
@@ -1542,18 +1544,18 @@ struct megasas_evt_detail {
struct {
struct megasas_evtarg_ld ld;
- u64 count;
+ __le64 count;
} __attribute__ ((packed)) ld_count;
struct {
- u64 lba;
+ __le64 lba;
struct megasas_evtarg_ld ld;
} __attribute__ ((packed)) ld_lba;
struct {
struct megasas_evtarg_ld ld;
- u32 prevOwner;
- u32 newOwner;
+ __le32 prevOwner;
+ __le32 newOwner;
} __attribute__ ((packed)) ld_owner;
struct {
@@ -1610,7 +1612,7 @@ struct megasas_evt_detail {
struct {
u16 vendorId;
- u16 deviceId;
+ __le16 deviceId;
u16 subVendorId;
u16 subDeviceId;
} __attribute__ ((packed)) pci;
@@ -1630,9 +1632,9 @@ struct megasas_evt_detail {
} __attribute__ ((packed)) ecc;
u8 b[96];
- u16 s[48];
- u32 w[24];
- u64 d[12];
+ __le16 s[48];
+ __le32 w[24];
+ __le64 d[12];
} args;
char description[128];
@@ -1649,12 +1651,22 @@ struct megasas_irq_context {
u32 MSIxIndex;
};
+struct MR_DRV_SYSTEM_INFO {
+ u8 infoVersion;
+ u8 systemIdLength;
+ u16 reserved0;
+ u8 systemId[64];
+ u8 reserved[1980];
+};
+
struct megasas_instance {
- u32 *producer;
+ __le32 *producer;
dma_addr_t producer_h;
- u32 *consumer;
+ __le32 *consumer;
dma_addr_t consumer_h;
+ struct MR_DRV_SYSTEM_INFO *system_info_buf;
+ dma_addr_t system_info_h;
struct MR_LD_VF_AFFILIATION *vf_affiliation;
dma_addr_t vf_affiliation_h;
struct MR_LD_VF_AFFILIATION_111 *vf_affiliation_111;
@@ -1662,7 +1674,7 @@ struct megasas_instance {
struct MR_CTRL_HB_HOST_MEM *hb_host_mem;
dma_addr_t hb_host_mem_h;
- u32 *reply_queue;
+ __le32 *reply_queue;
dma_addr_t reply_queue_h;
u32 *crash_dump_buf;
@@ -1681,7 +1693,7 @@ struct megasas_instance {
spinlock_t crashdump_lock;
struct megasas_register_set __iomem *reg_set;
- u32 *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY];
+ u32 __iomem *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY];
struct megasas_pd_list pd_list[MEGASAS_MAX_PD];
struct megasas_pd_list local_pd_list[MEGASAS_MAX_PD];
u8 ld_ids[MEGASAS_MAX_LD_IDS];
@@ -1769,6 +1781,7 @@ struct megasas_instance {
u16 throttlequeuedepth;
u8 mask_interrupts;
u8 is_imr;
+ bool dev_handle;
};
struct MR_LD_VF_MAP {
u32 size;
@@ -1864,9 +1877,13 @@ struct megasas_instance_template {
#define MEGASAS_IS_LOGICAL(scp) \
(scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
-#define MEGASAS_DEV_INDEX(inst, scp) \
- ((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \
- scp->device->id
+#define MEGASAS_DEV_INDEX(scp) \
+ (((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \
+ scp->device->id)
+
+#define MEGASAS_PD_INDEX(scp) \
+ ((scp->device->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + \
+ scp->device->id)
struct megasas_cmd {
@@ -1877,17 +1894,14 @@ struct megasas_cmd {
u32 index;
u8 sync_cmd;
- u8 cmd_status;
+ u8 cmd_status_drv;
u8 abort_aen;
u8 retry_for_fw_reset;
struct list_head list;
struct scsi_cmnd *scmd;
-
- void *mpt_pthr_cmd_blocked;
- atomic_t mfi_mpt_pthr;
- u8 is_wait_event;
+ u8 flags;
struct megasas_instance *instance;
union {
@@ -1963,10 +1977,10 @@ u8 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_DRV_RAID_MAP_ALL *map);
struct MR_LD_RAID *MR_LdRaidGet(u32 ld, struct MR_DRV_RAID_MAP_ALL *map);
u16 MR_ArPdGet(u32 ar, u32 arm, struct MR_DRV_RAID_MAP_ALL *map);
u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_DRV_RAID_MAP_ALL *map);
-u16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map);
+__le16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map);
u16 MR_GetLDTgtId(u32 ld, struct MR_DRV_RAID_MAP_ALL *map);
-u16 get_updated_dev_handle(struct megasas_instance *instance,
+__le16 get_updated_dev_handle(struct megasas_instance *instance,
struct LD_LOAD_BALANCE_INFO *lbInfo, struct IO_REQUEST_INFO *in_info);
void mr_update_load_balance_params(struct MR_DRV_RAID_MAP_ALL *map,
struct LD_LOAD_BALANCE_INFO *lbInfo);
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 890637fdd61e..71b884dae27c 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -94,8 +94,8 @@ MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disbale Defau
MODULE_LICENSE("GPL");
MODULE_VERSION(MEGASAS_VERSION);
-MODULE_AUTHOR("megaraidlinux@lsi.com");
-MODULE_DESCRIPTION("LSI MegaRAID SAS Driver");
+MODULE_AUTHOR("megaraidlinux.pdl@avagotech.com");
+MODULE_DESCRIPTION("Avago MegaRAID SAS Driver");
int megasas_transition_to_ready(struct megasas_instance *instance, int ocr);
static int megasas_get_pd_list(struct megasas_instance *instance);
@@ -215,7 +215,6 @@ struct megasas_cmd *megasas_get_cmd(struct megasas_instance
cmd = list_entry((&instance->cmd_pool)->next,
struct megasas_cmd, list);
list_del_init(&cmd->list);
- atomic_set(&cmd->mfi_mpt_pthr, MFI_MPT_DETACHED);
} else {
printk(KERN_ERR "megasas: Command pool empty!\n");
}
@@ -225,52 +224,41 @@ struct megasas_cmd *megasas_get_cmd(struct megasas_instance
}
/**
- * __megasas_return_cmd - Return a cmd to free command pool
+ * megasas_return_cmd - Return a cmd to free command pool
* @instance: Adapter soft state
* @cmd: Command packet to be returned to free command pool
*/
inline void
-__megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
+megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
{
- /*
- * Don't go ahead and free the MFI frame, if corresponding
- * MPT frame is not freed(valid for only fusion adapters).
- * In case of MFI adapters, anyways for any allocated MFI
- * frame will have cmd->mfi_mpt_mpthr set to MFI_MPT_DETACHED
+ unsigned long flags;
+ u32 blk_tags;
+ struct megasas_cmd_fusion *cmd_fusion;
+ struct fusion_context *fusion = instance->ctrl_context;
+
+ /* This flag is used only for fusion adapter.
+ * Wait for Interrupt for Polled mode DCMD
*/
- if (atomic_read(&cmd->mfi_mpt_pthr) != MFI_MPT_DETACHED)
+ if (cmd->flags & DRV_DCMD_POLLED_MODE)
return;
+ spin_lock_irqsave(&instance->mfi_pool_lock, flags);
+
+ if (fusion) {
+ blk_tags = instance->max_scsi_cmds + cmd->index;
+ cmd_fusion = fusion->cmd_list[blk_tags];
+ megasas_return_cmd_fusion(instance, cmd_fusion);
+ }
cmd->scmd = NULL;
cmd->frame_count = 0;
- cmd->is_wait_event = 0;
- cmd->mpt_pthr_cmd_blocked = NULL;
-
- if ((instance->pdev->device != PCI_DEVICE_ID_LSI_FUSION) &&
- (instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER) &&
- (instance->pdev->device != PCI_DEVICE_ID_LSI_FURY) &&
- (reset_devices))
+ cmd->flags = 0;
+ if (!fusion && reset_devices)
cmd->frame->hdr.cmd = MFI_CMD_INVALID;
-
- atomic_set(&cmd->mfi_mpt_pthr, MFI_LIST_ADDED);
list_add(&cmd->list, (&instance->cmd_pool)->next);
-}
-/**
- * megasas_return_cmd - Return a cmd to free command pool
- * @instance: Adapter soft state
- * @cmd: Command packet to be returned to free command pool
- */
-inline void
-megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&instance->mfi_pool_lock, flags);
- __megasas_return_cmd(instance, cmd);
spin_unlock_irqrestore(&instance->mfi_pool_lock, flags);
-}
+}
/**
* The following functions are defined for xscale
@@ -814,8 +802,8 @@ megasas_adp_reset_gen2(struct megasas_instance *instance,
{
u32 retry = 0 ;
u32 HostDiag;
- u32 *seq_offset = &reg_set->seq_offset;
- u32 *hostdiag_offset = &reg_set->host_diag;
+ u32 __iomem *seq_offset = &reg_set->seq_offset;
+ u32 __iomem *hostdiag_offset = &reg_set->host_diag;
if (instance->instancet == &megasas_instance_template_skinny) {
seq_offset = &reg_set->fusion_seq_offset;
@@ -910,7 +898,7 @@ extern struct megasas_instance_template megasas_instance_template_fusion;
* @instance: Adapter soft state
* @cmd: Command packet to be issued
*
- * For polling, MFI requires the cmd_status to be set to 0xFF before posting.
+ * For polling, MFI requires the cmd_status to be set to MFI_STAT_INVALID_STATUS before posting.
*/
int
megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd)
@@ -952,20 +940,20 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance,
struct megasas_cmd *cmd, int timeout)
{
int ret = 0;
- cmd->cmd_status = ENODATA;
+ cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS;
- cmd->is_wait_event = 1;
instance->instancet->issue_dcmd(instance, cmd);
if (timeout) {
ret = wait_event_timeout(instance->int_cmd_wait_q,
- cmd->cmd_status != ENODATA, timeout * HZ);
+ cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS, timeout * HZ);
if (!ret)
return 1;
} else
wait_event(instance->int_cmd_wait_q,
- cmd->cmd_status != ENODATA);
+ cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS);
- return 0;
+ return (cmd->cmd_status_drv == MFI_STAT_OK) ?
+ 0 : 1;
}
/**
@@ -998,7 +986,7 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
* Prepare and issue the abort frame
*/
abort_fr->cmd = MFI_CMD_ABORT;
- abort_fr->cmd_status = 0xFF;
+ abort_fr->cmd_status = MFI_STAT_INVALID_STATUS;
abort_fr->flags = cpu_to_le16(0);
abort_fr->abort_context = cpu_to_le32(cmd_to_abort->index);
abort_fr->abort_mfi_phys_addr_lo =
@@ -1007,13 +995,13 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
cpu_to_le32(upper_32_bits(cmd_to_abort->frame_phys_addr));
cmd->sync_cmd = 1;
- cmd->cmd_status = ENODATA;
+ cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS;
instance->instancet->issue_dcmd(instance, cmd);
if (timeout) {
ret = wait_event_timeout(instance->abort_cmd_wait_q,
- cmd->cmd_status != ENODATA, timeout * HZ);
+ cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS, timeout * HZ);
if (!ret) {
dev_err(&instance->pdev->dev, "Command timedout"
"from %s\n", __func__);
@@ -1021,7 +1009,7 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
}
} else
wait_event(instance->abort_cmd_wait_q,
- cmd->cmd_status != ENODATA);
+ cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS);
cmd->sync_cmd = 0;
@@ -1196,7 +1184,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
struct megasas_pthru_frame *pthru;
is_logical = MEGASAS_IS_LOGICAL(scp);
- device_id = MEGASAS_DEV_INDEX(instance, scp);
+ device_id = MEGASAS_DEV_INDEX(scp);
pthru = (struct megasas_pthru_frame *)cmd->frame;
if (scp->sc_data_direction == PCI_DMA_TODEVICE)
@@ -1232,7 +1220,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
*/
if (scp->device->type == TYPE_TAPE) {
if ((scp->request->timeout / HZ) > 0xFFFF)
- pthru->timeout = 0xFFFF;
+ pthru->timeout = cpu_to_le16(0xFFFF);
else
pthru->timeout = cpu_to_le16(scp->request->timeout / HZ);
}
@@ -1294,7 +1282,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
u16 flags = 0;
struct megasas_io_frame *ldio;
- device_id = MEGASAS_DEV_INDEX(instance, scp);
+ device_id = MEGASAS_DEV_INDEX(scp);
ldio = (struct megasas_io_frame *)cmd->frame;
if (scp->sc_data_direction == PCI_DMA_TODEVICE)
@@ -1698,7 +1686,7 @@ static int megasas_slave_alloc(struct scsi_device *sdev)
* @instance: Adapter soft state
*
*/
-void megasas_complete_outstanding_ioctls(struct megasas_instance *instance)
+static void megasas_complete_outstanding_ioctls(struct megasas_instance *instance)
{
int i;
struct megasas_cmd *cmd_mfi;
@@ -1922,22 +1910,24 @@ static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance,
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
- dcmd->flags = MFI_FRAME_DIR_BOTH;
+ dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH);
dcmd->timeout = 0;
dcmd->pad_0 = 0;
- dcmd->data_xfer_len = sizeof(struct MR_LD_VF_AFFILIATION_111);
- dcmd->opcode = MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111;
+ dcmd->data_xfer_len =
+ cpu_to_le32(sizeof(struct MR_LD_VF_AFFILIATION_111));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111);
if (initial)
dcmd->sgl.sge32[0].phys_addr =
- instance->vf_affiliation_111_h;
+ cpu_to_le32(instance->vf_affiliation_111_h);
else
- dcmd->sgl.sge32[0].phys_addr = new_affiliation_111_h;
+ dcmd->sgl.sge32[0].phys_addr =
+ cpu_to_le32(new_affiliation_111_h);
- dcmd->sgl.sge32[0].length =
- sizeof(struct MR_LD_VF_AFFILIATION_111);
+ dcmd->sgl.sge32[0].length = cpu_to_le32(
+ sizeof(struct MR_LD_VF_AFFILIATION_111));
printk(KERN_WARNING "megasas: SR-IOV: Getting LD/VF affiliation for "
"scsi%d\n", instance->host->host_no);
@@ -1976,11 +1966,7 @@ out:
new_affiliation_111_h);
}
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return retval;
}
@@ -2037,22 +2023,24 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance,
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
- dcmd->flags = MFI_FRAME_DIR_BOTH;
+ dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH);
dcmd->timeout = 0;
dcmd->pad_0 = 0;
- dcmd->data_xfer_len = (MAX_LOGICAL_DRIVES + 1) *
- sizeof(struct MR_LD_VF_AFFILIATION);
- dcmd->opcode = MR_DCMD_LD_VF_MAP_GET_ALL_LDS;
+ dcmd->data_xfer_len = cpu_to_le32((MAX_LOGICAL_DRIVES + 1) *
+ sizeof(struct MR_LD_VF_AFFILIATION));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_LD_VF_MAP_GET_ALL_LDS);
if (initial)
- dcmd->sgl.sge32[0].phys_addr = instance->vf_affiliation_h;
+ dcmd->sgl.sge32[0].phys_addr =
+ cpu_to_le32(instance->vf_affiliation_h);
else
- dcmd->sgl.sge32[0].phys_addr = new_affiliation_h;
+ dcmd->sgl.sge32[0].phys_addr =
+ cpu_to_le32(new_affiliation_h);
- dcmd->sgl.sge32[0].length = (MAX_LOGICAL_DRIVES + 1) *
- sizeof(struct MR_LD_VF_AFFILIATION);
+ dcmd->sgl.sge32[0].length = cpu_to_le32((MAX_LOGICAL_DRIVES + 1) *
+ sizeof(struct MR_LD_VF_AFFILIATION));
printk(KERN_WARNING "megasas: SR-IOV: Getting LD/VF affiliation for "
"scsi%d\n", instance->host->host_no);
@@ -2147,11 +2135,7 @@ out:
(MAX_LOGICAL_DRIVES + 1) *
sizeof(struct MR_LD_VF_AFFILIATION),
new_affiliation, new_affiliation_h);
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return retval;
}
@@ -2204,39 +2188,33 @@ int megasas_sriov_start_heartbeat(struct megasas_instance *instance,
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
- dcmd->mbox.s[0] = sizeof(struct MR_CTRL_HB_HOST_MEM);
+ dcmd->mbox.s[0] = cpu_to_le16(sizeof(struct MR_CTRL_HB_HOST_MEM));
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
- dcmd->flags = MFI_FRAME_DIR_BOTH;
+ dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH);
dcmd->timeout = 0;
dcmd->pad_0 = 0;
- dcmd->data_xfer_len = sizeof(struct MR_CTRL_HB_HOST_MEM);
- dcmd->opcode = MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC;
- dcmd->sgl.sge32[0].phys_addr = instance->hb_host_mem_h;
- dcmd->sgl.sge32[0].length = sizeof(struct MR_CTRL_HB_HOST_MEM);
+ dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_CTRL_HB_HOST_MEM));
+ dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC);
+ dcmd->sgl.sge32[0].phys_addr = cpu_to_le32(instance->hb_host_mem_h);
+ dcmd->sgl.sge32[0].length = cpu_to_le32(sizeof(struct MR_CTRL_HB_HOST_MEM));
printk(KERN_WARNING "megasas: SR-IOV: Starting heartbeat for scsi%d\n",
instance->host->host_no);
- if (!megasas_issue_polled(instance, cmd)) {
- retval = 0;
- } else {
- printk(KERN_WARNING "megasas: SR-IOV: MR_DCMD_CTRL_SHARED_HOST"
- "_MEM_ALLOC DCMD timed out for scsi%d\n",
- instance->host->host_no);
- retval = 1;
- goto out;
- }
-
+ if (instance->ctrl_context && !instance->mask_interrupts)
+ retval = megasas_issue_blocked_cmd(instance, cmd,
+ MEGASAS_ROUTINE_WAIT_TIME_VF);
+ else
+ retval = megasas_issue_polled(instance, cmd);
- if (dcmd->cmd_status) {
- printk(KERN_WARNING "megasas: SR-IOV: MR_DCMD_CTRL_SHARED_HOST"
- "_MEM_ALLOC DCMD failed with status 0x%x for scsi%d\n",
- dcmd->cmd_status,
- instance->host->host_no);
+ if (retval) {
+ dev_warn(&instance->pdev->dev, "SR-IOV: MR_DCMD_CTRL_SHARED_HOST"
+ "_MEM_ALLOC DCMD %s for scsi%d\n",
+ (dcmd->cmd_status == MFI_STAT_INVALID_STATUS) ?
+ "timed out" : "failed", instance->host->host_no);
retval = 1;
- goto out;
}
out:
@@ -2332,7 +2310,7 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
"reset queue\n",
reset_cmd);
- reset_cmd->cmd_status = ENODATA;
+ reset_cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS;
instance->instancet->fire_cmd(instance,
reset_cmd->frame_phys_addr,
0, instance->reg_set);
@@ -2612,11 +2590,7 @@ megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd)
instance->aen_cmd = NULL;
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
if ((instance->unload == 0) &&
((instance->issuepend_done == 1))) {
@@ -2786,7 +2760,7 @@ struct device_attribute *megaraid_host_attrs[] = {
static struct scsi_host_template megasas_template = {
.module = THIS_MODULE,
- .name = "LSI SAS based MegaRAID driver",
+ .name = "Avago SAS based MegaRAID driver",
.proc_name = "megaraid_sas",
.slave_configure = megasas_slave_configure,
.slave_alloc = megasas_slave_alloc,
@@ -2815,11 +2789,7 @@ static void
megasas_complete_int_cmd(struct megasas_instance *instance,
struct megasas_cmd *cmd)
{
- cmd->cmd_status = cmd->frame->io.cmd_status;
-
- if (cmd->cmd_status == ENODATA) {
- cmd->cmd_status = 0;
- }
+ cmd->cmd_status_drv = cmd->frame->io.cmd_status;
wake_up(&instance->int_cmd_wait_q);
}
@@ -2838,7 +2808,7 @@ megasas_complete_abort(struct megasas_instance *instance,
{
if (cmd->sync_cmd) {
cmd->sync_cmd = 0;
- cmd->cmd_status = 0;
+ cmd->cmd_status_drv = 0;
wake_up(&instance->abort_cmd_wait_q);
}
@@ -2978,8 +2948,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
"failed, status = 0x%x.\n",
cmd->frame->hdr.cmd_status);
else {
- megasas_return_mfi_mpt_pthr(instance,
- cmd, cmd->mpt_pthr_cmd_blocked);
+ megasas_return_cmd(instance, cmd);
spin_unlock_irqrestore(
instance->host->host_lock,
flags);
@@ -2987,8 +2956,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
}
} else
instance->map_id++;
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
+ megasas_return_cmd(instance, cmd);
/*
* Set fast path IO to ZERO.
@@ -3086,7 +3054,7 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance)
printk(KERN_NOTICE "megasas: %p synchronous cmd"
"on the internal reset queue,"
"issue it again.\n", cmd);
- cmd->cmd_status = ENODATA;
+ cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS;
instance->instancet->fire_cmd(instance,
cmd->frame_phys_addr ,
0, instance->reg_set);
@@ -3766,7 +3734,6 @@ int megasas_alloc_cmds(struct megasas_instance *instance)
cmd = instance->cmd_list[i];
memset(cmd, 0, sizeof(struct megasas_cmd));
cmd->index = i;
- atomic_set(&cmd->mfi_mpt_pthr, MFI_LIST_ADDED);
cmd->scmd = NULL;
cmd->instance = instance;
@@ -3827,7 +3794,7 @@ megasas_get_pd_list(struct megasas_instance *instance)
dcmd->mbox.b[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST;
dcmd->mbox.b[1] = 0;
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ);
dcmd->timeout = 0;
@@ -3874,11 +3841,7 @@ megasas_get_pd_list(struct megasas_instance *instance)
MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST),
ci, ci_h);
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return ret;
}
@@ -3927,7 +3890,7 @@ megasas_get_ld_list(struct megasas_instance *instance)
if (instance->supportmax256vd)
dcmd->mbox.b[0] = 1;
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ);
dcmd->timeout = 0;
@@ -3965,11 +3928,7 @@ megasas_get_ld_list(struct megasas_instance *instance)
ci,
ci_h);
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return ret;
}
@@ -4020,7 +3979,7 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type)
dcmd->mbox.b[2] = 1;
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ);
dcmd->timeout = 0;
@@ -4050,11 +4009,7 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type)
pci_free_consistent(instance->pdev, sizeof(struct MR_LD_TARGETID_LIST),
ci, ci_h);
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return ret;
}
@@ -4091,12 +4046,11 @@ static void megasas_update_ext_vd_details(struct megasas_instance *instance)
instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES;
instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES;
}
- dev_info(&instance->pdev->dev, "Firmware supports %d VD %d PD\n",
- instance->fw_supported_vd_count,
- instance->fw_supported_pd_count);
- dev_info(&instance->pdev->dev, "Driver supports %d VD %d PD\n",
- instance->drv_supported_vd_count,
- instance->drv_supported_pd_count);
+
+ dev_info(&instance->pdev->dev,
+ "firmware type\t: %s\n",
+ instance->supportmax256vd ? "Extended VD(240 VD)firmware" :
+ "Legacy(64 VD) firmware");
old_map_sz = sizeof(struct MR_FW_RAID_MAP) +
(sizeof(struct MR_LD_SPAN_MAP) *
@@ -4158,7 +4112,7 @@ megasas_get_ctrl_info(struct megasas_instance *instance)
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ);
dcmd->timeout = 0;
@@ -4181,16 +4135,17 @@ megasas_get_ctrl_info(struct megasas_instance *instance)
le32_to_cpus((u32 *)&ctrl_info->adapterOperations2);
le32_to_cpus((u32 *)&ctrl_info->adapterOperations3);
megasas_update_ext_vd_details(instance);
+ instance->is_imr = (ctrl_info->memory_size ? 0 : 1);
+ dev_info(&instance->pdev->dev,
+ "controller type\t: %s(%dMB)\n",
+ instance->is_imr ? "iMR" : "MR",
+ le16_to_cpu(ctrl_info->memory_size));
}
pci_free_consistent(instance->pdev, sizeof(struct megasas_ctrl_info),
ci, ci_h);
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return ret;
}
@@ -4229,7 +4184,7 @@ int megasas_set_crash_dump_params(struct megasas_instance *instance,
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
dcmd->mbox.b[0] = crash_buf_state;
dcmd->cmd = MFI_CMD_DCMD;
- dcmd->cmd_status = 0xFF;
+ dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
dcmd->sge_count = 1;
dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_NONE);
dcmd->timeout = 0;
@@ -4245,11 +4200,7 @@ int megasas_set_crash_dump_params(struct megasas_instance *instance,
else
ret = megasas_issue_polled(instance, cmd);
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return ret;
}
@@ -4262,7 +4213,7 @@ int megasas_set_crash_dump_params(struct megasas_instance *instance,
static int
megasas_issue_init_mfi(struct megasas_instance *instance)
{
- u32 context;
+ __le32 context;
struct megasas_cmd *cmd;
@@ -4300,7 +4251,7 @@ megasas_issue_init_mfi(struct megasas_instance *instance)
initq_info->consumer_index_phys_addr_lo = cpu_to_le32(instance->consumer_h);
init_frame->cmd = MFI_CMD_INIT;
- init_frame->cmd_status = 0xFF;
+ init_frame->cmd_status = MFI_STAT_INVALID_STATUS;
init_frame->queue_info_new_phys_addr_lo =
cpu_to_le32(lower_32_bits(initq_info_h));
init_frame->queue_info_new_phys_addr_hi =
@@ -4354,6 +4305,21 @@ megasas_init_adapter_mfi(struct megasas_instance *instance)
instance->max_num_sge = (instance->instancet->read_fw_status_reg(reg_set) & 0xFF0000) >>
0x10;
/*
+ * For MFI skinny adapters, MEGASAS_SKINNY_INT_CMDS commands
+ * are reserved for IOCTL + driver's internal DCMDs.
+ */
+ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ instance->max_scsi_cmds = (instance->max_fw_cmds -
+ MEGASAS_SKINNY_INT_CMDS);
+ sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS);
+ } else {
+ instance->max_scsi_cmds = (instance->max_fw_cmds -
+ MEGASAS_INT_CMDS);
+ sema_init(&instance->ioctl_sem, (MEGASAS_MFI_IOCTL_CMDS));
+ }
+
+ /*
* Create a pool of commands
*/
if (megasas_alloc_cmds(instance))
@@ -4414,6 +4380,107 @@ fail_alloc_cmds:
return 1;
}
+/*
+ * megasas_setup_irqs_msix - register legacy interrupts.
+ * @instance: Adapter soft state
+ *
+ * Do not enable interrupt, only setup ISRs.
+ *
+ * Return 0 on success.
+ */
+static int
+megasas_setup_irqs_ioapic(struct megasas_instance *instance)
+{
+ struct pci_dev *pdev;
+
+ pdev = instance->pdev;
+ instance->irq_context[0].instance = instance;
+ instance->irq_context[0].MSIxIndex = 0;
+ if (request_irq(pdev->irq, instance->instancet->service_isr,
+ IRQF_SHARED, "megasas", &instance->irq_context[0])) {
+ dev_err(&instance->pdev->dev,
+ "Failed to register IRQ from %s %d\n",
+ __func__, __LINE__);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * megasas_setup_irqs_msix - register MSI-x interrupts.
+ * @instance: Adapter soft state
+ * @is_probe: Driver probe check
+ *
+ * Do not enable interrupt, only setup ISRs.
+ *
+ * Return 0 on success.
+ */
+static int
+megasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe)
+{
+ int i, j, cpu;
+ struct pci_dev *pdev;
+
+ pdev = instance->pdev;
+
+ /* Try MSI-x */
+ cpu = cpumask_first(cpu_online_mask);
+ for (i = 0; i < instance->msix_vectors; i++) {
+ instance->irq_context[i].instance = instance;
+ instance->irq_context[i].MSIxIndex = i;
+ if (request_irq(instance->msixentry[i].vector,
+ instance->instancet->service_isr, 0, "megasas",
+ &instance->irq_context[i])) {
+ dev_err(&instance->pdev->dev,
+ "Failed to register IRQ for vector %d.\n", i);
+ for (j = 0; j < i; j++) {
+ if (smp_affinity_enable)
+ irq_set_affinity_hint(
+ instance->msixentry[j].vector, NULL);
+ free_irq(instance->msixentry[j].vector,
+ &instance->irq_context[j]);
+ }
+ /* Retry irq register for IO_APIC*/
+ instance->msix_vectors = 0;
+ if (is_probe)
+ return megasas_setup_irqs_ioapic(instance);
+ else
+ return -1;
+ }
+ if (smp_affinity_enable) {
+ if (irq_set_affinity_hint(instance->msixentry[i].vector,
+ get_cpu_mask(cpu)))
+ dev_err(&instance->pdev->dev,
+ "Failed to set affinity hint"
+ " for cpu %d\n", cpu);
+ cpu = cpumask_next(cpu, cpu_online_mask);
+ }
+ }
+ return 0;
+}
+
+/*
+ * megasas_destroy_irqs- unregister interrupts.
+ * @instance: Adapter soft state
+ * return: void
+ */
+static void
+megasas_destroy_irqs(struct megasas_instance *instance) {
+
+ int i;
+
+ if (instance->msix_vectors)
+ for (i = 0; i < instance->msix_vectors; i++) {
+ if (smp_affinity_enable)
+ irq_set_affinity_hint(
+ instance->msixentry[i].vector, NULL);
+ free_irq(instance->msixentry[i].vector,
+ &instance->irq_context[i]);
+ }
+ else
+ free_irq(instance->pdev->irq, &instance->irq_context[0]);
+}
+
/**
* megasas_init_fw - Initializes the FW
* @instance: Adapter soft state
@@ -4499,7 +4566,7 @@ static int megasas_init_fw(struct megasas_instance *instance)
* It is used for all MPT based Adapters.
*/
instance->reply_post_host_index_addr[0] =
- (u32 *)((u8 *)instance->reg_set +
+ (u32 __iomem *)((u8 __iomem *)instance->reg_set +
MPI2_REPLY_POST_HOST_INDEX_OFFSET);
/* Check if MSI-X is supported while in ready state */
@@ -4531,7 +4598,8 @@ static int megasas_init_fw(struct megasas_instance *instance)
*/
for (loop = 1; loop < MR_MAX_MSIX_REG_ARRAY; loop++) {
instance->reply_post_host_index_addr[loop] =
- (u32 *)((u8 *)instance->reg_set +
+ (u32 __iomem *)
+ ((u8 __iomem *)instance->reg_set +
MPI2_SUP_REPLY_POST_HOST_INDEX_OFFSET
+ (loop * 0x10));
}
@@ -4551,14 +4619,19 @@ static int megasas_init_fw(struct megasas_instance *instance)
instance->msix_vectors = i;
else
instance->msix_vectors = 0;
-
- dev_info(&instance->pdev->dev, "[scsi%d]: FW supports"
- "<%d> MSIX vector,Online CPUs: <%d>,"
- "Current MSIX <%d>\n", instance->host->host_no,
- fw_msix_count, (unsigned int)num_online_cpus(),
- instance->msix_vectors);
}
+ dev_info(&instance->pdev->dev,
+ "firmware supports msix\t: (%d)", fw_msix_count);
+ dev_info(&instance->pdev->dev,
+ "current msix/online cpus\t: (%d/%d)\n",
+ instance->msix_vectors, (unsigned int)num_online_cpus());
+
+ if (instance->msix_vectors ?
+ megasas_setup_irqs_msix(instance, 1) :
+ megasas_setup_irqs_ioapic(instance))
+ goto fail_setup_irqs;
+
instance->ctrl_info = kzalloc(sizeof(struct megasas_ctrl_info),
GFP_KERNEL);
if (instance->ctrl_info == NULL)
@@ -4574,6 +4647,11 @@ static int megasas_init_fw(struct megasas_instance *instance)
if (instance->instancet->init_adapter(instance))
goto fail_init_adapter;
+ tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
+ (unsigned long)instance);
+
+ instance->instancet->enable_intr(instance);
+
printk(KERN_ERR "megasas: INIT adapter done\n");
/** for passthrough
@@ -4584,7 +4662,7 @@ static int megasas_init_fw(struct megasas_instance *instance)
(MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)));
if (megasas_get_pd_list(instance) < 0) {
printk(KERN_ERR "megasas: failed to get PD list\n");
- goto fail_init_adapter;
+ goto fail_get_pd_list;
}
memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
@@ -4610,17 +4688,6 @@ static int megasas_init_fw(struct megasas_instance *instance)
tmp_sectors = min_t(u32, max_sectors_1 , max_sectors_2);
- /*Check whether controller is iMR or MR */
- if (ctrl_info->memory_size) {
- instance->is_imr = 0;
- dev_info(&instance->pdev->dev, "Controller type: MR,"
- "Memory size is: %dMB\n",
- le16_to_cpu(ctrl_info->memory_size));
- } else {
- instance->is_imr = 1;
- dev_info(&instance->pdev->dev,
- "Controller type: iMR\n");
- }
instance->disableOnlineCtrlReset =
ctrl_info->properties.OnOffProperties.disableOnlineCtrlReset;
instance->mpio = ctrl_info->adapterOperations2.mpio;
@@ -4628,9 +4695,6 @@ static int megasas_init_fw(struct megasas_instance *instance)
ctrl_info->adapterOperations2.supportUnevenSpans;
if (instance->UnevenSpanSupport) {
struct fusion_context *fusion = instance->ctrl_context;
-
- dev_info(&instance->pdev->dev, "FW supports: "
- "UnevenSpanSupport=%x\n", instance->UnevenSpanSupport);
if (MR_ValidateMapInfo(instance))
fusion->fast_path_io = 1;
else
@@ -4657,13 +4721,11 @@ static int megasas_init_fw(struct megasas_instance *instance)
instance->crash_dump_drv_support =
(instance->crash_dump_fw_support &&
instance->crash_dump_buf);
- if (instance->crash_dump_drv_support) {
- dev_info(&instance->pdev->dev, "Firmware Crash dump "
- "feature is supported\n");
+ if (instance->crash_dump_drv_support)
megasas_set_crash_dump_params(instance,
MR_CRASH_BUF_TURN_OFF);
- } else {
+ else {
if (instance->crash_dump_buf)
pci_free_consistent(instance->pdev,
CRASH_DMA_BUF_SIZE,
@@ -4674,37 +4736,28 @@ static int megasas_init_fw(struct megasas_instance *instance)
instance->secure_jbod_support =
ctrl_info->adapterOperations3.supportSecurityonJBOD;
- if (instance->secure_jbod_support)
- dev_info(&instance->pdev->dev, "Firmware supports Secure JBOD\n");
+
+ dev_info(&instance->pdev->dev,
+ "pci id\t\t: (0x%04x)/(0x%04x)/(0x%04x)/(0x%04x)\n",
+ le16_to_cpu(ctrl_info->pci.vendor_id),
+ le16_to_cpu(ctrl_info->pci.device_id),
+ le16_to_cpu(ctrl_info->pci.sub_vendor_id),
+ le16_to_cpu(ctrl_info->pci.sub_device_id));
+ dev_info(&instance->pdev->dev, "unevenspan support : %s\n",
+ instance->UnevenSpanSupport ? "yes" : "no");
+ dev_info(&instance->pdev->dev, "disable ocr : %s\n",
+ instance->disableOnlineCtrlReset ? "yes" : "no");
+ dev_info(&instance->pdev->dev, "firmware crash dump : %s\n",
+ instance->crash_dump_drv_support ? "yes" : "no");
+ dev_info(&instance->pdev->dev, "secure jbod : %s\n",
+ instance->secure_jbod_support ? "yes" : "no");
+
+
instance->max_sectors_per_req = instance->max_num_sge *
PAGE_SIZE / 512;
if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors))
instance->max_sectors_per_req = tmp_sectors;
- /*
- * 1. For fusion adapters, 3 commands for IOCTL and 5 commands
- * for driver's internal DCMDs.
- * 2. For MFI skinny adapters, 5 commands for IOCTL + driver's
- * internal DCMDs.
- * 3. For rest of MFI adapters, 27 commands reserved for IOCTLs
- * and 5 commands for drivers's internal DCMD.
- */
- if (instance->ctrl_context) {
- instance->max_scsi_cmds = instance->max_fw_cmds -
- (MEGASAS_FUSION_INTERNAL_CMDS +
- MEGASAS_FUSION_IOCTL_CMDS);
- sema_init(&instance->ioctl_sem, MEGASAS_FUSION_IOCTL_CMDS);
- } else if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
- (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
- instance->max_scsi_cmds = instance->max_fw_cmds -
- MEGASAS_SKINNY_INT_CMDS;
- sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS);
- } else {
- instance->max_scsi_cmds = instance->max_fw_cmds -
- MEGASAS_INT_CMDS;
- sema_init(&instance->ioctl_sem, (MEGASAS_INT_CMDS - 5));
- }
-
/* Check for valid throttlequeuedepth module parameter */
if (throttlequeuedepth &&
throttlequeuedepth <= instance->max_scsi_cmds)
@@ -4713,12 +4766,6 @@ static int megasas_init_fw(struct megasas_instance *instance)
instance->throttlequeuedepth =
MEGASAS_THROTTLE_QUEUE_DEPTH;
- /*
- * Setup tasklet for cmd completion
- */
-
- tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
- (unsigned long)instance);
/* Launch SR-IOV heartbeat timer */
if (instance->requestorId) {
@@ -4733,7 +4780,14 @@ static int megasas_init_fw(struct megasas_instance *instance)
return 0;
+fail_get_pd_list:
+ instance->instancet->disable_intr(instance);
fail_init_adapter:
+ megasas_destroy_irqs(instance);
+fail_setup_irqs:
+ if (instance->msix_vectors)
+ pci_disable_msix(instance->pdev);
+ instance->msix_vectors = 0;
fail_ready_state:
kfree(instance->ctrl_info);
instance->ctrl_info = NULL;
@@ -4747,7 +4801,7 @@ fail_ready_state:
/**
* megasas_release_mfi - Reverses the FW initialization
- * @intance: Adapter soft state
+ * @instance: Adapter soft state
*/
static void megasas_release_mfi(struct megasas_instance *instance)
{
@@ -4822,21 +4876,17 @@ megasas_get_seq_num(struct megasas_instance *instance,
/*
* Copy the data back into callers buffer
*/
- eli->newest_seq_num = le32_to_cpu(el_info->newest_seq_num);
- eli->oldest_seq_num = le32_to_cpu(el_info->oldest_seq_num);
- eli->clear_seq_num = le32_to_cpu(el_info->clear_seq_num);
- eli->shutdown_seq_num = le32_to_cpu(el_info->shutdown_seq_num);
- eli->boot_seq_num = le32_to_cpu(el_info->boot_seq_num);
+ eli->newest_seq_num = el_info->newest_seq_num;
+ eli->oldest_seq_num = el_info->oldest_seq_num;
+ eli->clear_seq_num = el_info->clear_seq_num;
+ eli->shutdown_seq_num = el_info->shutdown_seq_num;
+ eli->boot_seq_num = el_info->boot_seq_num;
}
pci_free_consistent(instance->pdev, sizeof(struct megasas_evt_log_info),
el_info, el_info_h);
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return 0;
}
@@ -4877,8 +4927,8 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
if (instance->aen_cmd) {
- prev_aen.word = instance->aen_cmd->frame->dcmd.mbox.w[1];
- prev_aen.members.locale = le16_to_cpu(prev_aen.members.locale);
+ prev_aen.word =
+ le32_to_cpu(instance->aen_cmd->frame->dcmd.mbox.w[1]);
/*
* A class whose enum value is smaller is inclusive of all
@@ -4990,7 +5040,7 @@ static int megasas_start_aen(struct megasas_instance *instance)
class_locale.members.class = MR_EVT_CLASS_DEBUG;
return megasas_register_aen(instance,
- eli.newest_seq_num + 1,
+ le32_to_cpu(eli.newest_seq_num) + 1,
class_locale.word);
}
@@ -5001,6 +5051,7 @@ static int megasas_start_aen(struct megasas_instance *instance)
static int megasas_io_attach(struct megasas_instance *instance)
{
struct Scsi_Host *host = instance->host;
+ u32 error;
/*
* Export parameters required by SCSI mid-layer
@@ -5050,12 +5101,21 @@ static int megasas_io_attach(struct megasas_instance *instance)
host->hostt->eh_device_reset_handler = NULL;
host->hostt->eh_bus_reset_handler = NULL;
}
+ error = scsi_init_shared_tag_map(host, host->can_queue);
+ if (error) {
+ dev_err(&instance->pdev->dev,
+ "Failed to shared tag from %s %d\n",
+ __func__, __LINE__);
+ return -ENODEV;
+ }
/*
* Notify the mid-layer about the new controller
*/
if (scsi_add_host(host, &instance->pdev->dev)) {
- printk(KERN_DEBUG "megasas: scsi_add_host failed\n");
+ dev_err(&instance->pdev->dev,
+ "Failed to add host from %s %d\n",
+ __func__, __LINE__);
return -ENODEV;
}
@@ -5106,7 +5166,7 @@ fail_set_dma_mask:
static int megasas_probe_one(struct pci_dev *pdev,
const struct pci_device_id *id)
{
- int rval, pos, i, j, cpu;
+ int rval, pos;
struct Scsi_Host *host;
struct megasas_instance *instance;
u16 control = 0;
@@ -5129,16 +5189,6 @@ static int megasas_probe_one(struct pci_dev *pdev,
}
/*
- * Announce PCI information
- */
- printk(KERN_INFO "megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",
- pdev->vendor, pdev->device, pdev->subsystem_vendor,
- pdev->subsystem_device);
-
- printk("bus %d:slot %d:func %d\n",
- pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
-
- /*
* PCI prepping: enable device set bus mastering and dma mask
*/
rval = pci_enable_device_mem(pdev);
@@ -5183,8 +5233,6 @@ static int megasas_probe_one(struct pci_dev *pdev,
fusion = instance->ctrl_context;
memset(fusion, 0,
((1 << PAGE_SHIFT) << instance->ctrl_context_pages));
- INIT_LIST_HEAD(&fusion->cmd_pool);
- spin_lock_init(&fusion->mpt_pool_lock);
}
break;
default: /* For all other supported controllers */
@@ -5207,6 +5255,13 @@ static int megasas_probe_one(struct pci_dev *pdev,
break;
}
+ instance->system_info_buf = pci_zalloc_consistent(pdev,
+ sizeof(struct MR_DRV_SYSTEM_INFO),
+ &instance->system_info_h);
+
+ if (!instance->system_info_buf)
+ dev_info(&instance->pdev->dev, "Can't allocate system info buffer\n");
+
/* Crash dump feature related initialisation*/
instance->drv_buf_index = 0;
instance->drv_buf_alloc = 0;
@@ -5315,55 +5370,6 @@ static int megasas_probe_one(struct pci_dev *pdev,
}
}
-retry_irq_register:
- /*
- * Register IRQ
- */
- if (instance->msix_vectors) {
- cpu = cpumask_first(cpu_online_mask);
- for (i = 0; i < instance->msix_vectors; i++) {
- instance->irq_context[i].instance = instance;
- instance->irq_context[i].MSIxIndex = i;
- if (request_irq(instance->msixentry[i].vector,
- instance->instancet->service_isr, 0,
- "megasas",
- &instance->irq_context[i])) {
- printk(KERN_DEBUG "megasas: Failed to "
- "register IRQ for vector %d.\n", i);
- for (j = 0; j < i; j++) {
- if (smp_affinity_enable)
- irq_set_affinity_hint(
- instance->msixentry[j].vector, NULL);
- free_irq(
- instance->msixentry[j].vector,
- &instance->irq_context[j]);
- }
- /* Retry irq register for IO_APIC */
- instance->msix_vectors = 0;
- goto retry_irq_register;
- }
- if (smp_affinity_enable) {
- if (irq_set_affinity_hint(instance->msixentry[i].vector,
- get_cpu_mask(cpu)))
- dev_err(&instance->pdev->dev,
- "Error setting affinity hint "
- "for cpu %d\n", cpu);
- cpu = cpumask_next(cpu, cpu_online_mask);
- }
- }
- } else {
- instance->irq_context[0].instance = instance;
- instance->irq_context[0].MSIxIndex = 0;
- if (request_irq(pdev->irq, instance->instancet->service_isr,
- IRQF_SHARED, "megasas",
- &instance->irq_context[0])) {
- printk(KERN_DEBUG "megasas: Failed to register IRQ\n");
- goto fail_irq;
- }
- }
-
- instance->instancet->enable_intr(instance);
-
/*
* Store instance in PCI softstate
*/
@@ -5410,17 +5416,8 @@ retry_irq_register:
megasas_mgmt_info.max_index--;
instance->instancet->disable_intr(instance);
- if (instance->msix_vectors)
- for (i = 0; i < instance->msix_vectors; i++) {
- if (smp_affinity_enable)
- irq_set_affinity_hint(
- instance->msixentry[i].vector, NULL);
- free_irq(instance->msixentry[i].vector,
- &instance->irq_context[i]);
- }
- else
- free_irq(instance->pdev->irq, &instance->irq_context[0]);
-fail_irq:
+ megasas_destroy_irqs(instance);
+
if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
(instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) ||
(instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
@@ -5428,9 +5425,9 @@ fail_irq:
megasas_release_fusion(instance);
else
megasas_release_mfi(instance);
- fail_init_mfi:
if (instance->msix_vectors)
pci_disable_msix(instance->pdev);
+fail_init_mfi:
fail_alloc_dma_buf:
if (instance->evt_detail)
pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),
@@ -5487,11 +5484,7 @@ static void megasas_flush_cache(struct megasas_instance *instance)
dev_err(&instance->pdev->dev, "Command timedout"
" from %s\n", __func__);
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return;
}
@@ -5538,11 +5531,7 @@ static void megasas_shutdown_controller(struct megasas_instance *instance,
dev_err(&instance->pdev->dev, "Command timedout"
"from %s\n", __func__);
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return;
}
@@ -5558,7 +5547,6 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct Scsi_Host *host;
struct megasas_instance *instance;
- int i;
instance = pci_get_drvdata(pdev);
host = instance->host;
@@ -5583,16 +5571,8 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
pci_set_drvdata(instance->pdev, instance);
instance->instancet->disable_intr(instance);
- if (instance->msix_vectors)
- for (i = 0; i < instance->msix_vectors; i++) {
- if (smp_affinity_enable)
- irq_set_affinity_hint(
- instance->msixentry[i].vector, NULL);
- free_irq(instance->msixentry[i].vector,
- &instance->irq_context[i]);
- }
- else
- free_irq(instance->pdev->irq, &instance->irq_context[0]);
+ megasas_destroy_irqs(instance);
+
if (instance->msix_vectors)
pci_disable_msix(instance->pdev);
@@ -5611,7 +5591,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
static int
megasas_resume(struct pci_dev *pdev)
{
- int rval, i, j, cpu;
+ int rval;
struct Scsi_Host *host;
struct megasas_instance *instance;
@@ -5681,50 +5661,10 @@ megasas_resume(struct pci_dev *pdev)
tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
(unsigned long)instance);
- /*
- * Register IRQ
- */
- if (instance->msix_vectors) {
- cpu = cpumask_first(cpu_online_mask);
- for (i = 0 ; i < instance->msix_vectors; i++) {
- instance->irq_context[i].instance = instance;
- instance->irq_context[i].MSIxIndex = i;
- if (request_irq(instance->msixentry[i].vector,
- instance->instancet->service_isr, 0,
- "megasas",
- &instance->irq_context[i])) {
- printk(KERN_DEBUG "megasas: Failed to "
- "register IRQ for vector %d.\n", i);
- for (j = 0; j < i; j++) {
- if (smp_affinity_enable)
- irq_set_affinity_hint(
- instance->msixentry[j].vector, NULL);
- free_irq(
- instance->msixentry[j].vector,
- &instance->irq_context[j]);
- }
- goto fail_irq;
- }
-
- if (smp_affinity_enable) {
- if (irq_set_affinity_hint(instance->msixentry[i].vector,
- get_cpu_mask(cpu)))
- dev_err(&instance->pdev->dev, "Error "
- "setting affinity hint for cpu "
- "%d\n", cpu);
- cpu = cpumask_next(cpu, cpu_online_mask);
- }
- }
- } else {
- instance->irq_context[0].instance = instance;
- instance->irq_context[0].MSIxIndex = 0;
- if (request_irq(pdev->irq, instance->instancet->service_isr,
- IRQF_SHARED, "megasas",
- &instance->irq_context[0])) {
- printk(KERN_DEBUG "megasas: Failed to register IRQ\n");
- goto fail_irq;
- }
- }
+ if (instance->msix_vectors ?
+ megasas_setup_irqs_msix(instance, 0) :
+ megasas_setup_irqs_ioapic(instance))
+ goto fail_init_mfi;
/* Re-launch SR-IOV heartbeat timer */
if (instance->requestorId) {
@@ -5733,8 +5673,10 @@ megasas_resume(struct pci_dev *pdev)
&instance->sriov_heartbeat_timer,
megasas_sriov_heartbeat_handler,
MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF);
- else
+ else {
instance->skip_heartbeat_timer_del = 1;
+ goto fail_init_mfi;
+ }
}
instance->instancet->enable_intr(instance);
@@ -5748,7 +5690,6 @@ megasas_resume(struct pci_dev *pdev)
return 0;
-fail_irq:
fail_init_mfi:
if (instance->evt_detail)
pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),
@@ -5829,16 +5770,8 @@ static void megasas_detach_one(struct pci_dev *pdev)
instance->instancet->disable_intr(instance);
- if (instance->msix_vectors)
- for (i = 0; i < instance->msix_vectors; i++) {
- if (smp_affinity_enable)
- irq_set_affinity_hint(
- instance->msixentry[i].vector, NULL);
- free_irq(instance->msixentry[i].vector,
- &instance->irq_context[i]);
- }
- else
- free_irq(instance->pdev->irq, &instance->irq_context[0]);
+ megasas_destroy_irqs(instance);
+
if (instance->msix_vectors)
pci_disable_msix(instance->pdev);
@@ -5899,6 +5832,10 @@ static void megasas_detach_one(struct pci_dev *pdev)
pci_free_consistent(pdev, CRASH_DMA_BUF_SIZE,
instance->crash_dump_buf, instance->crash_dump_h);
+ if (instance->system_info_buf)
+ pci_free_consistent(pdev, sizeof(struct MR_DRV_SYSTEM_INFO),
+ instance->system_info_buf, instance->system_info_h);
+
scsi_host_put(host);
pci_disable_device(pdev);
@@ -5912,23 +5849,14 @@ static void megasas_detach_one(struct pci_dev *pdev)
*/
static void megasas_shutdown(struct pci_dev *pdev)
{
- int i;
struct megasas_instance *instance = pci_get_drvdata(pdev);
instance->unload = 1;
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
instance->instancet->disable_intr(instance);
- if (instance->msix_vectors)
- for (i = 0; i < instance->msix_vectors; i++) {
- if (smp_affinity_enable)
- irq_set_affinity_hint(
- instance->msixentry[i].vector, NULL);
- free_irq(instance->msixentry[i].vector,
- &instance->irq_context[i]);
- }
- else
- free_irq(instance->pdev->irq, &instance->irq_context[0]);
+ megasas_destroy_irqs(instance);
+
if (instance->msix_vectors)
pci_disable_msix(instance->pdev);
}
@@ -6211,11 +6139,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
kbuff_arr[i] = NULL;
}
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return error;
}
@@ -6502,6 +6426,15 @@ static ssize_t megasas_sysfs_show_version(struct device_driver *dd, char *buf)
static DRIVER_ATTR(version, S_IRUGO, megasas_sysfs_show_version, NULL);
static ssize_t
+megasas_sysfs_show_release_date(struct device_driver *dd, char *buf)
+{
+ return snprintf(buf, strlen(MEGASAS_RELDATE) + 2, "%s\n",
+ MEGASAS_RELDATE);
+}
+
+static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date, NULL);
+
+static ssize_t
megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf)
{
return sprintf(buf, "%u\n", support_poll_for_event);
@@ -6841,6 +6774,11 @@ static int __init megasas_init(void)
goto err_dcf_attr_ver;
rval = driver_create_file(&megasas_pci_driver.driver,
+ &driver_attr_release_date);
+ if (rval)
+ goto err_dcf_rel_date;
+
+ rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_support_poll_for_event);
if (rval)
goto err_dcf_support_poll_for_event;
@@ -6863,6 +6801,9 @@ err_dcf_dbg_lvl:
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_support_poll_for_event);
err_dcf_support_poll_for_event:
+ driver_remove_file(&megasas_pci_driver.driver,
+ &driver_attr_release_date);
+err_dcf_rel_date:
driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
err_dcf_attr_ver:
pci_unregister_driver(&megasas_pci_driver);
@@ -6882,6 +6823,8 @@ static void __exit megasas_exit(void)
&driver_attr_support_poll_for_event);
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_support_device_change);
+ driver_remove_file(&megasas_pci_driver.driver,
+ &driver_attr_release_date);
driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
pci_unregister_driver(&megasas_pci_driver);
diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c
index 4f72287860ee..be57b18675a4 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fp.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fp.c
@@ -66,7 +66,15 @@ MODULE_PARM_DESC(lb_pending_cmds, "Change raid-1 load balancing outstanding "
#define ABS_DIFF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a)))
#define MR_LD_STATE_OPTIMAL 3
+
+#ifdef FALSE
+#undef FALSE
+#endif
#define FALSE 0
+
+#ifdef TRUE
+#undef TRUE
+#endif
#define TRUE 1
#define SPAN_DEBUG 0
@@ -142,7 +150,7 @@ u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_DRV_RAID_MAP_ALL *map)
return le16_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span].span.arrayRef);
}
-u16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map)
+__le16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map)
{
return map->raidMap.devHndlInfo[pd].curDevHdl;
}
@@ -735,7 +743,7 @@ static u8 mr_spanset_get_phy_params(struct megasas_instance *instance, u32 ld,
u8 retval = TRUE;
u8 do_invader = 0;
u64 *pdBlock = &io_info->pdBlock;
- u16 *pDevHandle = &io_info->devHandle;
+ __le16 *pDevHandle = &io_info->devHandle;
u32 logArm, rowMod, armQ, arm;
if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER ||
@@ -769,7 +777,7 @@ static u8 mr_spanset_get_phy_params(struct megasas_instance *instance, u32 ld,
if (pd != MR_PD_INVALID)
*pDevHandle = MR_PdDevHandleGet(pd, map);
else {
- *pDevHandle = MR_PD_INVALID;
+ *pDevHandle = cpu_to_le16(MR_PD_INVALID);
if ((raid->level >= 5) &&
(!do_invader || (do_invader &&
(raid->regTypeReqOnRead != REGION_TYPE_UNUSED))))
@@ -817,7 +825,7 @@ u8 MR_GetPhyParams(struct megasas_instance *instance, u32 ld, u64 stripRow,
u8 retval = TRUE;
u8 do_invader = 0;
u64 *pdBlock = &io_info->pdBlock;
- u16 *pDevHandle = &io_info->devHandle;
+ __le16 *pDevHandle = &io_info->devHandle;
if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER ||
instance->pdev->device == PCI_DEVICE_ID_LSI_FURY))
@@ -864,7 +872,8 @@ u8 MR_GetPhyParams(struct megasas_instance *instance, u32 ld, u64 stripRow,
/* Get dev handle from Pd. */
*pDevHandle = MR_PdDevHandleGet(pd, map);
else {
- *pDevHandle = MR_PD_INVALID; /* set dev handle as invalid. */
+ /* set dev handle as invalid. */
+ *pDevHandle = cpu_to_le16(MR_PD_INVALID);
if ((raid->level >= 5) &&
(!do_invader || (do_invader &&
(raid->regTypeReqOnRead != REGION_TYPE_UNUSED))))
@@ -1109,7 +1118,7 @@ MR_BuildRaidContext(struct megasas_instance *instance,
ref_in_start_stripe, io_info,
pRAID_Context, map);
/* If IO on an invalid Pd, then FP is not possible.*/
- if (io_info->devHandle == MR_PD_INVALID)
+ if (io_info->devHandle == cpu_to_le16(MR_PD_INVALID))
io_info->fpOkForIo = FALSE;
return retval;
} else if (isRead) {
@@ -1341,11 +1350,11 @@ u8 megasas_get_best_arm_pd(struct megasas_instance *instance,
return io_info->pd_after_lb;
}
-u16 get_updated_dev_handle(struct megasas_instance *instance,
+__le16 get_updated_dev_handle(struct megasas_instance *instance,
struct LD_LOAD_BALANCE_INFO *lbInfo, struct IO_REQUEST_INFO *io_info)
{
u8 arm_pd;
- u16 devHandle;
+ __le16 devHandle;
struct fusion_context *fusion;
struct MR_DRV_RAID_MAP_ALL *drv_map;
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index 5a0800d19970..46a0f8f4f677 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -53,10 +53,12 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_dbg.h>
+#include <linux/dmi.h>
#include "megaraid_sas_fusion.h"
#include "megaraid_sas.h"
+
extern void megasas_free_cmds(struct megasas_instance *instance);
extern struct megasas_cmd *megasas_get_cmd(struct megasas_instance
*instance);
@@ -156,28 +158,15 @@ megasas_clear_intr_fusion(struct megasas_register_set __iomem *regs)
* megasas_get_cmd_fusion - Get a command from the free pool
* @instance: Adapter soft state
*
- * Returns a free command from the pool
+ * Returns a blk_tag indexed mpt frame
*/
-struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
- *instance)
+inline struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
+ *instance, u32 blk_tag)
{
- unsigned long flags;
- struct fusion_context *fusion =
- (struct fusion_context *)instance->ctrl_context;
- struct megasas_cmd_fusion *cmd = NULL;
-
- spin_lock_irqsave(&fusion->mpt_pool_lock, flags);
-
- if (!list_empty(&fusion->cmd_pool)) {
- cmd = list_entry((&fusion->cmd_pool)->next,
- struct megasas_cmd_fusion, list);
- list_del_init(&cmd->list);
- } else {
- printk(KERN_ERR "megasas: Command pool (fusion) empty!\n");
- }
+ struct fusion_context *fusion;
- spin_unlock_irqrestore(&fusion->mpt_pool_lock, flags);
- return cmd;
+ fusion = instance->ctrl_context;
+ return fusion->cmd_list[blk_tag];
}
/**
@@ -188,47 +177,35 @@ struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
inline void megasas_return_cmd_fusion(struct megasas_instance *instance,
struct megasas_cmd_fusion *cmd)
{
- unsigned long flags;
- struct fusion_context *fusion =
- (struct fusion_context *)instance->ctrl_context;
-
- spin_lock_irqsave(&fusion->mpt_pool_lock, flags);
-
cmd->scmd = NULL;
- cmd->sync_cmd_idx = (u32)ULONG_MAX;
memset(cmd->io_request, 0, sizeof(struct MPI2_RAID_SCSI_IO_REQUEST));
- list_add(&cmd->list, (&fusion->cmd_pool)->next);
-
- spin_unlock_irqrestore(&fusion->mpt_pool_lock, flags);
}
/**
- * megasas_return_mfi_mpt_pthr - Return a mfi and mpt to free command pool
- * @instance: Adapter soft state
- * @cmd_mfi: MFI Command packet to be returned to free command pool
- * @cmd_mpt: MPT Command packet to be returned to free command pool
+ * megasas_fire_cmd_fusion - Sends command to the FW
*/
-inline void megasas_return_mfi_mpt_pthr(struct megasas_instance *instance,
- struct megasas_cmd *cmd_mfi,
- struct megasas_cmd_fusion *cmd_fusion)
+static void
+megasas_fire_cmd_fusion(struct megasas_instance *instance,
+ union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc)
{
+#if defined(writeq) && defined(CONFIG_64BIT)
+ u64 req_data = (((u64)le32_to_cpu(req_desc->u.high) << 32) |
+ le32_to_cpu(req_desc->u.low));
+
+ writeq(req_data, &instance->reg_set->inbound_low_queue_port);
+#else
unsigned long flags;
- /*
- * TO DO: optimize this code and use only one lock instead of two
- * locks being used currently- mpt_pool_lock is acquired
- * inside mfi_pool_lock
- */
- spin_lock_irqsave(&instance->mfi_pool_lock, flags);
- megasas_return_cmd_fusion(instance, cmd_fusion);
- if (atomic_read(&cmd_mfi->mfi_mpt_pthr) != MFI_MPT_ATTACHED)
- dev_err(&instance->pdev->dev, "Possible bug from %s %d\n",
- __func__, __LINE__);
- atomic_set(&cmd_mfi->mfi_mpt_pthr, MFI_MPT_DETACHED);
- __megasas_return_cmd(instance, cmd_mfi);
- spin_unlock_irqrestore(&instance->mfi_pool_lock, flags);
+ spin_lock_irqsave(&instance->hba_lock, flags);
+ writel(le32_to_cpu(req_desc->u.low),
+ &instance->reg_set->inbound_low_queue_port);
+ writel(le32_to_cpu(req_desc->u.high),
+ &instance->reg_set->inbound_high_queue_port);
+ spin_unlock_irqrestore(&instance->hba_lock, flags);
+#endif
}
+
/**
* megasas_teardown_frame_pool_fusion - Destroy the cmd frame DMA pool
* @instance: Adapter soft state
@@ -326,7 +303,6 @@ megasas_free_cmds_fusion(struct megasas_instance *instance)
kfree(fusion->cmd_list);
fusion->cmd_list = NULL;
- INIT_LIST_HEAD(&fusion->cmd_pool);
}
/**
@@ -464,7 +440,7 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance)
reply_desc = fusion->reply_frames_desc;
for (i = 0; i < fusion->reply_q_depth * count; i++, reply_desc++)
- reply_desc->Words = ULLONG_MAX;
+ reply_desc->Words = cpu_to_le64(ULLONG_MAX);
io_frames_sz = fusion->io_frames_alloc_sz;
@@ -535,7 +511,9 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance)
memset(cmd, 0, sizeof(struct megasas_cmd_fusion));
cmd->index = i + 1;
cmd->scmd = NULL;
- cmd->sync_cmd_idx = (u32)ULONG_MAX; /* Set to Invalid */
+ cmd->sync_cmd_idx = (i >= instance->max_scsi_cmds) ?
+ (i - instance->max_scsi_cmds) :
+ (u32)ULONG_MAX; /* Set to Invalid */
cmd->instance = instance;
cmd->io_request =
(struct MPI2_RAID_SCSI_IO_REQUEST *)
@@ -543,8 +521,6 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance)
memset(cmd->io_request, 0,
sizeof(struct MPI2_RAID_SCSI_IO_REQUEST));
cmd->io_request_phys_addr = io_req_base_phys + offset;
-
- list_add_tail(&cmd->list, &fusion->cmd_pool);
}
/*
@@ -605,14 +581,11 @@ wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
msleep(20);
}
- if (frame_hdr->cmd_status == 0xff) {
- if (fusion)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
+ if (frame_hdr->cmd_status == 0xff)
return -ETIME;
- }
- return 0;
+ return (frame_hdr->cmd_status == MFI_STAT_OK) ?
+ 0 : 1;
}
/**
@@ -633,6 +606,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
union MEGASAS_REQUEST_DESCRIPTOR_UNION req_desc;
int i;
struct megasas_header *frame_hdr;
+ const char *sys_info;
fusion = instance->ctrl_context;
@@ -673,7 +647,9 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
frame_hdr = &cmd->frame->hdr;
frame_hdr->cmd_status = 0xFF;
- frame_hdr->flags |= cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE);
+ frame_hdr->flags = cpu_to_le16(
+ le16_to_cpu(frame_hdr->flags) |
+ MFI_FRAME_DONT_POST_IN_REPLY_QUEUE);
init_frame->cmd = MFI_CMD_INIT;
init_frame->cmd_status = 0xFF;
@@ -695,6 +671,16 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
/* Convert capability to LE32 */
cpu_to_le32s((u32 *)&init_frame->driver_operations.mfi_capabilities);
+ sys_info = dmi_get_system_info(DMI_PRODUCT_UUID);
+ if (instance->system_info_buf && sys_info) {
+ memcpy(instance->system_info_buf->systemId, sys_info,
+ strlen(sys_info) > 64 ? 64 : strlen(sys_info));
+ instance->system_info_buf->systemIdLength =
+ strlen(sys_info) > 64 ? 64 : strlen(sys_info);
+ init_frame->system_info_lo = instance->system_info_h;
+ init_frame->system_info_hi = 0;
+ }
+
init_frame->queue_info_new_phys_addr_hi =
cpu_to_le32(upper_32_bits(ioc_init_handle));
init_frame->queue_info_new_phys_addr_lo =
@@ -719,8 +705,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
break;
}
- instance->instancet->fire_cmd(instance, req_desc.u.low,
- req_desc.u.high, instance->reg_set);
+ megasas_fire_cmd_fusion(instance, &req_desc);
wait_and_poll(instance, cmd, MFI_POLL_TIMEOUT_SECS);
@@ -820,11 +805,7 @@ megasas_get_ld_map_info(struct megasas_instance *instance)
else
ret = megasas_issue_polled(instance, cmd);
- if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
- megasas_return_mfi_mpt_pthr(instance, cmd,
- cmd->mpt_pthr_cmd_blocked);
- else
- megasas_return_cmd(instance, cmd);
+ megasas_return_cmd(instance, cmd);
return ret;
}
@@ -1061,6 +1042,15 @@ megasas_init_adapter_fusion(struct megasas_instance *instance)
fusion->last_reply_idx[i] = 0;
/*
+ * For fusion adapters, 3 commands for IOCTL and 5 commands
+ * for driver's internal DCMDs.
+ */
+ instance->max_scsi_cmds = instance->max_fw_cmds -
+ (MEGASAS_FUSION_INTERNAL_CMDS +
+ MEGASAS_FUSION_IOCTL_CMDS);
+ sema_init(&instance->ioctl_sem, MEGASAS_FUSION_IOCTL_CMDS);
+
+ /*
* Allocate memory for descriptors
* Create a pool of commands
*/
@@ -1131,34 +1121,6 @@ fail_alloc_mfi_cmds:
}
/**
- * megasas_fire_cmd_fusion - Sends command to the FW
- * @frame_phys_addr : Physical address of cmd
- * @frame_count : Number of frames for the command
- * @regs : MFI register set
- */
-void
-megasas_fire_cmd_fusion(struct megasas_instance *instance,
- dma_addr_t req_desc_lo,
- u32 req_desc_hi,
- struct megasas_register_set __iomem *regs)
-{
-#if defined(writeq) && defined(CONFIG_64BIT)
- u64 req_data = (((u64)le32_to_cpu(req_desc_hi) << 32) |
- le32_to_cpu(req_desc_lo));
-
- writeq(req_data, &(regs)->inbound_low_queue_port);
-#else
- unsigned long flags;
-
- spin_lock_irqsave(&instance->hba_lock, flags);
-
- writel(le32_to_cpu(req_desc_lo), &(regs)->inbound_low_queue_port);
- writel(le32_to_cpu(req_desc_hi), &(regs)->inbound_high_queue_port);
- spin_unlock_irqrestore(&instance->hba_lock, flags);
-#endif
-}
-
-/**
* map_cmd_status - Maps FW cmd status to OS cmd status
* @cmd : Pointer to cmd
* @status : status of cmd returned by FW
@@ -1497,7 +1459,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
u8 *raidLUN;
- device_id = MEGASAS_DEV_INDEX(instance, scp);
+ device_id = MEGASAS_DEV_INDEX(scp);
fusion = instance->ctrl_context;
@@ -1621,6 +1583,14 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
cmd->pd_r1_lb = io_info.pd_after_lb;
} else
scp->SCp.Status &= ~MEGASAS_LOAD_BALANCE_FLAG;
+
+ if ((raidLUN[0] == 1) &&
+ (local_map_ptr->raidMap.devHndlInfo[io_info.pd_after_lb].validHandles > 2)) {
+ instance->dev_handle = !(instance->dev_handle);
+ io_info.devHandle =
+ local_map_ptr->raidMap.devHndlInfo[io_info.pd_after_lb].devHandle[instance->dev_handle];
+ }
+
cmd->request_desc->SCSIIO.DevHandle = io_info.devHandle;
io_request->DevHandle = io_info.devHandle;
/* populate the LUN field */
@@ -1650,121 +1620,68 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
}
/**
- * megasas_build_dcdb_fusion - Prepares IOs to devices
+ * megasas_build_ld_nonrw_fusion - prepares non rw ios for virtual disk
* @instance: Adapter soft state
* @scp: SCSI command
* @cmd: Command to be prepared
*
- * Prepares the io_request frame for non-io cmds
+ * Prepares the io_request frame for non-rw io cmds for vd.
*/
-static void
-megasas_build_dcdb_fusion(struct megasas_instance *instance,
- struct scsi_cmnd *scmd,
- struct megasas_cmd_fusion *cmd)
+static void megasas_build_ld_nonrw_fusion(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd)
{
u32 device_id;
struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
u16 pd_index = 0;
- u16 os_timeout_value;
- u16 timeout_limit;
struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
struct fusion_context *fusion = instance->ctrl_context;
u8 span, physArm;
- u16 devHandle;
+ __le16 devHandle;
u32 ld, arRef, pd;
struct MR_LD_RAID *raid;
struct RAID_CONTEXT *pRAID_Context;
+ u8 fp_possible = 1;
io_request = cmd->io_request;
- device_id = MEGASAS_DEV_INDEX(instance, scmd);
- pd_index = (scmd->device->channel * MEGASAS_MAX_DEV_PER_CHANNEL)
- +scmd->device->id;
+ device_id = MEGASAS_DEV_INDEX(scmd);
+ pd_index = MEGASAS_PD_INDEX(scmd);
local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)];
-
io_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
+ /* get RAID_Context pointer */
+ pRAID_Context = &io_request->RaidContext;
+ /* Check with FW team */
+ pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id);
+ pRAID_Context->regLockRowLBA = 0;
+ pRAID_Context->regLockLength = 0;
- if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS &&
- instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) {
- if (fusion->fast_path_io)
- io_request->DevHandle =
- local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl;
- io_request->RaidContext.RAIDFlags =
- MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD
- << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT;
- cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle;
- cmd->request_desc->SCSIIO.MSIxIndex =
- instance->msix_vectors ?
- raw_smp_processor_id() %
- instance->msix_vectors :
- 0;
- os_timeout_value = scmd->request->timeout / HZ;
-
- if (instance->secure_jbod_support &&
- (megasas_cmd_type(scmd) == NON_READ_WRITE_SYSPDIO)) {
- /* system pd firmware path */
- io_request->Function =
- MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
- cmd->request_desc->SCSIIO.RequestFlags =
- (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
- MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
- io_request->RaidContext.timeoutValue =
- cpu_to_le16(os_timeout_value);
- } else {
- /* system pd Fast Path */
- io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
- io_request->RaidContext.regLockFlags = 0;
- io_request->RaidContext.regLockRowLBA = 0;
- io_request->RaidContext.regLockLength = 0;
- timeout_limit = (scmd->device->type == TYPE_DISK) ?
- 255 : 0xFFFF;
- io_request->RaidContext.timeoutValue =
- cpu_to_le16((os_timeout_value > timeout_limit) ?
- timeout_limit : os_timeout_value);
- if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
- (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY))
- io_request->IoFlags |=
- cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
-
- cmd->request_desc->SCSIIO.RequestFlags =
- (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY <<
- MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
- }
- } else {
- if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS)
- goto NonFastPath;
-
- /*
- * For older firmware, Driver should not access ldTgtIdToLd
- * beyond index 127 and for Extended VD firmware, ldTgtIdToLd
- * should not go beyond 255.
- */
-
- if ((!fusion->fast_path_io) ||
- (device_id >= instance->fw_supported_vd_count))
- goto NonFastPath;
+ if (fusion->fast_path_io && (
+ device_id < instance->fw_supported_vd_count)) {
ld = MR_TargetIdToLdGet(device_id, local_map_ptr);
-
if (ld >= instance->fw_supported_vd_count)
- goto NonFastPath;
+ fp_possible = 0;
raid = MR_LdRaidGet(ld, local_map_ptr);
-
- /* check if this LD is FP capable */
if (!(raid->capability.fpNonRWCapable))
- /* not FP capable, send as non-FP */
- goto NonFastPath;
+ fp_possible = 0;
+ } else
+ fp_possible = 0;
- /* get RAID_Context pointer */
- pRAID_Context = &io_request->RaidContext;
+ if (!fp_possible) {
+ io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
+ io_request->DevHandle = cpu_to_le16(device_id);
+ io_request->LUN[1] = scmd->device->lun;
+ pRAID_Context->timeoutValue =
+ cpu_to_le16 (scmd->request->timeout / HZ);
+ cmd->request_desc->SCSIIO.RequestFlags =
+ (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ } else {
/* set RAID context values */
- pRAID_Context->regLockFlags = REGION_TYPE_SHARED_READ;
- pRAID_Context->timeoutValue = cpu_to_le16(raid->fpIoTimeoutForLd);
- pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id);
- pRAID_Context->regLockRowLBA = 0;
- pRAID_Context->regLockLength = 0;
- pRAID_Context->configSeqNum = raid->seqNum;
+ pRAID_Context->configSeqNum = raid->seqNum;
+ pRAID_Context->regLockFlags = REGION_TYPE_SHARED_READ;
+ pRAID_Context->timeoutValue = cpu_to_le16(raid->fpIoTimeoutForLd);
/* get the DevHandle for the PD (since this is
fpNonRWCapable, this is a single disk RAID0) */
@@ -1776,7 +1693,7 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance,
/* build request descriptor */
cmd->request_desc->SCSIIO.RequestFlags =
(MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY <<
- MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
cmd->request_desc->SCSIIO.DevHandle = devHandle;
/* populate the LUN field */
@@ -1785,18 +1702,87 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance,
/* build the raidScsiIO structure */
io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
io_request->DevHandle = devHandle;
+ }
+}
- return;
+/**
+ * megasas_build_syspd_fusion - prepares rw/non-rw ios for syspd
+ * @instance: Adapter soft state
+ * @scp: SCSI command
+ * @cmd: Command to be prepared
+ * @fp_possible: parameter to detect fast path or firmware path io.
+ *
+ * Prepares the io_request frame for rw/non-rw io cmds for syspds
+ */
+static void
+megasas_build_syspd_fusion(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd, u8 fp_possible)
+{
+ u32 device_id;
+ struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
+ u16 pd_index = 0;
+ u16 os_timeout_value;
+ u16 timeout_limit;
+ struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
+ struct RAID_CONTEXT *pRAID_Context;
+ struct fusion_context *fusion = instance->ctrl_context;
+
+ device_id = MEGASAS_DEV_INDEX(scmd);
+ pd_index = MEGASAS_PD_INDEX(scmd);
+ os_timeout_value = scmd->request->timeout / HZ;
-NonFastPath:
+ io_request = cmd->io_request;
+ /* get RAID_Context pointer */
+ pRAID_Context = &io_request->RaidContext;
+ io_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
+ io_request->LUN[1] = scmd->device->lun;
+ pRAID_Context->RAIDFlags = MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD
+ << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT;
+
+ pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id);
+ pRAID_Context->configSeqNum = 0;
+ local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)];
+ io_request->DevHandle =
+ local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl;
+
+ cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle;
+ cmd->request_desc->SCSIIO.MSIxIndex =
+ instance->msix_vectors ?
+ (raw_smp_processor_id() % instance->msix_vectors) : 0;
+
+
+ if (!fp_possible) {
+ /* system pd firmware path */
io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
- io_request->DevHandle = cpu_to_le16(device_id);
cmd->request_desc->SCSIIO.RequestFlags =
(MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
- MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ pRAID_Context->timeoutValue = cpu_to_le16(os_timeout_value);
+ } else {
+ /* system pd Fast Path */
+ io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
+ pRAID_Context->regLockFlags = 0;
+ pRAID_Context->regLockRowLBA = 0;
+ pRAID_Context->regLockLength = 0;
+ timeout_limit = (scmd->device->type == TYPE_DISK) ?
+ 255 : 0xFFFF;
+ pRAID_Context->timeoutValue =
+ cpu_to_le16((os_timeout_value > timeout_limit) ?
+ timeout_limit : os_timeout_value);
+ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) {
+ cmd->request_desc->SCSIIO.RequestFlags |=
+ (MEGASAS_REQ_DESCRIPT_FLAGS_NO_LOCK <<
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+ pRAID_Context->Type = MPI2_TYPE_CUDA;
+ pRAID_Context->nseg = 0x1;
+ io_request->IoFlags |=
+ cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
+ }
+ cmd->request_desc->SCSIIO.RequestFlags =
+ (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY <<
+ MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
}
- io_request->RaidContext.VirtualDiskTgtId = cpu_to_le16(device_id);
- int_to_scsilun(scmd->device->lun, (struct scsi_lun *)io_request->LUN);
}
/**
@@ -1813,11 +1799,10 @@ megasas_build_io_fusion(struct megasas_instance *instance,
struct scsi_cmnd *scp,
struct megasas_cmd_fusion *cmd)
{
- u32 device_id, sge_count;
+ u32 sge_count;
+ u8 cmd_type;
struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request;
- device_id = MEGASAS_DEV_INDEX(instance, scp);
-
/* Zero out some fields so they don't get reused */
memset(io_request->LUN, 0x0, 8);
io_request->CDB.EEDP32.PrimaryReferenceTag = 0;
@@ -1837,10 +1822,24 @@ megasas_build_io_fusion(struct megasas_instance *instance,
*/
io_request->IoFlags = cpu_to_le16(scp->cmd_len);
- if (megasas_cmd_type(scp) == READ_WRITE_LDIO)
+ switch (cmd_type = megasas_cmd_type(scp)) {
+ case READ_WRITE_LDIO:
megasas_build_ldio_fusion(instance, scp, cmd);
- else
- megasas_build_dcdb_fusion(instance, scp, cmd);
+ break;
+ case NON_READ_WRITE_LDIO:
+ megasas_build_ld_nonrw_fusion(instance, scp, cmd);
+ break;
+ case READ_WRITE_SYSPDIO:
+ case NON_READ_WRITE_SYSPDIO:
+ if (instance->secure_jbod_support &&
+ (cmd_type == NON_READ_WRITE_SYSPDIO))
+ megasas_build_syspd_fusion(instance, scp, cmd, 0);
+ else
+ megasas_build_syspd_fusion(instance, scp, cmd, 1);
+ break;
+ default:
+ break;
+ }
/*
* Construct SGL
@@ -1915,9 +1914,7 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance,
fusion = instance->ctrl_context;
- cmd = megasas_get_cmd_fusion(instance);
- if (!cmd)
- return SCSI_MLQUEUE_HOST_BUSY;
+ cmd = megasas_get_cmd_fusion(instance, scmd->request->tag);
index = cmd->index;
@@ -1948,9 +1945,7 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance,
*/
atomic_inc(&instance->fw_outstanding);
- instance->instancet->fire_cmd(instance,
- req_desc->u.low, req_desc->u.high,
- instance->reg_set);
+ megasas_fire_cmd_fusion(instance, req_desc);
return 0;
}
@@ -1975,6 +1970,7 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex)
union desc_value d_val;
struct LD_LOAD_BALANCE_INFO *lbinfo;
int threshold_reply_count = 0;
+ struct scsi_cmnd *scmd_local = NULL;
fusion = instance->ctrl_context;
@@ -1998,7 +1994,8 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex)
num_completed = 0;
- while ((d_val.u.low != UINT_MAX) && (d_val.u.high != UINT_MAX)) {
+ while (d_val.u.low != cpu_to_le32(UINT_MAX) &&
+ d_val.u.high != cpu_to_le32(UINT_MAX)) {
smid = le16_to_cpu(reply_desc->SMID);
cmd_fusion = fusion->cmd_list[smid - 1];
@@ -2010,14 +2007,14 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex)
if (cmd_fusion->scmd)
cmd_fusion->scmd->SCp.ptr = NULL;
+ scmd_local = cmd_fusion->scmd;
status = scsi_io_req->RaidContext.status;
extStatus = scsi_io_req->RaidContext.exStatus;
switch (scsi_io_req->Function) {
case MPI2_FUNCTION_SCSI_IO_REQUEST: /*Fast Path IO.*/
/* Update load balancing info */
- device_id = MEGASAS_DEV_INDEX(instance,
- cmd_fusion->scmd);
+ device_id = MEGASAS_DEV_INDEX(scmd_local);
lbinfo = &fusion->load_balance_info[device_id];
if (cmd_fusion->scmd->SCp.Status &
MEGASAS_LOAD_BALANCE_FLAG) {
@@ -2035,29 +2032,25 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex)
case MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST: /* LD-IO Path */
/* Map the FW Cmd Status */
map_cmd_status(cmd_fusion, status, extStatus);
- scsi_dma_unmap(cmd_fusion->scmd);
- cmd_fusion->scmd->scsi_done(cmd_fusion->scmd);
scsi_io_req->RaidContext.status = 0;
scsi_io_req->RaidContext.exStatus = 0;
megasas_return_cmd_fusion(instance, cmd_fusion);
+ scsi_dma_unmap(scmd_local);
+ scmd_local->scsi_done(scmd_local);
atomic_dec(&instance->fw_outstanding);
break;
case MEGASAS_MPI2_FUNCTION_PASSTHRU_IO_REQUEST: /*MFI command */
cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
- if (!cmd_mfi->mpt_pthr_cmd_blocked) {
- if (megasas_dbg_lvl == 5)
- dev_info(&instance->pdev->dev,
- "freeing mfi/mpt pass-through "
- "from %s %d\n",
- __func__, __LINE__);
- megasas_return_mfi_mpt_pthr(instance, cmd_mfi,
- cmd_fusion);
- }
-
- megasas_complete_cmd(instance, cmd_mfi, DID_OK);
- cmd_fusion->flags = 0;
+ /* Poll mode. Dummy free.
+ * In case of Interrupt mode, caller has reverse check.
+ */
+ if (cmd_mfi->flags & DRV_DCMD_POLLED_MODE) {
+ cmd_mfi->flags &= ~DRV_DCMD_POLLED_MODE;
+ megasas_return_cmd(instance, cmd_mfi);
+ } else
+ megasas_complete_cmd(instance, cmd_mfi, DID_OK);
break;
}
@@ -2066,7 +2059,7 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex)
fusion->reply_q_depth)
fusion->last_reply_idx[MSIxIndex] = 0;
- desc->Words = ULLONG_MAX;
+ desc->Words = cpu_to_le64(ULLONG_MAX);
num_completed++;
threshold_reply_count++;
@@ -2217,27 +2210,14 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance,
struct megasas_cmd_fusion *cmd;
struct fusion_context *fusion;
struct megasas_header *frame_hdr = &mfi_cmd->frame->hdr;
- u32 opcode;
- cmd = megasas_get_cmd_fusion(instance);
- if (!cmd)
- return 1;
+ fusion = instance->ctrl_context;
+
+ cmd = megasas_get_cmd_fusion(instance,
+ instance->max_scsi_cmds + mfi_cmd->index);
/* Save the smid. To be used for returning the cmd */
mfi_cmd->context.smid = cmd->index;
- cmd->sync_cmd_idx = mfi_cmd->index;
-
- /* Set this only for Blocked commands */
- opcode = le32_to_cpu(mfi_cmd->frame->dcmd.opcode);
- if ((opcode == MR_DCMD_LD_MAP_GET_INFO)
- && (mfi_cmd->frame->dcmd.mbox.b[1] == 1))
- mfi_cmd->is_wait_event = 1;
-
- if (opcode == MR_DCMD_CTRL_EVENT_WAIT)
- mfi_cmd->is_wait_event = 1;
-
- if (mfi_cmd->is_wait_event)
- mfi_cmd->mpt_pthr_cmd_blocked = cmd;
/*
* For cmds where the flag is set, store the flag and check
@@ -2246,9 +2226,8 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance,
*/
if (frame_hdr->flags & cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE))
- cmd->flags = MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
+ mfi_cmd->flags |= DRV_DCMD_POLLED_MODE;
- fusion = instance->ctrl_context;
io_req = cmd->io_request;
if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
@@ -2327,14 +2306,12 @@ megasas_issue_dcmd_fusion(struct megasas_instance *instance,
printk(KERN_ERR "Couldn't issue MFI pass thru cmd\n");
return;
}
- atomic_set(&cmd->mfi_mpt_pthr, MFI_MPT_ATTACHED);
- instance->instancet->fire_cmd(instance, req_desc->u.low,
- req_desc->u.high, instance->reg_set);
+ megasas_fire_cmd_fusion(instance, req_desc);
}
/**
* megasas_release_fusion - Reverses the FW initialization
- * @intance: Adapter soft state
+ * @instance: Adapter soft state
*/
void
megasas_release_fusion(struct megasas_instance *instance)
@@ -2508,7 +2485,42 @@ void megasas_reset_reply_desc(struct megasas_instance *instance)
fusion->last_reply_idx[i] = 0;
reply_desc = fusion->reply_frames_desc;
for (i = 0 ; i < fusion->reply_q_depth * count; i++, reply_desc++)
- reply_desc->Words = ULLONG_MAX;
+ reply_desc->Words = cpu_to_le64(ULLONG_MAX);
+}
+
+/*
+ * megasas_refire_mgmt_cmd : Re-fire management commands
+ * @instance: Controller's soft instance
+*/
+void megasas_refire_mgmt_cmd(struct megasas_instance *instance)
+{
+ int j;
+ struct megasas_cmd_fusion *cmd_fusion;
+ struct fusion_context *fusion;
+ struct megasas_cmd *cmd_mfi;
+ union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
+ u16 smid;
+
+ fusion = instance->ctrl_context;
+
+ /* Re-fire management commands.
+ * Do not traverse complet MPT frame pool. Start from max_scsi_cmds.
+ */
+ for (j = instance->max_scsi_cmds ; j < instance->max_fw_cmds; j++) {
+ cmd_fusion = fusion->cmd_list[j];
+ cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
+ smid = le16_to_cpu(cmd_mfi->context.smid);
+
+ if (!smid)
+ continue;
+ req_desc = megasas_get_request_descriptor
+ (instance, smid - 1);
+ if (req_desc && (cmd_mfi->frame->dcmd.opcode !=
+ cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO)))
+ megasas_fire_cmd_fusion(instance, req_desc);
+ else
+ megasas_return_cmd(instance, cmd_mfi);
+ }
}
/* Check for a second path that is currently UP */
@@ -2538,14 +2550,13 @@ out:
/* Core fusion reset function */
int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout)
{
- int retval = SUCCESS, i, j, retry = 0, convert = 0;
+ int retval = SUCCESS, i, retry = 0, convert = 0;
struct megasas_instance *instance;
struct megasas_cmd_fusion *cmd_fusion;
struct fusion_context *fusion;
- struct megasas_cmd *cmd_mfi;
- union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
u32 host_diag, abs_state, status_reg, reset_adapter;
u32 io_timeout_in_crash_mode = 0;
+ struct scsi_cmnd *scmd_local = NULL;
instance = (struct megasas_instance *)shost->hostdata;
fusion = instance->ctrl_context;
@@ -2613,15 +2624,16 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout)
iotimeout = 0;
/* Now return commands back to the OS */
- for (i = 0 ; i < instance->max_fw_cmds; i++) {
+ for (i = 0 ; i < instance->max_scsi_cmds; i++) {
cmd_fusion = fusion->cmd_list[i];
+ scmd_local = cmd_fusion->scmd;
if (cmd_fusion->scmd) {
- scsi_dma_unmap(cmd_fusion->scmd);
- cmd_fusion->scmd->result =
+ scmd_local->result =
megasas_check_mpio_paths(instance,
- cmd_fusion->scmd);
- cmd_fusion->scmd->scsi_done(cmd_fusion->scmd);
+ scmd_local);
megasas_return_cmd_fusion(instance, cmd_fusion);
+ scsi_dma_unmap(scmd_local);
+ scmd_local->scsi_done(scmd_local);
atomic_dec(&instance->fw_outstanding);
}
}
@@ -2790,44 +2802,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout)
continue;
}
- /* Re-fire management commands */
- for (j = 0 ; j < instance->max_fw_cmds; j++) {
- cmd_fusion = fusion->cmd_list[j];
- if (cmd_fusion->sync_cmd_idx !=
- (u32)ULONG_MAX) {
- cmd_mfi =
- instance->
- cmd_list[cmd_fusion->sync_cmd_idx];
- if (cmd_mfi->frame->dcmd.opcode ==
- cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO)) {
- megasas_return_mfi_mpt_pthr(instance, cmd_mfi, cmd_fusion);
- } else {
- req_desc =
- megasas_get_request_descriptor(
- instance,
- cmd_mfi->context.smid
- -1);
- if (!req_desc) {
- printk(KERN_WARNING
- "req_desc NULL"
- " for scsi%d\n",
- instance->host->host_no);
- /* Return leaked MPT
- frame */
- megasas_return_cmd_fusion(instance, cmd_fusion);
- } else {
- instance->instancet->
- fire_cmd(instance,
- req_desc->
- u.low,
- req_desc->
- u.high,
- instance->
- reg_set);
- }
- }
- }
- }
+ megasas_refire_mgmt_cmd(instance);
if (megasas_get_ctrl_info(instance)) {
dev_info(&instance->pdev->dev,
@@ -2978,7 +2953,6 @@ void megasas_fusion_ocr_wq(struct work_struct *work)
}
struct megasas_instance_template megasas_instance_template_fusion = {
- .fire_cmd = megasas_fire_cmd_fusion,
.enable_intr = megasas_enable_intr_fusion,
.disable_intr = megasas_disable_intr_fusion,
.clear_intr = megasas_clear_intr_fusion,
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h
index 56e6db2d5874..ced6dc0cf8e8 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.h
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h
@@ -104,18 +104,18 @@ struct RAID_CONTEXT {
u8 nseg:4;
#endif
u8 resvd0;
- u16 timeoutValue;
+ __le16 timeoutValue;
u8 regLockFlags;
u8 resvd1;
- u16 VirtualDiskTgtId;
- u64 regLockRowLBA;
- u32 regLockLength;
- u16 nextLMId;
+ __le16 VirtualDiskTgtId;
+ __le64 regLockRowLBA;
+ __le32 regLockLength;
+ __le16 nextLMId;
u8 exStatus;
u8 status;
u8 RAIDFlags;
u8 numSGE;
- u16 configSeqNum;
+ __le16 configSeqNum;
u8 spanArm;
u8 resvd2[3];
};
@@ -182,61 +182,61 @@ enum REGION_TYPE {
#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD)
struct MPI25_IEEE_SGE_CHAIN64 {
- u64 Address;
- u32 Length;
- u16 Reserved1;
+ __le64 Address;
+ __le32 Length;
+ __le16 Reserved1;
u8 NextChainOffset;
u8 Flags;
};
struct MPI2_SGE_SIMPLE_UNION {
- u32 FlagsLength;
+ __le32 FlagsLength;
union {
- u32 Address32;
- u64 Address64;
+ __le32 Address32;
+ __le64 Address64;
} u;
};
struct MPI2_SCSI_IO_CDB_EEDP32 {
u8 CDB[20]; /* 0x00 */
- u32 PrimaryReferenceTag; /* 0x14 */
- u16 PrimaryApplicationTag; /* 0x18 */
- u16 PrimaryApplicationTagMask; /* 0x1A */
- u32 TransferLength; /* 0x1C */
+ __be32 PrimaryReferenceTag; /* 0x14 */
+ __be16 PrimaryApplicationTag; /* 0x18 */
+ __be16 PrimaryApplicationTagMask; /* 0x1A */
+ __le32 TransferLength; /* 0x1C */
};
struct MPI2_SGE_CHAIN_UNION {
- u16 Length;
+ __le16 Length;
u8 NextChainOffset;
u8 Flags;
union {
- u32 Address32;
- u64 Address64;
+ __le32 Address32;
+ __le64 Address64;
} u;
};
struct MPI2_IEEE_SGE_SIMPLE32 {
- u32 Address;
- u32 FlagsLength;
+ __le32 Address;
+ __le32 FlagsLength;
};
struct MPI2_IEEE_SGE_CHAIN32 {
- u32 Address;
- u32 FlagsLength;
+ __le32 Address;
+ __le32 FlagsLength;
};
struct MPI2_IEEE_SGE_SIMPLE64 {
- u64 Address;
- u32 Length;
- u16 Reserved1;
+ __le64 Address;
+ __le32 Length;
+ __le16 Reserved1;
u8 Reserved2;
u8 Flags;
};
struct MPI2_IEEE_SGE_CHAIN64 {
- u64 Address;
- u32 Length;
- u16 Reserved1;
+ __le64 Address;
+ __le32 Length;
+ __le16 Reserved1;
u8 Reserved2;
u8 Flags;
};
@@ -269,34 +269,34 @@ union MPI2_SCSI_IO_CDB_UNION {
* Total SGE count will be one less than _MPI2_SCSI_IO_REQUEST
*/
struct MPI2_RAID_SCSI_IO_REQUEST {
- u16 DevHandle; /* 0x00 */
+ __le16 DevHandle; /* 0x00 */
u8 ChainOffset; /* 0x02 */
u8 Function; /* 0x03 */
- u16 Reserved1; /* 0x04 */
+ __le16 Reserved1; /* 0x04 */
u8 Reserved2; /* 0x06 */
u8 MsgFlags; /* 0x07 */
u8 VP_ID; /* 0x08 */
u8 VF_ID; /* 0x09 */
- u16 Reserved3; /* 0x0A */
- u32 SenseBufferLowAddress; /* 0x0C */
- u16 SGLFlags; /* 0x10 */
+ __le16 Reserved3; /* 0x0A */
+ __le32 SenseBufferLowAddress; /* 0x0C */
+ __le16 SGLFlags; /* 0x10 */
u8 SenseBufferLength; /* 0x12 */
u8 Reserved4; /* 0x13 */
u8 SGLOffset0; /* 0x14 */
u8 SGLOffset1; /* 0x15 */
u8 SGLOffset2; /* 0x16 */
u8 SGLOffset3; /* 0x17 */
- u32 SkipCount; /* 0x18 */
- u32 DataLength; /* 0x1C */
- u32 BidirectionalDataLength; /* 0x20 */
- u16 IoFlags; /* 0x24 */
- u16 EEDPFlags; /* 0x26 */
- u32 EEDPBlockSize; /* 0x28 */
- u32 SecondaryReferenceTag; /* 0x2C */
- u16 SecondaryApplicationTag; /* 0x30 */
- u16 ApplicationTagTranslationMask; /* 0x32 */
+ __le32 SkipCount; /* 0x18 */
+ __le32 DataLength; /* 0x1C */
+ __le32 BidirectionalDataLength; /* 0x20 */
+ __le16 IoFlags; /* 0x24 */
+ __le16 EEDPFlags; /* 0x26 */
+ __le32 EEDPBlockSize; /* 0x28 */
+ __le32 SecondaryReferenceTag; /* 0x2C */
+ __le16 SecondaryApplicationTag; /* 0x30 */
+ __le16 ApplicationTagTranslationMask; /* 0x32 */
u8 LUN[8]; /* 0x34 */
- u32 Control; /* 0x3C */
+ __le32 Control; /* 0x3C */
union MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */
struct RAID_CONTEXT RaidContext; /* 0x60 */
union MPI2_SGE_IO_UNION SGL; /* 0x80 */
@@ -315,45 +315,45 @@ struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR {
struct MPI2_DEFAULT_REQUEST_DESCRIPTOR {
u8 RequestFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 LMID; /* 0x04 */
- u16 DescriptorTypeDependent; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 LMID; /* 0x04 */
+ __le16 DescriptorTypeDependent; /* 0x06 */
};
/* High Priority Request Descriptor */
struct MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR {
u8 RequestFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 LMID; /* 0x04 */
- u16 Reserved1; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 LMID; /* 0x04 */
+ __le16 Reserved1; /* 0x06 */
};
/* SCSI IO Request Descriptor */
struct MPI2_SCSI_IO_REQUEST_DESCRIPTOR {
u8 RequestFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 LMID; /* 0x04 */
- u16 DevHandle; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 LMID; /* 0x04 */
+ __le16 DevHandle; /* 0x06 */
};
/* SCSI Target Request Descriptor */
struct MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR {
u8 RequestFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 LMID; /* 0x04 */
- u16 IoIndex; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 LMID; /* 0x04 */
+ __le16 IoIndex; /* 0x06 */
};
/* RAID Accelerator Request Descriptor */
struct MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR {
u8 RequestFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 LMID; /* 0x04 */
- u16 Reserved; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 LMID; /* 0x04 */
+ __le16 Reserved; /* 0x06 */
};
/* union of Request Descriptors */
@@ -366,10 +366,10 @@ union MEGASAS_REQUEST_DESCRIPTOR_UNION {
struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR MFAIo;
union {
struct {
- u32 low;
- u32 high;
+ __le32 low;
+ __le32 high;
} u;
- u64 Words;
+ __le64 Words;
};
};
@@ -377,35 +377,35 @@ union MEGASAS_REQUEST_DESCRIPTOR_UNION {
struct MPI2_DEFAULT_REPLY_DESCRIPTOR {
u8 ReplyFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 DescriptorTypeDependent1; /* 0x02 */
- u32 DescriptorTypeDependent2; /* 0x04 */
+ __le16 DescriptorTypeDependent1; /* 0x02 */
+ __le32 DescriptorTypeDependent2; /* 0x04 */
};
/* Address Reply Descriptor */
struct MPI2_ADDRESS_REPLY_DESCRIPTOR {
u8 ReplyFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u32 ReplyFrameAddress; /* 0x04 */
+ __le16 SMID; /* 0x02 */
+ __le32 ReplyFrameAddress; /* 0x04 */
};
/* SCSI IO Success Reply Descriptor */
struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR {
u8 ReplyFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u16 TaskTag; /* 0x04 */
- u16 Reserved1; /* 0x06 */
+ __le16 SMID; /* 0x02 */
+ __le16 TaskTag; /* 0x04 */
+ __le16 Reserved1; /* 0x06 */
};
/* TargetAssist Success Reply Descriptor */
struct MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR {
u8 ReplyFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
+ __le16 SMID; /* 0x02 */
u8 SequenceNumber; /* 0x04 */
u8 Reserved1; /* 0x05 */
- u16 IoIndex; /* 0x06 */
+ __le16 IoIndex; /* 0x06 */
};
/* Target Command Buffer Reply Descriptor */
@@ -414,16 +414,16 @@ struct MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR {
u8 MSIxIndex; /* 0x01 */
u8 VP_ID; /* 0x02 */
u8 Flags; /* 0x03 */
- u16 InitiatorDevHandle; /* 0x04 */
- u16 IoIndex; /* 0x06 */
+ __le16 InitiatorDevHandle; /* 0x04 */
+ __le16 IoIndex; /* 0x06 */
};
/* RAID Accelerator Success Reply Descriptor */
struct MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR {
u8 ReplyFlags; /* 0x00 */
u8 MSIxIndex; /* 0x01 */
- u16 SMID; /* 0x02 */
- u32 Reserved; /* 0x04 */
+ __le16 SMID; /* 0x02 */
+ __le32 Reserved; /* 0x04 */
};
/* union of Reply Descriptors */
@@ -435,7 +435,7 @@ union MPI2_REPLY_DESCRIPTORS_UNION {
struct MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer;
struct MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR
RAIDAcceleratorSuccess;
- u64 Words;
+ __le64 Words;
};
/* IOCInit Request message */
@@ -444,28 +444,28 @@ struct MPI2_IOC_INIT_REQUEST {
u8 Reserved1; /* 0x01 */
u8 ChainOffset; /* 0x02 */
u8 Function; /* 0x03 */
- u16 Reserved2; /* 0x04 */
+ __le16 Reserved2; /* 0x04 */
u8 Reserved3; /* 0x06 */
u8 MsgFlags; /* 0x07 */
u8 VP_ID; /* 0x08 */
u8 VF_ID; /* 0x09 */
- u16 Reserved4; /* 0x0A */
- u16 MsgVersion; /* 0x0C */
- u16 HeaderVersion; /* 0x0E */
+ __le16 Reserved4; /* 0x0A */
+ __le16 MsgVersion; /* 0x0C */
+ __le16 HeaderVersion; /* 0x0E */
u32 Reserved5; /* 0x10 */
- u16 Reserved6; /* 0x14 */
+ __le16 Reserved6; /* 0x14 */
u8 Reserved7; /* 0x16 */
u8 HostMSIxVectors; /* 0x17 */
- u16 Reserved8; /* 0x18 */
- u16 SystemRequestFrameSize; /* 0x1A */
- u16 ReplyDescriptorPostQueueDepth; /* 0x1C */
- u16 ReplyFreeQueueDepth; /* 0x1E */
- u32 SenseBufferAddressHigh; /* 0x20 */
- u32 SystemReplyAddressHigh; /* 0x24 */
- u64 SystemRequestFrameBaseAddress; /* 0x28 */
- u64 ReplyDescriptorPostQueueAddress;/* 0x30 */
- u64 ReplyFreeQueueAddress; /* 0x38 */
- u64 TimeStamp; /* 0x40 */
+ __le16 Reserved8; /* 0x18 */
+ __le16 SystemRequestFrameSize; /* 0x1A */
+ __le16 ReplyDescriptorPostQueueDepth; /* 0x1C */
+ __le16 ReplyFreeQueueDepth; /* 0x1E */
+ __le32 SenseBufferAddressHigh; /* 0x20 */
+ __le32 SystemReplyAddressHigh; /* 0x24 */
+ __le64 SystemRequestFrameBaseAddress; /* 0x28 */
+ __le64 ReplyDescriptorPostQueueAddress;/* 0x30 */
+ __le64 ReplyFreeQueueAddress; /* 0x38 */
+ __le64 TimeStamp; /* 0x40 */
};
/* mrpriv defines */
@@ -491,41 +491,41 @@ struct MPI2_IOC_INIT_REQUEST {
#define MR_DCMD_LD_VF_MAP_GET_ALL_LDS 0x03150200
struct MR_DEV_HANDLE_INFO {
- u16 curDevHdl;
+ __le16 curDevHdl;
u8 validHandles;
u8 reserved;
- u16 devHandle[2];
+ __le16 devHandle[2];
};
struct MR_ARRAY_INFO {
- u16 pd[MAX_RAIDMAP_ROW_SIZE];
+ __le16 pd[MAX_RAIDMAP_ROW_SIZE];
};
struct MR_QUAD_ELEMENT {
- u64 logStart;
- u64 logEnd;
- u64 offsetInSpan;
- u32 diff;
- u32 reserved1;
+ __le64 logStart;
+ __le64 logEnd;
+ __le64 offsetInSpan;
+ __le32 diff;
+ __le32 reserved1;
};
struct MR_SPAN_INFO {
- u32 noElements;
- u32 reserved1;
+ __le32 noElements;
+ __le32 reserved1;
struct MR_QUAD_ELEMENT quad[MAX_RAIDMAP_SPAN_DEPTH];
};
struct MR_LD_SPAN {
- u64 startBlk;
- u64 numBlks;
- u16 arrayRef;
+ __le64 startBlk;
+ __le64 numBlks;
+ __le16 arrayRef;
u8 spanRowSize;
u8 spanRowDataSize;
u8 reserved[4];
};
struct MR_SPAN_BLOCK_INFO {
- u64 num_rows;
+ __le64 num_rows;
struct MR_LD_SPAN span;
struct MR_SPAN_INFO block_span_info;
};
@@ -558,8 +558,8 @@ struct MR_LD_RAID {
u32 reserved4:7;
#endif
} capability;
- u32 reserved6;
- u64 size;
+ __le32 reserved6;
+ __le64 size;
u8 spanDepth;
u8 level;
u8 stripeShift;
@@ -568,12 +568,12 @@ struct MR_LD_RAID {
u8 writeMode;
u8 PRL;
u8 SRL;
- u16 targetId;
+ __le16 targetId;
u8 ldState;
u8 regTypeReqOnWrite;
u8 modFactor;
u8 regTypeReqOnRead;
- u16 seqNum;
+ __le16 seqNum;
struct {
u32 ldSyncRequired:1;
@@ -592,20 +592,20 @@ struct MR_LD_SPAN_MAP {
};
struct MR_FW_RAID_MAP {
- u32 totalSize;
+ __le32 totalSize;
union {
struct {
- u32 maxLd;
- u32 maxSpanDepth;
- u32 maxRowSize;
- u32 maxPdCount;
- u32 maxArrays;
+ __le32 maxLd;
+ __le32 maxSpanDepth;
+ __le32 maxRowSize;
+ __le32 maxPdCount;
+ __le32 maxArrays;
} validationInfo;
- u32 version[5];
+ __le32 version[5];
};
- u32 ldCount;
- u32 Reserved1;
+ __le32 ldCount;
+ __le32 Reserved1;
u8 ldTgtIdToLd[MAX_RAIDMAP_LOGICAL_DRIVES+
MAX_RAIDMAP_VIEWS];
u8 fpPdIoTimeoutSec;
@@ -620,7 +620,7 @@ struct IO_REQUEST_INFO {
u32 numBlocks;
u16 ldTgtId;
u8 isRead;
- u16 devHandle;
+ __le16 devHandle;
u64 pdBlock;
u8 fpOkForIo;
u8 IoforUnevenSpan;
@@ -634,7 +634,7 @@ struct IO_REQUEST_INFO {
struct MR_LD_TARGET_SYNC {
u8 targetId;
u8 reserved;
- u16 seqNum;
+ __le16 seqNum;
};
#define IEEE_SGE_FLAGS_ADDR_MASK (0x03)
@@ -679,7 +679,6 @@ struct megasas_cmd_fusion {
*/
u32 sync_cmd_idx;
u32 index;
- u8 flags;
u8 pd_r1_lb;
};
@@ -720,27 +719,27 @@ struct MR_DRV_RAID_MAP {
* This feild will be manupulated by driver for ext raid map,
* else pick the value from firmware raid map.
*/
- u32 totalSize;
+ __le32 totalSize;
union {
struct {
- u32 maxLd;
- u32 maxSpanDepth;
- u32 maxRowSize;
- u32 maxPdCount;
- u32 maxArrays;
+ __le32 maxLd;
+ __le32 maxSpanDepth;
+ __le32 maxRowSize;
+ __le32 maxPdCount;
+ __le32 maxArrays;
} validationInfo;
- u32 version[5];
+ __le32 version[5];
};
/* timeout value used by driver in FP IOs*/
u8 fpPdIoTimeoutSec;
u8 reserved2[7];
- u16 ldCount;
- u16 arCount;
- u16 spanCount;
- u16 reserve3;
+ __le16 ldCount;
+ __le16 arCount;
+ __le16 spanCount;
+ __le16 reserve3;
struct MR_DEV_HANDLE_INFO devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES];
u8 ldTgtIdToLd[MAX_LOGICAL_DRIVES_EXT];
@@ -779,10 +778,10 @@ struct MR_FW_RAID_MAP_EXT {
u8 fpPdIoTimeoutSec;
u8 reserved2[7];
- u16 ldCount;
- u16 arCount;
- u16 spanCount;
- u16 reserve3;
+ __le16 ldCount;
+ __le16 arCount;
+ __le16 spanCount;
+ __le16 reserve3;
struct MR_DEV_HANDLE_INFO devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES];
u8 ldTgtIdToLd[MAX_LOGICAL_DRIVES_EXT];
@@ -792,10 +791,6 @@ struct MR_FW_RAID_MAP_EXT {
struct fusion_context {
struct megasas_cmd_fusion **cmd_list;
- struct list_head cmd_pool;
-
- spinlock_t mpt_pool_lock;
-
dma_addr_t req_frames_desc_phys;
u8 *req_frames_desc;
@@ -839,10 +834,10 @@ struct fusion_context {
};
union desc_value {
- u64 word;
+ __le64 word;
struct {
- u32 low;
- u32 high;
+ __le32 low;
+ __le32 high;
} u;
};
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
index 53030b0e8015..d40d734aa53a 100644
--- a/drivers/scsi/mvsas/mv_init.c
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -56,7 +56,6 @@ static struct scsi_host_template mvs_sht = {
.change_queue_depth = sas_change_queue_depth,
.bios_param = sas_bios_param,
.can_queue = 1,
- .cmd_per_lun = 1,
.this_id = -1,
.sg_tablesize = SG_ALL,
.max_sectors = SCSI_DEFAULT_MAX_SECTORS,
diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
index c6077cefbeca..53c84771f0e8 100644
--- a/drivers/scsi/nsp32.c
+++ b/drivers/scsi/nsp32.c
@@ -274,7 +274,6 @@ static struct scsi_host_template nsp32_template = {
.can_queue = 1,
.sg_tablesize = NSP32_SG_SIZE,
.max_sectors = 128,
- .cmd_per_lun = 1,
.this_id = NSP32_HOST_SCSIID,
.use_clustering = DISABLE_CLUSTERING,
.eh_abort_handler = nsp32_eh_abort,
diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c
index 1b6c8833a304..5fb6eefc6541 100644
--- a/drivers/scsi/pcmcia/nsp_cs.c
+++ b/drivers/scsi/pcmcia/nsp_cs.c
@@ -86,7 +86,6 @@ static struct scsi_host_template nsp_driver_template = {
.can_queue = 1,
.this_id = NSP_INITIATOR_ID,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
};
diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c
index bcaf89fe0c9e..c670dc704c74 100644
--- a/drivers/scsi/pcmcia/qlogic_stub.c
+++ b/drivers/scsi/pcmcia/qlogic_stub.c
@@ -72,7 +72,6 @@ static struct scsi_host_template qlogicfas_driver_template = {
.can_queue = 1,
.this_id = -1,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
};
diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c
index 155f9573021f..20011c8afbb5 100644
--- a/drivers/scsi/pcmcia/sym53c500_cs.c
+++ b/drivers/scsi/pcmcia/sym53c500_cs.c
@@ -680,7 +680,6 @@ static struct scsi_host_template sym53c500_driver_template = {
.can_queue = 1,
.this_id = 7,
.sg_tablesize = 32,
- .cmd_per_lun = 1,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = SYM53C500_shost_attrs
};
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index 65555916d3b8..a132f2664d2f 100644
--- a/drivers/scsi/pm8001/pm8001_init.c
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -78,7 +78,6 @@ static struct scsi_host_template pm8001_sht = {
.change_queue_depth = sas_change_queue_depth,
.bios_param = sas_bios_param,
.can_queue = 1,
- .cmd_per_lun = 1,
.this_id = -1,
.sg_tablesize = SG_ALL,
.max_sectors = SCSI_DEFAULT_MAX_SECTORS,
diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c
index 1db8b26063b4..ee00e27ba396 100644
--- a/drivers/scsi/ppa.c
+++ b/drivers/scsi/ppa.c
@@ -974,7 +974,6 @@ static struct scsi_host_template ppa_template = {
.bios_param = ppa_biosparam,
.this_id = -1,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = ENABLE_CLUSTERING,
.can_queue = 1,
.slave_alloc = ppa_adjust_queue,
diff --git a/drivers/scsi/ps3rom.c b/drivers/scsi/ps3rom.c
index 5298def33733..4924424d20fe 100644
--- a/drivers/scsi/ps3rom.c
+++ b/drivers/scsi/ps3rom.c
@@ -347,7 +347,6 @@ static struct scsi_host_template ps3rom_host_template = {
.can_queue = 1,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.emulated = 1, /* only sg driver uses this */
.max_sectors = PS3ROM_MAX_SECTORS,
.use_clustering = ENABLE_CLUSTERING,
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index c68a66e8cfc1..5d0ec42a9317 100644
--- a/drivers/scsi/qla1280.c
+++ b/drivers/scsi/qla1280.c
@@ -4217,7 +4217,6 @@ static struct scsi_host_template qla1280_driver_template = {
.can_queue = 0xfffff,
.this_id = -1,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = ENABLE_CLUSTERING,
};
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 285cb204f300..664013115c9d 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -708,7 +708,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
if (rval != QLA_SUCCESS) {
ql_log(ql_log_warn, vha, 0x00d4,
"Unable to initialize ISP84XX.\n");
- qla84xx_put_chip(vha);
+ qla84xx_put_chip(vha);
}
}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index a1ab25fca874..36fbd4c7af8f 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2797,10 +2797,10 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds)
handle = req->current_outstanding_cmd;
for (index = 1; index < req->num_outstanding_cmds; index++) {
handle++;
- if (handle == req->num_outstanding_cmds)
- handle = 1;
- if (!req->outstanding_cmds[handle])
- break;
+ if (handle == req->num_outstanding_cmds)
+ handle = 1;
+ if (!req->outstanding_cmds[handle])
+ break;
}
if (index == req->num_outstanding_cmds) {
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 6dc14cd782b2..5559d5e75bbf 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -1580,7 +1580,7 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
ql_log(ql_log_warn, fcport->vha, 0x503c,
"Async-%s error - hdl=%x response(%x).\n",
type, sp->handle, sts->data[3]);
- iocb->u.tmf.data = QLA_FUNCTION_FAILED;
+ iocb->u.tmf.data = QLA_FUNCTION_FAILED;
}
}
@@ -1979,7 +1979,7 @@ qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt,
rval = EXT_STATUS_ERR;
break;
}
- bsg_job->reply->reply_payload_rcv_len = 0;
+ bsg_job->reply->reply_payload_rcv_len = 0;
done:
/* Return the vendor specific reply to API */
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index 7d2b18f2675c..1620b0ec977b 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -1843,7 +1843,7 @@ qla82xx_set_product_offset(struct qla_hw_data *ha)
ptab_desc = qla82xx_get_table_desc(unirom,
QLA82XX_URI_DIR_SECT_PRODUCT_TBL);
- if (!ptab_desc)
+ if (!ptab_desc)
return -1;
entries = cpu_to_le32(ptab_desc->num_entries);
diff --git a/drivers/scsi/qla2xxx/qla_nx2.c b/drivers/scsi/qla2xxx/qla_nx2.c
index ed4d6b6b53e3..000c57e4d033 100644
--- a/drivers/scsi/qla2xxx/qla_nx2.c
+++ b/drivers/scsi/qla2xxx/qla_nx2.c
@@ -397,11 +397,11 @@ qla8044_idc_lock(struct qla_hw_data *ha)
* has the lock, wait for 2secs
* and retry
*/
- ql_dbg(ql_dbg_p3p, vha, 0xb08a,
- "%s: IDC lock Recovery by %d "
- "failed, Retrying timeout\n", __func__,
- ha->portnum);
- timeout = 0;
+ ql_dbg(ql_dbg_p3p, vha, 0xb08a,
+ "%s: IDC lock Recovery by %d "
+ "failed, Retrying timeout\n", __func__,
+ ha->portnum);
+ timeout = 0;
}
}
msleep(QLA8044_DRV_LOCK_MSLEEP);
@@ -3141,8 +3141,7 @@ qla8044_minidump_process_rdmdio(struct scsi_qla_host *vha,
goto error;
addr7 = addr2 - (4 * stride1);
- data = qla8044_ipmdio_rd_reg(vha, addr1, addr3,
- mask, addr7);
+ data = qla8044_ipmdio_rd_reg(vha, addr1, addr3, mask, addr7);
if (data == -1)
goto error;
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 7462dd70b150..a28815b8276f 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4418,7 +4418,10 @@ retry_lock2:
void
qla83xx_idc_unlock(scsi_qla_host_t *base_vha, uint16_t requester_id)
{
- uint16_t options = (requester_id << 15) | BIT_7, retry;
+#if 0
+ uint16_t options = (requester_id << 15) | BIT_7;
+#endif
+ uint16_t retry;
uint32_t data;
struct qla_hw_data *ha = base_vha->hw;
@@ -4454,6 +4457,7 @@ retry_unlock:
return;
+#if 0
/* XXX: IDC-unlock implementation using access-control mbx */
retry = 0;
retry_unlock2:
@@ -4469,6 +4473,7 @@ retry_unlock2:
}
return;
+#endif
}
int
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index fe8a8d157e22..4a484d60be0d 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -3712,6 +3712,14 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset)
{
+#if 1
+ /*
+ * FIXME: Reject non zero SRR relative offset until we can test
+ * this code properly.
+ */
+ pr_debug("Rejecting non zero SRR rel_offs: %u\n", offset);
+ return -1;
+#else
struct scatterlist *sg, *sgp, *sg_srr, *sg_srr_start = NULL;
size_t first_offset = 0, rem_offset = offset, tmp = 0;
int i, sg_srr_cnt, bufflen = 0;
@@ -3721,13 +3729,6 @@ static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset)
"cmd->sg_cnt: %u, direction: %d\n",
cmd, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction);
- /*
- * FIXME: Reject non zero SRR relative offset until we can test
- * this code properly.
- */
- pr_debug("Rejecting non zero SRR rel_offs: %u\n", offset);
- return -1;
-
if (!cmd->sg || !cmd->sg_cnt) {
ql_dbg(ql_dbg_tgt, cmd->vha, 0xe055,
"Missing cmd->sg or zero cmd->sg_cnt in"
@@ -3810,6 +3811,7 @@ static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset)
BUG();
return 0;
+#endif
}
static inline int qlt_srr_adjust_data(struct qla_tgt_cmd *cmd,
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
index 68c2002e78bf..5c9e680aa375 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
@@ -1020,8 +1020,7 @@ static void tcm_qla2xxx_depend_tpg(struct work_struct *work)
struct se_portal_group *se_tpg = &base_tpg->se_tpg;
struct scsi_qla_host *base_vha = base_tpg->lport->qla_vha;
- if (!configfs_depend_item(se_tpg->se_tpg_tfo->tf_subsys,
- &se_tpg->tpg_group.cg_item)) {
+ if (!target_depend_item(&se_tpg->tpg_group.cg_item)) {
atomic_set(&base_tpg->lport_tpg_enabled, 1);
qlt_enable_vha(base_vha);
}
@@ -1037,8 +1036,7 @@ static void tcm_qla2xxx_undepend_tpg(struct work_struct *work)
if (!qlt_stop_phase1(base_vha->vha_tgt.qla_tgt)) {
atomic_set(&base_tpg->lport_tpg_enabled, 0);
- configfs_undepend_item(se_tpg->se_tpg_tfo->tf_subsys,
- &se_tpg->tpg_group.cg_item);
+ target_undepend_item(&se_tpg->tpg_group.cg_item);
}
complete(&base_tpg->tpg_base_comp);
}
diff --git a/drivers/scsi/qla4xxx/ql4_83xx.c b/drivers/scsi/qla4xxx/ql4_83xx.c
index 556c1525f881..5d4f8e67fb25 100644
--- a/drivers/scsi/qla4xxx/ql4_83xx.c
+++ b/drivers/scsi/qla4xxx/ql4_83xx.c
@@ -828,7 +828,7 @@ void qla4_83xx_read_reset_template(struct scsi_qla_host *ha)
ret_val = qla4_83xx_flash_read_u32(ha, addr, p_buff,
tmplt_hdr_def_size);
if (ret_val != QLA_SUCCESS) {
- ql4_printk(KERN_ERR, ha, "%s: Failed to read reset tempelate\n",
+ ql4_printk(KERN_ERR, ha, "%s: Failed to read reset template\n",
__func__);
goto exit_read_template_error;
}
diff --git a/drivers/scsi/qla4xxx/ql4_bsg.c b/drivers/scsi/qla4xxx/ql4_bsg.c
index 9f92cbf96477..415ee5eb3fc7 100644
--- a/drivers/scsi/qla4xxx/ql4_bsg.c
+++ b/drivers/scsi/qla4xxx/ql4_bsg.c
@@ -571,7 +571,7 @@ static int qla4_83xx_pre_loopback_config(struct scsi_qla_host *ha,
if ((config & ENABLE_INTERNAL_LOOPBACK) ||
(config & ENABLE_EXTERNAL_LOOPBACK)) {
- ql4_printk(KERN_INFO, ha, "%s: Loopback diagnostics already in progress. Invalid requiest\n",
+ ql4_printk(KERN_INFO, ha, "%s: Loopback diagnostics already in progress. Invalid request\n",
__func__);
goto exit_pre_loopback_config;
}
diff --git a/drivers/scsi/qlogicfas.c b/drivers/scsi/qlogicfas.c
index a22bb1b40ce2..61cac87fb86f 100644
--- a/drivers/scsi/qlogicfas.c
+++ b/drivers/scsi/qlogicfas.c
@@ -193,7 +193,6 @@ static struct scsi_host_template qlogicfas_driver_template = {
.can_queue = 1,
.this_id = -1,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
.use_clustering = DISABLE_CLUSTERING,
};
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index fe122700cad8..676385ff28ef 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -1287,7 +1287,6 @@ static struct scsi_host_template qpti_template = {
.can_queue = QLOGICPTI_REQ_QUEUE_LEN,
.this_id = 7,
.sg_tablesize = QLOGICPTI_MAX_SG(QLOGICPTI_REQ_QUEUE_LEN),
- .cmd_per_lun = 1,
.use_clustering = ENABLE_CLUSTERING,
};
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 3833bf59fb66..207d6a7a1bd0 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -98,52 +98,6 @@ EXPORT_SYMBOL(scsi_sd_probe_domain);
ASYNC_DOMAIN_EXCLUSIVE(scsi_sd_pm_domain);
EXPORT_SYMBOL(scsi_sd_pm_domain);
-/* NB: These are exposed through /proc/scsi/scsi and form part of the ABI.
- * You may not alter any existing entry (although adding new ones is
- * encouraged once assigned by ANSI/INCITS T10
- */
-static const char *const scsi_device_types[] = {
- "Direct-Access ",
- "Sequential-Access",
- "Printer ",
- "Processor ",
- "WORM ",
- "CD-ROM ",
- "Scanner ",
- "Optical Device ",
- "Medium Changer ",
- "Communications ",
- "ASC IT8 ",
- "ASC IT8 ",
- "RAID ",
- "Enclosure ",
- "Direct-Access-RBC",
- "Optical card ",
- "Bridge controller",
- "Object storage ",
- "Automation/Drive ",
- "Security Manager ",
- "Direct-Access-ZBC",
-};
-
-/**
- * scsi_device_type - Return 17 char string indicating device type.
- * @type: type number to look up
- */
-
-const char * scsi_device_type(unsigned type)
-{
- if (type == 0x1e)
- return "Well-known LUN ";
- if (type == 0x1f)
- return "No Device ";
- if (type >= ARRAY_SIZE(scsi_device_types))
- return "Unknown ";
- return scsi_device_types[type];
-}
-
-EXPORT_SYMBOL(scsi_device_type);
-
struct scsi_host_cmd_pool {
struct kmem_cache *cmd_slab;
struct kmem_cache *sense_slab;
diff --git a/drivers/scsi/scsi_common.c b/drivers/scsi/scsi_common.c
new file mode 100644
index 000000000000..2ff092252b76
--- /dev/null
+++ b/drivers/scsi/scsi_common.c
@@ -0,0 +1,178 @@
+/*
+ * SCSI functions used by both the initiator and the target code.
+ */
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <scsi/scsi_common.h>
+
+/* NB: These are exposed through /proc/scsi/scsi and form part of the ABI.
+ * You may not alter any existing entry (although adding new ones is
+ * encouraged once assigned by ANSI/INCITS T10
+ */
+static const char *const scsi_device_types[] = {
+ "Direct-Access ",
+ "Sequential-Access",
+ "Printer ",
+ "Processor ",
+ "WORM ",
+ "CD-ROM ",
+ "Scanner ",
+ "Optical Device ",
+ "Medium Changer ",
+ "Communications ",
+ "ASC IT8 ",
+ "ASC IT8 ",
+ "RAID ",
+ "Enclosure ",
+ "Direct-Access-RBC",
+ "Optical card ",
+ "Bridge controller",
+ "Object storage ",
+ "Automation/Drive ",
+ "Security Manager ",
+ "Direct-Access-ZBC",
+};
+
+/**
+ * scsi_device_type - Return 17 char string indicating device type.
+ * @type: type number to look up
+ */
+const char *scsi_device_type(unsigned type)
+{
+ if (type == 0x1e)
+ return "Well-known LUN ";
+ if (type == 0x1f)
+ return "No Device ";
+ if (type >= ARRAY_SIZE(scsi_device_types))
+ return "Unknown ";
+ return scsi_device_types[type];
+}
+EXPORT_SYMBOL(scsi_device_type);
+
+/**
+ * scsilun_to_int - convert a scsi_lun to an int
+ * @scsilun: struct scsi_lun to be converted.
+ *
+ * Description:
+ * Convert @scsilun from a struct scsi_lun to a four byte host byte-ordered
+ * integer, and return the result. The caller must check for
+ * truncation before using this function.
+ *
+ * Notes:
+ * For a description of the LUN format, post SCSI-3 see the SCSI
+ * Architecture Model, for SCSI-3 see the SCSI Controller Commands.
+ *
+ * Given a struct scsi_lun of: d2 04 0b 03 00 00 00 00, this function
+ * returns the integer: 0x0b03d204
+ *
+ * This encoding will return a standard integer LUN for LUNs smaller
+ * than 256, which typically use a single level LUN structure with
+ * addressing method 0.
+ */
+u64 scsilun_to_int(struct scsi_lun *scsilun)
+{
+ int i;
+ u64 lun;
+
+ lun = 0;
+ for (i = 0; i < sizeof(lun); i += 2)
+ lun = lun | (((u64)scsilun->scsi_lun[i] << ((i + 1) * 8)) |
+ ((u64)scsilun->scsi_lun[i + 1] << (i * 8)));
+ return lun;
+}
+EXPORT_SYMBOL(scsilun_to_int);
+
+/**
+ * int_to_scsilun - reverts an int into a scsi_lun
+ * @lun: integer to be reverted
+ * @scsilun: struct scsi_lun to be set.
+ *
+ * Description:
+ * Reverts the functionality of the scsilun_to_int, which packed
+ * an 8-byte lun value into an int. This routine unpacks the int
+ * back into the lun value.
+ *
+ * Notes:
+ * Given an integer : 0x0b03d204, this function returns a
+ * struct scsi_lun of: d2 04 0b 03 00 00 00 00
+ *
+ */
+void int_to_scsilun(u64 lun, struct scsi_lun *scsilun)
+{
+ int i;
+
+ memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun));
+
+ for (i = 0; i < sizeof(lun); i += 2) {
+ scsilun->scsi_lun[i] = (lun >> 8) & 0xFF;
+ scsilun->scsi_lun[i+1] = lun & 0xFF;
+ lun = lun >> 16;
+ }
+}
+EXPORT_SYMBOL(int_to_scsilun);
+
+/**
+ * scsi_normalize_sense - normalize main elements from either fixed or
+ * descriptor sense data format into a common format.
+ *
+ * @sense_buffer: byte array containing sense data returned by device
+ * @sb_len: number of valid bytes in sense_buffer
+ * @sshdr: pointer to instance of structure that common
+ * elements are written to.
+ *
+ * Notes:
+ * The "main elements" from sense data are: response_code, sense_key,
+ * asc, ascq and additional_length (only for descriptor format).
+ *
+ * Typically this function can be called after a device has
+ * responded to a SCSI command with the CHECK_CONDITION status.
+ *
+ * Return value:
+ * true if valid sense data information found, else false;
+ */
+bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
+ struct scsi_sense_hdr *sshdr)
+{
+ if (!sense_buffer || !sb_len)
+ return false;
+
+ memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
+
+ sshdr->response_code = (sense_buffer[0] & 0x7f);
+
+ if (!scsi_sense_valid(sshdr))
+ return false;
+
+ if (sshdr->response_code >= 0x72) {
+ /*
+ * descriptor format
+ */
+ if (sb_len > 1)
+ sshdr->sense_key = (sense_buffer[1] & 0xf);
+ if (sb_len > 2)
+ sshdr->asc = sense_buffer[2];
+ if (sb_len > 3)
+ sshdr->ascq = sense_buffer[3];
+ if (sb_len > 7)
+ sshdr->additional_length = sense_buffer[7];
+ } else {
+ /*
+ * fixed format
+ */
+ if (sb_len > 2)
+ sshdr->sense_key = (sense_buffer[2] & 0xf);
+ if (sb_len > 7) {
+ sb_len = (sb_len < (sense_buffer[7] + 8)) ?
+ sb_len : (sense_buffer[7] + 8);
+ if (sb_len > 12)
+ sshdr->asc = sense_buffer[12];
+ if (sb_len > 13)
+ sshdr->ascq = sense_buffer[13];
+ }
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(scsi_normalize_sense);
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 262ab837a704..9f77d23239a2 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -226,6 +226,7 @@ static struct {
{"PIONEER", "CD-ROM DRM-624X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
{"Promise", "VTrak E610f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC},
{"Promise", "", NULL, BLIST_SPARSELUN},
+ {"QNAP", "iSCSI Storage", NULL, BLIST_MAX_1024},
{"QUANTUM", "XP34301", "1071", BLIST_NOTQ},
{"REGAL", "CDC-4X", NULL, BLIST_MAX5LUN | BLIST_SINGLELUN},
{"SanDisk", "ImageMate CF-SD1", NULL, BLIST_FORCELUN},
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index c95a4e943fc6..106884a5444e 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -2399,70 +2399,6 @@ out_put_autopm_host:
}
EXPORT_SYMBOL(scsi_ioctl_reset);
-/**
- * scsi_normalize_sense - normalize main elements from either fixed or
- * descriptor sense data format into a common format.
- *
- * @sense_buffer: byte array containing sense data returned by device
- * @sb_len: number of valid bytes in sense_buffer
- * @sshdr: pointer to instance of structure that common
- * elements are written to.
- *
- * Notes:
- * The "main elements" from sense data are: response_code, sense_key,
- * asc, ascq and additional_length (only for descriptor format).
- *
- * Typically this function can be called after a device has
- * responded to a SCSI command with the CHECK_CONDITION status.
- *
- * Return value:
- * true if valid sense data information found, else false;
- */
-bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
- struct scsi_sense_hdr *sshdr)
-{
- if (!sense_buffer || !sb_len)
- return false;
-
- memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
-
- sshdr->response_code = (sense_buffer[0] & 0x7f);
-
- if (!scsi_sense_valid(sshdr))
- return false;
-
- if (sshdr->response_code >= 0x72) {
- /*
- * descriptor format
- */
- if (sb_len > 1)
- sshdr->sense_key = (sense_buffer[1] & 0xf);
- if (sb_len > 2)
- sshdr->asc = sense_buffer[2];
- if (sb_len > 3)
- sshdr->ascq = sense_buffer[3];
- if (sb_len > 7)
- sshdr->additional_length = sense_buffer[7];
- } else {
- /*
- * fixed format
- */
- if (sb_len > 2)
- sshdr->sense_key = (sense_buffer[2] & 0xf);
- if (sb_len > 7) {
- sb_len = (sb_len < (sense_buffer[7] + 8)) ?
- sb_len : (sense_buffer[7] + 8);
- if (sb_len > 12)
- sshdr->asc = sense_buffer[12];
- if (sb_len > 13)
- sshdr->ascq = sense_buffer[13];
- }
- }
-
- return true;
-}
-EXPORT_SYMBOL(scsi_normalize_sense);
-
bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd,
struct scsi_sense_hdr *sshdr)
{
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 60aae01caa89..f9f3f8203d42 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -280,7 +280,8 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
sdev->host->cmd_per_lun, shost->bqt,
shost->hostt->tag_alloc_policy);
}
- scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun);
+ scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun ?
+ sdev->host->cmd_per_lun : 1);
scsi_sysfs_device_initialize(sdev);
@@ -897,6 +898,12 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
*/
if (*bflags & BLIST_MAX_512)
blk_queue_max_hw_sectors(sdev->request_queue, 512);
+ /*
+ * Max 1024 sector transfer length for targets that report incorrect
+ * max/optimal lengths and relied on the old block layer safe default
+ */
+ else if (*bflags & BLIST_MAX_1024)
+ blk_queue_max_hw_sectors(sdev->request_queue, 1024);
/*
* Some devices may not want to have a start command automatically
@@ -1263,68 +1270,6 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget,
}
/**
- * scsilun_to_int - convert a scsi_lun to an int
- * @scsilun: struct scsi_lun to be converted.
- *
- * Description:
- * Convert @scsilun from a struct scsi_lun to a four byte host byte-ordered
- * integer, and return the result. The caller must check for
- * truncation before using this function.
- *
- * Notes:
- * For a description of the LUN format, post SCSI-3 see the SCSI
- * Architecture Model, for SCSI-3 see the SCSI Controller Commands.
- *
- * Given a struct scsi_lun of: d2 04 0b 03 00 00 00 00, this function
- * returns the integer: 0x0b03d204
- *
- * This encoding will return a standard integer LUN for LUNs smaller
- * than 256, which typically use a single level LUN structure with
- * addressing method 0.
- **/
-u64 scsilun_to_int(struct scsi_lun *scsilun)
-{
- int i;
- u64 lun;
-
- lun = 0;
- for (i = 0; i < sizeof(lun); i += 2)
- lun = lun | (((u64)scsilun->scsi_lun[i] << ((i + 1) * 8)) |
- ((u64)scsilun->scsi_lun[i + 1] << (i * 8)));
- return lun;
-}
-EXPORT_SYMBOL(scsilun_to_int);
-
-/**
- * int_to_scsilun - reverts an int into a scsi_lun
- * @lun: integer to be reverted
- * @scsilun: struct scsi_lun to be set.
- *
- * Description:
- * Reverts the functionality of the scsilun_to_int, which packed
- * an 8-byte lun value into an int. This routine unpacks the int
- * back into the lun value.
- *
- * Notes:
- * Given an integer : 0x0b03d204, this function returns a
- * struct scsi_lun of: d2 04 0b 03 00 00 00 00
- *
- **/
-void int_to_scsilun(u64 lun, struct scsi_lun *scsilun)
-{
- int i;
-
- memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun));
-
- for (i = 0; i < sizeof(lun); i += 2) {
- scsilun->scsi_lun[i] = (lun >> 8) & 0xFF;
- scsilun->scsi_lun[i+1] = lun & 0xFF;
- lun = lun >> 16;
- }
-}
-EXPORT_SYMBOL(int_to_scsilun);
-
-/**
* scsi_report_lun_scan - Scan using SCSI REPORT LUN results
* @starget: which target
* @bflags: Zero or a mix of BLIST_NOLUN, BLIST_REPORTLUN2, or BLIST_NOREPORTLUN
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 67d43e35693d..55647aae065c 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -204,6 +204,8 @@ iscsi_create_endpoint(int dd_size)
iscsi_match_epid);
if (!dev)
break;
+ else
+ put_device(dev);
}
if (id == ISCSI_MAX_EPID) {
printk(KERN_ERR "Too many connections. Max supported %u\n",
diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c
index ae45bd99baed..a85292b1d09d 100644
--- a/drivers/scsi/scsi_transport_srp.c
+++ b/drivers/scsi/scsi_transport_srp.c
@@ -61,6 +61,11 @@ static inline struct Scsi_Host *rport_to_shost(struct srp_rport *r)
return dev_to_shost(r->dev.parent);
}
+static inline struct srp_rport *shost_to_rport(struct Scsi_Host *shost)
+{
+ return transport_class_to_srp_rport(&shost->shost_gendev);
+}
+
/**
* srp_tmo_valid() - check timeout combination validity
* @reconnect_delay: Reconnect delay in seconds.
@@ -396,6 +401,36 @@ static void srp_reconnect_work(struct work_struct *work)
}
}
+/**
+ * scsi_request_fn_active() - number of kernel threads inside scsi_request_fn()
+ * @shost: SCSI host for which to count the number of scsi_request_fn() callers.
+ *
+ * To do: add support for scsi-mq in this function.
+ */
+static int scsi_request_fn_active(struct Scsi_Host *shost)
+{
+ struct scsi_device *sdev;
+ struct request_queue *q;
+ int request_fn_active = 0;
+
+ shost_for_each_device(sdev, shost) {
+ q = sdev->request_queue;
+
+ spin_lock_irq(q->queue_lock);
+ request_fn_active += q->request_fn_active;
+ spin_unlock_irq(q->queue_lock);
+ }
+
+ return request_fn_active;
+}
+
+/* Wait until ongoing shost->hostt->queuecommand() calls have finished. */
+static void srp_wait_for_queuecommand(struct Scsi_Host *shost)
+{
+ while (scsi_request_fn_active(shost))
+ msleep(20);
+}
+
static void __rport_fail_io_fast(struct srp_rport *rport)
{
struct Scsi_Host *shost = rport_to_shost(rport);
@@ -409,8 +444,10 @@ static void __rport_fail_io_fast(struct srp_rport *rport)
/* Involve the LLD if possible to terminate all I/O on the rport. */
i = to_srp_internal(shost->transportt);
- if (i->f->terminate_rport_io)
+ if (i->f->terminate_rport_io) {
+ srp_wait_for_queuecommand(shost);
i->f->terminate_rport_io(rport);
+ }
}
/**
@@ -504,27 +541,6 @@ void srp_start_tl_fail_timers(struct srp_rport *rport)
EXPORT_SYMBOL(srp_start_tl_fail_timers);
/**
- * scsi_request_fn_active() - number of kernel threads inside scsi_request_fn()
- * @shost: SCSI host for which to count the number of scsi_request_fn() callers.
- */
-static int scsi_request_fn_active(struct Scsi_Host *shost)
-{
- struct scsi_device *sdev;
- struct request_queue *q;
- int request_fn_active = 0;
-
- shost_for_each_device(sdev, shost) {
- q = sdev->request_queue;
-
- spin_lock_irq(q->queue_lock);
- request_fn_active += q->request_fn_active;
- spin_unlock_irq(q->queue_lock);
- }
-
- return request_fn_active;
-}
-
-/**
* srp_reconnect_rport() - reconnect to an SRP target port
* @rport: SRP target port.
*
@@ -559,8 +575,7 @@ int srp_reconnect_rport(struct srp_rport *rport)
if (res)
goto out;
scsi_target_block(&shost->shost_gendev);
- while (scsi_request_fn_active(shost))
- msleep(20);
+ srp_wait_for_queuecommand(shost);
res = rport->state != SRP_RPORT_LOST ? i->f->reconnect(rport) : -ENODEV;
pr_debug("%s (state %d): transport.reconnect() returned %d\n",
dev_name(&shost->shost_gendev), rport->state, res);
@@ -618,9 +633,11 @@ static enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd)
struct scsi_device *sdev = scmd->device;
struct Scsi_Host *shost = sdev->host;
struct srp_internal *i = to_srp_internal(shost->transportt);
+ struct srp_rport *rport = shost_to_rport(shost);
pr_debug("timeout for sdev %s\n", dev_name(&sdev->sdev_gendev));
- return i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ?
+ return rport->fast_io_fail_tmo < 0 && rport->dev_loss_tmo < 0 &&
+ i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ?
BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED;
}
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 79beebf53302..3b2fcb4fada0 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1600,6 +1600,7 @@ static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
{
u64 start_lba = blk_rq_pos(scmd->request);
u64 end_lba = blk_rq_pos(scmd->request) + (scsi_bufflen(scmd) / 512);
+ u64 factor = scmd->device->sector_size / 512;
u64 bad_lba;
int info_valid;
/*
@@ -1621,16 +1622,9 @@ static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
if (scsi_bufflen(scmd) <= scmd->device->sector_size)
return 0;
- if (scmd->device->sector_size < 512) {
- /* only legitimate sector_size here is 256 */
- start_lba <<= 1;
- end_lba <<= 1;
- } else {
- /* be careful ... don't want any overflows */
- unsigned int factor = scmd->device->sector_size / 512;
- do_div(start_lba, factor);
- do_div(end_lba, factor);
- }
+ /* be careful ... don't want any overflows */
+ do_div(start_lba, factor);
+ do_div(end_lba, factor);
/* The bad lba was reported incorrectly, we have no idea where
* the error is.
@@ -2188,8 +2182,7 @@ got_data:
if (sector_size != 512 &&
sector_size != 1024 &&
sector_size != 2048 &&
- sector_size != 4096 &&
- sector_size != 256) {
+ sector_size != 4096) {
sd_printk(KERN_NOTICE, sdkp, "Unsupported sector size %d.\n",
sector_size);
/*
@@ -2244,8 +2237,6 @@ got_data:
sdkp->capacity <<= 2;
else if (sector_size == 1024)
sdkp->capacity <<= 1;
- else if (sector_size == 256)
- sdkp->capacity >>= 1;
blk_queue_physical_block_size(sdp->request_queue,
sdkp->physical_block_size);
@@ -2997,7 +2988,8 @@ static int sd_probe(struct device *dev)
sdkp->dev.class = &sd_disk_class;
dev_set_name(&sdkp->dev, "%s", dev_name(dev));
- if (device_add(&sdkp->dev))
+ error = device_add(&sdkp->dev);
+ if (error)
goto out_free_index;
get_device(dev);
diff --git a/drivers/scsi/snic/Makefile b/drivers/scsi/snic/Makefile
new file mode 100644
index 000000000000..ef7c0dd47f40
--- /dev/null
+++ b/drivers/scsi/snic/Makefile
@@ -0,0 +1,17 @@
+obj-$(CONFIG_SCSI_SNIC) += snic.o
+
+snic-y := \
+ snic_attrs.o \
+ snic_main.o \
+ snic_res.o \
+ snic_isr.o \
+ snic_ctl.o \
+ snic_io.o \
+ snic_scsi.o \
+ snic_disc.o \
+ vnic_cq.o \
+ vnic_intr.o \
+ vnic_dev.o \
+ vnic_wq.o
+
+snic-$(CONFIG_SCSI_SNIC_DEBUG_FS) += snic_debugfs.o snic_trc.o
diff --git a/drivers/scsi/snic/cq_desc.h b/drivers/scsi/snic/cq_desc.h
new file mode 100644
index 000000000000..a5290562c1fa
--- /dev/null
+++ b/drivers/scsi/snic/cq_desc.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _CQ_DESC_H_
+#define _CQ_DESC_H_
+
+/*
+ * Completion queue descriptor types
+ */
+enum cq_desc_types {
+ CQ_DESC_TYPE_WQ_ENET = 0,
+ CQ_DESC_TYPE_DESC_COPY = 1,
+ CQ_DESC_TYPE_WQ_EXCH = 2,
+ CQ_DESC_TYPE_RQ_ENET = 3,
+ CQ_DESC_TYPE_RQ_FCP = 4,
+};
+
+/* Completion queue descriptor: 16B
+ *
+ * All completion queues have this basic layout. The
+ * type_specific area is unique for each completion
+ * queue type.
+ */
+struct cq_desc {
+ __le16 completed_index;
+ __le16 q_number;
+ u8 type_specific[11];
+ u8 type_color;
+};
+
+#define CQ_DESC_TYPE_BITS 4
+#define CQ_DESC_TYPE_MASK ((1 << CQ_DESC_TYPE_BITS) - 1)
+#define CQ_DESC_COLOR_MASK 1
+#define CQ_DESC_COLOR_SHIFT 7
+#define CQ_DESC_Q_NUM_BITS 10
+#define CQ_DESC_Q_NUM_MASK ((1 << CQ_DESC_Q_NUM_BITS) - 1)
+#define CQ_DESC_COMP_NDX_BITS 12
+#define CQ_DESC_COMP_NDX_MASK ((1 << CQ_DESC_COMP_NDX_BITS) - 1)
+
+static inline void cq_desc_dec(const struct cq_desc *desc_arg,
+ u8 *type, u8 *color, u16 *q_number, u16 *completed_index)
+{
+ const struct cq_desc *desc = desc_arg;
+ const u8 type_color = desc->type_color;
+
+ *color = (type_color >> CQ_DESC_COLOR_SHIFT) & CQ_DESC_COLOR_MASK;
+
+ /*
+ * Make sure color bit is read from desc *before* other fields
+ * are read from desc. Hardware guarantees color bit is last
+ * bit (byte) written. Adding the rmb() prevents the compiler
+ * and/or CPU from reordering the reads which would potentially
+ * result in reading stale values.
+ */
+ rmb();
+
+ *type = type_color & CQ_DESC_TYPE_MASK;
+ *q_number = le16_to_cpu(desc->q_number) & CQ_DESC_Q_NUM_MASK;
+ *completed_index = le16_to_cpu(desc->completed_index) &
+ CQ_DESC_COMP_NDX_MASK;
+}
+
+#endif /* _CQ_DESC_H_ */
diff --git a/drivers/scsi/snic/cq_enet_desc.h b/drivers/scsi/snic/cq_enet_desc.h
new file mode 100644
index 000000000000..0a1be2ed0288
--- /dev/null
+++ b/drivers/scsi/snic/cq_enet_desc.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _CQ_ENET_DESC_H_
+#define _CQ_ENET_DESC_H_
+
+#include "cq_desc.h"
+
+/* Ethernet completion queue descriptor: 16B */
+struct cq_enet_wq_desc {
+ __le16 completed_index;
+ __le16 q_number;
+ u8 reserved[11];
+ u8 type_color;
+};
+
+static inline void cq_enet_wq_desc_dec(struct cq_enet_wq_desc *desc,
+ u8 *type, u8 *color, u16 *q_number, u16 *completed_index)
+{
+ cq_desc_dec((struct cq_desc *)desc, type,
+ color, q_number, completed_index);
+}
+
+#endif /* _CQ_ENET_DESC_H_ */
diff --git a/drivers/scsi/snic/snic.h b/drivers/scsi/snic/snic.h
new file mode 100644
index 000000000000..d7f5ba6ba84c
--- /dev/null
+++ b/drivers/scsi/snic/snic.h
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _SNIC_H_
+#define _SNIC_H_
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+#include <linux/mempool.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include "snic_disc.h"
+#include "snic_io.h"
+#include "snic_res.h"
+#include "snic_trc.h"
+#include "snic_stats.h"
+#include "vnic_dev.h"
+#include "vnic_wq.h"
+#include "vnic_cq.h"
+#include "vnic_intr.h"
+#include "vnic_stats.h"
+#include "vnic_snic.h"
+
+#define SNIC_DRV_NAME "snic"
+#define SNIC_DRV_DESCRIPTION "Cisco SCSI NIC Driver"
+#define SNIC_DRV_VERSION "0.0.1.18"
+#define PFX SNIC_DRV_NAME ":"
+#define DFX SNIC_DRV_NAME "%d: "
+
+#define DESC_CLEAN_LOW_WATERMARK 8
+#define SNIC_UCSM_DFLT_THROTTLE_CNT_BLD 16 /* UCSM default throttle count */
+#define SNIC_MAX_IO_REQ 50 /* scsi_cmnd tag map entries */
+#define SNIC_MIN_IO_REQ 8 /* Min IO throttle count */
+#define SNIC_IO_LOCKS 64 /* IO locks: power of 2 */
+#define SNIC_DFLT_QUEUE_DEPTH 32 /* Default Queue Depth */
+#define SNIC_MAX_QUEUE_DEPTH 64 /* Max Queue Depth */
+#define SNIC_DFLT_CMD_TIMEOUT 90 /* Extended tmo for FW */
+
+/*
+ * Tag bits used for special requests.
+ */
+#define SNIC_TAG_ABORT BIT(30) /* Tag indicating abort */
+#define SNIC_TAG_DEV_RST BIT(29) /* Tag for device reset */
+#define SNIC_TAG_IOCTL_DEV_RST BIT(28) /* Tag for User Device Reset */
+#define SNIC_TAG_MASK (BIT(24) - 1) /* Mask for lookup */
+#define SNIC_NO_TAG -1
+
+/*
+ * Command flags to identify the type of command and for other future use
+ */
+#define SNIC_NO_FLAGS 0
+#define SNIC_IO_INITIALIZED BIT(0)
+#define SNIC_IO_ISSUED BIT(1)
+#define SNIC_IO_DONE BIT(2)
+#define SNIC_IO_REQ_NULL BIT(3)
+#define SNIC_IO_ABTS_PENDING BIT(4)
+#define SNIC_IO_ABORTED BIT(5)
+#define SNIC_IO_ABTS_ISSUED BIT(6)
+#define SNIC_IO_TERM_ISSUED BIT(7)
+#define SNIC_IO_ABTS_TIMEDOUT BIT(8)
+#define SNIC_IO_ABTS_TERM_DONE BIT(9)
+#define SNIC_IO_ABTS_TERM_REQ_NULL BIT(10)
+#define SNIC_IO_ABTS_TERM_TIMEDOUT BIT(11)
+#define SNIC_IO_INTERNAL_TERM_PENDING BIT(12)
+#define SNIC_IO_INTERNAL_TERM_ISSUED BIT(13)
+#define SNIC_DEVICE_RESET BIT(14)
+#define SNIC_DEV_RST_ISSUED BIT(15)
+#define SNIC_DEV_RST_TIMEDOUT BIT(16)
+#define SNIC_DEV_RST_ABTS_ISSUED BIT(17)
+#define SNIC_DEV_RST_TERM_ISSUED BIT(18)
+#define SNIC_DEV_RST_DONE BIT(19)
+#define SNIC_DEV_RST_REQ_NULL BIT(20)
+#define SNIC_DEV_RST_ABTS_DONE BIT(21)
+#define SNIC_DEV_RST_TERM_DONE BIT(22)
+#define SNIC_DEV_RST_ABTS_PENDING BIT(23)
+#define SNIC_DEV_RST_PENDING BIT(24)
+#define SNIC_DEV_RST_NOTSUP BIT(25)
+#define SNIC_SCSI_CLEANUP BIT(26)
+#define SNIC_HOST_RESET_ISSUED BIT(27)
+
+#define SNIC_ABTS_TIMEOUT 30000 /* msec */
+#define SNIC_LUN_RESET_TIMEOUT 30000 /* msec */
+#define SNIC_HOST_RESET_TIMEOUT 30000 /* msec */
+
+
+/*
+ * These are protected by the hashed req_lock.
+ */
+#define CMD_SP(Cmnd) \
+ (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->rqi)
+#define CMD_STATE(Cmnd) \
+ (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->state)
+#define CMD_ABTS_STATUS(Cmnd) \
+ (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->abts_status)
+#define CMD_LR_STATUS(Cmnd) \
+ (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->lr_status)
+#define CMD_FLAGS(Cmnd) \
+ (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->flags)
+
+#define SNIC_INVALID_CODE 0x100 /* Hdr Status val unused by firmware */
+
+#define SNIC_MAX_TARGET 256
+#define SNIC_FLAGS_NONE (0)
+
+/* snic module params */
+extern unsigned int snic_max_qdepth;
+
+/* snic debugging */
+extern unsigned int snic_log_level;
+
+#define SNIC_MAIN_LOGGING 0x1
+#define SNIC_SCSI_LOGGING 0x2
+#define SNIC_ISR_LOGGING 0x8
+#define SNIC_DESC_LOGGING 0x10
+
+#define SNIC_CHECK_LOGGING(LEVEL, CMD) \
+do { \
+ if (unlikely(snic_log_level & LEVEL)) \
+ do { \
+ CMD; \
+ } while (0); \
+} while (0)
+
+#define SNIC_MAIN_DBG(host, fmt, args...) \
+ SNIC_CHECK_LOGGING(SNIC_MAIN_LOGGING, \
+ shost_printk(KERN_INFO, host, fmt, ## args);)
+
+#define SNIC_SCSI_DBG(host, fmt, args...) \
+ SNIC_CHECK_LOGGING(SNIC_SCSI_LOGGING, \
+ shost_printk(KERN_INFO, host, fmt, ##args);)
+
+#define SNIC_DISC_DBG(host, fmt, args...) \
+ SNIC_CHECK_LOGGING(SNIC_SCSI_LOGGING, \
+ shost_printk(KERN_INFO, host, fmt, ##args);)
+
+#define SNIC_ISR_DBG(host, fmt, args...) \
+ SNIC_CHECK_LOGGING(SNIC_ISR_LOGGING, \
+ shost_printk(KERN_INFO, host, fmt, ##args);)
+
+#define SNIC_HOST_ERR(host, fmt, args...) \
+ shost_printk(KERN_ERR, host, fmt, ##args)
+
+#define SNIC_HOST_INFO(host, fmt, args...) \
+ shost_printk(KERN_INFO, host, fmt, ##args)
+
+#define SNIC_INFO(fmt, args...) \
+ pr_info(PFX fmt, ## args)
+
+#define SNIC_DBG(fmt, args...) \
+ pr_info(PFX fmt, ## args)
+
+#define SNIC_ERR(fmt, args...) \
+ pr_err(PFX fmt, ## args)
+
+#ifdef DEBUG
+#define SNIC_BUG_ON(EXPR) \
+ ({ \
+ if (EXPR) { \
+ SNIC_ERR("SNIC BUG(%s)\n", #EXPR); \
+ BUG_ON(EXPR); \
+ } \
+ })
+#else
+#define SNIC_BUG_ON(EXPR) \
+ ({ \
+ if (EXPR) { \
+ SNIC_ERR("SNIC BUG(%s) at %s : %d\n", \
+ #EXPR, __func__, __LINE__); \
+ WARN_ON_ONCE(EXPR); \
+ } \
+ })
+#endif
+
+/* Soft assert */
+#define SNIC_ASSERT_NOT_IMPL(EXPR) \
+ ({ \
+ if (EXPR) {\
+ SNIC_INFO("Functionality not impl'ed at %s:%d\n", \
+ __func__, __LINE__); \
+ WARN_ON_ONCE(EXPR); \
+ } \
+ })
+
+
+extern const char *snic_state_str[];
+
+enum snic_intx_intr_index {
+ SNIC_INTX_WQ_RQ_COPYWQ,
+ SNIC_INTX_ERR,
+ SNIC_INTX_NOTIFY,
+ SNIC_INTX_INTR_MAX,
+};
+
+enum snic_msix_intr_index {
+ SNIC_MSIX_WQ,
+ SNIC_MSIX_IO_CMPL,
+ SNIC_MSIX_ERR_NOTIFY,
+ SNIC_MSIX_INTR_MAX,
+};
+
+struct snic_msix_entry {
+ int requested;
+ char devname[IFNAMSIZ];
+ irqreturn_t (*isr)(int, void *);
+ void *devid;
+};
+
+enum snic_state {
+ SNIC_INIT = 0,
+ SNIC_ERROR,
+ SNIC_ONLINE,
+ SNIC_OFFLINE,
+ SNIC_FWRESET,
+};
+
+#define SNIC_WQ_MAX 1
+#define SNIC_CQ_IO_CMPL_MAX 1
+#define SNIC_CQ_MAX (SNIC_WQ_MAX + SNIC_CQ_IO_CMPL_MAX)
+
+/* firmware version information */
+struct snic_fw_info {
+ u32 fw_ver;
+ u32 hid; /* u16 hid | u16 vnic id */
+ u32 max_concur_ios; /* max concurrent ios */
+ u32 max_sgs_per_cmd; /* max sgls per IO */
+ u32 max_io_sz; /* max io size supported */
+ u32 hba_cap; /* hba capabilities */
+ u32 max_tgts; /* max tgts supported */
+ u16 io_tmo; /* FW Extended timeout */
+ struct completion *wait; /* protected by snic lock*/
+};
+
+/*
+ * snic_work item : defined to process asynchronous events
+ */
+struct snic_work {
+ struct work_struct work;
+ u16 ev_id;
+ u64 *ev_data;
+};
+
+/*
+ * snic structure to represent SCSI vNIC
+ */
+struct snic {
+ /* snic specific members */
+ struct list_head list;
+ char name[IFNAMSIZ];
+ atomic_t state;
+ spinlock_t snic_lock;
+ struct completion *remove_wait;
+ bool in_remove;
+ bool stop_link_events; /* stop processing link events */
+
+ /* discovery related */
+ struct snic_disc disc;
+
+ /* Scsi Host info */
+ struct Scsi_Host *shost;
+
+ /* vnic related structures */
+ struct vnic_dev_bar bar0;
+
+ struct vnic_stats *stats;
+ unsigned long stats_time;
+ unsigned long stats_reset_time;
+
+ struct vnic_dev *vdev;
+
+ /* hw resource info */
+ unsigned int wq_count;
+ unsigned int cq_count;
+ unsigned int intr_count;
+ unsigned int err_intr_offset;
+
+ int link_status; /* retrieved from svnic_dev_link_status() */
+ u32 link_down_cnt;
+
+ /* pci related */
+ struct pci_dev *pdev;
+ struct msix_entry msix_entry[SNIC_MSIX_INTR_MAX];
+ struct snic_msix_entry msix[SNIC_MSIX_INTR_MAX];
+
+ /* io related info */
+ mempool_t *req_pool[SNIC_REQ_MAX_CACHES]; /* (??) */
+ ____cacheline_aligned spinlock_t io_req_lock[SNIC_IO_LOCKS];
+
+ /* Maintain snic specific commands, cmds with no tag in spl_cmd_list */
+ ____cacheline_aligned spinlock_t spl_cmd_lock;
+ struct list_head spl_cmd_list;
+
+ unsigned int max_tag_id;
+ atomic_t ios_inflight; /* io in flight counter */
+
+ struct vnic_snic_config config;
+
+ struct work_struct link_work;
+
+ /* firmware information */
+ struct snic_fw_info fwinfo;
+
+ /* Work for processing Target related work */
+ struct work_struct tgt_work;
+
+ /* Work for processing Discovery */
+ struct work_struct disc_work;
+
+ /* stats related */
+ unsigned int reset_stats;
+ atomic64_t io_cmpl_skip;
+ struct snic_stats s_stats; /* Per SNIC driver stats */
+
+ /* platform specific */
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+ struct dentry *stats_host; /* Per snic debugfs root */
+ struct dentry *stats_file; /* Per snic debugfs file */
+ struct dentry *reset_stats_file;/* Per snic reset stats file */
+#endif
+
+ /* completion queue cache line section */
+ ____cacheline_aligned struct vnic_cq cq[SNIC_CQ_MAX];
+
+ /* work queue cache line section */
+ ____cacheline_aligned struct vnic_wq wq[SNIC_WQ_MAX];
+ spinlock_t wq_lock[SNIC_WQ_MAX];
+
+ /* interrupt resource cache line section */
+ ____cacheline_aligned struct vnic_intr intr[SNIC_MSIX_INTR_MAX];
+}; /* end of snic structure */
+
+/*
+ * SNIC Driver's Global Data
+ */
+struct snic_global {
+ struct list_head snic_list;
+ spinlock_t snic_list_lock;
+
+ struct kmem_cache *req_cache[SNIC_REQ_MAX_CACHES];
+
+ struct workqueue_struct *event_q;
+
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+ /* debugfs related global data */
+ struct dentry *trc_root;
+ struct dentry *stats_root;
+
+ struct snic_trc trc ____cacheline_aligned;
+#endif
+};
+
+extern struct snic_global *snic_glob;
+
+int snic_glob_init(void);
+void snic_glob_cleanup(void);
+
+extern struct workqueue_struct *snic_event_queue;
+extern struct device_attribute *snic_attrs[];
+
+int snic_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
+int snic_abort_cmd(struct scsi_cmnd *);
+int snic_device_reset(struct scsi_cmnd *);
+int snic_host_reset(struct scsi_cmnd *);
+int snic_reset(struct Scsi_Host *, struct scsi_cmnd *);
+void snic_shutdown_scsi_cleanup(struct snic *);
+
+
+int snic_request_intr(struct snic *);
+void snic_free_intr(struct snic *);
+int snic_set_intr_mode(struct snic *);
+void snic_clear_intr_mode(struct snic *);
+
+int snic_fwcq_cmpl_handler(struct snic *, int);
+int snic_wq_cmpl_handler(struct snic *, int);
+void snic_free_wq_buf(struct vnic_wq *, struct vnic_wq_buf *);
+
+
+void snic_log_q_error(struct snic *);
+void snic_handle_link_event(struct snic *);
+void snic_handle_link(struct work_struct *);
+
+int snic_queue_exch_ver_req(struct snic *);
+int snic_io_exch_ver_cmpl_handler(struct snic *, struct snic_fw_req *);
+
+int snic_queue_wq_desc(struct snic *, void *os_buf, u16 len);
+
+void snic_handle_untagged_req(struct snic *, struct snic_req_info *);
+void snic_release_untagged_req(struct snic *, struct snic_req_info *);
+void snic_free_all_untagged_reqs(struct snic *);
+int snic_get_conf(struct snic *);
+void snic_set_state(struct snic *, enum snic_state);
+int snic_get_state(struct snic *);
+const char *snic_state_to_str(unsigned int);
+void snic_hex_dump(char *, char *, int);
+void snic_print_desc(const char *fn, char *os_buf, int len);
+const char *show_opcode_name(int val);
+#endif /* _SNIC_H */
diff --git a/drivers/scsi/snic/snic_attrs.c b/drivers/scsi/snic/snic_attrs.c
new file mode 100644
index 000000000000..32d5d556b6f8
--- /dev/null
+++ b/drivers/scsi/snic/snic_attrs.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/string.h>
+#include <linux/device.h>
+
+#include "snic.h"
+
+static ssize_t
+snic_show_sym_name(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snic *snic = shost_priv(class_to_shost(dev));
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", snic->name);
+}
+
+static ssize_t
+snic_show_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snic *snic = shost_priv(class_to_shost(dev));
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ snic_state_str[snic_get_state(snic)]);
+}
+
+static ssize_t
+snic_show_drv_version(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", SNIC_DRV_VERSION);
+}
+
+static ssize_t
+snic_show_link_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct snic *snic = shost_priv(class_to_shost(dev));
+
+ if (snic->config.xpt_type == SNIC_DAS)
+ snic->link_status = svnic_dev_link_status(snic->vdev);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ (snic->link_status) ? "Link Up" : "Link Down");
+}
+
+static DEVICE_ATTR(snic_sym_name, S_IRUGO, snic_show_sym_name, NULL);
+static DEVICE_ATTR(snic_state, S_IRUGO, snic_show_state, NULL);
+static DEVICE_ATTR(drv_version, S_IRUGO, snic_show_drv_version, NULL);
+static DEVICE_ATTR(link_state, S_IRUGO, snic_show_link_state, NULL);
+
+struct device_attribute *snic_attrs[] = {
+ &dev_attr_snic_sym_name,
+ &dev_attr_snic_state,
+ &dev_attr_drv_version,
+ &dev_attr_link_state,
+ NULL,
+};
diff --git a/drivers/scsi/snic/snic_ctl.c b/drivers/scsi/snic/snic_ctl.c
new file mode 100644
index 000000000000..aebe75320ed3
--- /dev/null
+++ b/drivers/scsi/snic/snic_ctl.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/mempool.h>
+#include <scsi/scsi_tcq.h>
+#include <linux/ctype.h>
+
+#include "snic_io.h"
+#include "snic.h"
+#include "cq_enet_desc.h"
+#include "snic_fwint.h"
+
+/*
+ * snic_handle_link : Handles link flaps.
+ */
+void
+snic_handle_link(struct work_struct *work)
+{
+ struct snic *snic = container_of(work, struct snic, link_work);
+
+ if (snic->config.xpt_type != SNIC_DAS) {
+ SNIC_HOST_INFO(snic->shost, "Link Event Received.\n");
+ SNIC_ASSERT_NOT_IMPL(1);
+
+ return;
+ }
+
+ snic->link_status = svnic_dev_link_status(snic->vdev);
+ snic->link_down_cnt = svnic_dev_link_down_cnt(snic->vdev);
+ SNIC_HOST_INFO(snic->shost, "Link Event: Link %s.\n",
+ ((snic->link_status) ? "Up" : "Down"));
+}
+
+
+/*
+ * snic_ver_enc : Encodes version str to int
+ * version string is similar to netmask string
+ */
+static int
+snic_ver_enc(const char *s)
+{
+ int v[4] = {0};
+ int i = 0, x = 0;
+ char c;
+ const char *p = s;
+
+ /* validate version string */
+ if ((strlen(s) > 15) || (strlen(s) < 7))
+ goto end;
+
+ while ((c = *p++)) {
+ if (c == '.') {
+ i++;
+ continue;
+ }
+
+ if (i > 4 || !isdigit(c))
+ goto end;
+
+ v[i] = v[i] * 10 + (c - '0');
+ }
+
+ /* validate sub version numbers */
+ for (i = 3; i >= 0; i--)
+ if (v[i] > 0xff)
+ goto end;
+
+ x |= (v[0] << 24) | v[1] << 16 | v[2] << 8 | v[3];
+
+end:
+ if (x == 0) {
+ SNIC_ERR("Invalid version string [%s].\n", s);
+
+ return -1;
+ }
+
+ return x;
+} /* end of snic_ver_enc */
+
+/*
+ * snic_qeueue_exch_ver_req :
+ *
+ * Queues Exchange Version Request, to communicate host information
+ * in return, it gets firmware version details
+ */
+int
+snic_queue_exch_ver_req(struct snic *snic)
+{
+ struct snic_req_info *rqi = NULL;
+ struct snic_host_req *req = NULL;
+ u32 ver = 0;
+ int ret = 0;
+
+ SNIC_HOST_INFO(snic->shost, "Exch Ver Req Preparing...\n");
+
+ rqi = snic_req_init(snic, 0);
+ if (!rqi) {
+ SNIC_HOST_ERR(snic->shost,
+ "Queuing Exch Ver Req failed, err = %d\n",
+ ret);
+
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ req = rqi_to_req(rqi);
+
+ /* Initialize snic_host_req */
+ snic_io_hdr_enc(&req->hdr, SNIC_REQ_EXCH_VER, 0, SCSI_NO_TAG,
+ snic->config.hid, 0, (ulong)rqi);
+ ver = snic_ver_enc(SNIC_DRV_VERSION);
+ req->u.exch_ver.drvr_ver = cpu_to_le32(ver);
+ req->u.exch_ver.os_type = cpu_to_le32(SNIC_OS_LINUX);
+
+ snic_handle_untagged_req(snic, rqi);
+
+ ret = snic_queue_wq_desc(snic, req, sizeof(*req));
+ if (ret) {
+ snic_release_untagged_req(snic, rqi);
+ SNIC_HOST_ERR(snic->shost,
+ "Queuing Exch Ver Req failed, err = %d\n",
+ ret);
+ goto error;
+ }
+
+ SNIC_HOST_INFO(snic->shost, "Exch Ver Req is issued. ret = %d\n", ret);
+
+error:
+ return ret;
+} /* end of snic_queue_exch_ver_req */
+
+/*
+ * snic_io_exch_ver_cmpl_handler
+ */
+int
+snic_io_exch_ver_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+ struct snic_req_info *rqi = NULL;
+ struct snic_exch_ver_rsp *exv_cmpl = &fwreq->u.exch_ver_cmpl;
+ u8 typ, hdr_stat;
+ u32 cmnd_id, hid, max_sgs;
+ ulong ctx = 0;
+ unsigned long flags;
+ int ret = 0;
+
+ SNIC_HOST_INFO(snic->shost, "Exch Ver Compl Received.\n");
+ snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx);
+ SNIC_BUG_ON(snic->config.hid != hid);
+ rqi = (struct snic_req_info *) ctx;
+
+ if (hdr_stat) {
+ SNIC_HOST_ERR(snic->shost,
+ "Exch Ver Completed w/ err status %d\n",
+ hdr_stat);
+
+ goto exch_cmpl_end;
+ }
+
+ spin_lock_irqsave(&snic->snic_lock, flags);
+ snic->fwinfo.fw_ver = le32_to_cpu(exv_cmpl->version);
+ snic->fwinfo.hid = le32_to_cpu(exv_cmpl->hid);
+ snic->fwinfo.max_concur_ios = le32_to_cpu(exv_cmpl->max_concur_ios);
+ snic->fwinfo.max_sgs_per_cmd = le32_to_cpu(exv_cmpl->max_sgs_per_cmd);
+ snic->fwinfo.max_io_sz = le32_to_cpu(exv_cmpl->max_io_sz);
+ snic->fwinfo.max_tgts = le32_to_cpu(exv_cmpl->max_tgts);
+ snic->fwinfo.io_tmo = le16_to_cpu(exv_cmpl->io_timeout);
+
+ SNIC_HOST_INFO(snic->shost,
+ "vers %u hid %u max_concur_ios %u max_sgs_per_cmd %u max_io_sz %u max_tgts %u fw tmo %u\n",
+ snic->fwinfo.fw_ver,
+ snic->fwinfo.hid,
+ snic->fwinfo.max_concur_ios,
+ snic->fwinfo.max_sgs_per_cmd,
+ snic->fwinfo.max_io_sz,
+ snic->fwinfo.max_tgts,
+ snic->fwinfo.io_tmo);
+
+ SNIC_HOST_INFO(snic->shost,
+ "HBA Capabilities = 0x%x\n",
+ le32_to_cpu(exv_cmpl->hba_cap));
+
+ /* Updating SGList size */
+ max_sgs = snic->fwinfo.max_sgs_per_cmd;
+ if (max_sgs && max_sgs < SNIC_MAX_SG_DESC_CNT) {
+ snic->shost->sg_tablesize = max_sgs;
+ SNIC_HOST_INFO(snic->shost, "Max SGs set to %d\n",
+ snic->shost->sg_tablesize);
+ } else if (max_sgs > snic->shost->sg_tablesize) {
+ SNIC_HOST_INFO(snic->shost,
+ "Target type %d Supports Larger Max SGList %d than driver's Max SG List %d.\n",
+ snic->config.xpt_type, max_sgs,
+ snic->shost->sg_tablesize);
+ }
+
+ if (snic->shost->can_queue > snic->fwinfo.max_concur_ios)
+ snic->shost->can_queue = snic->fwinfo.max_concur_ios;
+
+ snic->shost->max_sectors = snic->fwinfo.max_io_sz >> 9;
+ if (snic->fwinfo.wait)
+ complete(snic->fwinfo.wait);
+
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+exch_cmpl_end:
+ snic_release_untagged_req(snic, rqi);
+
+ SNIC_HOST_INFO(snic->shost, "Exch_cmpl Done, hdr_stat %d.\n", hdr_stat);
+
+ return ret;
+} /* end of snic_io_exch_ver_cmpl_handler */
+
+/*
+ * snic_get_conf
+ *
+ * Synchronous call, and Retrieves snic params.
+ */
+int
+snic_get_conf(struct snic *snic)
+{
+ DECLARE_COMPLETION_ONSTACK(wait);
+ unsigned long flags;
+ int ret;
+ int nr_retries = 3;
+
+ SNIC_HOST_INFO(snic->shost, "Retrieving snic params.\n");
+ spin_lock_irqsave(&snic->snic_lock, flags);
+ memset(&snic->fwinfo, 0, sizeof(snic->fwinfo));
+ snic->fwinfo.wait = &wait;
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+ /* Additional delay to handle HW Resource initialization. */
+ msleep(50);
+
+ /*
+ * Exch ver req can be ignored by FW, if HW Resource initialization
+ * is in progress, Hence retry.
+ */
+ do {
+ ret = snic_queue_exch_ver_req(snic);
+ if (ret)
+ return ret;
+
+ wait_for_completion_timeout(&wait, msecs_to_jiffies(2000));
+ spin_lock_irqsave(&snic->snic_lock, flags);
+ ret = (snic->fwinfo.fw_ver != 0) ? 0 : -ETIMEDOUT;
+ if (ret)
+ SNIC_HOST_ERR(snic->shost,
+ "Failed to retrieve snic params,\n");
+
+ /* Unset fwinfo.wait, on success or on last retry */
+ if (ret == 0 || nr_retries == 1)
+ snic->fwinfo.wait = NULL;
+
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+ } while (ret && --nr_retries);
+
+ return ret;
+} /* end of snic_get_info */
diff --git a/drivers/scsi/snic/snic_debugfs.c b/drivers/scsi/snic/snic_debugfs.c
new file mode 100644
index 000000000000..1686f0196251
--- /dev/null
+++ b/drivers/scsi/snic/snic_debugfs.c
@@ -0,0 +1,560 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/debugfs.h>
+
+#include "snic.h"
+
+/*
+ * snic_debugfs_init - Initialize debugfs for snic debug logging
+ *
+ * Description:
+ * When Debugfs is configured this routine sets up fnic debugfs
+ * filesystem. If not already created. this routine will crate the
+ * fnic directory and statistics directory for trace buffer and
+ * stats logging
+ */
+
+int
+snic_debugfs_init(void)
+{
+ int rc = -1;
+ struct dentry *de = NULL;
+
+ de = debugfs_create_dir("snic", NULL);
+ if (!de) {
+ SNIC_DBG("Cannot create debugfs root\n");
+
+ return rc;
+ }
+ snic_glob->trc_root = de;
+
+ de = debugfs_create_dir("statistics", snic_glob->trc_root);
+ if (!de) {
+ SNIC_DBG("Cannot create Statistics directory\n");
+
+ return rc;
+ }
+ snic_glob->stats_root = de;
+
+ rc = 0;
+
+ return rc;
+} /* end of snic_debugfs_init */
+
+/*
+ * snic_debugfs_term - Tear down debugfs intrastructure
+ *
+ * Description:
+ * When Debufs is configured this routine removes debugfs file system
+ * elements that are specific to snic
+ */
+void
+snic_debugfs_term(void)
+{
+ debugfs_remove(snic_glob->stats_root);
+ snic_glob->stats_root = NULL;
+
+ debugfs_remove(snic_glob->trc_root);
+ snic_glob->trc_root = NULL;
+}
+
+/*
+ * snic_reset_stats_open - Open the reset_stats file
+ */
+static int
+snic_reset_stats_open(struct inode *inode, struct file *filp)
+{
+ SNIC_BUG_ON(!inode->i_private);
+ filp->private_data = inode->i_private;
+
+ return 0;
+}
+
+/*
+ * snic_reset_stats_read - Read a reset_stats debugfs file
+ * @filp: The file pointer to read from.
+ * @ubuf: The buffer tocopy the data to.
+ * @cnt: The number of bytes to read.
+ * @ppos: The position in the file to start reading frm.
+ *
+ * Description:
+ * This routine reads value of variable reset_stats
+ * and stores into local @buf. It will start reading file @ppos and
+ * copy up to @cnt of data to @ubuf from @buf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read.
+ */
+static ssize_t
+snic_reset_stats_read(struct file *filp,
+ char __user *ubuf,
+ size_t cnt,
+ loff_t *ppos)
+{
+ struct snic *snic = (struct snic *) filp->private_data;
+ char buf[64];
+ int len;
+
+ len = sprintf(buf, "%u\n", snic->reset_stats);
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
+}
+
+/*
+ * snic_reset_stats_write - Write to reset_stats debugfs file
+ * @filp: The file pointer to write from
+ * @ubuf: The buffer to copy the data from.
+ * @cnt: The number of bytes to write.
+ * @ppos: The position in the file to start writing to.
+ *
+ * Description:
+ * This routine writes data from user buffer @ubuf to buffer @buf and
+ * resets cumulative stats of snic.
+ *
+ * Returns:
+ * This function returns the amount of data that was written.
+ */
+static ssize_t
+snic_reset_stats_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt,
+ loff_t *ppos)
+{
+ struct snic *snic = (struct snic *) filp->private_data;
+ struct snic_stats *stats = &snic->s_stats;
+ u64 *io_stats_p = (u64 *) &stats->io;
+ u64 *fw_stats_p = (u64 *) &stats->fw;
+ char buf[64];
+ unsigned long val;
+ int ret;
+
+ if (cnt >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = '\0';
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ snic->reset_stats = val;
+
+ if (snic->reset_stats) {
+ /* Skip variable is used to avoid descrepancies to Num IOs
+ * and IO Completions stats. Skip incrementing No IO Compls
+ * for pending active IOs after reset_stats
+ */
+ atomic64_set(&snic->io_cmpl_skip,
+ atomic64_read(&stats->io.active));
+ memset(&stats->abts, 0, sizeof(struct snic_abort_stats));
+ memset(&stats->reset, 0, sizeof(struct snic_reset_stats));
+ memset(&stats->misc, 0, sizeof(struct snic_misc_stats));
+ memset(io_stats_p+1,
+ 0,
+ sizeof(struct snic_io_stats) - sizeof(u64));
+ memset(fw_stats_p+1,
+ 0,
+ sizeof(struct snic_fw_stats) - sizeof(u64));
+ }
+
+ (*ppos)++;
+
+ SNIC_HOST_INFO(snic->shost, "Reset Op: Driver statistics.\n");
+
+ return cnt;
+}
+
+static int
+snic_reset_stats_release(struct inode *inode, struct file *filp)
+{
+ filp->private_data = NULL;
+
+ return 0;
+}
+
+/*
+ * snic_stats_show - Formats and prints per host specific driver stats.
+ */
+static int
+snic_stats_show(struct seq_file *sfp, void *data)
+{
+ struct snic *snic = (struct snic *) sfp->private;
+ struct snic_stats *stats = &snic->s_stats;
+ struct timespec last_isr_tms, last_ack_tms;
+ u64 maxio_tm;
+ int i;
+
+ /* Dump IO Stats */
+ seq_printf(sfp,
+ "------------------------------------------\n"
+ "\t\t IO Statistics\n"
+ "------------------------------------------\n");
+
+ maxio_tm = (u64) atomic64_read(&stats->io.max_time);
+ seq_printf(sfp,
+ "Active IOs : %lld\n"
+ "Max Active IOs : %lld\n"
+ "Total IOs : %lld\n"
+ "IOs Completed : %lld\n"
+ "IOs Failed : %lld\n"
+ "IOs Not Found : %lld\n"
+ "Memory Alloc Failures : %lld\n"
+ "REQs Null : %lld\n"
+ "SCSI Cmd Pointers Null : %lld\n"
+ "Max SGL for any IO : %lld\n"
+ "Max IO Size : %lld Sectors\n"
+ "Max Queuing Time : %lld\n"
+ "Max Completion Time : %lld\n"
+ "Max IO Process Time(FW) : %lld (%u msec)\n",
+ (u64) atomic64_read(&stats->io.active),
+ (u64) atomic64_read(&stats->io.max_active),
+ (u64) atomic64_read(&stats->io.num_ios),
+ (u64) atomic64_read(&stats->io.compl),
+ (u64) atomic64_read(&stats->io.fail),
+ (u64) atomic64_read(&stats->io.io_not_found),
+ (u64) atomic64_read(&stats->io.alloc_fail),
+ (u64) atomic64_read(&stats->io.req_null),
+ (u64) atomic64_read(&stats->io.sc_null),
+ (u64) atomic64_read(&stats->io.max_sgl),
+ (u64) atomic64_read(&stats->io.max_io_sz),
+ (u64) atomic64_read(&stats->io.max_qtime),
+ (u64) atomic64_read(&stats->io.max_cmpl_time),
+ maxio_tm,
+ jiffies_to_msecs(maxio_tm));
+
+ seq_puts(sfp, "\nSGL Counters\n");
+
+ for (i = 0; i < SNIC_MAX_SG_DESC_CNT; i++) {
+ seq_printf(sfp,
+ "%10lld ",
+ (u64) atomic64_read(&stats->io.sgl_cnt[i]));
+
+ if ((i + 1) % 8 == 0)
+ seq_puts(sfp, "\n");
+ }
+
+ /* Dump Abort Stats */
+ seq_printf(sfp,
+ "\n-------------------------------------------\n"
+ "\t\t Abort Statistics\n"
+ "---------------------------------------------\n");
+
+ seq_printf(sfp,
+ "Aborts : %lld\n"
+ "Aborts Fail : %lld\n"
+ "Aborts Driver Timeout : %lld\n"
+ "Abort FW Timeout : %lld\n"
+ "Abort IO NOT Found : %lld\n",
+ (u64) atomic64_read(&stats->abts.num),
+ (u64) atomic64_read(&stats->abts.fail),
+ (u64) atomic64_read(&stats->abts.drv_tmo),
+ (u64) atomic64_read(&stats->abts.fw_tmo),
+ (u64) atomic64_read(&stats->abts.io_not_found));
+
+ /* Dump Reset Stats */
+ seq_printf(sfp,
+ "\n-------------------------------------------\n"
+ "\t\t Reset Statistics\n"
+ "---------------------------------------------\n");
+
+ seq_printf(sfp,
+ "HBA Resets : %lld\n"
+ "HBA Reset Cmpls : %lld\n"
+ "HBA Reset Fail : %lld\n",
+ (u64) atomic64_read(&stats->reset.hba_resets),
+ (u64) atomic64_read(&stats->reset.hba_reset_cmpl),
+ (u64) atomic64_read(&stats->reset.hba_reset_fail));
+
+ /* Dump Firmware Stats */
+ seq_printf(sfp,
+ "\n-------------------------------------------\n"
+ "\t\t Firmware Statistics\n"
+ "---------------------------------------------\n");
+
+ seq_printf(sfp,
+ "Active FW Requests : %lld\n"
+ "Max FW Requests : %lld\n"
+ "FW Out Of Resource Errs : %lld\n"
+ "FW IO Errors : %lld\n"
+ "FW SCSI Errors : %lld\n",
+ (u64) atomic64_read(&stats->fw.actv_reqs),
+ (u64) atomic64_read(&stats->fw.max_actv_reqs),
+ (u64) atomic64_read(&stats->fw.out_of_res),
+ (u64) atomic64_read(&stats->fw.io_errs),
+ (u64) atomic64_read(&stats->fw.scsi_errs));
+
+
+ /* Dump Miscellenous Stats */
+ seq_printf(sfp,
+ "\n---------------------------------------------\n"
+ "\t\t Other Statistics\n"
+ "\n---------------------------------------------\n");
+
+ jiffies_to_timespec(stats->misc.last_isr_time, &last_isr_tms);
+ jiffies_to_timespec(stats->misc.last_ack_time, &last_ack_tms);
+
+ seq_printf(sfp,
+ "Last ISR Time : %llu (%8lu.%8lu)\n"
+ "Last Ack Time : %llu (%8lu.%8lu)\n"
+ "ISRs : %llu\n"
+ "Max CQ Entries : %lld\n"
+ "Data Count Mismatch : %lld\n"
+ "IOs w/ Timeout Status : %lld\n"
+ "IOs w/ Aborted Status : %lld\n"
+ "IOs w/ SGL Invalid Stat : %lld\n"
+ "WQ Desc Alloc Fail : %lld\n"
+ "Queue Full : %lld\n"
+ "Target Not Ready : %lld\n",
+ (u64) stats->misc.last_isr_time,
+ last_isr_tms.tv_sec, last_isr_tms.tv_nsec,
+ (u64)stats->misc.last_ack_time,
+ last_ack_tms.tv_sec, last_ack_tms.tv_nsec,
+ (u64) atomic64_read(&stats->misc.isr_cnt),
+ (u64) atomic64_read(&stats->misc.max_cq_ents),
+ (u64) atomic64_read(&stats->misc.data_cnt_mismat),
+ (u64) atomic64_read(&stats->misc.io_tmo),
+ (u64) atomic64_read(&stats->misc.io_aborted),
+ (u64) atomic64_read(&stats->misc.sgl_inval),
+ (u64) atomic64_read(&stats->misc.wq_alloc_fail),
+ (u64) atomic64_read(&stats->misc.qfull),
+ (u64) atomic64_read(&stats->misc.tgt_not_rdy));
+
+ return 0;
+}
+
+/*
+ * snic_stats_open - Open the stats file for specific host
+ *
+ * Description:
+ * This routine opens a debugfs file stats of specific host
+ */
+static int
+snic_stats_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, snic_stats_show, inode->i_private);
+}
+
+static const struct file_operations snic_stats_fops = {
+ .owner = THIS_MODULE,
+ .open = snic_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations snic_reset_stats_fops = {
+ .owner = THIS_MODULE,
+ .open = snic_reset_stats_open,
+ .read = snic_reset_stats_read,
+ .write = snic_reset_stats_write,
+ .release = snic_reset_stats_release,
+};
+
+/*
+ * snic_stats_init - Initialize stats struct and create stats file
+ * per snic
+ *
+ * Description:
+ * When debugfs is cofigured this routine sets up the stats file per snic
+ * It will create file stats and reset_stats under statistics/host# directory
+ * to log per snic stats
+ */
+int
+snic_stats_debugfs_init(struct snic *snic)
+{
+ int rc = -1;
+ char name[16];
+ struct dentry *de = NULL;
+
+ snprintf(name, sizeof(name), "host%d", snic->shost->host_no);
+ if (!snic_glob->stats_root) {
+ SNIC_DBG("snic_stats root doesn't exist\n");
+
+ return rc;
+ }
+
+ de = debugfs_create_dir(name, snic_glob->stats_root);
+ if (!de) {
+ SNIC_DBG("Cannot create host directory\n");
+
+ return rc;
+ }
+ snic->stats_host = de;
+
+ de = debugfs_create_file("stats",
+ S_IFREG|S_IRUGO,
+ snic->stats_host,
+ snic,
+ &snic_stats_fops);
+ if (!de) {
+ SNIC_DBG("Cannot create host's stats file\n");
+
+ return rc;
+ }
+ snic->stats_file = de;
+
+ de = debugfs_create_file("reset_stats",
+ S_IFREG|S_IRUGO|S_IWUSR,
+ snic->stats_host,
+ snic,
+ &snic_reset_stats_fops);
+
+ if (!de) {
+ SNIC_DBG("Cannot create host's reset_stats file\n");
+
+ return rc;
+ }
+ snic->reset_stats_file = de;
+ rc = 0;
+
+ return rc;
+} /* end of snic_stats_debugfs_init */
+
+/*
+ * snic_stats_debugfs_remove - Tear down debugfs infrastructure of stats
+ *
+ * Description:
+ * When Debufs is configured this routine removes debugfs file system
+ * elements that are specific to to snic stats
+ */
+void
+snic_stats_debugfs_remove(struct snic *snic)
+{
+ debugfs_remove(snic->stats_file);
+ snic->stats_file = NULL;
+
+ debugfs_remove(snic->reset_stats_file);
+ snic->reset_stats_file = NULL;
+
+ debugfs_remove(snic->stats_host);
+ snic->stats_host = NULL;
+}
+
+/* Trace Facility related API */
+static void *
+snic_trc_seq_start(struct seq_file *sfp, loff_t *pos)
+{
+ return &snic_glob->trc;
+}
+
+static void *
+snic_trc_seq_next(struct seq_file *sfp, void *data, loff_t *pos)
+{
+ return NULL;
+}
+
+static void
+snic_trc_seq_stop(struct seq_file *sfp, void *data)
+{
+}
+
+#define SNIC_TRC_PBLEN 256
+static int
+snic_trc_seq_show(struct seq_file *sfp, void *data)
+{
+ char buf[SNIC_TRC_PBLEN];
+
+ if (snic_get_trc_data(buf, SNIC_TRC_PBLEN) > 0)
+ seq_printf(sfp, "%s\n", buf);
+
+ return 0;
+}
+
+static const struct seq_operations snic_trc_seq_ops = {
+ .start = snic_trc_seq_start,
+ .next = snic_trc_seq_next,
+ .stop = snic_trc_seq_stop,
+ .show = snic_trc_seq_show,
+};
+
+static int
+snic_trc_open(struct inode *inode, struct file *filp)
+{
+ return seq_open(filp, &snic_trc_seq_ops);
+}
+
+static const struct file_operations snic_trc_fops = {
+ .owner = THIS_MODULE,
+ .open = snic_trc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/*
+ * snic_trc_debugfs_init : creates trace/tracing_enable files for trace
+ * under debugfs
+ */
+int
+snic_trc_debugfs_init(void)
+{
+ struct dentry *de = NULL;
+ int ret = -1;
+
+ if (!snic_glob->trc_root) {
+ SNIC_ERR("Debugfs root directory for snic doesn't exist.\n");
+
+ return ret;
+ }
+
+ de = debugfs_create_bool("tracing_enable",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ snic_glob->trc_root,
+ &snic_glob->trc.enable);
+
+ if (!de) {
+ SNIC_ERR("Can't create trace_enable file.\n");
+
+ return ret;
+ }
+ snic_glob->trc.trc_enable = de;
+
+ de = debugfs_create_file("trace",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ snic_glob->trc_root,
+ NULL,
+ &snic_trc_fops);
+
+ if (!de) {
+ SNIC_ERR("Cann't create trace file.\n");
+
+ return ret;
+ }
+ snic_glob->trc.trc_file = de;
+ ret = 0;
+
+ return ret;
+} /* end of snic_trc_debugfs_init */
+
+/*
+ * snic_trc_debugfs_term : cleans up the files created for trace under debugfs
+ */
+void
+snic_trc_debugfs_term(void)
+{
+ debugfs_remove(snic_glob->trc.trc_file);
+ snic_glob->trc.trc_file = NULL;
+
+ debugfs_remove(snic_glob->trc.trc_enable);
+ snic_glob->trc.trc_enable = NULL;
+}
diff --git a/drivers/scsi/snic/snic_disc.c b/drivers/scsi/snic/snic_disc.c
new file mode 100644
index 000000000000..5f6321759ad9
--- /dev/null
+++ b/drivers/scsi/snic/snic_disc.c
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/mempool.h>
+
+#include <scsi/scsi_tcq.h>
+
+#include "snic_disc.h"
+#include "snic.h"
+#include "snic_io.h"
+
+
+/* snic target types */
+static const char * const snic_tgt_type_str[] = {
+ [SNIC_TGT_DAS] = "DAS",
+ [SNIC_TGT_SAN] = "SAN",
+};
+
+static inline const char *
+snic_tgt_type_to_str(int typ)
+{
+ return ((typ > SNIC_TGT_NONE && typ <= SNIC_TGT_SAN) ?
+ snic_tgt_type_str[typ] : "Unknown");
+}
+
+static const char * const snic_tgt_state_str[] = {
+ [SNIC_TGT_STAT_INIT] = "INIT",
+ [SNIC_TGT_STAT_ONLINE] = "ONLINE",
+ [SNIC_TGT_STAT_OFFLINE] = "OFFLINE",
+ [SNIC_TGT_STAT_DEL] = "DELETION IN PROGRESS",
+};
+
+const char *
+snic_tgt_state_to_str(int state)
+{
+ return ((state >= SNIC_TGT_STAT_INIT && state <= SNIC_TGT_STAT_DEL) ?
+ snic_tgt_state_str[state] : "UNKNOWN");
+}
+
+/*
+ * Initiate report_tgt req desc
+ */
+static void
+snic_report_tgt_init(struct snic_host_req *req, u32 hid, u8 *buf, u32 len,
+ dma_addr_t rsp_buf_pa, ulong ctx)
+{
+ struct snic_sg_desc *sgd = NULL;
+
+
+ snic_io_hdr_enc(&req->hdr, SNIC_REQ_REPORT_TGTS, 0, SCSI_NO_TAG, hid,
+ 1, ctx);
+
+ req->u.rpt_tgts.sg_cnt = cpu_to_le16(1);
+ sgd = req_to_sgl(req);
+ sgd[0].addr = cpu_to_le64(rsp_buf_pa);
+ sgd[0].len = cpu_to_le32(len);
+ sgd[0]._resvd = 0;
+ req->u.rpt_tgts.sg_addr = cpu_to_le64((ulong)sgd);
+}
+
+/*
+ * snic_queue_report_tgt_req: Queues report target request.
+ */
+static int
+snic_queue_report_tgt_req(struct snic *snic)
+{
+ struct snic_req_info *rqi = NULL;
+ u32 ntgts, buf_len = 0;
+ u8 *buf = NULL;
+ dma_addr_t pa = 0;
+ int ret = 0;
+
+ rqi = snic_req_init(snic, 1);
+ if (!rqi) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (snic->fwinfo.max_tgts)
+ ntgts = min_t(u32, snic->fwinfo.max_tgts, snic->shost->max_id);
+ else
+ ntgts = snic->shost->max_id;
+
+ /* Allocate Response Buffer */
+ SNIC_BUG_ON(ntgts == 0);
+ buf_len = ntgts * sizeof(struct snic_tgt_id) + SNIC_SG_DESC_ALIGN;
+
+ buf = kzalloc(buf_len, GFP_KERNEL|GFP_DMA);
+ if (!buf) {
+ snic_req_free(snic, rqi);
+ SNIC_HOST_ERR(snic->shost, "Resp Buf Alloc Failed.\n");
+
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ SNIC_BUG_ON((((unsigned long)buf) % SNIC_SG_DESC_ALIGN) != 0);
+
+ pa = pci_map_single(snic->pdev, buf, buf_len, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(snic->pdev, pa)) {
+ kfree(buf);
+ snic_req_free(snic, rqi);
+ SNIC_HOST_ERR(snic->shost,
+ "Rpt-tgt rspbuf %p: PCI DMA Mapping Failed\n",
+ buf);
+ ret = -EINVAL;
+
+ goto error;
+ }
+
+
+ SNIC_BUG_ON(pa == 0);
+ rqi->sge_va = (ulong) buf;
+
+ snic_report_tgt_init(rqi->req,
+ snic->config.hid,
+ buf,
+ buf_len,
+ pa,
+ (ulong)rqi);
+
+ snic_handle_untagged_req(snic, rqi);
+
+ ret = snic_queue_wq_desc(snic, rqi->req, rqi->req_len);
+ if (ret) {
+ pci_unmap_single(snic->pdev, pa, buf_len, PCI_DMA_FROMDEVICE);
+ kfree(buf);
+ rqi->sge_va = 0;
+ snic_release_untagged_req(snic, rqi);
+ SNIC_HOST_ERR(snic->shost, "Queuing Report Tgts Failed.\n");
+
+ goto error;
+ }
+
+ SNIC_DISC_DBG(snic->shost, "Report Targets Issued.\n");
+
+ return ret;
+
+error:
+ SNIC_HOST_ERR(snic->shost,
+ "Queuing Report Targets Failed, err = %d\n",
+ ret);
+ return ret;
+} /* end of snic_queue_report_tgt_req */
+
+/* call into SML */
+static void
+snic_scsi_scan_tgt(struct work_struct *work)
+{
+ struct snic_tgt *tgt = container_of(work, struct snic_tgt, scan_work);
+ struct Scsi_Host *shost = dev_to_shost(&tgt->dev);
+ unsigned long flags;
+
+ SNIC_HOST_INFO(shost, "Scanning Target id 0x%x\n", tgt->id);
+ scsi_scan_target(&tgt->dev,
+ tgt->channel,
+ tgt->scsi_tgt_id,
+ SCAN_WILD_CARD,
+ 1);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ tgt->flags &= ~SNIC_TGT_SCAN_PENDING;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+} /* end of snic_scsi_scan_tgt */
+
+/*
+ * snic_tgt_lookup :
+ */
+static struct snic_tgt *
+snic_tgt_lookup(struct snic *snic, struct snic_tgt_id *tgtid)
+{
+ struct list_head *cur, *nxt;
+ struct snic_tgt *tgt = NULL;
+
+ list_for_each_safe(cur, nxt, &snic->disc.tgt_list) {
+ tgt = list_entry(cur, struct snic_tgt, list);
+ if (tgt->id == le32_to_cpu(tgtid->tgt_id))
+ return tgt;
+ tgt = NULL;
+ }
+
+ return tgt;
+} /* end of snic_tgt_lookup */
+
+/*
+ * snic_tgt_dev_release : Called on dropping last ref for snic_tgt object
+ */
+void
+snic_tgt_dev_release(struct device *dev)
+{
+ struct snic_tgt *tgt = dev_to_tgt(dev);
+
+ SNIC_HOST_INFO(snic_tgt_to_shost(tgt),
+ "Target Device ID %d (%s) Permanently Deleted.\n",
+ tgt->id,
+ dev_name(dev));
+
+ SNIC_BUG_ON(!list_empty(&tgt->list));
+ kfree(tgt);
+}
+
+/*
+ * snic_tgt_del : work function to delete snic_tgt
+ */
+static void
+snic_tgt_del(struct work_struct *work)
+{
+ struct snic_tgt *tgt = container_of(work, struct snic_tgt, del_work);
+ struct Scsi_Host *shost = snic_tgt_to_shost(tgt);
+
+ if (tgt->flags & SNIC_TGT_SCAN_PENDING)
+ scsi_flush_work(shost);
+
+ /* Block IOs on child devices, stops new IOs */
+ scsi_target_block(&tgt->dev);
+
+ /* Cleanup IOs */
+ snic_tgt_scsi_abort_io(tgt);
+
+ /* Unblock IOs now, to flush if there are any. */
+ scsi_target_unblock(&tgt->dev, SDEV_TRANSPORT_OFFLINE);
+
+ /* Delete SCSI Target and sdevs */
+ scsi_remove_target(&tgt->dev); /* ?? */
+ device_del(&tgt->dev);
+ put_device(&tgt->dev);
+} /* end of snic_tgt_del */
+
+/* snic_tgt_create: checks for existence of snic_tgt, if it doesn't
+ * it creates one.
+ */
+static struct snic_tgt *
+snic_tgt_create(struct snic *snic, struct snic_tgt_id *tgtid)
+{
+ struct snic_tgt *tgt = NULL;
+ unsigned long flags;
+ int ret;
+
+ tgt = snic_tgt_lookup(snic, tgtid);
+ if (tgt) {
+ /* update the information if required */
+ return tgt;
+ }
+
+ tgt = kzalloc(sizeof(*tgt), GFP_KERNEL);
+ if (!tgt) {
+ SNIC_HOST_ERR(snic->shost, "Failure to allocate snic_tgt.\n");
+ ret = -ENOMEM;
+
+ return tgt;
+ }
+
+ INIT_LIST_HEAD(&tgt->list);
+ tgt->id = le32_to_cpu(tgtid->tgt_id);
+ tgt->channel = 0;
+
+ SNIC_BUG_ON(le16_to_cpu(tgtid->tgt_type) > SNIC_TGT_SAN);
+ tgt->tdata.typ = le16_to_cpu(tgtid->tgt_type);
+
+ /*
+ * Plugging into SML Device Tree
+ */
+ tgt->tdata.disc_id = 0;
+ tgt->state = SNIC_TGT_STAT_INIT;
+ device_initialize(&tgt->dev);
+ tgt->dev.parent = get_device(&snic->shost->shost_gendev);
+ tgt->dev.release = snic_tgt_dev_release;
+ INIT_WORK(&tgt->scan_work, snic_scsi_scan_tgt);
+ INIT_WORK(&tgt->del_work, snic_tgt_del);
+ switch (tgt->tdata.typ) {
+ case SNIC_TGT_DAS:
+ dev_set_name(&tgt->dev, "snic_das_tgt:%d:%d-%d",
+ snic->shost->host_no, tgt->channel, tgt->id);
+ break;
+
+ case SNIC_TGT_SAN:
+ dev_set_name(&tgt->dev, "snic_san_tgt:%d:%d-%d",
+ snic->shost->host_no, tgt->channel, tgt->id);
+ break;
+
+ default:
+ SNIC_HOST_INFO(snic->shost, "Target type Unknown Detected.\n");
+ dev_set_name(&tgt->dev, "snic_das_tgt:%d:%d-%d",
+ snic->shost->host_no, tgt->channel, tgt->id);
+ break;
+ }
+
+ spin_lock_irqsave(snic->shost->host_lock, flags);
+ list_add_tail(&tgt->list, &snic->disc.tgt_list);
+ tgt->scsi_tgt_id = snic->disc.nxt_tgt_id++;
+ tgt->state = SNIC_TGT_STAT_ONLINE;
+ spin_unlock_irqrestore(snic->shost->host_lock, flags);
+
+ SNIC_HOST_INFO(snic->shost,
+ "Tgt %d, type = %s detected. Adding..\n",
+ tgt->id, snic_tgt_type_to_str(tgt->tdata.typ));
+
+ ret = device_add(&tgt->dev);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "Snic Tgt: device_add, with err = %d\n",
+ ret);
+
+ put_device(&snic->shost->shost_gendev);
+ kfree(tgt);
+ tgt = NULL;
+
+ return tgt;
+ }
+
+ SNIC_HOST_INFO(snic->shost, "Scanning %s.\n", dev_name(&tgt->dev));
+
+ scsi_queue_work(snic->shost, &tgt->scan_work);
+
+ return tgt;
+} /* end of snic_tgt_create */
+
+/* Handler for discovery */
+void
+snic_handle_tgt_disc(struct work_struct *work)
+{
+ struct snic *snic = container_of(work, struct snic, tgt_work);
+ struct snic_tgt_id *tgtid = NULL;
+ struct snic_tgt *tgt = NULL;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&snic->snic_lock, flags);
+ if (snic->in_remove) {
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+ kfree(snic->disc.rtgt_info);
+
+ return;
+ }
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+ mutex_lock(&snic->disc.mutex);
+ /* Discover triggered during disc in progress */
+ if (snic->disc.req_cnt) {
+ snic->disc.state = SNIC_DISC_DONE;
+ snic->disc.req_cnt = 0;
+ mutex_unlock(&snic->disc.mutex);
+ kfree(snic->disc.rtgt_info);
+ snic->disc.rtgt_info = NULL;
+
+ SNIC_HOST_INFO(snic->shost, "tgt_disc: Discovery restart.\n");
+ /* Start Discovery Again */
+ snic_disc_start(snic);
+
+ return;
+ }
+
+ tgtid = (struct snic_tgt_id *)snic->disc.rtgt_info;
+
+ SNIC_BUG_ON(snic->disc.rtgt_cnt == 0 || tgtid == NULL);
+
+ for (i = 0; i < snic->disc.rtgt_cnt; i++) {
+ tgt = snic_tgt_create(snic, &tgtid[i]);
+ if (!tgt) {
+ int buf_sz = snic->disc.rtgt_cnt * sizeof(*tgtid);
+
+ SNIC_HOST_ERR(snic->shost, "Failed to create tgt.\n");
+ snic_hex_dump("rpt_tgt_rsp", (char *)tgtid, buf_sz);
+ break;
+ }
+ }
+
+ snic->disc.rtgt_info = NULL;
+ snic->disc.state = SNIC_DISC_DONE;
+ mutex_unlock(&snic->disc.mutex);
+
+ SNIC_HOST_INFO(snic->shost, "Discovery Completed.\n");
+
+ kfree(tgtid);
+} /* end of snic_handle_tgt_disc */
+
+
+int
+snic_report_tgt_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+
+ u8 typ, cmpl_stat;
+ u32 cmnd_id, hid, tgt_cnt = 0;
+ ulong ctx;
+ struct snic_req_info *rqi = NULL;
+ struct snic_tgt_id *tgtid;
+ int i, ret = 0;
+
+ snic_io_hdr_dec(&fwreq->hdr, &typ, &cmpl_stat, &cmnd_id, &hid, &ctx);
+ rqi = (struct snic_req_info *) ctx;
+ tgtid = (struct snic_tgt_id *) rqi->sge_va;
+
+ tgt_cnt = le32_to_cpu(fwreq->u.rpt_tgts_cmpl.tgt_cnt);
+ if (tgt_cnt == 0) {
+ SNIC_HOST_ERR(snic->shost, "No Targets Found on this host.\n");
+ ret = 1;
+
+ goto end;
+ }
+
+ /* printing list of targets here */
+ SNIC_HOST_INFO(snic->shost, "Target Count = %d\n", tgt_cnt);
+
+ SNIC_BUG_ON(tgt_cnt > snic->fwinfo.max_tgts);
+
+ for (i = 0; i < tgt_cnt; i++)
+ SNIC_HOST_INFO(snic->shost,
+ "Tgt id = 0x%x\n",
+ le32_to_cpu(tgtid[i].tgt_id));
+
+ /*
+ * Queue work for further processing,
+ * Response Buffer Memory is freed after creating targets
+ */
+ snic->disc.rtgt_cnt = tgt_cnt;
+ snic->disc.rtgt_info = (u8 *) tgtid;
+ queue_work(snic_glob->event_q, &snic->tgt_work);
+ ret = 0;
+
+end:
+ /* Unmap Response Buffer */
+ snic_pci_unmap_rsp_buf(snic, rqi);
+ if (ret)
+ kfree(tgtid);
+
+ rqi->sge_va = 0;
+ snic_release_untagged_req(snic, rqi);
+
+ return ret;
+} /* end of snic_report_tgt_cmpl_handler */
+
+/* Discovery init fn */
+void
+snic_disc_init(struct snic_disc *disc)
+{
+ INIT_LIST_HEAD(&disc->tgt_list);
+ mutex_init(&disc->mutex);
+ disc->disc_id = 0;
+ disc->nxt_tgt_id = 0;
+ disc->state = SNIC_DISC_INIT;
+ disc->req_cnt = 0;
+ disc->rtgt_cnt = 0;
+ disc->rtgt_info = NULL;
+ disc->cb = NULL;
+} /* end of snic_disc_init */
+
+/* Discovery, uninit fn */
+void
+snic_disc_term(struct snic *snic)
+{
+ struct snic_disc *disc = &snic->disc;
+
+ mutex_lock(&disc->mutex);
+ if (disc->req_cnt) {
+ disc->req_cnt = 0;
+ SNIC_SCSI_DBG(snic->shost, "Terminating Discovery.\n");
+ }
+ mutex_unlock(&disc->mutex);
+}
+
+/*
+ * snic_disc_start: Discovery Start ...
+ */
+int
+snic_disc_start(struct snic *snic)
+{
+ struct snic_disc *disc = &snic->disc;
+ int ret = 0;
+
+ SNIC_SCSI_DBG(snic->shost, "Discovery Start.\n");
+
+ mutex_lock(&disc->mutex);
+ if (disc->state == SNIC_DISC_PENDING) {
+ disc->req_cnt++;
+ mutex_unlock(&disc->mutex);
+
+ return ret;
+ }
+ disc->state = SNIC_DISC_PENDING;
+ mutex_unlock(&disc->mutex);
+
+ ret = snic_queue_report_tgt_req(snic);
+ if (ret)
+ SNIC_HOST_INFO(snic->shost, "Discovery Failed, err=%d.\n", ret);
+
+ return ret;
+} /* end of snic_disc_start */
+
+/*
+ * snic_disc_work :
+ */
+void
+snic_handle_disc(struct work_struct *work)
+{
+ struct snic *snic = container_of(work, struct snic, disc_work);
+ int ret = 0;
+
+ SNIC_HOST_INFO(snic->shost, "disc_work: Discovery\n");
+
+ ret = snic_disc_start(snic);
+ if (ret)
+ goto disc_err;
+
+disc_err:
+ SNIC_HOST_ERR(snic->shost,
+ "disc_work: Discovery Failed w/ err = %d\n",
+ ret);
+} /* end of snic_disc_work */
+
+/*
+ * snic_tgt_del_all : cleanup all snic targets
+ * Called on unbinding the interface
+ */
+void
+snic_tgt_del_all(struct snic *snic)
+{
+ struct snic_tgt *tgt = NULL;
+ struct list_head *cur, *nxt;
+ unsigned long flags;
+
+ mutex_lock(&snic->disc.mutex);
+ spin_lock_irqsave(snic->shost->host_lock, flags);
+
+ list_for_each_safe(cur, nxt, &snic->disc.tgt_list) {
+ tgt = list_entry(cur, struct snic_tgt, list);
+ tgt->state = SNIC_TGT_STAT_DEL;
+ list_del_init(&tgt->list);
+ SNIC_HOST_INFO(snic->shost, "Tgt %d q'ing for del\n", tgt->id);
+ queue_work(snic_glob->event_q, &tgt->del_work);
+ tgt = NULL;
+ }
+ spin_unlock_irqrestore(snic->shost->host_lock, flags);
+
+ scsi_flush_work(snic->shost);
+ mutex_unlock(&snic->disc.mutex);
+} /* end of snic_tgt_del_all */
diff --git a/drivers/scsi/snic/snic_disc.h b/drivers/scsi/snic/snic_disc.h
new file mode 100644
index 000000000000..97fa3f5c5bb4
--- /dev/null
+++ b/drivers/scsi/snic/snic_disc.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __SNIC_DISC_H
+#define __SNIC_DISC_H
+
+#include "snic_fwint.h"
+
+enum snic_disc_state {
+ SNIC_DISC_NONE,
+ SNIC_DISC_INIT,
+ SNIC_DISC_PENDING,
+ SNIC_DISC_DONE
+};
+
+struct snic;
+struct snic_disc {
+ struct list_head tgt_list;
+ enum snic_disc_state state;
+ struct mutex mutex;
+ u16 disc_id;
+ u8 req_cnt;
+ u32 nxt_tgt_id;
+ u32 rtgt_cnt;
+ u8 *rtgt_info;
+ struct delayed_work disc_timeout;
+ void (*cb)(struct snic *);
+};
+
+#define SNIC_TGT_NAM_LEN 16
+
+enum snic_tgt_state {
+ SNIC_TGT_STAT_NONE,
+ SNIC_TGT_STAT_INIT,
+ SNIC_TGT_STAT_ONLINE, /* Target is Online */
+ SNIC_TGT_STAT_OFFLINE, /* Target is Offline */
+ SNIC_TGT_STAT_DEL,
+};
+
+struct snic_tgt_priv {
+ struct list_head list;
+ enum snic_tgt_type typ;
+ u16 disc_id;
+ char *name[SNIC_TGT_NAM_LEN];
+
+ union {
+ /*DAS Target specific info */
+ /*SAN Target specific info */
+ u8 dummmy;
+ } u;
+};
+
+/* snic tgt flags */
+#define SNIC_TGT_SCAN_PENDING 0x01
+
+struct snic_tgt {
+ struct list_head list;
+ u16 id;
+ u16 channel;
+ u32 flags;
+ u32 scsi_tgt_id;
+ enum snic_tgt_state state;
+ struct device dev;
+ struct work_struct scan_work;
+ struct work_struct del_work;
+ struct snic_tgt_priv tdata;
+};
+
+
+struct snic_fw_req;
+
+void snic_disc_init(struct snic_disc *);
+int snic_disc_start(struct snic *);
+void snic_disc_term(struct snic *);
+int snic_report_tgt_cmpl_handler(struct snic *, struct snic_fw_req *);
+int snic_tgtinfo_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq);
+void snic_process_report_tgts_rsp(struct work_struct *);
+void snic_handle_tgt_disc(struct work_struct *);
+void snic_handle_disc(struct work_struct *);
+void snic_tgt_dev_release(struct device *);
+void snic_tgt_del_all(struct snic *);
+
+#define dev_to_tgt(d) \
+ container_of(d, struct snic_tgt, dev)
+
+static inline int
+is_snic_target(struct device *dev)
+{
+ return dev->release == snic_tgt_dev_release;
+}
+
+#define starget_to_tgt(st) \
+ (is_snic_target(((struct scsi_target *) st)->dev.parent) ? \
+ dev_to_tgt(st->dev.parent) : NULL)
+
+#define snic_tgt_to_shost(t) \
+ dev_to_shost(t->dev.parent)
+
+static inline int
+snic_tgt_chkready(struct snic_tgt *tgt)
+{
+ if (tgt->state == SNIC_TGT_STAT_ONLINE)
+ return 0;
+ else
+ return DID_NO_CONNECT << 16;
+}
+
+const char *snic_tgt_state_to_str(int);
+int snic_tgt_scsi_abort_io(struct snic_tgt *);
+#endif /* end of __SNIC_DISC_H */
diff --git a/drivers/scsi/snic/snic_fwint.h b/drivers/scsi/snic/snic_fwint.h
new file mode 100644
index 000000000000..2cfaf2dc915f
--- /dev/null
+++ b/drivers/scsi/snic/snic_fwint.h
@@ -0,0 +1,525 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __SNIC_FWINT_H
+#define __SNIC_FWINT_H
+
+#define SNIC_CDB_LEN 32 /* SCSI CDB size 32, can be used for 16 bytes */
+#define LUN_ADDR_LEN 8
+
+/*
+ * Command entry type
+ */
+enum snic_io_type {
+ /*
+ * Initiator request types
+ */
+ SNIC_REQ_REPORT_TGTS = 0x2, /* Report Targets */
+ SNIC_REQ_ICMND, /* Initiator command for SCSI IO */
+ SNIC_REQ_ITMF, /* Initiator command for Task Mgmt */
+ SNIC_REQ_HBA_RESET, /* SNIC Reset */
+ SNIC_REQ_EXCH_VER, /* Exchange Version Information */
+ SNIC_REQ_TGT_INFO, /* Backend/Target Information */
+ SNIC_REQ_BOOT_LUNS,
+
+ /*
+ * Response type
+ */
+ SNIC_RSP_REPORT_TGTS_CMPL = 0x12,/* Report Targets Completion */
+ SNIC_RSP_ICMND_CMPL, /* SCSI IO Completion */
+ SNIC_RSP_ITMF_CMPL, /* Task Management Completion */
+ SNIC_RSP_HBA_RESET_CMPL, /* SNIC Reset Completion */
+ SNIC_RSP_EXCH_VER_CMPL, /* Exchange Version Completion*/
+ SNIC_RSP_BOOT_LUNS_CMPL,
+
+ /*
+ * Misc Request types
+ */
+ SNIC_MSG_ACK = 0x80, /* Ack: snic_notify_msg */
+ SNIC_MSG_ASYNC_EVNOTIFY, /* Asynchronous Event Notification */
+}; /* end of enum snic_io_type */
+
+
+/*
+ * Header status codes from firmware
+ */
+enum snic_io_status {
+ SNIC_STAT_IO_SUCCESS = 0, /* request was successful */
+
+ /*
+ * If a request to the fw is rejected, the original request header
+ * will be returned with the status set to one of the following:
+ */
+ SNIC_STAT_INVALID_HDR, /* header contains invalid data */
+ SNIC_STAT_OUT_OF_RES, /* out of resources to complete request */
+ SNIC_STAT_INVALID_PARM, /* some parameter in request is not valid */
+ SNIC_STAT_REQ_NOT_SUP, /* req type is not supported */
+ SNIC_STAT_IO_NOT_FOUND, /* requested IO was not found */
+
+ /*
+ * Once a request is processed, the fw will usually return
+ * a cmpl message type. In cases where errors occurred,
+ * the header status would be filled in with one of the following:
+ */
+ SNIC_STAT_ABORTED, /* req was aborted */
+ SNIC_STAT_TIMEOUT, /* req was timed out */
+ SNIC_STAT_SGL_INVALID, /* req was aborted due to sgl error */
+ SNIC_STAT_DATA_CNT_MISMATCH, /*recv/sent more/less data than expec */
+ SNIC_STAT_FW_ERR, /* req was terminated due to fw error */
+ SNIC_STAT_ITMF_REJECT, /* itmf req was rejected by target */
+ SNIC_STAT_ITMF_FAIL, /* itmf req was failed */
+ SNIC_STAT_ITMF_INCORRECT_LUN, /* itmf req has incorrect LUN id*/
+ SNIC_STAT_CMND_REJECT, /* req was invalid and rejected */
+ SNIC_STAT_DEV_OFFLINE, /* req sent to offline device */
+ SNIC_STAT_NO_BOOTLUN,
+ SNIC_STAT_SCSI_ERR, /* SCSI error returned by Target. */
+ SNIC_STAT_NOT_READY, /* sNIC Subsystem is not ready */
+ SNIC_STAT_FATAL_ERROR, /* sNIC is in unrecoverable state */
+}; /* end of enum snic_io_status */
+
+/*
+ * snic_io_hdr : host <--> firmare
+ *
+ * for any other message that will be queued to firmware should
+ * have the following request header
+ */
+struct snic_io_hdr {
+ __le32 hid;
+ __le32 cmnd_id; /* tag here */
+ ulong init_ctx; /* initiator context */
+ u8 type; /* request/response type */
+ u8 status; /* header status entry */
+ u8 protocol; /* Protocol specific, may needed for RoCE*/
+ u8 flags;
+ __le16 sg_cnt;
+ u16 resvd;
+};
+
+/* auxillary funciton for encoding the snic_io_hdr */
+static inline void
+snic_io_hdr_enc(struct snic_io_hdr *hdr, u8 typ, u8 status, u32 id, u32 hid,
+ u16 sg_cnt, ulong ctx)
+{
+ hdr->type = typ;
+ hdr->status = status;
+ hdr->protocol = 0;
+ hdr->hid = cpu_to_le32(hid);
+ hdr->cmnd_id = cpu_to_le32(id);
+ hdr->sg_cnt = cpu_to_le16(sg_cnt);
+ hdr->init_ctx = ctx;
+ hdr->flags = 0;
+}
+
+/* auxillary funciton for decoding the snic_io_hdr */
+static inline void
+snic_io_hdr_dec(struct snic_io_hdr *hdr, u8 *typ, u8 *stat, u32 *cmnd_id,
+ u32 *hid, ulong *ctx)
+{
+ *typ = hdr->type;
+ *stat = hdr->status;
+ *hid = le32_to_cpu(hdr->hid);
+ *cmnd_id = le32_to_cpu(hdr->cmnd_id);
+ *ctx = hdr->init_ctx;
+}
+
+/*
+ * snic_host_info: host -> firmware
+ *
+ * Used for sending host information to firmware, and request fw version
+ */
+struct snic_exch_ver_req {
+ __le32 drvr_ver; /* for debugging, when fw dump captured */
+ __le32 os_type; /* for OS specific features */
+};
+
+/*
+ * os_type flags
+ * Bit 0-7 : OS information
+ * Bit 8-31: Feature/Capability Information
+ */
+#define SNIC_OS_LINUX 0x1
+#define SNIC_OS_WIN 0x2
+#define SNIC_OS_ESX 0x3
+
+/*
+ * HBA Capabilities
+ * Bit 1: Reserved.
+ * Bit 2: Dynamic Discovery of LUNs.
+ * Bit 3: Async event notifications on on tgt online/offline events.
+ * Bit 4: IO timeout support in FW.
+ * Bit 5-31: Reserved.
+ */
+#define SNIC_HBA_CAP_DDL 0x02 /* Supports Dynamic Discovery of LUNs */
+#define SNIC_HBA_CAP_AEN 0x04 /* Supports Async Event Noitifcation */
+#define SNIC_HBA_CAP_TMO 0x08 /* Supports IO timeout in FW */
+
+/*
+ * snic_exch_ver_rsp : firmware -> host
+ *
+ * Used by firmware to send response to version request
+ */
+struct snic_exch_ver_rsp {
+ __le32 version;
+ __le32 hid;
+ __le32 max_concur_ios; /* max concurrent ios */
+ __le32 max_sgs_per_cmd; /* max sgls per IO */
+ __le32 max_io_sz; /* max io size supported */
+ __le32 hba_cap; /* hba capabilities */
+ __le32 max_tgts; /* max tgts supported */
+ __le16 io_timeout; /* FW extended timeout */
+ u16 rsvd;
+};
+
+
+/*
+ * snic_report_tgts : host -> firmware request
+ *
+ * Used by the host to request list of targets
+ */
+struct snic_report_tgts {
+ __le16 sg_cnt;
+ __le16 flags; /* specific flags from fw */
+ u8 _resvd[4];
+ __le64 sg_addr; /* Points to SGL */
+ __le64 sense_addr;
+};
+
+enum snic_type {
+ SNIC_NONE = 0x0,
+ SNIC_DAS,
+ SNIC_SAN,
+};
+
+
+/* Report Target Response */
+enum snic_tgt_type {
+ SNIC_TGT_NONE = 0x0,
+ SNIC_TGT_DAS, /* DAS Target */
+ SNIC_TGT_SAN, /* SAN Target */
+};
+
+/* target id format */
+struct snic_tgt_id {
+ __le32 tgt_id; /* target id */
+ __le16 tgt_type; /* tgt type */
+ __le16 vnic_id; /* corresponding vnic id */
+};
+
+/*
+ * snic_report_tgts_cmpl : firmware -> host response
+ *
+ * Used by firmware to send response to Report Targets request
+ */
+struct snic_report_tgts_cmpl {
+ __le32 tgt_cnt; /* Number of Targets accessible */
+ u32 _resvd;
+};
+
+/*
+ * Command flags
+ *
+ * Bit 0: Read flags
+ * Bit 1: Write flag
+ * Bit 2: ESGL - sg/esg array contains extended sg
+ * ESGE - is a host buffer contains sg elements
+ * Bit 3-4: Task Attributes
+ * 00b - simple
+ * 01b - head of queue
+ * 10b - ordered
+ * Bit 5-7: Priority - future use
+ * Bit 8-15: Reserved
+ */
+
+#define SNIC_ICMND_WR 0x01 /* write command */
+#define SNIC_ICMND_RD 0x02 /* read command */
+#define SNIC_ICMND_ESGL 0x04 /* SGE/ESGE array contains valid data*/
+
+/*
+ * Priority/Task Attribute settings
+ */
+#define SNIC_ICMND_TSK_SHIFT 2 /* task attr starts at bit 2 */
+#define SNIC_ICMND_TSK_MASK(x) ((x>>SNIC_ICMND_TSK_SHIFT) & ~(0xffff))
+#define SNIC_ICMND_TSK_SIMPLE 0 /* simple task attr */
+#define SNIC_ICMND_TSK_HEAD_OF_QUEUE 1 /* head of qeuue task attr */
+#define SNIC_ICMND_TSK_ORDERED 2 /* ordered task attr */
+
+#define SNIC_ICMND_PRI_SHIFT 5 /* prio val starts at bit 5 */
+
+/*
+ * snic_icmnd : host-> firmware request
+ *
+ * used for sending out an initiator SCSI 16/32-byte command
+ */
+struct snic_icmnd {
+ __le16 sg_cnt; /* Number of SG Elements */
+ __le16 flags; /* flags */
+ __le32 sense_len; /* Sense buffer length */
+ __le64 tgt_id; /* Destination Target ID */
+ __le64 lun_id; /* Destination LUN ID */
+ u8 cdb_len;
+ u8 _resvd;
+ __le16 time_out; /* ms time for Res allocations fw to handle io*/
+ __le32 data_len; /* Total number of bytes to be transferred */
+ u8 cdb[SNIC_CDB_LEN];
+ __le64 sg_addr; /* Points to SG List */
+ __le64 sense_addr; /* Sense buffer address */
+};
+
+
+/* Response flags */
+/* Bit 0: Under run
+ * Bit 1: Over Run
+ * Bit 2-7: Reserved
+ */
+#define SNIC_ICMND_CMPL_UNDR_RUN 0x01 /* resid under and valid */
+#define SNIC_ICMND_CMPL_OVER_RUN 0x02 /* resid over and valid */
+
+/*
+ * snic_icmnd_cmpl: firmware -> host response
+ *
+ * Used for sending the host a response to an icmnd (initiator command)
+ */
+struct snic_icmnd_cmpl {
+ u8 scsi_status; /* value as per SAM */
+ u8 flags;
+ __le16 sense_len; /* Sense Length */
+ __le32 resid; /* Residue : # bytes under or over run */
+};
+
+/*
+ * snic_itmf: host->firmware request
+ *
+ * used for requesting the firmware to abort a request and/or send out
+ * a task management function
+ *
+ * the req_id field is valid in case of abort task and clear task
+ */
+struct snic_itmf {
+ u8 tm_type; /* SCSI Task Management request */
+ u8 resvd;
+ __le16 flags; /* flags */
+ __le32 req_id; /* Command id of snic req to be aborted */
+ __le64 tgt_id; /* Target ID */
+ __le64 lun_id; /* Destination LUN ID */
+ __le16 timeout; /* in sec */
+};
+
+/*
+ * Task Management Request
+ */
+enum snic_itmf_tm_type {
+ SNIC_ITMF_ABTS_TASK = 0x01, /* Abort Task */
+ SNIC_ITMF_ABTS_TASK_SET, /* Abort Task Set */
+ SNIC_ITMF_CLR_TASK, /* Clear Task */
+ SNIC_ITMF_CLR_TASKSET, /* Clear Task Set */
+ SNIC_ITMF_LUN_RESET, /* Lun Reset */
+ SNIC_ITMF_ABTS_TASK_TERM, /* Supported for SAN Targets */
+};
+
+/*
+ * snic_itmf_cmpl: firmware -> host resposne
+ *
+ * used for sending the host a response for a itmf request
+ */
+struct snic_itmf_cmpl {
+ __le32 nterminated; /* # IOs terminated as a result of tmf */
+ u8 flags; /* flags */
+ u8 _resvd[3];
+};
+
+/*
+ * itmfl_cmpl flags
+ * Bit 0 : 1 - Num terminated field valid
+ * Bit 1 - 7 : Reserved
+ */
+#define SNIC_NUM_TERM_VALID 0x01 /* Number of IOs terminated */
+
+/*
+ * snic_hba_reset: host -> firmware request
+ *
+ * used for requesting firmware to reset snic
+ */
+struct snic_hba_reset {
+ __le16 flags; /* flags */
+ u8 _resvd[6];
+};
+
+/*
+ * snic_hba_reset_cmpl: firmware -> host response
+ *
+ * Used by firmware to respond to the host's hba reset request
+ */
+struct snic_hba_reset_cmpl {
+ u8 flags; /* flags : more info needs to be added*/
+ u8 _resvd[7];
+};
+
+/*
+ * snic_notify_msg: firmware -> host response
+ *
+ * Used by firmware to notify host of the last work queue entry received
+ */
+struct snic_notify_msg {
+ __le32 wqe_num; /* wq entry number */
+ u8 flags; /* flags, macros */
+ u8 _resvd[4];
+};
+
+
+#define SNIC_EVDATA_LEN 24 /* in bytes */
+/* snic_async_evnotify: firmware -> host notification
+ *
+ * Used by firmware to notify the host about configuration/state changes
+ */
+struct snic_async_evnotify {
+ u8 FLS_EVENT_DESC;
+ u8 vnic; /* vnic id */
+ u8 _resvd[2];
+ __le32 ev_id; /* Event ID */
+ u8 ev_data[SNIC_EVDATA_LEN]; /* Event Data */
+ u8 _resvd2[4];
+};
+
+/* async event flags */
+enum snic_ev_type {
+ SNIC_EV_TGT_OFFLINE = 0x01, /* Target Offline, PL contains TGT ID */
+ SNIC_EV_TGT_ONLINE, /* Target Online, PL contains TGT ID */
+ SNIC_EV_LUN_OFFLINE, /* LUN Offline, PL contains LUN ID */
+ SNIC_EV_LUN_ONLINE, /* LUN Online, PL contains LUN ID */
+ SNIC_EV_CONF_CHG, /* Dev Config/Attr Change Event */
+ SNIC_EV_TGT_ADDED, /* Target Added */
+ SNIC_EV_TGT_DELTD, /* Target Del'd, PL contains TGT ID */
+ SNIC_EV_LUN_ADDED, /* LUN Added */
+ SNIC_EV_LUN_DELTD, /* LUN Del'd, PL cont. TGT & LUN ID */
+
+ SNIC_EV_DISC_CMPL = 0x10, /* Discovery Completed Event */
+};
+
+
+#define SNIC_HOST_REQ_LEN 128 /*Exp length of host req, wq desc sz*/
+/* Payload 88 bytes = 128 - 24 - 16 */
+#define SNIC_HOST_REQ_PAYLOAD ((int)(SNIC_HOST_REQ_LEN - \
+ sizeof(struct snic_io_hdr) - \
+ (2 * sizeof(u64))))
+
+/*
+ * snic_host_req: host -> firmware request
+ *
+ * Basic structure for all snic requests that are sent from the host to
+ * firmware. They are 128 bytes in size.
+ */
+struct snic_host_req {
+ u64 ctrl_data[2]; /*16 bytes - Control Data */
+ struct snic_io_hdr hdr;
+ union {
+ /*
+ * Entry specific space, last byte contains color
+ */
+ u8 buf[SNIC_HOST_REQ_PAYLOAD];
+
+ /*
+ * Exchange firmware version
+ */
+ struct snic_exch_ver_req exch_ver;
+
+ /* report targets */
+ struct snic_report_tgts rpt_tgts;
+
+ /* io request */
+ struct snic_icmnd icmnd;
+
+ /* task management request */
+ struct snic_itmf itmf;
+
+ /* hba reset */
+ struct snic_hba_reset reset;
+ } u;
+}; /* end of snic_host_req structure */
+
+
+#define SNIC_FW_REQ_LEN 64 /* Expected length of fw req */
+struct snic_fw_req {
+ struct snic_io_hdr hdr;
+ union {
+ /*
+ * Entry specific space, last byte contains color
+ */
+ u8 buf[SNIC_FW_REQ_LEN - sizeof(struct snic_io_hdr)];
+
+ /* Exchange Version Response */
+ struct snic_exch_ver_rsp exch_ver_cmpl;
+
+ /* Report Targets Response */
+ struct snic_report_tgts_cmpl rpt_tgts_cmpl;
+
+ /* scsi response */
+ struct snic_icmnd_cmpl icmnd_cmpl;
+
+ /* task management response */
+ struct snic_itmf_cmpl itmf_cmpl;
+
+ /* hba reset response */
+ struct snic_hba_reset_cmpl reset_cmpl;
+
+ /* notify message */
+ struct snic_notify_msg ack;
+
+ /* async notification event */
+ struct snic_async_evnotify async_ev;
+
+ } u;
+}; /* end of snic_fw_req structure */
+
+/*
+ * Auxillary macro to verify specific snic req/cmpl structures
+ * to ensure that it will be aligned to 64 bit, and not using
+ * color bit field
+ */
+#define VERIFY_REQ_SZ(x)
+#define VERIFY_CMPL_SZ(x)
+
+/*
+ * Access routines to encode and decode the color bit, which is the most
+ * significant bit of the structure.
+ */
+static inline void
+snic_color_enc(struct snic_fw_req *req, u8 color)
+{
+ u8 *c = ((u8 *) req) + sizeof(struct snic_fw_req) - 1;
+
+ if (color)
+ *c |= 0x80;
+ else
+ *c &= ~0x80;
+}
+
+static inline void
+snic_color_dec(struct snic_fw_req *req, u8 *color)
+{
+ u8 *c = ((u8 *) req) + sizeof(struct snic_fw_req) - 1;
+
+ *color = *c >> 7;
+
+ /* Make sure color bit is read from desc *before* other fields
+ * are read from desc. Hardware guarantees color bit is last
+ * bit (byte) written. Adding the rmb() prevents the compiler
+ * and/or CPU from reordering the reads which would potentially
+ * result in reading stale values.
+ */
+ rmb();
+}
+#endif /* end of __SNIC_FWINT_H */
diff --git a/drivers/scsi/snic/snic_io.c b/drivers/scsi/snic/snic_io.c
new file mode 100644
index 000000000000..993db7de4e4b
--- /dev/null
+++ b/drivers/scsi/snic/snic_io.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/mempool.h>
+#include <scsi/scsi_tcq.h>
+
+#include "snic_io.h"
+#include "snic.h"
+#include "cq_enet_desc.h"
+#include "snic_fwint.h"
+
+static void
+snic_wq_cmpl_frame_send(struct vnic_wq *wq,
+ struct cq_desc *cq_desc,
+ struct vnic_wq_buf *buf,
+ void *opaque)
+{
+ struct snic *snic = svnic_dev_priv(wq->vdev);
+
+ SNIC_BUG_ON(buf->os_buf == NULL);
+
+ if (snic_log_level & SNIC_DESC_LOGGING)
+ SNIC_HOST_INFO(snic->shost,
+ "Ack received for snic_host_req %p.\n",
+ buf->os_buf);
+
+ SNIC_TRC(snic->shost->host_no, 0, 0,
+ ((ulong)(buf->os_buf) - sizeof(struct snic_req_info)), 0, 0,
+ 0);
+ pci_unmap_single(snic->pdev, buf->dma_addr, buf->len, PCI_DMA_TODEVICE);
+ buf->os_buf = NULL;
+}
+
+static int
+snic_wq_cmpl_handler_cont(struct vnic_dev *vdev,
+ struct cq_desc *cq_desc,
+ u8 type,
+ u16 q_num,
+ u16 cmpl_idx,
+ void *opaque)
+{
+ struct snic *snic = svnic_dev_priv(vdev);
+ unsigned long flags;
+
+ SNIC_BUG_ON(q_num != 0);
+
+ spin_lock_irqsave(&snic->wq_lock[q_num], flags);
+ svnic_wq_service(&snic->wq[q_num],
+ cq_desc,
+ cmpl_idx,
+ snic_wq_cmpl_frame_send,
+ NULL);
+ spin_unlock_irqrestore(&snic->wq_lock[q_num], flags);
+
+ return 0;
+} /* end of snic_cmpl_handler_cont */
+
+int
+snic_wq_cmpl_handler(struct snic *snic, int work_to_do)
+{
+ unsigned int work_done = 0;
+ unsigned int i;
+
+ snic->s_stats.misc.last_ack_time = jiffies;
+ for (i = 0; i < snic->wq_count; i++) {
+ work_done += svnic_cq_service(&snic->cq[i],
+ work_to_do,
+ snic_wq_cmpl_handler_cont,
+ NULL);
+ }
+
+ return work_done;
+} /* end of snic_wq_cmpl_handler */
+
+void
+snic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
+{
+
+ struct snic_host_req *req = buf->os_buf;
+ struct snic *snic = svnic_dev_priv(wq->vdev);
+ struct snic_req_info *rqi = NULL;
+ unsigned long flags;
+
+ pci_unmap_single(snic->pdev, buf->dma_addr, buf->len, PCI_DMA_TODEVICE);
+
+ rqi = req_to_rqi(req);
+ spin_lock_irqsave(&snic->spl_cmd_lock, flags);
+ if (list_empty(&rqi->list)) {
+ spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+ goto end;
+ }
+
+ SNIC_BUG_ON(rqi->list.next == NULL); /* if not added to spl_cmd_list */
+ list_del_init(&rqi->list);
+ spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+
+ if (rqi->sge_va) {
+ snic_pci_unmap_rsp_buf(snic, rqi);
+ kfree((void *)rqi->sge_va);
+ rqi->sge_va = 0;
+ }
+ snic_req_free(snic, rqi);
+ SNIC_HOST_INFO(snic->shost, "snic_free_wq_buf .. freed.\n");
+
+end:
+ return;
+}
+
+/* Criteria to select work queue in multi queue mode */
+static int
+snic_select_wq(struct snic *snic)
+{
+ /* No multi queue support for now */
+ BUILD_BUG_ON(SNIC_WQ_MAX > 1);
+
+ return 0;
+}
+
+int
+snic_queue_wq_desc(struct snic *snic, void *os_buf, u16 len)
+{
+ dma_addr_t pa = 0;
+ unsigned long flags;
+ struct snic_fw_stats *fwstats = &snic->s_stats.fw;
+ long act_reqs;
+ int q_num = 0;
+
+ snic_print_desc(__func__, os_buf, len);
+
+ /* Map request buffer */
+ pa = pci_map_single(snic->pdev, os_buf, len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(snic->pdev, pa)) {
+ SNIC_HOST_ERR(snic->shost, "qdesc: PCI DMA Mapping Fail.\n");
+
+ return -ENOMEM;
+ }
+
+ q_num = snic_select_wq(snic);
+
+ spin_lock_irqsave(&snic->wq_lock[q_num], flags);
+ if (!svnic_wq_desc_avail(snic->wq)) {
+ pci_unmap_single(snic->pdev, pa, len, PCI_DMA_TODEVICE);
+ spin_unlock_irqrestore(&snic->wq_lock[q_num], flags);
+ atomic64_inc(&snic->s_stats.misc.wq_alloc_fail);
+ SNIC_DBG("host = %d, WQ is Full\n", snic->shost->host_no);
+
+ return -ENOMEM;
+ }
+
+ snic_queue_wq_eth_desc(&snic->wq[q_num], os_buf, pa, len, 0, 0, 1);
+ spin_unlock_irqrestore(&snic->wq_lock[q_num], flags);
+
+ /* Update stats */
+ act_reqs = atomic64_inc_return(&fwstats->actv_reqs);
+ if (act_reqs > atomic64_read(&fwstats->max_actv_reqs))
+ atomic64_set(&fwstats->max_actv_reqs, act_reqs);
+
+ return 0;
+} /* end of snic_queue_wq_desc() */
+
+/*
+ * snic_handle_untagged_req: Adds snic specific requests to spl_cmd_list.
+ * Purpose : Used during driver unload to clean up the requests.
+ */
+void
+snic_handle_untagged_req(struct snic *snic, struct snic_req_info *rqi)
+{
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&rqi->list);
+
+ spin_lock_irqsave(&snic->spl_cmd_lock, flags);
+ list_add_tail(&rqi->list, &snic->spl_cmd_list);
+ spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+}
+
+/*
+ * snic_req_init:
+ * Allocates snic_req_info + snic_host_req + sgl data, and initializes.
+ */
+struct snic_req_info *
+snic_req_init(struct snic *snic, int sg_cnt)
+{
+ u8 typ;
+ struct snic_req_info *rqi = NULL;
+
+ typ = (sg_cnt <= SNIC_REQ_CACHE_DFLT_SGL) ?
+ SNIC_REQ_CACHE_DFLT_SGL : SNIC_REQ_CACHE_MAX_SGL;
+
+ rqi = mempool_alloc(snic->req_pool[typ], GFP_ATOMIC);
+ if (!rqi) {
+ atomic64_inc(&snic->s_stats.io.alloc_fail);
+ SNIC_HOST_ERR(snic->shost,
+ "Failed to allocate memory from snic req pool id = %d\n",
+ typ);
+ return rqi;
+ }
+
+ memset(rqi, 0, sizeof(*rqi));
+ rqi->rq_pool_type = typ;
+ rqi->start_time = jiffies;
+ rqi->req = (struct snic_host_req *) (rqi + 1);
+ rqi->req_len = sizeof(struct snic_host_req);
+ rqi->snic = snic;
+
+ rqi->req = (struct snic_host_req *)(rqi + 1);
+
+ if (sg_cnt == 0)
+ goto end;
+
+ rqi->req_len += (sg_cnt * sizeof(struct snic_sg_desc));
+
+ if (sg_cnt > atomic64_read(&snic->s_stats.io.max_sgl))
+ atomic64_set(&snic->s_stats.io.max_sgl, sg_cnt);
+
+ SNIC_BUG_ON(sg_cnt > SNIC_MAX_SG_DESC_CNT);
+ atomic64_inc(&snic->s_stats.io.sgl_cnt[sg_cnt - 1]);
+
+end:
+ memset(rqi->req, 0, rqi->req_len);
+
+ /* pre initialization of init_ctx to support req_to_rqi */
+ rqi->req->hdr.init_ctx = (ulong) rqi;
+
+ SNIC_SCSI_DBG(snic->shost, "Req_alloc:rqi = %p allocatd.\n", rqi);
+
+ return rqi;
+} /* end of snic_req_init */
+
+/*
+ * snic_abort_req_init : Inits abort request.
+ */
+struct snic_host_req *
+snic_abort_req_init(struct snic *snic, struct snic_req_info *rqi)
+{
+ struct snic_host_req *req = NULL;
+
+ SNIC_BUG_ON(!rqi);
+
+ /* If abort to be issued second time, then reuse */
+ if (rqi->abort_req)
+ return rqi->abort_req;
+
+
+ req = mempool_alloc(snic->req_pool[SNIC_REQ_TM_CACHE], GFP_ATOMIC);
+ if (!req) {
+ SNIC_HOST_ERR(snic->shost, "abts:Failed to alloc tm req.\n");
+ WARN_ON_ONCE(1);
+
+ return NULL;
+ }
+
+ rqi->abort_req = req;
+ memset(req, 0, sizeof(struct snic_host_req));
+ /* pre initialization of init_ctx to support req_to_rqi */
+ req->hdr.init_ctx = (ulong) rqi;
+
+ return req;
+} /* end of snic_abort_req_init */
+
+/*
+ * snic_dr_req_init : Inits device reset req
+ */
+struct snic_host_req *
+snic_dr_req_init(struct snic *snic, struct snic_req_info *rqi)
+{
+ struct snic_host_req *req = NULL;
+
+ SNIC_BUG_ON(!rqi);
+
+ req = mempool_alloc(snic->req_pool[SNIC_REQ_TM_CACHE], GFP_ATOMIC);
+ if (!req) {
+ SNIC_HOST_ERR(snic->shost, "dr:Failed to alloc tm req.\n");
+ WARN_ON_ONCE(1);
+
+ return NULL;
+ }
+
+ SNIC_BUG_ON(rqi->dr_req != NULL);
+ rqi->dr_req = req;
+ memset(req, 0, sizeof(struct snic_host_req));
+ /* pre initialization of init_ctx to support req_to_rqi */
+ req->hdr.init_ctx = (ulong) rqi;
+
+ return req;
+} /* end of snic_dr_req_init */
+
+/* frees snic_req_info and snic_host_req */
+void
+snic_req_free(struct snic *snic, struct snic_req_info *rqi)
+{
+ SNIC_BUG_ON(rqi->req == rqi->abort_req);
+ SNIC_BUG_ON(rqi->req == rqi->dr_req);
+ SNIC_BUG_ON(rqi->sge_va != 0);
+
+ SNIC_SCSI_DBG(snic->shost,
+ "Req_free:rqi %p:ioreq %p:abt %p:dr %p\n",
+ rqi, rqi->req, rqi->abort_req, rqi->dr_req);
+
+ if (rqi->abort_req)
+ mempool_free(rqi->abort_req, snic->req_pool[SNIC_REQ_TM_CACHE]);
+
+ if (rqi->dr_req)
+ mempool_free(rqi->dr_req, snic->req_pool[SNIC_REQ_TM_CACHE]);
+
+ mempool_free(rqi, snic->req_pool[rqi->rq_pool_type]);
+}
+
+void
+snic_pci_unmap_rsp_buf(struct snic *snic, struct snic_req_info *rqi)
+{
+ struct snic_sg_desc *sgd;
+
+ sgd = req_to_sgl(rqi_to_req(rqi));
+ SNIC_BUG_ON(sgd[0].addr == 0);
+ pci_unmap_single(snic->pdev,
+ le64_to_cpu(sgd[0].addr),
+ le32_to_cpu(sgd[0].len),
+ PCI_DMA_FROMDEVICE);
+}
+
+/*
+ * snic_free_all_untagged_reqs: Walks through untagged reqs and frees them.
+ */
+void
+snic_free_all_untagged_reqs(struct snic *snic)
+{
+ struct snic_req_info *rqi;
+ struct list_head *cur, *nxt;
+ unsigned long flags;
+
+ spin_lock_irqsave(&snic->spl_cmd_lock, flags);
+ list_for_each_safe(cur, nxt, &snic->spl_cmd_list) {
+ rqi = list_entry(cur, struct snic_req_info, list);
+ list_del_init(&rqi->list);
+ if (rqi->sge_va) {
+ snic_pci_unmap_rsp_buf(snic, rqi);
+ kfree((void *)rqi->sge_va);
+ rqi->sge_va = 0;
+ }
+
+ snic_req_free(snic, rqi);
+ }
+ spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+}
+
+/*
+ * snic_release_untagged_req : Unlinks the untagged req and frees it.
+ */
+void
+snic_release_untagged_req(struct snic *snic, struct snic_req_info *rqi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&snic->snic_lock, flags);
+ if (snic->in_remove) {
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+ goto end;
+ }
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+ spin_lock_irqsave(&snic->spl_cmd_lock, flags);
+ if (list_empty(&rqi->list)) {
+ spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+ goto end;
+ }
+ list_del_init(&rqi->list);
+ spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+ snic_req_free(snic, rqi);
+
+end:
+ return;
+}
+
+/* dump buf in hex fmt */
+void
+snic_hex_dump(char *pfx, char *data, int len)
+{
+ SNIC_INFO("%s Dumping Data of Len = %d\n", pfx, len);
+ print_hex_dump_bytes(pfx, DUMP_PREFIX_NONE, data, len);
+}
+
+#define LINE_BUFSZ 128 /* for snic_print_desc fn */
+static void
+snic_dump_desc(const char *fn, char *os_buf, int len)
+{
+ struct snic_host_req *req = (struct snic_host_req *) os_buf;
+ struct snic_fw_req *fwreq = (struct snic_fw_req *) os_buf;
+ struct snic_req_info *rqi = NULL;
+ char line[LINE_BUFSZ] = { '\0' };
+ char *cmd_str = NULL;
+
+ if (req->hdr.type >= SNIC_RSP_REPORT_TGTS_CMPL)
+ rqi = (struct snic_req_info *) fwreq->hdr.init_ctx;
+ else
+ rqi = (struct snic_req_info *) req->hdr.init_ctx;
+
+ SNIC_BUG_ON(rqi == NULL || rqi->req == NULL);
+ switch (req->hdr.type) {
+ case SNIC_REQ_REPORT_TGTS:
+ cmd_str = "report-tgt : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_REQ_REPORT_TGTS :");
+ break;
+
+ case SNIC_REQ_ICMND:
+ cmd_str = "icmnd : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_REQ_ICMND : 0x%x :",
+ req->u.icmnd.cdb[0]);
+ break;
+
+ case SNIC_REQ_ITMF:
+ cmd_str = "itmf : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_REQ_ITMF :");
+ break;
+
+ case SNIC_REQ_HBA_RESET:
+ cmd_str = "hba reset :";
+ snprintf(line, LINE_BUFSZ, "SNIC_REQ_HBA_RESET :");
+ break;
+
+ case SNIC_REQ_EXCH_VER:
+ cmd_str = "exch ver : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_REQ_EXCH_VER :");
+ break;
+
+ case SNIC_REQ_TGT_INFO:
+ cmd_str = "tgt info : ";
+ break;
+
+ case SNIC_RSP_REPORT_TGTS_CMPL:
+ cmd_str = "report tgt cmpl : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_RSP_REPORT_TGTS_CMPL :");
+ break;
+
+ case SNIC_RSP_ICMND_CMPL:
+ cmd_str = "icmnd_cmpl : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_RSP_ICMND_CMPL : 0x%x :",
+ rqi->req->u.icmnd.cdb[0]);
+ break;
+
+ case SNIC_RSP_ITMF_CMPL:
+ cmd_str = "itmf_cmpl : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_RSP_ITMF_CMPL :");
+ break;
+
+ case SNIC_RSP_HBA_RESET_CMPL:
+ cmd_str = "hba_reset_cmpl : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_RSP_HBA_RESET_CMPL :");
+ break;
+
+ case SNIC_RSP_EXCH_VER_CMPL:
+ cmd_str = "exch_ver_cmpl : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_RSP_EXCH_VER_CMPL :");
+ break;
+
+ case SNIC_MSG_ACK:
+ cmd_str = "msg ack : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_MSG_ACK :");
+ break;
+
+ case SNIC_MSG_ASYNC_EVNOTIFY:
+ cmd_str = "async notify : ";
+ snprintf(line, LINE_BUFSZ, "SNIC_MSG_ASYNC_EVNOTIFY :");
+ break;
+
+ default:
+ cmd_str = "unknown : ";
+ SNIC_BUG_ON(1);
+ break;
+ }
+
+ SNIC_INFO("%s:%s >>cmndid=%x:sg_cnt = %x:status = %x:ctx = %lx.\n",
+ fn, line, req->hdr.cmnd_id, req->hdr.sg_cnt, req->hdr.status,
+ req->hdr.init_ctx);
+
+ /* Enable it, to dump byte stream */
+ if (snic_log_level & 0x20)
+ snic_hex_dump(cmd_str, os_buf, len);
+} /* end of __snic_print_desc */
+
+void
+snic_print_desc(const char *fn, char *os_buf, int len)
+{
+ if (snic_log_level & SNIC_DESC_LOGGING)
+ snic_dump_desc(fn, os_buf, len);
+}
+
+void
+snic_calc_io_process_time(struct snic *snic, struct snic_req_info *rqi)
+{
+ u64 duration;
+
+ duration = jiffies - rqi->start_time;
+
+ if (duration > atomic64_read(&snic->s_stats.io.max_time))
+ atomic64_set(&snic->s_stats.io.max_time, duration);
+}
diff --git a/drivers/scsi/snic/snic_io.h b/drivers/scsi/snic/snic_io.h
new file mode 100644
index 000000000000..093d6524cd42
--- /dev/null
+++ b/drivers/scsi/snic/snic_io.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _SNIC_IO_H
+#define _SNIC_IO_H
+
+#define SNIC_DFLT_SG_DESC_CNT 32 /* Default descriptors for sgl */
+#define SNIC_MAX_SG_DESC_CNT 60 /* Max descriptor for sgl */
+#define SNIC_SG_DESC_ALIGN 16 /* Descriptor address alignment */
+
+/* SG descriptor for snic */
+struct snic_sg_desc {
+ __le64 addr;
+ __le32 len;
+ u32 _resvd;
+};
+
+struct snic_dflt_sgl {
+ struct snic_sg_desc sg_desc[SNIC_DFLT_SG_DESC_CNT];
+};
+
+struct snic_max_sgl {
+ struct snic_sg_desc sg_desc[SNIC_MAX_SG_DESC_CNT];
+};
+
+enum snic_req_cache_type {
+ SNIC_REQ_CACHE_DFLT_SGL = 0, /* cache with default size sgl */
+ SNIC_REQ_CACHE_MAX_SGL, /* cache with max size sgl */
+ SNIC_REQ_TM_CACHE, /* cache for task mgmt reqs contains
+ snic_host_req objects only*/
+ SNIC_REQ_MAX_CACHES /* number of sgl caches */
+};
+
+/* Per IO internal state */
+struct snic_internal_io_state {
+ char *rqi;
+ u64 flags;
+ u32 state;
+ u32 abts_status; /* Abort completion status */
+ u32 lr_status; /* device reset completion status */
+};
+
+/* IO state machine */
+enum snic_ioreq_state {
+ SNIC_IOREQ_NOT_INITED = 0,
+ SNIC_IOREQ_PENDING,
+ SNIC_IOREQ_ABTS_PENDING,
+ SNIC_IOREQ_ABTS_COMPLETE,
+ SNIC_IOREQ_LR_PENDING,
+ SNIC_IOREQ_LR_COMPLETE,
+ SNIC_IOREQ_COMPLETE,
+};
+
+struct snic;
+struct snic_host_req;
+
+/*
+ * snic_req_info : Contains info about IO, one per scsi command.
+ * Notes: Make sure that the structure is aligned to 16 B
+ * this helps in easy access to snic_req_info from snic_host_req
+ */
+struct snic_req_info {
+ struct list_head list;
+ struct snic_host_req *req;
+ u64 start_time; /* start time in jiffies */
+ u16 rq_pool_type; /* noticion of request pool type */
+ u16 req_len; /* buf len passing to fw (req + sgl)*/
+ u32 tgt_id;
+
+ u32 tm_tag;
+ u8 io_cmpl:1; /* sets to 1 when fw completes IO */
+ u8 resvd[3];
+ struct scsi_cmnd *sc; /* Associated scsi cmd */
+ struct snic *snic; /* Associated snic */
+ ulong sge_va; /* Pointer to Resp Buffer */
+ u64 snsbuf_va;
+
+ struct snic_host_req *abort_req;
+ struct completion *abts_done;
+
+ struct snic_host_req *dr_req;
+ struct completion *dr_done;
+};
+
+
+#define rqi_to_req(rqi) \
+ ((struct snic_host_req *) (((struct snic_req_info *)rqi)->req))
+
+#define req_to_rqi(req) \
+ ((struct snic_req_info *) (((struct snic_host_req *)req)->hdr.init_ctx))
+
+#define req_to_sgl(req) \
+ ((struct snic_sg_desc *) (((struct snic_host_req *)req)+1))
+
+struct snic_req_info *
+snic_req_init(struct snic *, int sg_cnt);
+void snic_req_free(struct snic *, struct snic_req_info *);
+void snic_calc_io_process_time(struct snic *, struct snic_req_info *);
+void snic_pci_unmap_rsp_buf(struct snic *, struct snic_req_info *);
+struct snic_host_req *
+snic_abort_req_init(struct snic *, struct snic_req_info *);
+struct snic_host_req *
+snic_dr_req_init(struct snic *, struct snic_req_info *);
+#endif /* _SNIC_IO_H */
diff --git a/drivers/scsi/snic/snic_isr.c b/drivers/scsi/snic/snic_isr.c
new file mode 100644
index 000000000000..a85fae25ec8c
--- /dev/null
+++ b/drivers/scsi/snic/snic_isr.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include "vnic_dev.h"
+#include "vnic_intr.h"
+#include "vnic_stats.h"
+#include "snic_io.h"
+#include "snic.h"
+
+
+/*
+ * snic_isr_msix_wq : MSIx ISR for work queue.
+ */
+
+static irqreturn_t
+snic_isr_msix_wq(int irq, void *data)
+{
+ struct snic *snic = data;
+ unsigned long wq_work_done = 0;
+
+ snic->s_stats.misc.last_isr_time = jiffies;
+ atomic64_inc(&snic->s_stats.misc.isr_cnt);
+
+ wq_work_done = snic_wq_cmpl_handler(snic, -1);
+ svnic_intr_return_credits(&snic->intr[SNIC_MSIX_WQ],
+ wq_work_done,
+ 1 /* unmask intr */,
+ 1 /* reset intr timer */);
+
+ return IRQ_HANDLED;
+} /* end of snic_isr_msix_wq */
+
+static irqreturn_t
+snic_isr_msix_io_cmpl(int irq, void *data)
+{
+ struct snic *snic = data;
+ unsigned long iocmpl_work_done = 0;
+
+ snic->s_stats.misc.last_isr_time = jiffies;
+ atomic64_inc(&snic->s_stats.misc.isr_cnt);
+
+ iocmpl_work_done = snic_fwcq_cmpl_handler(snic, -1);
+ svnic_intr_return_credits(&snic->intr[SNIC_MSIX_IO_CMPL],
+ iocmpl_work_done,
+ 1 /* unmask intr */,
+ 1 /* reset intr timer */);
+
+ return IRQ_HANDLED;
+} /* end of snic_isr_msix_io_cmpl */
+
+static irqreturn_t
+snic_isr_msix_err_notify(int irq, void *data)
+{
+ struct snic *snic = data;
+
+ snic->s_stats.misc.last_isr_time = jiffies;
+ atomic64_inc(&snic->s_stats.misc.isr_cnt);
+
+ svnic_intr_return_all_credits(&snic->intr[SNIC_MSIX_ERR_NOTIFY]);
+ snic_log_q_error(snic);
+
+ /*Handling link events */
+ snic_handle_link_event(snic);
+
+ return IRQ_HANDLED;
+} /* end of snic_isr_msix_err_notify */
+
+
+void
+snic_free_intr(struct snic *snic)
+{
+ int i;
+
+ /* ONLY interrupt mode MSIX is supported */
+ for (i = 0; i < ARRAY_SIZE(snic->msix); i++) {
+ if (snic->msix[i].requested) {
+ free_irq(snic->msix_entry[i].vector,
+ snic->msix[i].devid);
+ }
+ }
+} /* end of snic_free_intr */
+
+int
+snic_request_intr(struct snic *snic)
+{
+ int ret = 0, i;
+ enum vnic_dev_intr_mode intr_mode;
+
+ intr_mode = svnic_dev_get_intr_mode(snic->vdev);
+ SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
+
+ /*
+ * Currently HW supports single WQ and CQ. So passing devid as snic.
+ * When hardware supports multiple WQs and CQs, one idea is
+ * to pass devid as corresponding WQ or CQ ptr and retrieve snic
+ * from queue ptr.
+ * Except for err_notify, which is always one.
+ */
+ sprintf(snic->msix[SNIC_MSIX_WQ].devname,
+ "%.11s-scsi-wq",
+ snic->name);
+ snic->msix[SNIC_MSIX_WQ].isr = snic_isr_msix_wq;
+ snic->msix[SNIC_MSIX_WQ].devid = snic;
+
+ sprintf(snic->msix[SNIC_MSIX_IO_CMPL].devname,
+ "%.11s-io-cmpl",
+ snic->name);
+ snic->msix[SNIC_MSIX_IO_CMPL].isr = snic_isr_msix_io_cmpl;
+ snic->msix[SNIC_MSIX_IO_CMPL].devid = snic;
+
+ sprintf(snic->msix[SNIC_MSIX_ERR_NOTIFY].devname,
+ "%.11s-err-notify",
+ snic->name);
+ snic->msix[SNIC_MSIX_ERR_NOTIFY].isr = snic_isr_msix_err_notify;
+ snic->msix[SNIC_MSIX_ERR_NOTIFY].devid = snic;
+
+ for (i = 0; i < ARRAY_SIZE(snic->msix); i++) {
+ ret = request_irq(snic->msix_entry[i].vector,
+ snic->msix[i].isr,
+ 0,
+ snic->msix[i].devname,
+ snic->msix[i].devid);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "MSI-X: requrest_irq(%d) failed %d\n",
+ i,
+ ret);
+ snic_free_intr(snic);
+ break;
+ }
+ snic->msix[i].requested = 1;
+ }
+
+ return ret;
+} /* end of snic_requrest_intr */
+
+int
+snic_set_intr_mode(struct snic *snic)
+{
+ unsigned int n = ARRAY_SIZE(snic->wq);
+ unsigned int m = SNIC_CQ_IO_CMPL_MAX;
+ unsigned int i;
+
+ /*
+ * We need n WQs, m CQs, and n+m+1 INTRs
+ * (last INTR is used for WQ/CQ errors and notification area
+ */
+
+ BUILD_BUG_ON((ARRAY_SIZE(snic->wq) + SNIC_CQ_IO_CMPL_MAX) >
+ ARRAY_SIZE(snic->intr));
+ SNIC_BUG_ON(ARRAY_SIZE(snic->msix_entry) < (n + m + 1));
+
+ for (i = 0; i < (n + m + 1); i++)
+ snic->msix_entry[i].entry = i;
+
+ if (snic->wq_count >= n && snic->cq_count >= (n + m)) {
+ if (!pci_enable_msix(snic->pdev,
+ snic->msix_entry,
+ (n + m + 1))) {
+ snic->wq_count = n;
+ snic->cq_count = n + m;
+ snic->intr_count = n + m + 1;
+ snic->err_intr_offset = SNIC_MSIX_ERR_NOTIFY;
+
+ SNIC_ISR_DBG(snic->shost,
+ "Using MSI-X Interrupts\n");
+ svnic_dev_set_intr_mode(snic->vdev,
+ VNIC_DEV_INTR_MODE_MSIX);
+
+ return 0;
+ }
+ }
+
+ svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN);
+
+ return -EINVAL;
+} /* end of snic_set_intr_mode */
+
+void
+snic_clear_intr_mode(struct snic *snic)
+{
+ pci_disable_msix(snic->pdev);
+
+ svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_INTX);
+}
diff --git a/drivers/scsi/snic/snic_main.c b/drivers/scsi/snic/snic_main.c
new file mode 100644
index 000000000000..b2b87cef00fc
--- /dev/null
+++ b/drivers/scsi/snic/snic_main.c
@@ -0,0 +1,1044 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/mempool.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+
+#include "snic.h"
+#include "snic_fwint.h"
+
+#define PCI_DEVICE_ID_CISCO_SNIC 0x0046
+
+/* Supported devices by snic module */
+static struct pci_device_id snic_id_table[] = {
+ {PCI_DEVICE(0x1137, PCI_DEVICE_ID_CISCO_SNIC) },
+ { 0, } /* end of table */
+};
+
+unsigned int snic_log_level = 0x0;
+module_param(snic_log_level, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(snic_log_level, "bitmask for snic logging levels");
+
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+unsigned int snic_trace_max_pages = 16;
+module_param(snic_trace_max_pages, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(snic_trace_max_pages,
+ "Total allocated memory pages for snic trace buffer");
+
+#endif
+unsigned int snic_max_qdepth = SNIC_DFLT_QUEUE_DEPTH;
+module_param(snic_max_qdepth, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(snic_max_qdepth, "Queue depth to report for each LUN");
+
+/*
+ * snic_slave_alloc : callback function to SCSI Mid Layer, called on
+ * scsi device initialization.
+ */
+static int
+snic_slave_alloc(struct scsi_device *sdev)
+{
+ struct snic_tgt *tgt = starget_to_tgt(scsi_target(sdev));
+
+ if (!tgt || snic_tgt_chkready(tgt))
+ return -ENXIO;
+
+ return 0;
+}
+
+/*
+ * snic_slave_configure : callback function to SCSI Mid Layer, called on
+ * scsi device initialization.
+ */
+static int
+snic_slave_configure(struct scsi_device *sdev)
+{
+ struct snic *snic = shost_priv(sdev->host);
+ u32 qdepth = 0, max_ios = 0;
+ int tmo = SNIC_DFLT_CMD_TIMEOUT * HZ;
+
+ /* Set Queue Depth */
+ max_ios = snic_max_qdepth;
+ qdepth = min_t(u32, max_ios, SNIC_MAX_QUEUE_DEPTH);
+ scsi_change_queue_depth(sdev, qdepth);
+
+ if (snic->fwinfo.io_tmo > 1)
+ tmo = snic->fwinfo.io_tmo * HZ;
+
+ /* FW requires extended timeouts */
+ blk_queue_rq_timeout(sdev->request_queue, tmo);
+
+ return 0;
+}
+
+static int
+snic_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+ int qsz = 0;
+
+ qsz = min_t(u32, qdepth, SNIC_MAX_QUEUE_DEPTH);
+ scsi_change_queue_depth(sdev, qsz);
+ SNIC_INFO("QDepth Changed to %d\n", sdev->queue_depth);
+
+ return sdev->queue_depth;
+}
+
+static struct scsi_host_template snic_host_template = {
+ .module = THIS_MODULE,
+ .name = SNIC_DRV_NAME,
+ .queuecommand = snic_queuecommand,
+ .eh_abort_handler = snic_abort_cmd,
+ .eh_device_reset_handler = snic_device_reset,
+ .eh_host_reset_handler = snic_host_reset,
+ .slave_alloc = snic_slave_alloc,
+ .slave_configure = snic_slave_configure,
+ .change_queue_depth = snic_change_queue_depth,
+ .this_id = -1,
+ .cmd_per_lun = SNIC_DFLT_QUEUE_DEPTH,
+ .can_queue = SNIC_MAX_IO_REQ,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = SNIC_MAX_SG_DESC_CNT,
+ .max_sectors = 0x800,
+ .shost_attrs = snic_attrs,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct snic_internal_io_state),
+ .proc_name = "snic_scsi",
+};
+
+/*
+ * snic_handle_link_event : Handles link events such as link up/down/error
+ */
+void
+snic_handle_link_event(struct snic *snic)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&snic->snic_lock, flags);
+ if (snic->stop_link_events) {
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+ return;
+ }
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+ queue_work(snic_glob->event_q, &snic->link_work);
+} /* end of snic_handle_link_event */
+
+/*
+ * snic_notify_set : sets notification area
+ * This notification area is to receive events from fw
+ * Note: snic supports only MSIX interrupts, in which we can just call
+ * svnic_dev_notify_set directly
+ */
+static int
+snic_notify_set(struct snic *snic)
+{
+ int ret = 0;
+ enum vnic_dev_intr_mode intr_mode;
+
+ intr_mode = svnic_dev_get_intr_mode(snic->vdev);
+
+ if (intr_mode == VNIC_DEV_INTR_MODE_MSIX) {
+ ret = svnic_dev_notify_set(snic->vdev, SNIC_MSIX_ERR_NOTIFY);
+ } else {
+ SNIC_HOST_ERR(snic->shost,
+ "Interrupt mode should be setup before devcmd notify set %d\n",
+ intr_mode);
+ ret = -1;
+ }
+
+ return ret;
+} /* end of snic_notify_set */
+
+/*
+ * snic_dev_wait : polls vnic open status.
+ */
+static int
+snic_dev_wait(struct vnic_dev *vdev,
+ int (*start)(struct vnic_dev *, int),
+ int (*finished)(struct vnic_dev *, int *),
+ int arg)
+{
+ unsigned long time;
+ int ret, done;
+ int retry_cnt = 0;
+
+ ret = start(vdev, arg);
+ if (ret)
+ return ret;
+
+ /*
+ * Wait for func to complete...2 seconds max.
+ *
+ * Sometimes schedule_timeout_uninterruptible take long time
+ * to wakeup, which results skipping retry. The retry counter
+ * ensures to retry at least two times.
+ */
+ time = jiffies + (HZ * 2);
+ do {
+ ret = finished(vdev, &done);
+ if (ret)
+ return ret;
+
+ if (done)
+ return 0;
+ schedule_timeout_uninterruptible(HZ/10);
+ ++retry_cnt;
+ } while (time_after(time, jiffies) || (retry_cnt < 3));
+
+ return -ETIMEDOUT;
+} /* end of snic_dev_wait */
+
+/*
+ * snic_cleanup: called by snic_remove
+ * Stops the snic device, masks all interrupts, Completed CQ entries are
+ * drained. Posted WQ/RQ/Copy-WQ entries are cleanup
+ */
+static int
+snic_cleanup(struct snic *snic)
+{
+ unsigned int i;
+ int ret;
+
+ svnic_dev_disable(snic->vdev);
+ for (i = 0; i < snic->intr_count; i++)
+ svnic_intr_mask(&snic->intr[i]);
+
+ for (i = 0; i < snic->wq_count; i++) {
+ ret = svnic_wq_disable(&snic->wq[i]);
+ if (ret)
+ return ret;
+ }
+
+ /* Clean up completed IOs */
+ snic_fwcq_cmpl_handler(snic, -1);
+
+ snic_wq_cmpl_handler(snic, -1);
+
+ /* Clean up the IOs that have not completed */
+ for (i = 0; i < snic->wq_count; i++)
+ svnic_wq_clean(&snic->wq[i], snic_free_wq_buf);
+
+ for (i = 0; i < snic->cq_count; i++)
+ svnic_cq_clean(&snic->cq[i]);
+
+ for (i = 0; i < snic->intr_count; i++)
+ svnic_intr_clean(&snic->intr[i]);
+
+ /* Cleanup snic specific requests */
+ snic_free_all_untagged_reqs(snic);
+
+ /* Cleanup Pending SCSI commands */
+ snic_shutdown_scsi_cleanup(snic);
+
+ for (i = 0; i < SNIC_REQ_MAX_CACHES; i++)
+ mempool_destroy(snic->req_pool[i]);
+
+ return 0;
+} /* end of snic_cleanup */
+
+
+static void
+snic_iounmap(struct snic *snic)
+{
+ if (snic->bar0.vaddr)
+ iounmap(snic->bar0.vaddr);
+}
+
+/*
+ * snic_vdev_open_done : polls for svnic_dev_open cmd completion.
+ */
+static int
+snic_vdev_open_done(struct vnic_dev *vdev, int *done)
+{
+ struct snic *snic = svnic_dev_priv(vdev);
+ int ret;
+ int nretries = 5;
+
+ do {
+ ret = svnic_dev_open_done(vdev, done);
+ if (ret == 0)
+ break;
+
+ SNIC_HOST_INFO(snic->shost, "VNIC_DEV_OPEN Timedout.\n");
+ } while (nretries--);
+
+ return ret;
+} /* end of snic_vdev_open_done */
+
+/*
+ * snic_add_host : registers scsi host with ML
+ */
+static int
+snic_add_host(struct Scsi_Host *shost, struct pci_dev *pdev)
+{
+ int ret = 0;
+
+ ret = scsi_add_host(shost, &pdev->dev);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "snic: scsi_add_host failed. %d\n",
+ ret);
+
+ return ret;
+ }
+
+ SNIC_BUG_ON(shost->work_q != NULL);
+ snprintf(shost->work_q_name, sizeof(shost->work_q_name), "scsi_wq_%d",
+ shost->host_no);
+ shost->work_q = create_singlethread_workqueue(shost->work_q_name);
+ if (!shost->work_q) {
+ SNIC_HOST_ERR(shost, "Failed to Create ScsiHost wq.\n");
+
+ ret = -ENOMEM;
+ }
+
+ return ret;
+} /* end of snic_add_host */
+
+static void
+snic_del_host(struct Scsi_Host *shost)
+{
+ if (!shost->work_q)
+ return;
+
+ destroy_workqueue(shost->work_q);
+ shost->work_q = NULL;
+ scsi_remove_host(shost);
+}
+
+int
+snic_get_state(struct snic *snic)
+{
+ return atomic_read(&snic->state);
+}
+
+void
+snic_set_state(struct snic *snic, enum snic_state state)
+{
+ SNIC_HOST_INFO(snic->shost, "snic state change from %s to %s\n",
+ snic_state_to_str(snic_get_state(snic)),
+ snic_state_to_str(state));
+
+ atomic_set(&snic->state, state);
+}
+
+/*
+ * snic_probe : Initialize the snic interface.
+ */
+static int
+snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct Scsi_Host *shost;
+ struct snic *snic;
+ mempool_t *pool;
+ unsigned long flags;
+ u32 max_ios = 0;
+ int ret, i;
+
+ /* Device Information */
+ SNIC_INFO("snic device %4x:%4x:%4x:%4x: ",
+ pdev->vendor, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+
+ SNIC_INFO("snic device bus %x: slot %x: fn %x\n",
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn));
+
+ /*
+ * Allocate SCSI Host and setup association between host, and snic
+ */
+ shost = scsi_host_alloc(&snic_host_template, sizeof(struct snic));
+ if (!shost) {
+ SNIC_ERR("Unable to alloc scsi_host\n");
+ ret = -ENOMEM;
+
+ goto prob_end;
+ }
+ snic = shost_priv(shost);
+ snic->shost = shost;
+
+ snprintf(snic->name, sizeof(snic->name) - 1, "%s%d", SNIC_DRV_NAME,
+ shost->host_no);
+
+ SNIC_HOST_INFO(shost,
+ "snic%d = %p shost = %p device bus %x: slot %x: fn %x\n",
+ shost->host_no, snic, shost, pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+ /* Per snic debugfs init */
+ ret = snic_stats_debugfs_init(snic);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "Failed to initialize debugfs stats\n");
+ snic_stats_debugfs_remove(snic);
+ }
+#endif
+
+ /* Setup PCI Resources */
+ pci_set_drvdata(pdev, snic);
+ snic->pdev = pdev;
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Cannot enable PCI Resources, aborting : %d\n",
+ ret);
+
+ goto err_free_snic;
+ }
+
+ ret = pci_request_regions(pdev, SNIC_DRV_NAME);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Cannot obtain PCI Resources, aborting : %d\n",
+ ret);
+
+ goto err_pci_disable;
+ }
+
+ pci_set_master(pdev);
+
+ /*
+ * Query PCI Controller on system for DMA addressing
+ * limitation for the device. Try 43-bit first, and
+ * fail to 32-bit.
+ */
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(43));
+ if (ret) {
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "No Usable DMA Configuration, aborting %d\n",
+ ret);
+
+ goto err_rel_regions;
+ }
+
+ ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Unable to obtain 32-bit DMA for consistent allocations, aborting: %d\n",
+ ret);
+
+ goto err_rel_regions;
+ }
+ } else {
+ ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(43));
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Unable to obtain 43-bit DMA for consistent allocations. aborting: %d\n",
+ ret);
+
+ goto err_rel_regions;
+ }
+ }
+
+
+ /* Map vNIC resources from BAR0 */
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+ SNIC_HOST_ERR(shost, "BAR0 not memory mappable aborting.\n");
+
+ ret = -ENODEV;
+ goto err_rel_regions;
+ }
+
+ snic->bar0.vaddr = pci_iomap(pdev, 0, 0);
+ if (!snic->bar0.vaddr) {
+ SNIC_HOST_ERR(shost,
+ "Cannot memory map BAR0 res hdr aborting.\n");
+
+ ret = -ENODEV;
+ goto err_rel_regions;
+ }
+
+ snic->bar0.bus_addr = pci_resource_start(pdev, 0);
+ snic->bar0.len = pci_resource_len(pdev, 0);
+ SNIC_BUG_ON(snic->bar0.bus_addr == 0);
+
+ /* Devcmd2 Resource Allocation and Initialization */
+ snic->vdev = svnic_dev_alloc_discover(NULL, snic, pdev, &snic->bar0, 1);
+ if (!snic->vdev) {
+ SNIC_HOST_ERR(shost, "vNIC Resource Discovery Failed.\n");
+
+ ret = -ENODEV;
+ goto err_iounmap;
+ }
+
+ ret = svnic_dev_cmd_init(snic->vdev, 0);
+ if (ret) {
+ SNIC_HOST_INFO(shost, "Devcmd2 Init Failed. err = %d\n", ret);
+
+ goto err_vnic_unreg;
+ }
+
+ ret = snic_dev_wait(snic->vdev, svnic_dev_open, snic_vdev_open_done, 0);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "vNIC dev open failed, aborting. %d\n",
+ ret);
+
+ goto err_vnic_unreg;
+ }
+
+ ret = svnic_dev_init(snic->vdev, 0);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "vNIC dev init failed. aborting. %d\n",
+ ret);
+
+ goto err_dev_close;
+ }
+
+ /* Get vNIC information */
+ ret = snic_get_vnic_config(snic);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Get vNIC configuration failed, aborting. %d\n",
+ ret);
+
+ goto err_dev_close;
+ }
+
+ /* Configure Maximum Outstanding IO reqs */
+ max_ios = snic->config.io_throttle_count;
+ if (max_ios != SNIC_UCSM_DFLT_THROTTLE_CNT_BLD)
+ shost->can_queue = min_t(u32, SNIC_MAX_IO_REQ,
+ max_t(u32, SNIC_MIN_IO_REQ, max_ios));
+
+ snic->max_tag_id = shost->can_queue;
+
+ ret = scsi_init_shared_tag_map(shost, snic->max_tag_id);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Unable to alloc shared tag map. %d\n",
+ ret);
+
+ goto err_dev_close;
+ }
+
+ shost->max_lun = snic->config.luns_per_tgt;
+ shost->max_id = SNIC_MAX_TARGET;
+
+ shost->max_cmd_len = MAX_COMMAND_SIZE; /*defined in scsi_cmnd.h*/
+
+ snic_get_res_counts(snic);
+
+ /*
+ * Assumption: Only MSIx is supported
+ */
+ ret = snic_set_intr_mode(snic);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Failed to set intr mode aborting. %d\n",
+ ret);
+
+ goto err_dev_close;
+ }
+
+ ret = snic_alloc_vnic_res(snic);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Failed to alloc vNIC resources aborting. %d\n",
+ ret);
+
+ goto err_clear_intr;
+ }
+
+ /* Initialize specific lists */
+ INIT_LIST_HEAD(&snic->list);
+
+ /*
+ * spl_cmd_list for maintaining snic specific cmds
+ * such as EXCH_VER_REQ, REPORT_TARGETS etc
+ */
+ INIT_LIST_HEAD(&snic->spl_cmd_list);
+ spin_lock_init(&snic->spl_cmd_lock);
+
+ /* initialize all snic locks */
+ spin_lock_init(&snic->snic_lock);
+
+ for (i = 0; i < SNIC_WQ_MAX; i++)
+ spin_lock_init(&snic->wq_lock[i]);
+
+ for (i = 0; i < SNIC_IO_LOCKS; i++)
+ spin_lock_init(&snic->io_req_lock[i]);
+
+ pool = mempool_create_slab_pool(2,
+ snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]);
+ if (!pool) {
+ SNIC_HOST_ERR(shost, "dflt sgl pool creation failed\n");
+
+ goto err_free_res;
+ }
+
+ snic->req_pool[SNIC_REQ_CACHE_DFLT_SGL] = pool;
+
+ pool = mempool_create_slab_pool(2,
+ snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]);
+ if (!pool) {
+ SNIC_HOST_ERR(shost, "max sgl pool creation failed\n");
+
+ goto err_free_dflt_sgl_pool;
+ }
+
+ snic->req_pool[SNIC_REQ_CACHE_MAX_SGL] = pool;
+
+ pool = mempool_create_slab_pool(2,
+ snic_glob->req_cache[SNIC_REQ_TM_CACHE]);
+ if (!pool) {
+ SNIC_HOST_ERR(shost, "snic tmreq info pool creation failed.\n");
+
+ goto err_free_max_sgl_pool;
+ }
+
+ snic->req_pool[SNIC_REQ_TM_CACHE] = pool;
+
+ /* Initialize snic state */
+ atomic_set(&snic->state, SNIC_INIT);
+
+ atomic_set(&snic->ios_inflight, 0);
+
+ /* Setup notification buffer area */
+ ret = snic_notify_set(snic);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Failed to alloc notify buffer aborting. %d\n",
+ ret);
+
+ goto err_free_tmreq_pool;
+ }
+
+ /*
+ * Initialization done with PCI system, hardware, firmware.
+ * Add shost to SCSI
+ */
+ ret = snic_add_host(shost, pdev);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Adding scsi host Failed ... exiting. %d\n",
+ ret);
+
+ goto err_notify_unset;
+ }
+
+ spin_lock_irqsave(&snic_glob->snic_list_lock, flags);
+ list_add_tail(&snic->list, &snic_glob->snic_list);
+ spin_unlock_irqrestore(&snic_glob->snic_list_lock, flags);
+
+ snic_disc_init(&snic->disc);
+ INIT_WORK(&snic->tgt_work, snic_handle_tgt_disc);
+ INIT_WORK(&snic->disc_work, snic_handle_disc);
+ INIT_WORK(&snic->link_work, snic_handle_link);
+
+ /* Enable all queues */
+ for (i = 0; i < snic->wq_count; i++)
+ svnic_wq_enable(&snic->wq[i]);
+
+ ret = svnic_dev_enable_wait(snic->vdev);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "vNIC dev enable failed w/ error %d\n",
+ ret);
+
+ goto err_vdev_enable;
+ }
+
+ ret = snic_request_intr(snic);
+ if (ret) {
+ SNIC_HOST_ERR(shost, "Unable to request irq. %d\n", ret);
+
+ goto err_req_intr;
+ }
+
+ for (i = 0; i < snic->intr_count; i++)
+ svnic_intr_unmask(&snic->intr[i]);
+
+ snic_set_state(snic, SNIC_ONLINE);
+
+ /* Get snic params */
+ ret = snic_get_conf(snic);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "Failed to get snic io config from FW w err %d\n",
+ ret);
+
+ goto err_get_conf;
+ }
+
+ ret = snic_disc_start(snic);
+ if (ret) {
+ SNIC_HOST_ERR(shost, "snic_probe:Discovery Failed w err = %d\n",
+ ret);
+
+ goto err_get_conf;
+ }
+
+ SNIC_HOST_INFO(shost, "SNIC Device Probe Successful.\n");
+
+ return 0;
+
+err_get_conf:
+ snic_free_all_untagged_reqs(snic);
+
+ for (i = 0; i < snic->intr_count; i++)
+ svnic_intr_mask(&snic->intr[i]);
+
+ snic_free_intr(snic);
+
+err_req_intr:
+ svnic_dev_disable(snic->vdev);
+
+err_vdev_enable:
+ for (i = 0; i < snic->wq_count; i++) {
+ int rc = 0;
+
+ rc = svnic_wq_disable(&snic->wq[i]);
+ if (rc) {
+ SNIC_HOST_ERR(shost,
+ "WQ Disable Failed w/ err = %d\n", rc);
+
+ break;
+ }
+ }
+ snic_del_host(snic->shost);
+
+err_notify_unset:
+ svnic_dev_notify_unset(snic->vdev);
+
+err_free_tmreq_pool:
+ mempool_destroy(snic->req_pool[SNIC_REQ_TM_CACHE]);
+
+err_free_max_sgl_pool:
+ mempool_destroy(snic->req_pool[SNIC_REQ_CACHE_MAX_SGL]);
+
+err_free_dflt_sgl_pool:
+ mempool_destroy(snic->req_pool[SNIC_REQ_CACHE_DFLT_SGL]);
+
+err_free_res:
+ snic_free_vnic_res(snic);
+
+err_clear_intr:
+ snic_clear_intr_mode(snic);
+
+err_dev_close:
+ svnic_dev_close(snic->vdev);
+
+err_vnic_unreg:
+ svnic_dev_unregister(snic->vdev);
+
+err_iounmap:
+ snic_iounmap(snic);
+
+err_rel_regions:
+ pci_release_regions(pdev);
+
+err_pci_disable:
+ pci_disable_device(pdev);
+
+err_free_snic:
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+ snic_stats_debugfs_remove(snic);
+#endif
+ scsi_host_put(shost);
+ pci_set_drvdata(pdev, NULL);
+
+prob_end:
+ SNIC_INFO("sNIC device : bus %d: slot %d: fn %d Registration Failed.\n",
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn));
+
+ return ret;
+} /* end of snic_probe */
+
+
+/*
+ * snic_remove : invoked on unbinding the interface to cleanup the
+ * resources allocated in snic_probe on initialization.
+ */
+static void
+snic_remove(struct pci_dev *pdev)
+{
+ struct snic *snic = pci_get_drvdata(pdev);
+ unsigned long flags;
+
+ if (!snic) {
+ SNIC_INFO("sNIC dev: bus %d slot %d fn %d snic inst is null.\n",
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn));
+
+ return;
+ }
+
+ /*
+ * Mark state so that the workqueue thread stops forwarding
+ * received frames and link events. ISR and other threads
+ * that can queue work items will also stop creating work
+ * items on the snic workqueue
+ */
+ snic_set_state(snic, SNIC_OFFLINE);
+ spin_lock_irqsave(&snic->snic_lock, flags);
+ snic->stop_link_events = 1;
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+ flush_workqueue(snic_glob->event_q);
+ snic_disc_term(snic);
+
+ spin_lock_irqsave(&snic->snic_lock, flags);
+ snic->in_remove = 1;
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+ /*
+ * This stops the snic device, masks all interrupts, Completed
+ * CQ entries are drained. Posted WQ/RQ/Copy-WQ entries are
+ * cleanup
+ */
+ snic_cleanup(snic);
+
+ spin_lock_irqsave(&snic_glob->snic_list_lock, flags);
+ list_del(&snic->list);
+ spin_unlock_irqrestore(&snic_glob->snic_list_lock, flags);
+
+ snic_tgt_del_all(snic);
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+ snic_stats_debugfs_remove(snic);
+#endif
+ snic_del_host(snic->shost);
+
+ svnic_dev_notify_unset(snic->vdev);
+ snic_free_intr(snic);
+ snic_free_vnic_res(snic);
+ snic_clear_intr_mode(snic);
+ svnic_dev_close(snic->vdev);
+ svnic_dev_unregister(snic->vdev);
+ snic_iounmap(snic);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+
+ /* this frees Scsi_Host and snic memory (continuous chunk) */
+ scsi_host_put(snic->shost);
+} /* end of snic_remove */
+
+
+struct snic_global *snic_glob;
+
+/*
+ * snic_global_data_init: Initialize SNIC Global Data
+ * Notes: All the global lists, variables should be part of global data
+ * this helps in debugging.
+ */
+static int
+snic_global_data_init(void)
+{
+ int ret = 0;
+ struct kmem_cache *cachep;
+ ssize_t len = 0;
+
+ snic_glob = kzalloc(sizeof(*snic_glob), GFP_KERNEL);
+
+ if (!snic_glob) {
+ SNIC_ERR("Failed to allocate Global Context.\n");
+
+ ret = -ENOMEM;
+ goto gdi_end;
+ }
+
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+ /* Debugfs related Initialization */
+ /* Create debugfs entries for snic */
+ ret = snic_debugfs_init();
+ if (ret < 0) {
+ SNIC_ERR("Failed to create sysfs dir for tracing and stats.\n");
+ snic_debugfs_term();
+ /* continue even if it fails */
+ }
+
+ /* Trace related Initialization */
+ /* Allocate memory for trace buffer */
+ ret = snic_trc_init();
+ if (ret < 0) {
+ SNIC_ERR("Trace buffer init failed, SNIC tracing disabled\n");
+ snic_trc_free();
+ /* continue even if it fails */
+ }
+
+#endif
+ INIT_LIST_HEAD(&snic_glob->snic_list);
+ spin_lock_init(&snic_glob->snic_list_lock);
+
+ /* Create a cache for allocation of snic_host_req+default size ESGLs */
+ len = sizeof(struct snic_req_info);
+ len += sizeof(struct snic_host_req) + sizeof(struct snic_dflt_sgl);
+ cachep = kmem_cache_create("snic_req_dfltsgl", len, SNIC_SG_DESC_ALIGN,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!cachep) {
+ SNIC_ERR("Failed to create snic default sgl slab\n");
+ ret = -ENOMEM;
+
+ goto err_dflt_req_slab;
+ }
+ snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL] = cachep;
+
+ /* Create a cache for allocation of max size Extended SGLs */
+ len = sizeof(struct snic_req_info);
+ len += sizeof(struct snic_host_req) + sizeof(struct snic_max_sgl);
+ cachep = kmem_cache_create("snic_req_maxsgl", len, SNIC_SG_DESC_ALIGN,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!cachep) {
+ SNIC_ERR("Failed to create snic max sgl slab\n");
+ ret = -ENOMEM;
+
+ goto err_max_req_slab;
+ }
+ snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL] = cachep;
+
+ len = sizeof(struct snic_host_req);
+ cachep = kmem_cache_create("snic_req_maxsgl", len, SNIC_SG_DESC_ALIGN,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!cachep) {
+ SNIC_ERR("Failed to create snic tm req slab\n");
+ ret = -ENOMEM;
+
+ goto err_tmreq_slab;
+ }
+ snic_glob->req_cache[SNIC_REQ_TM_CACHE] = cachep;
+
+ /* snic_event queue */
+ snic_glob->event_q = create_singlethread_workqueue("snic_event_wq");
+ if (!snic_glob->event_q) {
+ SNIC_ERR("snic event queue create failed\n");
+ ret = -ENOMEM;
+
+ goto err_eventq;
+ }
+
+ return ret;
+
+err_eventq:
+ kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_TM_CACHE]);
+
+err_tmreq_slab:
+ kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]);
+
+err_max_req_slab:
+ kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]);
+
+err_dflt_req_slab:
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+ snic_trc_free();
+ snic_debugfs_term();
+#endif
+ kfree(snic_glob);
+ snic_glob = NULL;
+
+gdi_end:
+ return ret;
+} /* end of snic_glob_init */
+
+/*
+ * snic_global_data_cleanup : Frees SNIC Global Data
+ */
+static void
+snic_global_data_cleanup(void)
+{
+ SNIC_BUG_ON(snic_glob == NULL);
+
+ destroy_workqueue(snic_glob->event_q);
+ kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_TM_CACHE]);
+ kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]);
+ kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]);
+
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+ /* Freeing Trace Resources */
+ snic_trc_free();
+
+ /* Freeing Debugfs Resources */
+ snic_debugfs_term();
+#endif
+ kfree(snic_glob);
+ snic_glob = NULL;
+} /* end of snic_glob_cleanup */
+
+static struct pci_driver snic_driver = {
+ .name = SNIC_DRV_NAME,
+ .id_table = snic_id_table,
+ .probe = snic_probe,
+ .remove = snic_remove,
+};
+
+static int __init
+snic_init_module(void)
+{
+ int ret = 0;
+
+#ifndef __x86_64__
+ SNIC_INFO("SNIC Driver is supported only for x86_64 platforms!\n");
+ add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+#endif
+
+ SNIC_INFO("%s, ver %s\n", SNIC_DRV_DESCRIPTION, SNIC_DRV_VERSION);
+
+ ret = snic_global_data_init();
+ if (ret) {
+ SNIC_ERR("Failed to Initialize Global Data.\n");
+
+ return ret;
+ }
+
+ ret = pci_register_driver(&snic_driver);
+ if (ret < 0) {
+ SNIC_ERR("PCI driver register error\n");
+
+ goto err_pci_reg;
+ }
+
+ return ret;
+
+err_pci_reg:
+ snic_global_data_cleanup();
+
+ return ret;
+}
+
+static void __exit
+snic_cleanup_module(void)
+{
+ pci_unregister_driver(&snic_driver);
+ snic_global_data_cleanup();
+}
+
+module_init(snic_init_module);
+module_exit(snic_cleanup_module);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(SNIC_DRV_DESCRIPTION);
+MODULE_VERSION(SNIC_DRV_VERSION);
+MODULE_DEVICE_TABLE(pci, snic_id_table);
+MODULE_AUTHOR("Narsimhulu Musini <nmusini@cisco.com>, "
+ "Sesidhar Baddela <sebaddel@cisco.com>");
diff --git a/drivers/scsi/snic/snic_res.c b/drivers/scsi/snic/snic_res.c
new file mode 100644
index 000000000000..b54912c8ca0c
--- /dev/null
+++ b/drivers/scsi/snic/snic_res.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+
+#include "wq_enet_desc.h"
+#include "cq_enet_desc.h"
+#include "vnic_resource.h"
+#include "vnic_dev.h"
+#include "vnic_wq.h"
+#include "vnic_cq.h"
+#include "vnic_intr.h"
+#include "vnic_stats.h"
+#include "snic.h"
+
+int
+snic_get_vnic_config(struct snic *snic)
+{
+ struct vnic_snic_config *c = &snic->config;
+ int ret;
+
+#define GET_CONFIG(m) \
+ do { \
+ ret = svnic_dev_spec(snic->vdev, \
+ offsetof(struct vnic_snic_config, m), \
+ sizeof(c->m), \
+ &c->m); \
+ if (ret) { \
+ SNIC_HOST_ERR(snic->shost, \
+ "Error getting %s, %d\n", #m, ret); \
+ return ret; \
+ } \
+ } while (0)
+
+ GET_CONFIG(wq_enet_desc_count);
+ GET_CONFIG(maxdatafieldsize);
+ GET_CONFIG(intr_timer);
+ GET_CONFIG(intr_timer_type);
+ GET_CONFIG(flags);
+ GET_CONFIG(io_throttle_count);
+ GET_CONFIG(port_down_timeout);
+ GET_CONFIG(port_down_io_retries);
+ GET_CONFIG(luns_per_tgt);
+ GET_CONFIG(xpt_type);
+ GET_CONFIG(hid);
+
+ c->wq_enet_desc_count = min_t(u32,
+ VNIC_SNIC_WQ_DESCS_MAX,
+ max_t(u32,
+ VNIC_SNIC_WQ_DESCS_MIN,
+ c->wq_enet_desc_count));
+
+ c->wq_enet_desc_count = ALIGN(c->wq_enet_desc_count, 16);
+
+ c->maxdatafieldsize = min_t(u32,
+ VNIC_SNIC_MAXDATAFIELDSIZE_MAX,
+ max_t(u32,
+ VNIC_SNIC_MAXDATAFIELDSIZE_MIN,
+ c->maxdatafieldsize));
+
+ c->io_throttle_count = min_t(u32,
+ VNIC_SNIC_IO_THROTTLE_COUNT_MAX,
+ max_t(u32,
+ VNIC_SNIC_IO_THROTTLE_COUNT_MIN,
+ c->io_throttle_count));
+
+ c->port_down_timeout = min_t(u32,
+ VNIC_SNIC_PORT_DOWN_TIMEOUT_MAX,
+ c->port_down_timeout);
+
+ c->port_down_io_retries = min_t(u32,
+ VNIC_SNIC_PORT_DOWN_IO_RETRIES_MAX,
+ c->port_down_io_retries);
+
+ c->luns_per_tgt = min_t(u32,
+ VNIC_SNIC_LUNS_PER_TARGET_MAX,
+ max_t(u32,
+ VNIC_SNIC_LUNS_PER_TARGET_MIN,
+ c->luns_per_tgt));
+
+ c->intr_timer = min_t(u32, VNIC_INTR_TIMER_MAX, c->intr_timer);
+
+ SNIC_INFO("vNIC resources wq %d\n", c->wq_enet_desc_count);
+ SNIC_INFO("vNIC mtu %d intr timer %d\n",
+ c->maxdatafieldsize,
+ c->intr_timer);
+
+ SNIC_INFO("vNIC flags 0x%x luns per tgt %d\n",
+ c->flags,
+ c->luns_per_tgt);
+
+ SNIC_INFO("vNIC io throttle count %d\n", c->io_throttle_count);
+ SNIC_INFO("vNIC port down timeout %d port down io retries %d\n",
+ c->port_down_timeout,
+ c->port_down_io_retries);
+
+ SNIC_INFO("vNIC back end type = %d\n", c->xpt_type);
+ SNIC_INFO("vNIC hid = %d\n", c->hid);
+
+ return 0;
+}
+
+void
+snic_get_res_counts(struct snic *snic)
+{
+ snic->wq_count = svnic_dev_get_res_count(snic->vdev, RES_TYPE_WQ);
+ SNIC_BUG_ON(snic->wq_count == 0);
+ snic->cq_count = svnic_dev_get_res_count(snic->vdev, RES_TYPE_CQ);
+ SNIC_BUG_ON(snic->cq_count == 0);
+ snic->intr_count = svnic_dev_get_res_count(snic->vdev,
+ RES_TYPE_INTR_CTRL);
+ SNIC_BUG_ON(snic->intr_count == 0);
+}
+
+void
+snic_free_vnic_res(struct snic *snic)
+{
+ unsigned int i;
+
+ for (i = 0; i < snic->wq_count; i++)
+ svnic_wq_free(&snic->wq[i]);
+
+ for (i = 0; i < snic->cq_count; i++)
+ svnic_cq_free(&snic->cq[i]);
+
+ for (i = 0; i < snic->intr_count; i++)
+ svnic_intr_free(&snic->intr[i]);
+}
+
+int
+snic_alloc_vnic_res(struct snic *snic)
+{
+ enum vnic_dev_intr_mode intr_mode;
+ unsigned int mask_on_assertion;
+ unsigned int intr_offset;
+ unsigned int err_intr_enable;
+ unsigned int err_intr_offset;
+ unsigned int i;
+ int ret;
+
+ intr_mode = svnic_dev_get_intr_mode(snic->vdev);
+
+ SNIC_INFO("vNIC interrupt mode: %s\n",
+ ((intr_mode == VNIC_DEV_INTR_MODE_INTX) ?
+ "Legacy PCI INTx" :
+ ((intr_mode == VNIC_DEV_INTR_MODE_MSI) ?
+ "MSI" :
+ ((intr_mode == VNIC_DEV_INTR_MODE_MSIX) ?
+ "MSI-X" : "Unknown"))));
+
+ /* only MSI-X is supported */
+ SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
+
+ SNIC_INFO("wq %d cq %d intr %d\n", snic->wq_count,
+ snic->cq_count,
+ snic->intr_count);
+
+
+ /* Allocate WQs used for SCSI IOs */
+ for (i = 0; i < snic->wq_count; i++) {
+ ret = svnic_wq_alloc(snic->vdev,
+ &snic->wq[i],
+ i,
+ snic->config.wq_enet_desc_count,
+ sizeof(struct wq_enet_desc));
+ if (ret)
+ goto error_cleanup;
+ }
+
+ /* CQ for each WQ */
+ for (i = 0; i < snic->wq_count; i++) {
+ ret = svnic_cq_alloc(snic->vdev,
+ &snic->cq[i],
+ i,
+ snic->config.wq_enet_desc_count,
+ sizeof(struct cq_enet_wq_desc));
+ if (ret)
+ goto error_cleanup;
+ }
+
+ SNIC_BUG_ON(snic->cq_count != 2 * snic->wq_count);
+ /* CQ for FW TO host */
+ for (i = snic->wq_count; i < snic->cq_count; i++) {
+ ret = svnic_cq_alloc(snic->vdev,
+ &snic->cq[i],
+ i,
+ (snic->config.wq_enet_desc_count * 3),
+ sizeof(struct snic_fw_req));
+ if (ret)
+ goto error_cleanup;
+ }
+
+ for (i = 0; i < snic->intr_count; i++) {
+ ret = svnic_intr_alloc(snic->vdev, &snic->intr[i], i);
+ if (ret)
+ goto error_cleanup;
+ }
+
+ /*
+ * Init WQ Resources.
+ * WQ[0 to n] points to CQ[0 to n-1]
+ * firmware to host comm points to CQ[n to m+1]
+ */
+ err_intr_enable = 1;
+ err_intr_offset = snic->err_intr_offset;
+
+ for (i = 0; i < snic->wq_count; i++) {
+ svnic_wq_init(&snic->wq[i],
+ i,
+ err_intr_enable,
+ err_intr_offset);
+ }
+
+ for (i = 0; i < snic->cq_count; i++) {
+ intr_offset = i;
+
+ svnic_cq_init(&snic->cq[i],
+ 0 /* flow_control_enable */,
+ 1 /* color_enable */,
+ 0 /* cq_head */,
+ 0 /* cq_tail */,
+ 1 /* cq_tail_color */,
+ 1 /* interrupt_enable */,
+ 1 /* cq_entry_enable */,
+ 0 /* cq_message_enable */,
+ intr_offset,
+ 0 /* cq_message_addr */);
+ }
+
+ /*
+ * Init INTR resources
+ * Assumption : snic is always in MSI-X mode
+ */
+ SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
+ mask_on_assertion = 1;
+
+ for (i = 0; i < snic->intr_count; i++) {
+ svnic_intr_init(&snic->intr[i],
+ snic->config.intr_timer,
+ snic->config.intr_timer_type,
+ mask_on_assertion);
+ }
+
+ /* init the stats memory by making the first call here */
+ ret = svnic_dev_stats_dump(snic->vdev, &snic->stats);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "svnic_dev_stats_dump failed - x%x\n",
+ ret);
+ goto error_cleanup;
+ }
+
+ /* Clear LIF stats */
+ svnic_dev_stats_clear(snic->vdev);
+ ret = 0;
+
+ return ret;
+
+error_cleanup:
+ snic_free_vnic_res(snic);
+
+ return ret;
+}
+
+void
+snic_log_q_error(struct snic *snic)
+{
+ unsigned int i;
+ u32 err_status;
+
+ for (i = 0; i < snic->wq_count; i++) {
+ err_status = ioread32(&snic->wq[i].ctrl->error_status);
+ if (err_status)
+ SNIC_HOST_ERR(snic->shost,
+ "WQ[%d] error status %d\n",
+ i,
+ err_status);
+ }
+} /* end of snic_log_q_error */
diff --git a/drivers/scsi/snic/snic_res.h b/drivers/scsi/snic/snic_res.h
new file mode 100644
index 000000000000..273f72f2a023
--- /dev/null
+++ b/drivers/scsi/snic/snic_res.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __SNIC_RES_H
+#define __SNIC_RES_H
+
+#include "snic_io.h"
+#include "wq_enet_desc.h"
+#include "vnic_wq.h"
+#include "snic_fwint.h"
+#include "vnic_cq_fw.h"
+
+static inline void
+snic_icmnd_init(struct snic_host_req *req, u32 cmnd_id, u32 host_id, u64 ctx,
+ u16 flags, u64 tgt_id, u8 *lun, u8 *scsi_cdb, u8 cdb_len,
+ u32 data_len, u16 sg_cnt, ulong sgl_addr,
+ dma_addr_t sns_addr_pa, u32 sense_len)
+{
+ snic_io_hdr_enc(&req->hdr, SNIC_REQ_ICMND, 0, cmnd_id, host_id, sg_cnt,
+ ctx);
+
+ req->u.icmnd.flags = cpu_to_le16(flags);
+ req->u.icmnd.tgt_id = cpu_to_le64(tgt_id);
+ memcpy(&req->u.icmnd.lun_id, lun, LUN_ADDR_LEN);
+ req->u.icmnd.cdb_len = cdb_len;
+ memset(req->u.icmnd.cdb, 0, SNIC_CDB_LEN);
+ memcpy(req->u.icmnd.cdb, scsi_cdb, cdb_len);
+ req->u.icmnd.data_len = cpu_to_le32(data_len);
+ req->u.icmnd.sg_addr = cpu_to_le64(sgl_addr);
+ req->u.icmnd.sense_len = cpu_to_le32(sense_len);
+ req->u.icmnd.sense_addr = cpu_to_le64(sns_addr_pa);
+}
+
+static inline void
+snic_itmf_init(struct snic_host_req *req, u32 cmnd_id, u32 host_id, ulong ctx,
+ u16 flags, u32 req_id, u64 tgt_id, u8 *lun, u8 tm_type)
+{
+ snic_io_hdr_enc(&req->hdr, SNIC_REQ_ITMF, 0, cmnd_id, host_id, 0, ctx);
+
+ req->u.itmf.tm_type = tm_type;
+ req->u.itmf.flags = cpu_to_le16(flags);
+ /* req_id valid only in abort, clear task */
+ req->u.itmf.req_id = cpu_to_le32(req_id);
+ req->u.itmf.tgt_id = cpu_to_le64(tgt_id);
+ memcpy(&req->u.itmf.lun_id, lun, LUN_ADDR_LEN);
+}
+
+static inline void
+snic_queue_wq_eth_desc(struct vnic_wq *wq,
+ void *os_buf,
+ dma_addr_t dma_addr,
+ unsigned int len,
+ int vlan_tag_insert,
+ unsigned int vlan_tag,
+ int cq_entry)
+{
+ struct wq_enet_desc *desc = svnic_wq_next_desc(wq);
+
+ wq_enet_desc_enc(desc,
+ (u64)dma_addr | VNIC_PADDR_TARGET,
+ (u16)len,
+ 0, /* mss_or_csum_offset */
+ 0, /* fc_eof */
+ 0, /* offload mode */
+ 1, /* eop */
+ (u8)cq_entry,
+ 0, /* fcoe_encap */
+ (u8)vlan_tag_insert,
+ (u16)vlan_tag,
+ 0 /* loopback */);
+
+ svnic_wq_post(wq, os_buf, dma_addr, len, 1, 1);
+}
+
+struct snic;
+
+int snic_get_vnic_config(struct snic *);
+int snic_alloc_vnic_res(struct snic *);
+void snic_free_vnic_res(struct snic *);
+void snic_get_res_counts(struct snic *);
+void snic_log_q_error(struct snic *);
+int snic_get_vnic_resources_size(struct snic *);
+#endif /* __SNIC_RES_H */
diff --git a/drivers/scsi/snic/snic_scsi.c b/drivers/scsi/snic/snic_scsi.c
new file mode 100644
index 000000000000..2c7b4c321cbe
--- /dev/null
+++ b/drivers/scsi/snic/snic_scsi.c
@@ -0,0 +1,2632 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/mempool.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dbg.h>
+
+#include "snic_io.h"
+#include "snic.h"
+
+#define snic_cmd_tag(sc) (((struct scsi_cmnd *) sc)->request->tag)
+
+const char *snic_state_str[] = {
+ [SNIC_INIT] = "SNIC_INIT",
+ [SNIC_ERROR] = "SNIC_ERROR",
+ [SNIC_ONLINE] = "SNIC_ONLINE",
+ [SNIC_OFFLINE] = "SNIC_OFFLINE",
+ [SNIC_FWRESET] = "SNIC_FWRESET",
+};
+
+static const char * const snic_req_state_str[] = {
+ [SNIC_IOREQ_NOT_INITED] = "SNIC_IOREQ_NOT_INITED",
+ [SNIC_IOREQ_PENDING] = "SNIC_IOREQ_PENDING",
+ [SNIC_IOREQ_ABTS_PENDING] = "SNIC_IOREQ_ABTS_PENDING",
+ [SNIC_IOREQ_ABTS_COMPLETE] = "SNIC_IOREQ_ABTS_COMPELTE",
+ [SNIC_IOREQ_LR_PENDING] = "SNIC_IOREQ_LR_PENDING",
+ [SNIC_IOREQ_LR_COMPLETE] = "SNIC_IOREQ_LR_COMPELTE",
+ [SNIC_IOREQ_COMPLETE] = "SNIC_IOREQ_CMD_COMPELTE",
+};
+
+/* snic cmd status strings */
+static const char * const snic_io_status_str[] = {
+ [SNIC_STAT_IO_SUCCESS] = "SNIC_STAT_IO_SUCCESS", /* 0x0 */
+ [SNIC_STAT_INVALID_HDR] = "SNIC_STAT_INVALID_HDR",
+ [SNIC_STAT_OUT_OF_RES] = "SNIC_STAT_OUT_OF_RES",
+ [SNIC_STAT_INVALID_PARM] = "SNIC_STAT_INVALID_PARM",
+ [SNIC_STAT_REQ_NOT_SUP] = "SNIC_STAT_REQ_NOT_SUP",
+ [SNIC_STAT_IO_NOT_FOUND] = "SNIC_STAT_IO_NOT_FOUND",
+ [SNIC_STAT_ABORTED] = "SNIC_STAT_ABORTED",
+ [SNIC_STAT_TIMEOUT] = "SNIC_STAT_TIMEOUT",
+ [SNIC_STAT_SGL_INVALID] = "SNIC_STAT_SGL_INVALID",
+ [SNIC_STAT_DATA_CNT_MISMATCH] = "SNIC_STAT_DATA_CNT_MISMATCH",
+ [SNIC_STAT_FW_ERR] = "SNIC_STAT_FW_ERR",
+ [SNIC_STAT_ITMF_REJECT] = "SNIC_STAT_ITMF_REJECT",
+ [SNIC_STAT_ITMF_FAIL] = "SNIC_STAT_ITMF_FAIL",
+ [SNIC_STAT_ITMF_INCORRECT_LUN] = "SNIC_STAT_ITMF_INCORRECT_LUN",
+ [SNIC_STAT_CMND_REJECT] = "SNIC_STAT_CMND_REJECT",
+ [SNIC_STAT_DEV_OFFLINE] = "SNIC_STAT_DEV_OFFLINE",
+ [SNIC_STAT_NO_BOOTLUN] = "SNIC_STAT_NO_BOOTLUN",
+ [SNIC_STAT_SCSI_ERR] = "SNIC_STAT_SCSI_ERR",
+ [SNIC_STAT_NOT_READY] = "SNIC_STAT_NOT_READY",
+ [SNIC_STAT_FATAL_ERROR] = "SNIC_STAT_FATAL_ERROR",
+};
+
+static void snic_scsi_cleanup(struct snic *, int);
+
+const char *
+snic_state_to_str(unsigned int state)
+{
+ if (state >= ARRAY_SIZE(snic_state_str) || !snic_state_str[state])
+ return "Unknown";
+
+ return snic_state_str[state];
+}
+
+static const char *
+snic_io_status_to_str(unsigned int state)
+{
+ if ((state >= ARRAY_SIZE(snic_io_status_str)) ||
+ (!snic_io_status_str[state]))
+ return "Unknown";
+
+ return snic_io_status_str[state];
+}
+
+static const char *
+snic_ioreq_state_to_str(unsigned int state)
+{
+ if (state >= ARRAY_SIZE(snic_req_state_str) ||
+ !snic_req_state_str[state])
+ return "Unknown";
+
+ return snic_req_state_str[state];
+}
+
+static inline spinlock_t *
+snic_io_lock_hash(struct snic *snic, struct scsi_cmnd *sc)
+{
+ u32 hash = snic_cmd_tag(sc) & (SNIC_IO_LOCKS - 1);
+
+ return &snic->io_req_lock[hash];
+}
+
+static inline spinlock_t *
+snic_io_lock_tag(struct snic *snic, int tag)
+{
+ return &snic->io_req_lock[tag & (SNIC_IO_LOCKS - 1)];
+}
+
+/* snic_release_req_buf : Releases snic_req_info */
+static void
+snic_release_req_buf(struct snic *snic,
+ struct snic_req_info *rqi,
+ struct scsi_cmnd *sc)
+{
+ struct snic_host_req *req = rqi_to_req(rqi);
+
+ /* Freeing cmd without marking completion, not okay */
+ SNIC_BUG_ON(!((CMD_STATE(sc) == SNIC_IOREQ_COMPLETE) ||
+ (CMD_STATE(sc) == SNIC_IOREQ_ABTS_COMPLETE) ||
+ (CMD_FLAGS(sc) & SNIC_DEV_RST_NOTSUP) ||
+ (CMD_FLAGS(sc) & SNIC_IO_INTERNAL_TERM_ISSUED) ||
+ (CMD_FLAGS(sc) & SNIC_DEV_RST_TERM_ISSUED) ||
+ (CMD_FLAGS(sc) & SNIC_SCSI_CLEANUP) ||
+ (CMD_STATE(sc) == SNIC_IOREQ_LR_COMPLETE)));
+
+ SNIC_SCSI_DBG(snic->shost,
+ "Rel_req:sc %p:tag %x:rqi %p:ioreq %p:abt %p:dr %p: state %s:flags 0x%llx\n",
+ sc, snic_cmd_tag(sc), rqi, rqi->req, rqi->abort_req,
+ rqi->dr_req, snic_ioreq_state_to_str(CMD_STATE(sc)),
+ CMD_FLAGS(sc));
+
+ if (req->u.icmnd.sense_addr)
+ pci_unmap_single(snic->pdev,
+ le64_to_cpu(req->u.icmnd.sense_addr),
+ SCSI_SENSE_BUFFERSIZE,
+ PCI_DMA_FROMDEVICE);
+
+ scsi_dma_unmap(sc);
+
+ snic_req_free(snic, rqi);
+} /* end of snic_release_req_buf */
+
+/*
+ * snic_queue_icmnd_req : Queues snic_icmnd request
+ */
+static int
+snic_queue_icmnd_req(struct snic *snic,
+ struct snic_req_info *rqi,
+ struct scsi_cmnd *sc,
+ int sg_cnt)
+{
+ struct scatterlist *sg;
+ struct snic_sg_desc *sgd;
+ dma_addr_t pa = 0;
+ struct scsi_lun lun;
+ u16 flags = 0;
+ int ret = 0;
+ unsigned int i;
+
+ if (sg_cnt) {
+ flags = SNIC_ICMND_ESGL;
+ sgd = (struct snic_sg_desc *) req_to_sgl(rqi->req);
+
+ for_each_sg(scsi_sglist(sc), sg, sg_cnt, i) {
+ sgd->addr = cpu_to_le64(sg_dma_address(sg));
+ sgd->len = cpu_to_le32(sg_dma_len(sg));
+ sgd->_resvd = 0;
+ sgd++;
+ }
+ }
+
+ pa = pci_map_single(snic->pdev,
+ sc->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
+ PCI_DMA_FROMDEVICE);
+
+ if (pci_dma_mapping_error(snic->pdev, pa)) {
+ SNIC_HOST_ERR(snic->shost,
+ "QIcmnd:PCI Map Failed for sns buf %p tag %x\n",
+ sc->sense_buffer, snic_cmd_tag(sc));
+ ret = -ENOMEM;
+
+ return ret;
+ }
+
+ int_to_scsilun(sc->device->lun, &lun);
+ if (sc->sc_data_direction == DMA_FROM_DEVICE)
+ flags |= SNIC_ICMND_RD;
+ if (sc->sc_data_direction == DMA_TO_DEVICE)
+ flags |= SNIC_ICMND_WR;
+
+ /* Initialize icmnd */
+ snic_icmnd_init(rqi->req,
+ snic_cmd_tag(sc),
+ snic->config.hid, /* hid */
+ (ulong) rqi,
+ flags, /* command flags */
+ rqi->tgt_id,
+ lun.scsi_lun,
+ sc->cmnd,
+ sc->cmd_len,
+ scsi_bufflen(sc),
+ sg_cnt,
+ (ulong) req_to_sgl(rqi->req),
+ pa, /* sense buffer pa */
+ SCSI_SENSE_BUFFERSIZE);
+
+ ret = snic_queue_wq_desc(snic, rqi->req, rqi->req_len);
+ if (ret)
+ SNIC_HOST_ERR(snic->shost,
+ "QIcmnd: Queuing Icmnd Failed. ret = %d\n",
+ ret);
+
+ return ret;
+} /* end of snic_queue_icmnd_req */
+
+/*
+ * snic_issue_scsi_req : Prepares IO request and Issues to FW.
+ */
+static int
+snic_issue_scsi_req(struct snic *snic,
+ struct snic_tgt *tgt,
+ struct scsi_cmnd *sc)
+{
+ struct snic_req_info *rqi = NULL;
+ int sg_cnt = 0;
+ int ret = 0;
+ u32 tag = snic_cmd_tag(sc);
+ u64 cmd_trc = 0, cmd_st_flags = 0;
+ spinlock_t *io_lock = NULL;
+ unsigned long flags;
+
+ CMD_STATE(sc) = SNIC_IOREQ_NOT_INITED;
+ CMD_FLAGS(sc) = SNIC_NO_FLAGS;
+ sg_cnt = scsi_dma_map(sc);
+ if (sg_cnt < 0) {
+ SNIC_TRC((u16)snic->shost->host_no, tag, (ulong) sc, 0,
+ sc->cmnd[0], sg_cnt, CMD_STATE(sc));
+
+ SNIC_HOST_ERR(snic->shost, "issue_sc:Failed to map SG List.\n");
+ ret = -ENOMEM;
+
+ goto issue_sc_end;
+ }
+
+ rqi = snic_req_init(snic, sg_cnt);
+ if (!rqi) {
+ scsi_dma_unmap(sc);
+ ret = -ENOMEM;
+
+ goto issue_sc_end;
+ }
+
+ rqi->tgt_id = tgt->id;
+ rqi->sc = sc;
+
+ CMD_STATE(sc) = SNIC_IOREQ_PENDING;
+ CMD_SP(sc) = (char *) rqi;
+ cmd_trc = SNIC_TRC_CMD(sc);
+ CMD_FLAGS(sc) |= (SNIC_IO_INITIALIZED | SNIC_IO_ISSUED);
+ cmd_st_flags = SNIC_TRC_CMD_STATE_FLAGS(sc);
+ io_lock = snic_io_lock_hash(snic, sc);
+
+ /* create wq desc and enqueue it */
+ ret = snic_queue_icmnd_req(snic, rqi, sc, sg_cnt);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "issue_sc: icmnd qing Failed for sc %p, err %d\n",
+ sc, ret);
+
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ CMD_SP(sc) = NULL;
+ CMD_STATE(sc) = SNIC_IOREQ_COMPLETE;
+ CMD_FLAGS(sc) &= ~SNIC_IO_ISSUED; /* turn off the flag */
+ spin_unlock_irqrestore(io_lock, flags);
+
+ if (rqi)
+ snic_release_req_buf(snic, rqi, sc);
+
+ SNIC_TRC(snic->shost->host_no, tag, (ulong) sc, 0, 0, 0,
+ SNIC_TRC_CMD_STATE_FLAGS(sc));
+ } else {
+ u32 io_sz = scsi_bufflen(sc) >> 9;
+ u32 qtime = jiffies - rqi->start_time;
+ struct snic_io_stats *iostats = &snic->s_stats.io;
+
+ if (io_sz > atomic64_read(&iostats->max_io_sz))
+ atomic64_set(&iostats->max_io_sz, io_sz);
+
+ if (qtime > atomic64_read(&iostats->max_qtime))
+ atomic64_set(&iostats->max_qtime, qtime);
+
+ SNIC_SCSI_DBG(snic->shost,
+ "issue_sc:sc %p, tag %d queued to WQ.\n",
+ sc, tag);
+
+ SNIC_TRC(snic->shost->host_no, tag, (ulong) sc, (ulong) rqi,
+ sg_cnt, cmd_trc, cmd_st_flags);
+ }
+
+issue_sc_end:
+
+ return ret;
+} /* end of snic_issue_scsi_req */
+
+
+/*
+ * snic_queuecommand
+ * Routine to send a scsi cdb to LLD
+ * Called with host_lock held and interrupts disabled
+ */
+int
+snic_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc)
+{
+ struct snic_tgt *tgt = NULL;
+ struct snic *snic = shost_priv(shost);
+ int ret;
+
+ tgt = starget_to_tgt(scsi_target(sc->device));
+ ret = snic_tgt_chkready(tgt);
+ if (ret) {
+ SNIC_HOST_ERR(shost, "Tgt %p id %d Not Ready.\n", tgt, tgt->id);
+ atomic64_inc(&snic->s_stats.misc.tgt_not_rdy);
+ sc->result = ret;
+ sc->scsi_done(sc);
+
+ return 0;
+ }
+
+ if (snic_get_state(snic) != SNIC_ONLINE) {
+ SNIC_HOST_ERR(shost, "snic state is %s\n",
+ snic_state_str[snic_get_state(snic)]);
+
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+ atomic_inc(&snic->ios_inflight);
+
+ SNIC_SCSI_DBG(shost, "sc %p Tag %d (sc %0x) lun %lld in snic_qcmd\n",
+ sc, snic_cmd_tag(sc), sc->cmnd[0], sc->device->lun);
+
+ memset(scsi_cmd_priv(sc), 0, sizeof(struct snic_internal_io_state));
+
+ ret = snic_issue_scsi_req(snic, tgt, sc);
+ if (ret) {
+ SNIC_HOST_ERR(shost, "Failed to Q, Scsi Req w/ err %d.\n", ret);
+ ret = SCSI_MLQUEUE_HOST_BUSY;
+ } else
+ snic_stats_update_active_ios(&snic->s_stats);
+
+ atomic_dec(&snic->ios_inflight);
+
+ return ret;
+} /* end of snic_queuecommand */
+
+/*
+ * snic_process_abts_pending_state:
+ * caller should hold IO lock
+ */
+static void
+snic_proc_tmreq_pending_state(struct snic *snic,
+ struct scsi_cmnd *sc,
+ u8 cmpl_status)
+{
+ int state = CMD_STATE(sc);
+
+ if (state == SNIC_IOREQ_ABTS_PENDING)
+ CMD_FLAGS(sc) |= SNIC_IO_ABTS_PENDING;
+ else if (state == SNIC_IOREQ_LR_PENDING)
+ CMD_FLAGS(sc) |= SNIC_DEV_RST_PENDING;
+ else
+ SNIC_BUG_ON(1);
+
+ switch (cmpl_status) {
+ case SNIC_STAT_IO_SUCCESS:
+ CMD_FLAGS(sc) |= SNIC_IO_DONE;
+ break;
+
+ case SNIC_STAT_ABORTED:
+ CMD_FLAGS(sc) |= SNIC_IO_ABORTED;
+ break;
+
+ default:
+ SNIC_BUG_ON(1);
+ }
+}
+
+/*
+ * snic_process_io_failed_state:
+ * Processes IO's error states
+ */
+static void
+snic_process_io_failed_state(struct snic *snic,
+ struct snic_icmnd_cmpl *icmnd_cmpl,
+ struct scsi_cmnd *sc,
+ u8 cmpl_stat)
+{
+ int res = 0;
+
+ switch (cmpl_stat) {
+ case SNIC_STAT_TIMEOUT: /* Req was timedout */
+ atomic64_inc(&snic->s_stats.misc.io_tmo);
+ res = DID_TIME_OUT;
+ break;
+
+ case SNIC_STAT_ABORTED: /* Req was aborted */
+ atomic64_inc(&snic->s_stats.misc.io_aborted);
+ res = DID_ABORT;
+ break;
+
+ case SNIC_STAT_DATA_CNT_MISMATCH:/* Recv/Sent more/less data than exp */
+ atomic64_inc(&snic->s_stats.misc.data_cnt_mismat);
+ scsi_set_resid(sc, le32_to_cpu(icmnd_cmpl->resid));
+ res = DID_ERROR;
+ break;
+
+ case SNIC_STAT_OUT_OF_RES: /* Out of resources to complete request */
+ atomic64_inc(&snic->s_stats.fw.out_of_res);
+ res = DID_REQUEUE;
+ break;
+
+ case SNIC_STAT_IO_NOT_FOUND: /* Requested I/O was not found */
+ atomic64_inc(&snic->s_stats.io.io_not_found);
+ res = DID_ERROR;
+ break;
+
+ case SNIC_STAT_SGL_INVALID: /* Req was aborted to due to sgl error*/
+ atomic64_inc(&snic->s_stats.misc.sgl_inval);
+ res = DID_ERROR;
+ break;
+
+ case SNIC_STAT_FW_ERR: /* Req terminated due to FW Error */
+ atomic64_inc(&snic->s_stats.fw.io_errs);
+ res = DID_ERROR;
+ break;
+
+ case SNIC_STAT_SCSI_ERR: /* FW hits SCSI Error */
+ atomic64_inc(&snic->s_stats.fw.scsi_errs);
+ break;
+
+ case SNIC_STAT_NOT_READY: /* XPT yet to initialize */
+ case SNIC_STAT_DEV_OFFLINE: /* Device offline */
+ res = DID_NO_CONNECT;
+ break;
+
+ case SNIC_STAT_INVALID_HDR: /* Hdr contains invalid data */
+ case SNIC_STAT_INVALID_PARM: /* Some param in req is invalid */
+ case SNIC_STAT_REQ_NOT_SUP: /* Req type is not supported */
+ case SNIC_STAT_CMND_REJECT: /* Req rejected */
+ case SNIC_STAT_FATAL_ERROR: /* XPT Error */
+ default:
+ SNIC_SCSI_DBG(snic->shost,
+ "Invalid Hdr/Param or Req Not Supported or Cmnd Rejected or Device Offline. or Unknown\n");
+ res = DID_ERROR;
+ break;
+ }
+
+ SNIC_HOST_ERR(snic->shost, "fw returns failed status %s flags 0x%llx\n",
+ snic_io_status_to_str(cmpl_stat), CMD_FLAGS(sc));
+
+ /* Set sc->result */
+ sc->result = (res << 16) | icmnd_cmpl->scsi_status;
+} /* end of snic_process_io_failed_state */
+
+/*
+ * snic_tmreq_pending : is task management in progress.
+ */
+static int
+snic_tmreq_pending(struct scsi_cmnd *sc)
+{
+ int state = CMD_STATE(sc);
+
+ return ((state == SNIC_IOREQ_ABTS_PENDING) ||
+ (state == SNIC_IOREQ_LR_PENDING));
+}
+
+/*
+ * snic_process_icmnd_cmpl_status:
+ * Caller should hold io_lock
+ */
+static int
+snic_process_icmnd_cmpl_status(struct snic *snic,
+ struct snic_icmnd_cmpl *icmnd_cmpl,
+ u8 cmpl_stat,
+ struct scsi_cmnd *sc)
+{
+ u8 scsi_stat = icmnd_cmpl->scsi_status;
+ u64 xfer_len = 0;
+ int ret = 0;
+
+ /* Mark the IO as complete */
+ CMD_STATE(sc) = SNIC_IOREQ_COMPLETE;
+
+ if (likely(cmpl_stat == SNIC_STAT_IO_SUCCESS)) {
+ sc->result = (DID_OK << 16) | scsi_stat;
+
+ xfer_len = scsi_bufflen(sc);
+
+ /* Update SCSI Cmd with resid value */
+ scsi_set_resid(sc, le32_to_cpu(icmnd_cmpl->resid));
+
+ if (icmnd_cmpl->flags & SNIC_ICMND_CMPL_UNDR_RUN) {
+ xfer_len -= le32_to_cpu(icmnd_cmpl->resid);
+ atomic64_inc(&snic->s_stats.misc.io_under_run);
+ }
+
+ if (icmnd_cmpl->scsi_status == SAM_STAT_TASK_SET_FULL)
+ atomic64_inc(&snic->s_stats.misc.qfull);
+
+ ret = 0;
+ } else {
+ snic_process_io_failed_state(snic, icmnd_cmpl, sc, cmpl_stat);
+ atomic64_inc(&snic->s_stats.io.fail);
+ SNIC_HOST_ERR(snic->shost,
+ "icmnd_cmpl: IO Failed : Hdr Status %s flags 0x%llx\n",
+ snic_io_status_to_str(cmpl_stat), CMD_FLAGS(sc));
+ ret = 1;
+ }
+
+ return ret;
+} /* end of snic_process_icmnd_cmpl_status */
+
+
+/*
+ * snic_icmnd_cmpl_handler
+ * Routine to handle icmnd completions
+ */
+static void
+snic_icmnd_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+ u8 typ, hdr_stat;
+ u32 cmnd_id, hid;
+ ulong ctx;
+ struct scsi_cmnd *sc = NULL;
+ struct snic_icmnd_cmpl *icmnd_cmpl = NULL;
+ struct snic_host_req *req = NULL;
+ struct snic_req_info *rqi = NULL;
+ unsigned long flags, start_time;
+ spinlock_t *io_lock;
+ u8 sc_stat = 0;
+
+ snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx);
+ icmnd_cmpl = &fwreq->u.icmnd_cmpl;
+ sc_stat = icmnd_cmpl->scsi_status;
+
+ SNIC_SCSI_DBG(snic->shost,
+ "Icmnd_cmpl: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x,i ctx = %lx\n",
+ typ, hdr_stat, cmnd_id, hid, ctx);
+
+ if (cmnd_id >= snic->max_tag_id) {
+ SNIC_HOST_ERR(snic->shost,
+ "Icmnd_cmpl:Tag Error:Out of Range Tag %d, hdr status = %s\n",
+ cmnd_id, snic_io_status_to_str(hdr_stat));
+ return;
+ }
+
+ sc = scsi_host_find_tag(snic->shost, cmnd_id);
+ WARN_ON_ONCE(!sc);
+
+ if (!sc) {
+ atomic64_inc(&snic->s_stats.io.sc_null);
+ SNIC_HOST_ERR(snic->shost,
+ "Icmnd_cmpl: Scsi Cmnd Not found, sc = NULL Hdr Status = %s tag = 0x%x fwreq = 0x%p\n",
+ snic_io_status_to_str(hdr_stat),
+ cmnd_id,
+ fwreq);
+
+ SNIC_TRC(snic->shost->host_no, cmnd_id, 0,
+ ((u64)hdr_stat << 16 |
+ (u64)sc_stat << 8 | (u64)icmnd_cmpl->flags),
+ (ulong) fwreq, le32_to_cpu(icmnd_cmpl->resid), ctx);
+
+ return;
+ }
+
+ io_lock = snic_io_lock_hash(snic, sc);
+
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ SNIC_SCSI_DBG(snic->shost,
+ "Icmnd_cmpl:lun %lld sc %p cmd %xtag %d flags 0x%llx rqi %p\n",
+ sc->device->lun, sc, sc->cmnd[0], snic_cmd_tag(sc),
+ CMD_FLAGS(sc), rqi);
+
+ SNIC_BUG_ON(rqi != (struct snic_req_info *)ctx);
+ WARN_ON_ONCE(req);
+ if (!rqi) {
+ atomic64_inc(&snic->s_stats.io.req_null);
+ CMD_FLAGS(sc) |= SNIC_IO_REQ_NULL;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ SNIC_HOST_ERR(snic->shost,
+ "Icmnd_cmpl:Host Req Not Found(null), Hdr Status %s, Tag 0x%x, sc 0x%p flags 0x%llx\n",
+ snic_io_status_to_str(hdr_stat),
+ cmnd_id, sc, CMD_FLAGS(sc));
+ return;
+ }
+
+ rqi = (struct snic_req_info *) ctx;
+ start_time = rqi->start_time;
+
+ /* firmware completed the io */
+ rqi->io_cmpl = 1;
+
+ /*
+ * if SCSI-ML has already issued abort on this command,
+ * ignore completion of the IO. The abts path will clean it up
+ */
+ if (unlikely(snic_tmreq_pending(sc))) {
+ snic_proc_tmreq_pending_state(snic, sc, hdr_stat);
+ spin_unlock_irqrestore(io_lock, flags);
+
+ snic_stats_update_io_cmpl(&snic->s_stats);
+
+ /* Expected value is SNIC_STAT_ABORTED */
+ if (likely(hdr_stat == SNIC_STAT_ABORTED))
+ return;
+
+ SNIC_SCSI_DBG(snic->shost,
+ "icmnd_cmpl:TM Req Pending(%s), Hdr Status %s sc 0x%p scsi status %x resid %d flags 0x%llx\n",
+ snic_ioreq_state_to_str(CMD_STATE(sc)),
+ snic_io_status_to_str(hdr_stat),
+ sc, sc_stat, le32_to_cpu(icmnd_cmpl->resid),
+ CMD_FLAGS(sc));
+
+ SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc,
+ jiffies_to_msecs(jiffies - start_time), (ulong) fwreq,
+ SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+ return;
+ }
+
+ if (snic_process_icmnd_cmpl_status(snic, icmnd_cmpl, hdr_stat, sc)) {
+ scsi_print_command(sc);
+ SNIC_HOST_ERR(snic->shost,
+ "icmnd_cmpl:IO Failed, sc 0x%p Tag %d Cmd %x Hdr Status %s flags 0x%llx\n",
+ sc, sc->cmnd[0], cmnd_id,
+ snic_io_status_to_str(hdr_stat), CMD_FLAGS(sc));
+ }
+
+ /* Break link with the SCSI Command */
+ CMD_SP(sc) = NULL;
+ CMD_FLAGS(sc) |= SNIC_IO_DONE;
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /* For now, consider only successful IO. */
+ snic_calc_io_process_time(snic, rqi);
+
+ snic_release_req_buf(snic, rqi, sc);
+
+ SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc,
+ jiffies_to_msecs(jiffies - start_time), (ulong) fwreq,
+ SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+
+ if (sc->scsi_done)
+ sc->scsi_done(sc);
+
+ snic_stats_update_io_cmpl(&snic->s_stats);
+} /* end of snic_icmnd_cmpl_handler */
+
+static void
+snic_proc_dr_cmpl_locked(struct snic *snic,
+ struct snic_fw_req *fwreq,
+ u8 cmpl_stat,
+ u32 cmnd_id,
+ struct scsi_cmnd *sc)
+{
+ struct snic_req_info *rqi = (struct snic_req_info *) CMD_SP(sc);
+ u32 start_time = rqi->start_time;
+
+ CMD_LR_STATUS(sc) = cmpl_stat;
+
+ SNIC_SCSI_DBG(snic->shost, "itmf_cmpl: Cmd State = %s\n",
+ snic_ioreq_state_to_str(CMD_STATE(sc)));
+
+ if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) {
+ CMD_FLAGS(sc) |= SNIC_DEV_RST_ABTS_PENDING;
+
+ SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc,
+ jiffies_to_msecs(jiffies - start_time),
+ (ulong) fwreq, 0, SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+ SNIC_SCSI_DBG(snic->shost,
+ "itmf_cmpl: Terminate Pending Dev Reset Cmpl Recvd.id %x, status %s flags 0x%llx\n",
+ (int)(cmnd_id & SNIC_TAG_MASK),
+ snic_io_status_to_str(cmpl_stat),
+ CMD_FLAGS(sc));
+
+ return;
+ }
+
+
+ if (CMD_FLAGS(sc) & SNIC_DEV_RST_TIMEDOUT) {
+ SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc,
+ jiffies_to_msecs(jiffies - start_time),
+ (ulong) fwreq, 0, SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+ SNIC_SCSI_DBG(snic->shost,
+ "itmf_cmpl:Dev Reset Completion Received after timeout. id %d cmpl status %s flags 0x%llx\n",
+ (int)(cmnd_id & SNIC_TAG_MASK),
+ snic_io_status_to_str(cmpl_stat),
+ CMD_FLAGS(sc));
+
+ return;
+ }
+
+ CMD_STATE(sc) = SNIC_IOREQ_LR_COMPLETE;
+ CMD_FLAGS(sc) |= SNIC_DEV_RST_DONE;
+
+ SNIC_SCSI_DBG(snic->shost,
+ "itmf_cmpl:Dev Reset Cmpl Recvd id %d cmpl status %s flags 0x%llx\n",
+ (int)(cmnd_id & SNIC_TAG_MASK),
+ snic_io_status_to_str(cmpl_stat),
+ CMD_FLAGS(sc));
+
+ if (rqi->dr_done)
+ complete(rqi->dr_done);
+} /* end of snic_proc_dr_cmpl_locked */
+
+/*
+ * snic_update_abort_stats : Updates abort stats based on completion status.
+ */
+static void
+snic_update_abort_stats(struct snic *snic, u8 cmpl_stat)
+{
+ struct snic_abort_stats *abt_stats = &snic->s_stats.abts;
+
+ SNIC_SCSI_DBG(snic->shost, "Updating Abort stats.\n");
+
+ switch (cmpl_stat) {
+ case SNIC_STAT_IO_SUCCESS:
+ break;
+
+ case SNIC_STAT_TIMEOUT:
+ atomic64_inc(&abt_stats->fw_tmo);
+ break;
+
+ case SNIC_STAT_IO_NOT_FOUND:
+ atomic64_inc(&abt_stats->io_not_found);
+ break;
+
+ default:
+ atomic64_inc(&abt_stats->fail);
+ break;
+ }
+}
+
+static int
+snic_process_itmf_cmpl(struct snic *snic,
+ struct snic_fw_req *fwreq,
+ u32 cmnd_id,
+ u8 cmpl_stat,
+ struct scsi_cmnd *sc)
+{
+ struct snic_req_info *rqi = NULL;
+ u32 tm_tags = 0;
+ spinlock_t *io_lock = NULL;
+ unsigned long flags;
+ u32 start_time = 0;
+ int ret = 0;
+
+ io_lock = snic_io_lock_hash(snic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ WARN_ON_ONCE(!rqi);
+
+ if (!rqi) {
+ atomic64_inc(&snic->s_stats.io.req_null);
+ spin_unlock_irqrestore(io_lock, flags);
+ CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL;
+ SNIC_HOST_ERR(snic->shost,
+ "itmf_cmpl: rqi is null,Hdr stat = %s Tag = 0x%x sc = 0x%p flags 0x%llx\n",
+ snic_io_status_to_str(cmpl_stat), cmnd_id, sc,
+ CMD_FLAGS(sc));
+
+ return ret;
+ }
+
+ /* Extract task management flags */
+ tm_tags = cmnd_id & ~(SNIC_TAG_MASK);
+
+ start_time = rqi->start_time;
+ cmnd_id &= (SNIC_TAG_MASK);
+
+ switch (tm_tags) {
+ case SNIC_TAG_ABORT:
+ /* Abort only issued on cmd */
+ snic_update_abort_stats(snic, cmpl_stat);
+
+ if (CMD_STATE(sc) != SNIC_IOREQ_ABTS_PENDING) {
+ /* This is a late completion. Ignore it. */
+ ret = -1;
+ spin_unlock_irqrestore(io_lock, flags);
+ break;
+ }
+
+ CMD_STATE(sc) = SNIC_IOREQ_ABTS_COMPLETE;
+ CMD_ABTS_STATUS(sc) = cmpl_stat;
+ CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_DONE;
+
+ SNIC_SCSI_DBG(snic->shost,
+ "itmf_cmpl:Abort Cmpl Recvd.Tag 0x%x Status %s flags 0x%llx\n",
+ cmnd_id,
+ snic_io_status_to_str(cmpl_stat),
+ CMD_FLAGS(sc));
+
+ /*
+ * If scsi_eh thread is blocked waiting for abts complete,
+ * signal completion to it. IO will be cleaned in the thread,
+ * else clean it in this context.
+ */
+ if (rqi->abts_done) {
+ complete(rqi->abts_done);
+ spin_unlock_irqrestore(io_lock, flags);
+
+ break; /* jump out */
+ }
+
+ CMD_SP(sc) = NULL;
+ sc->result = (DID_ERROR << 16);
+ SNIC_SCSI_DBG(snic->shost,
+ "itmf_cmpl: Completing IO. sc %p flags 0x%llx\n",
+ sc, CMD_FLAGS(sc));
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ snic_release_req_buf(snic, rqi, sc);
+
+ if (sc->scsi_done) {
+ SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc,
+ jiffies_to_msecs(jiffies - start_time),
+ (ulong) fwreq, SNIC_TRC_CMD(sc),
+ SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+ sc->scsi_done(sc);
+ }
+
+ break;
+
+ case SNIC_TAG_DEV_RST:
+ case SNIC_TAG_DEV_RST | SNIC_TAG_IOCTL_DEV_RST:
+ snic_proc_dr_cmpl_locked(snic, fwreq, cmpl_stat, cmnd_id, sc);
+ spin_unlock_irqrestore(io_lock, flags);
+ ret = 0;
+
+ break;
+
+ case SNIC_TAG_ABORT | SNIC_TAG_DEV_RST:
+ /* Abort and terminate completion of device reset req */
+
+ CMD_STATE(sc) = SNIC_IOREQ_ABTS_COMPLETE;
+ CMD_ABTS_STATUS(sc) = cmpl_stat;
+ CMD_FLAGS(sc) |= SNIC_DEV_RST_DONE;
+
+ SNIC_SCSI_DBG(snic->shost,
+ "itmf_cmpl:dev reset abts cmpl recvd. id %d status %s flags 0x%llx\n",
+ cmnd_id, snic_io_status_to_str(cmpl_stat),
+ CMD_FLAGS(sc));
+
+ if (rqi->abts_done)
+ complete(rqi->abts_done);
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ break;
+
+ default:
+ spin_unlock_irqrestore(io_lock, flags);
+ SNIC_HOST_ERR(snic->shost,
+ "itmf_cmpl: Unknown TM tag bit 0x%x\n", tm_tags);
+
+ SNIC_HOST_ERR(snic->shost,
+ "itmf_cmpl:Unexpected itmf io stat %s Tag = 0x%x flags 0x%llx\n",
+ snic_ioreq_state_to_str(CMD_STATE(sc)),
+ cmnd_id,
+ CMD_FLAGS(sc));
+ ret = -1;
+ SNIC_BUG_ON(1);
+
+ break;
+ }
+
+ return ret;
+} /* end of snic_process_itmf_cmpl_status */
+
+/*
+ * snic_itmf_cmpl_handler.
+ * Routine to handle itmf completions.
+ */
+static void
+snic_itmf_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+ struct scsi_cmnd *sc = NULL;
+ struct snic_req_info *rqi = NULL;
+ struct snic_itmf_cmpl *itmf_cmpl = NULL;
+ ulong ctx;
+ u32 cmnd_id;
+ u32 hid;
+ u8 typ;
+ u8 hdr_stat;
+
+ snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx);
+ SNIC_SCSI_DBG(snic->shost,
+ "Itmf_cmpl: %s: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x,ctx = %lx\n",
+ __func__, typ, hdr_stat, cmnd_id, hid, ctx);
+
+ itmf_cmpl = &fwreq->u.itmf_cmpl;
+ SNIC_SCSI_DBG(snic->shost,
+ "Itmf_cmpl: nterm %u , flags 0x%x\n",
+ le32_to_cpu(itmf_cmpl->nterminated), itmf_cmpl->flags);
+
+ /* spl case, dev reset issued through ioctl */
+ if (cmnd_id & SNIC_TAG_IOCTL_DEV_RST) {
+ rqi = (struct snic_req_info *) ctx;
+ sc = rqi->sc;
+
+ goto ioctl_dev_rst;
+ }
+
+ if ((cmnd_id & SNIC_TAG_MASK) >= snic->max_tag_id) {
+ SNIC_HOST_ERR(snic->shost,
+ "Itmf_cmpl: Tag 0x%x out of Range,HdrStat %s\n",
+ cmnd_id, snic_io_status_to_str(hdr_stat));
+ SNIC_BUG_ON(1);
+
+ return;
+ }
+
+ sc = scsi_host_find_tag(snic->shost, cmnd_id & SNIC_TAG_MASK);
+ WARN_ON_ONCE(!sc);
+
+ioctl_dev_rst:
+ if (!sc) {
+ atomic64_inc(&snic->s_stats.io.sc_null);
+ SNIC_HOST_ERR(snic->shost,
+ "Itmf_cmpl: sc is NULL - Hdr Stat %s Tag 0x%x\n",
+ snic_io_status_to_str(hdr_stat), cmnd_id);
+
+ return;
+ }
+
+ snic_process_itmf_cmpl(snic, fwreq, cmnd_id, hdr_stat, sc);
+} /* end of snic_itmf_cmpl_handler */
+
+
+
+static void
+snic_hba_reset_scsi_cleanup(struct snic *snic, struct scsi_cmnd *sc)
+{
+ struct snic_stats *st = &snic->s_stats;
+ long act_ios = 0, act_fwreqs = 0;
+
+ SNIC_SCSI_DBG(snic->shost, "HBA Reset scsi cleanup.\n");
+ snic_scsi_cleanup(snic, snic_cmd_tag(sc));
+
+ /* Update stats on pending IOs */
+ act_ios = atomic64_read(&st->io.active);
+ atomic64_add(act_ios, &st->io.compl);
+ atomic64_sub(act_ios, &st->io.active);
+
+ act_fwreqs = atomic64_read(&st->fw.actv_reqs);
+ atomic64_sub(act_fwreqs, &st->fw.actv_reqs);
+}
+
+/*
+ * snic_hba_reset_cmpl_handler :
+ *
+ * Notes :
+ * 1. Cleanup all the scsi cmds, release all snic specific cmds
+ * 2. Issue Report Targets in case of SAN targets
+ */
+static int
+snic_hba_reset_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+ ulong ctx;
+ u32 cmnd_id;
+ u32 hid;
+ u8 typ;
+ u8 hdr_stat;
+ struct scsi_cmnd *sc = NULL;
+ struct snic_req_info *rqi = NULL;
+ spinlock_t *io_lock = NULL;
+ unsigned long flags, gflags;
+ int ret = 0;
+
+ SNIC_HOST_INFO(snic->shost,
+ "reset_cmpl:HBA Reset Completion received.\n");
+
+ snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx);
+ SNIC_SCSI_DBG(snic->shost,
+ "reset_cmpl: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x, ctx = %lx\n",
+ typ, hdr_stat, cmnd_id, hid, ctx);
+
+ /* spl case, host reset issued through ioctl */
+ if (cmnd_id == SCSI_NO_TAG) {
+ rqi = (struct snic_req_info *) ctx;
+ sc = rqi->sc;
+
+ goto ioctl_hba_rst;
+ }
+
+ if (cmnd_id >= snic->max_tag_id) {
+ SNIC_HOST_ERR(snic->shost,
+ "reset_cmpl: Tag 0x%x out of Range,HdrStat %s\n",
+ cmnd_id, snic_io_status_to_str(hdr_stat));
+ SNIC_BUG_ON(1);
+
+ return 1;
+ }
+
+ sc = scsi_host_find_tag(snic->shost, cmnd_id);
+ioctl_hba_rst:
+ if (!sc) {
+ atomic64_inc(&snic->s_stats.io.sc_null);
+ SNIC_HOST_ERR(snic->shost,
+ "reset_cmpl: sc is NULL - Hdr Stat %s Tag 0x%x\n",
+ snic_io_status_to_str(hdr_stat), cmnd_id);
+ ret = 1;
+
+ return ret;
+ }
+
+ io_lock = snic_io_lock_hash(snic, sc);
+ spin_lock_irqsave(io_lock, flags);
+
+ if (!snic->remove_wait) {
+ spin_unlock_irqrestore(io_lock, flags);
+ SNIC_HOST_ERR(snic->shost,
+ "reset_cmpl:host reset completed after timout\n");
+ ret = 1;
+
+ return ret;
+ }
+
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ WARN_ON_ONCE(!rqi);
+
+ if (!rqi) {
+ atomic64_inc(&snic->s_stats.io.req_null);
+ spin_unlock_irqrestore(io_lock, flags);
+ CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL;
+ SNIC_HOST_ERR(snic->shost,
+ "reset_cmpl: rqi is null,Hdr stat %s Tag 0x%x sc 0x%p flags 0x%llx\n",
+ snic_io_status_to_str(hdr_stat), cmnd_id, sc,
+ CMD_FLAGS(sc));
+
+ ret = 1;
+
+ return ret;
+ }
+ /* stats */
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /* scsi cleanup */
+ snic_hba_reset_scsi_cleanup(snic, sc);
+
+ SNIC_BUG_ON(snic_get_state(snic) != SNIC_OFFLINE &&
+ snic_get_state(snic) != SNIC_FWRESET);
+
+ /* Careful locking between snic_lock and io lock */
+ spin_lock_irqsave(io_lock, flags);
+ spin_lock_irqsave(&snic->snic_lock, gflags);
+ if (snic_get_state(snic) == SNIC_FWRESET)
+ snic_set_state(snic, SNIC_ONLINE);
+ spin_unlock_irqrestore(&snic->snic_lock, gflags);
+
+ if (snic->remove_wait)
+ complete(snic->remove_wait);
+
+ spin_unlock_irqrestore(io_lock, flags);
+ atomic64_inc(&snic->s_stats.reset.hba_reset_cmpl);
+
+ ret = 0;
+ /* Rediscovery is for SAN */
+ if (snic->config.xpt_type == SNIC_DAS)
+ return ret;
+
+ SNIC_SCSI_DBG(snic->shost, "reset_cmpl: Queuing discovery work.\n");
+ queue_work(snic_glob->event_q, &snic->disc_work);
+
+ return ret;
+}
+
+static void
+snic_msg_ack_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+ SNIC_HOST_INFO(snic->shost, "Message Ack Received.\n");
+
+ SNIC_ASSERT_NOT_IMPL(1);
+}
+
+static void
+snic_aen_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+ u8 typ, hdr_stat;
+ u32 cmnd_id, hid;
+ ulong ctx;
+ struct snic_async_evnotify *aen = &fwreq->u.async_ev;
+ u32 event_id = 0;
+
+ snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx);
+ SNIC_SCSI_DBG(snic->shost,
+ "aen: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x, ctx = %lx\n",
+ typ, hdr_stat, cmnd_id, hid, ctx);
+
+ event_id = le32_to_cpu(aen->ev_id);
+
+ switch (event_id) {
+ case SNIC_EV_TGT_OFFLINE:
+ SNIC_HOST_INFO(snic->shost, "aen:TGT_OFFLINE Event Recvd.\n");
+ break;
+
+ case SNIC_EV_TGT_ONLINE:
+ SNIC_HOST_INFO(snic->shost, "aen:TGT_ONLINE Event Recvd.\n");
+ break;
+
+ case SNIC_EV_LUN_OFFLINE:
+ SNIC_HOST_INFO(snic->shost, "aen:LUN_OFFLINE Event Recvd.\n");
+ break;
+
+ case SNIC_EV_LUN_ONLINE:
+ SNIC_HOST_INFO(snic->shost, "aen:LUN_ONLINE Event Recvd.\n");
+ break;
+
+ case SNIC_EV_CONF_CHG:
+ SNIC_HOST_INFO(snic->shost, "aen:Config Change Event Recvd.\n");
+ break;
+
+ case SNIC_EV_TGT_ADDED:
+ SNIC_HOST_INFO(snic->shost, "aen:TGT_ADD Event Recvd.\n");
+ break;
+
+ case SNIC_EV_TGT_DELTD:
+ SNIC_HOST_INFO(snic->shost, "aen:TGT_DEL Event Recvd.\n");
+ break;
+
+ case SNIC_EV_LUN_ADDED:
+ SNIC_HOST_INFO(snic->shost, "aen:LUN_ADD Event Recvd.\n");
+ break;
+
+ case SNIC_EV_LUN_DELTD:
+ SNIC_HOST_INFO(snic->shost, "aen:LUN_DEL Event Recvd.\n");
+ break;
+
+ case SNIC_EV_DISC_CMPL:
+ SNIC_HOST_INFO(snic->shost, "aen:DISC_CMPL Event Recvd.\n");
+ break;
+
+ default:
+ SNIC_HOST_INFO(snic->shost, "aen:Unknown Event Recvd.\n");
+ SNIC_BUG_ON(1);
+ break;
+ }
+
+ SNIC_ASSERT_NOT_IMPL(1);
+} /* end of snic_aen_handler */
+
+/*
+ * snic_io_cmpl_handler
+ * Routine to process CQ entries(IO Completions) posted by fw.
+ */
+static int
+snic_io_cmpl_handler(struct vnic_dev *vdev,
+ unsigned int cq_idx,
+ struct snic_fw_req *fwreq)
+{
+ struct snic *snic = svnic_dev_priv(vdev);
+ u64 start = jiffies, cmpl_time;
+
+ snic_print_desc(__func__, (char *)fwreq, sizeof(*fwreq));
+
+ /* Update FW Stats */
+ if ((fwreq->hdr.type >= SNIC_RSP_REPORT_TGTS_CMPL) &&
+ (fwreq->hdr.type <= SNIC_RSP_BOOT_LUNS_CMPL))
+ atomic64_dec(&snic->s_stats.fw.actv_reqs);
+
+ SNIC_BUG_ON((fwreq->hdr.type > SNIC_RSP_BOOT_LUNS_CMPL) &&
+ (fwreq->hdr.type < SNIC_MSG_ASYNC_EVNOTIFY));
+
+ /* Check for snic subsys errors */
+ switch (fwreq->hdr.status) {
+ case SNIC_STAT_NOT_READY: /* XPT yet to initialize */
+ SNIC_HOST_ERR(snic->shost,
+ "sNIC SubSystem is NOT Ready.\n");
+ break;
+
+ case SNIC_STAT_FATAL_ERROR: /* XPT Error */
+ SNIC_HOST_ERR(snic->shost,
+ "sNIC SubSystem in Unrecoverable State.\n");
+ break;
+ }
+
+ switch (fwreq->hdr.type) {
+ case SNIC_RSP_EXCH_VER_CMPL:
+ snic_io_exch_ver_cmpl_handler(snic, fwreq);
+ break;
+
+ case SNIC_RSP_REPORT_TGTS_CMPL:
+ snic_report_tgt_cmpl_handler(snic, fwreq);
+ break;
+
+ case SNIC_RSP_ICMND_CMPL:
+ snic_icmnd_cmpl_handler(snic, fwreq);
+ break;
+
+ case SNIC_RSP_ITMF_CMPL:
+ snic_itmf_cmpl_handler(snic, fwreq);
+ break;
+
+ case SNIC_RSP_HBA_RESET_CMPL:
+ snic_hba_reset_cmpl_handler(snic, fwreq);
+ break;
+
+ case SNIC_MSG_ACK:
+ snic_msg_ack_handler(snic, fwreq);
+ break;
+
+ case SNIC_MSG_ASYNC_EVNOTIFY:
+ snic_aen_handler(snic, fwreq);
+ break;
+
+ default:
+ SNIC_BUG_ON(1);
+ SNIC_SCSI_DBG(snic->shost,
+ "Unknown Firmwqre completion request type %d\n",
+ fwreq->hdr.type);
+ break;
+ }
+
+ /* Update Stats */
+ cmpl_time = jiffies - start;
+ if (cmpl_time > atomic64_read(&snic->s_stats.io.max_cmpl_time))
+ atomic64_set(&snic->s_stats.io.max_cmpl_time, cmpl_time);
+
+ return 0;
+} /* end of snic_io_cmpl_handler */
+
+/*
+ * snic_fwcq_cmpl_handler
+ * Routine to process fwCQ
+ * This CQ is independent, and not associated with wq/rq/wq_copy queues
+ */
+int
+snic_fwcq_cmpl_handler(struct snic *snic, int io_cmpl_work)
+{
+ unsigned int num_ent = 0; /* number cq entries processed */
+ unsigned int cq_idx;
+ unsigned int nent_per_cq;
+ struct snic_misc_stats *misc_stats = &snic->s_stats.misc;
+
+ for (cq_idx = snic->wq_count; cq_idx < snic->cq_count; cq_idx++) {
+ nent_per_cq = vnic_cq_fw_service(&snic->cq[cq_idx],
+ snic_io_cmpl_handler,
+ io_cmpl_work);
+ num_ent += nent_per_cq;
+
+ if (nent_per_cq > atomic64_read(&misc_stats->max_cq_ents))
+ atomic64_set(&misc_stats->max_cq_ents, nent_per_cq);
+ }
+
+ return num_ent;
+} /* end of snic_fwcq_cmpl_handler */
+
+/*
+ * snic_queue_itmf_req: Common API to queue Task Management requests.
+ * Use rqi->tm_tag for passing special tags.
+ * @req_id : aborted request's tag, -1 for lun reset.
+ */
+static int
+snic_queue_itmf_req(struct snic *snic,
+ struct snic_host_req *tmreq,
+ struct scsi_cmnd *sc,
+ u32 tmf,
+ u32 req_id)
+{
+ struct snic_req_info *rqi = req_to_rqi(tmreq);
+ struct scsi_lun lun;
+ int tm_tag = snic_cmd_tag(sc) | rqi->tm_tag;
+ int ret = 0;
+
+ SNIC_BUG_ON(!rqi);
+ SNIC_BUG_ON(!rqi->tm_tag);
+
+ /* fill in lun info */
+ int_to_scsilun(sc->device->lun, &lun);
+
+ /* Initialize snic_host_req: itmf */
+ snic_itmf_init(tmreq,
+ tm_tag,
+ snic->config.hid,
+ (ulong) rqi,
+ 0 /* flags */,
+ req_id, /* Command to be aborted. */
+ rqi->tgt_id,
+ lun.scsi_lun,
+ tmf);
+
+ /*
+ * In case of multiple aborts on same cmd,
+ * use try_wait_for_completion and completion_done() to check
+ * whether it queues aborts even after completion of abort issued
+ * prior.SNIC_BUG_ON(completion_done(&rqi->done));
+ */
+
+ ret = snic_queue_wq_desc(snic, tmreq, sizeof(*tmreq));
+ if (ret)
+ SNIC_HOST_ERR(snic->shost,
+ "qitmf:Queuing ITMF(%d) Req sc %p, rqi %p, req_id %d tag %d Failed, ret = %d\n",
+ tmf, sc, rqi, req_id, snic_cmd_tag(sc), ret);
+ else
+ SNIC_SCSI_DBG(snic->shost,
+ "qitmf:Queuing ITMF(%d) Req sc %p, rqi %p, req_id %d, tag %d (req_id)- Success.",
+ tmf, sc, rqi, req_id, snic_cmd_tag(sc));
+
+ return ret;
+} /* end of snic_queue_itmf_req */
+
+static int
+snic_issue_tm_req(struct snic *snic,
+ struct snic_req_info *rqi,
+ struct scsi_cmnd *sc,
+ int tmf)
+{
+ struct snic_host_req *tmreq = NULL;
+ int req_id = 0, tag = snic_cmd_tag(sc);
+ int ret = 0;
+
+ if (snic_get_state(snic) == SNIC_FWRESET)
+ return -EBUSY;
+
+ atomic_inc(&snic->ios_inflight);
+
+ SNIC_SCSI_DBG(snic->shost,
+ "issu_tmreq: Task mgmt req %d. rqi %p w/ tag %x\n",
+ tmf, rqi, tag);
+
+
+ if (tmf == SNIC_ITMF_LUN_RESET) {
+ tmreq = snic_dr_req_init(snic, rqi);
+ req_id = SCSI_NO_TAG;
+ } else {
+ tmreq = snic_abort_req_init(snic, rqi);
+ req_id = tag;
+ }
+
+ if (!tmreq) {
+ ret = -ENOMEM;
+
+ goto tmreq_err;
+ }
+
+ ret = snic_queue_itmf_req(snic, tmreq, sc, tmf, req_id);
+ if (ret)
+ goto tmreq_err;
+
+ ret = 0;
+
+tmreq_err:
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "issu_tmreq: Queing ITMF(%d) Req, sc %p rqi %p req_id %d tag %x fails err = %d\n",
+ tmf, sc, rqi, req_id, tag, ret);
+ } else {
+ SNIC_SCSI_DBG(snic->shost,
+ "issu_tmreq: Queuing ITMF(%d) Req, sc %p, rqi %p, req_id %d tag %x - Success.\n",
+ tmf, sc, rqi, req_id, tag);
+ }
+
+ atomic_dec(&snic->ios_inflight);
+
+ return ret;
+}
+
+/*
+ * snic_queue_abort_req : Queues abort req to WQ
+ */
+static int
+snic_queue_abort_req(struct snic *snic,
+ struct snic_req_info *rqi,
+ struct scsi_cmnd *sc,
+ int tmf)
+{
+ SNIC_SCSI_DBG(snic->shost, "q_abtreq: sc %p, rqi %p, tag %x, tmf %d\n",
+ sc, rqi, snic_cmd_tag(sc), tmf);
+
+ /* Add special tag for abort */
+ rqi->tm_tag |= SNIC_TAG_ABORT;
+
+ return snic_issue_tm_req(snic, rqi, sc, tmf);
+}
+
+/*
+ * snic_abort_finish : called by snic_abort_cmd on queuing abort successfully.
+ */
+static int
+snic_abort_finish(struct snic *snic, struct scsi_cmnd *sc)
+{
+ struct snic_req_info *rqi = NULL;
+ spinlock_t *io_lock = NULL;
+ unsigned long flags;
+ int ret = 0, tag = snic_cmd_tag(sc);
+
+ io_lock = snic_io_lock_hash(snic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (!rqi) {
+ atomic64_inc(&snic->s_stats.io.req_null);
+ CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL;
+
+ SNIC_SCSI_DBG(snic->shost,
+ "abt_fini:req info is null tag 0x%x, sc 0x%p flags 0x%llx\n",
+ tag, sc, CMD_FLAGS(sc));
+ ret = FAILED;
+
+ goto abort_fail;
+ }
+
+ rqi->abts_done = NULL;
+
+ ret = FAILED;
+
+ /* Check the abort status. */
+ switch (CMD_ABTS_STATUS(sc)) {
+ case SNIC_INVALID_CODE:
+ /* Firmware didn't complete abort req, timedout */
+ CMD_FLAGS(sc) |= SNIC_IO_ABTS_TIMEDOUT;
+ atomic64_inc(&snic->s_stats.abts.drv_tmo);
+ SNIC_SCSI_DBG(snic->shost,
+ "abt_fini:sc %p Tag %x Driver Timeout.flags 0x%llx\n",
+ sc, snic_cmd_tag(sc), CMD_FLAGS(sc));
+ /* do not release snic request in timedout case */
+ rqi = NULL;
+
+ goto abort_fail;
+
+ case SNIC_STAT_IO_SUCCESS:
+ case SNIC_STAT_IO_NOT_FOUND:
+ ret = SUCCESS;
+ break;
+
+ default:
+ /* Firmware completed abort with error */
+ ret = FAILED;
+ break;
+ }
+
+ CMD_SP(sc) = NULL;
+ SNIC_HOST_INFO(snic->shost,
+ "abt_fini: Tag %x, Cmpl Status %s flags 0x%llx\n",
+ tag, snic_io_status_to_str(CMD_ABTS_STATUS(sc)),
+ CMD_FLAGS(sc));
+
+abort_fail:
+ spin_unlock_irqrestore(io_lock, flags);
+ if (rqi)
+ snic_release_req_buf(snic, rqi, sc);
+
+ return ret;
+} /* end of snic_abort_finish */
+
+/*
+ * snic_send_abort_and_wait : Issues Abort, and Waits
+ */
+static int
+snic_send_abort_and_wait(struct snic *snic, struct scsi_cmnd *sc)
+{
+ struct snic_req_info *rqi = NULL;
+ enum snic_ioreq_state sv_state;
+ struct snic_tgt *tgt = NULL;
+ spinlock_t *io_lock = NULL;
+ DECLARE_COMPLETION_ONSTACK(tm_done);
+ unsigned long flags;
+ int ret = 0, tmf = 0, tag = snic_cmd_tag(sc);
+
+ tgt = starget_to_tgt(scsi_target(sc->device));
+ if ((snic_tgt_chkready(tgt) != 0) && (tgt->tdata.typ == SNIC_TGT_SAN))
+ tmf = SNIC_ITMF_ABTS_TASK_TERM;
+ else
+ tmf = SNIC_ITMF_ABTS_TASK;
+
+ /* stats */
+
+ io_lock = snic_io_lock_hash(snic, sc);
+
+ /*
+ * Avoid a race between SCSI issuing the abort and the device
+ * completing the command.
+ *
+ * If the command is already completed by fw_cmpl code,
+ * we just return SUCCESS from here. This means that the abort
+ * succeeded. In the SCSI ML, since the timeout for command has
+ * happend, the completion wont actually complete the command
+ * and it will be considered as an aborted command
+ *
+ * The CMD_SP will not be cleared except while holding io_lock
+ */
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (!rqi) {
+ spin_unlock_irqrestore(io_lock, flags);
+
+ SNIC_HOST_ERR(snic->shost,
+ "abt_cmd: rqi is null. Tag %d flags 0x%llx\n",
+ tag, CMD_FLAGS(sc));
+
+ ret = SUCCESS;
+
+ goto send_abts_end;
+ }
+
+ rqi->abts_done = &tm_done;
+ if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(io_lock, flags);
+
+ ret = 0;
+ goto abts_pending;
+ }
+ SNIC_BUG_ON(!rqi->abts_done);
+
+ /* Save Command State, should be restored on failed to Queue. */
+ sv_state = CMD_STATE(sc);
+
+ /*
+ * Command is still pending, need to abort it
+ * If the fw completes the command after this point,
+ * the completion won't be done till mid-layer, since abot
+ * has already started.
+ */
+ CMD_STATE(sc) = SNIC_IOREQ_ABTS_PENDING;
+ CMD_ABTS_STATUS(sc) = SNIC_INVALID_CODE;
+
+ SNIC_SCSI_DBG(snic->shost, "send_abt_cmd: TAG 0x%x\n", tag);
+
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /* Now Queue the abort command to firmware */
+ ret = snic_queue_abort_req(snic, rqi, sc, tmf);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "send_abt_cmd: IO w/ Tag 0x%x fail w/ err %d flags 0x%llx\n",
+ tag, ret, CMD_FLAGS(sc));
+
+ spin_lock_irqsave(io_lock, flags);
+ /* Restore Command's previous state */
+ CMD_STATE(sc) = sv_state;
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (rqi)
+ rqi->abts_done = NULL;
+ spin_unlock_irqrestore(io_lock, flags);
+ ret = FAILED;
+
+ goto send_abts_end;
+ }
+
+ spin_lock_irqsave(io_lock, flags);
+ if (tmf == SNIC_ITMF_ABTS_TASK) {
+ CMD_FLAGS(sc) |= SNIC_IO_ABTS_ISSUED;
+ atomic64_inc(&snic->s_stats.abts.num);
+ } else {
+ /* term stats */
+ CMD_FLAGS(sc) |= SNIC_IO_TERM_ISSUED;
+ }
+ spin_unlock_irqrestore(io_lock, flags);
+
+ SNIC_SCSI_DBG(snic->shost,
+ "send_abt_cmd: sc %p Tag %x flags 0x%llx\n",
+ sc, tag, CMD_FLAGS(sc));
+
+
+ ret = 0;
+
+abts_pending:
+ /*
+ * Queued an abort IO, wait for its completion.
+ * Once the fw completes the abort command, it will
+ * wakeup this thread.
+ */
+ wait_for_completion_timeout(&tm_done, SNIC_ABTS_TIMEOUT);
+
+send_abts_end:
+ return ret;
+} /* end of snic_send_abort_and_wait */
+
+/*
+ * This function is exported to SCSI for sending abort cmnds.
+ * A SCSI IO is represent by snic_ioreq in the driver.
+ * The snic_ioreq is linked to the SCSI Cmd, thus a link with the ULP'S IO
+ */
+int
+snic_abort_cmd(struct scsi_cmnd *sc)
+{
+ struct snic *snic = shost_priv(sc->device->host);
+ int ret = SUCCESS, tag = snic_cmd_tag(sc);
+ u32 start_time = jiffies;
+
+ SNIC_SCSI_DBG(snic->shost, "abt_cmd:sc %p :0x%x :req = %p :tag = %d\n",
+ sc, sc->cmnd[0], sc->request, tag);
+
+ if (unlikely(snic_get_state(snic) != SNIC_ONLINE)) {
+ SNIC_HOST_ERR(snic->shost,
+ "abt_cmd: tag %x Parent Devs are not rdy\n",
+ tag);
+ ret = FAST_IO_FAIL;
+
+ goto abort_end;
+ }
+
+
+ ret = snic_send_abort_and_wait(snic, sc);
+ if (ret)
+ goto abort_end;
+
+ ret = snic_abort_finish(snic, sc);
+
+abort_end:
+ SNIC_TRC(snic->shost->host_no, tag, (ulong) sc,
+ jiffies_to_msecs(jiffies - start_time), 0,
+ SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+ SNIC_SCSI_DBG(snic->shost,
+ "abts: Abort Req Status = %s\n",
+ (ret == SUCCESS) ? "SUCCESS" :
+ ((ret == FAST_IO_FAIL) ? "FAST_IO_FAIL" : "FAILED"));
+
+ return ret;
+}
+
+
+
+static int
+snic_is_abts_pending(struct snic *snic, struct scsi_cmnd *lr_sc)
+{
+ struct snic_req_info *rqi = NULL;
+ struct scsi_cmnd *sc = NULL;
+ struct scsi_device *lr_sdev = NULL;
+ spinlock_t *io_lock = NULL;
+ u32 tag;
+ unsigned long flags;
+
+ if (lr_sc)
+ lr_sdev = lr_sc->device;
+
+ /* walk through the tag map, an dcheck if IOs are still pending in fw*/
+ for (tag = 0; tag < snic->max_tag_id; tag++) {
+ io_lock = snic_io_lock_tag(snic, tag);
+
+ spin_lock_irqsave(io_lock, flags);
+ sc = scsi_host_find_tag(snic->shost, tag);
+
+ if (!sc || (lr_sc && (sc->device != lr_sdev || sc == lr_sc))) {
+ spin_unlock_irqrestore(io_lock, flags);
+
+ continue;
+ }
+
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (!rqi) {
+ spin_unlock_irqrestore(io_lock, flags);
+
+ continue;
+ }
+
+ /*
+ * Found IO that is still pending w/ firmware and belongs to
+ * the LUN that is under reset, if lr_sc != NULL
+ */
+ SNIC_SCSI_DBG(snic->shost, "Found IO in %s on LUN\n",
+ snic_ioreq_state_to_str(CMD_STATE(sc)));
+
+ if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) {
+ spin_unlock_irqrestore(io_lock, flags);
+
+ return 1;
+ }
+
+ spin_unlock_irqrestore(io_lock, flags);
+ }
+
+ return 0;
+} /* end of snic_is_abts_pending */
+
+static int
+snic_dr_clean_single_req(struct snic *snic,
+ u32 tag,
+ struct scsi_device *lr_sdev)
+{
+ struct snic_req_info *rqi = NULL;
+ struct snic_tgt *tgt = NULL;
+ struct scsi_cmnd *sc = NULL;
+ spinlock_t *io_lock = NULL;
+ u32 sv_state = 0, tmf = 0;
+ DECLARE_COMPLETION_ONSTACK(tm_done);
+ unsigned long flags;
+ int ret = 0;
+
+ io_lock = snic_io_lock_tag(snic, tag);
+ spin_lock_irqsave(io_lock, flags);
+ sc = scsi_host_find_tag(snic->shost, tag);
+
+ /* Ignore Cmd that don't belong to Lun Reset device */
+ if (!sc || sc->device != lr_sdev)
+ goto skip_clean;
+
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+
+ if (!rqi)
+ goto skip_clean;
+
+
+ if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING)
+ goto skip_clean;
+
+
+ if ((CMD_FLAGS(sc) & SNIC_DEVICE_RESET) &&
+ (!(CMD_FLAGS(sc) & SNIC_DEV_RST_ISSUED))) {
+
+ SNIC_SCSI_DBG(snic->shost,
+ "clean_single_req: devrst is not pending sc 0x%p\n",
+ sc);
+
+ goto skip_clean;
+ }
+
+ SNIC_SCSI_DBG(snic->shost,
+ "clean_single_req: Found IO in %s on lun\n",
+ snic_ioreq_state_to_str(CMD_STATE(sc)));
+
+ /* Save Command State */
+ sv_state = CMD_STATE(sc);
+
+ /*
+ * Any pending IO issued prior to reset is expected to be
+ * in abts pending state, if not we need to set SNIC_IOREQ_ABTS_PENDING
+ * to indicate the IO is abort pending.
+ * When IO is completed, the IO will be handed over and handled
+ * in this function.
+ */
+
+ CMD_STATE(sc) = SNIC_IOREQ_ABTS_PENDING;
+ SNIC_BUG_ON(rqi->abts_done);
+
+ if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET) {
+ rqi->tm_tag = SNIC_TAG_DEV_RST;
+
+ SNIC_SCSI_DBG(snic->shost,
+ "clean_single_req:devrst sc 0x%p\n", sc);
+ }
+
+ CMD_ABTS_STATUS(sc) = SNIC_INVALID_CODE;
+ rqi->abts_done = &tm_done;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ tgt = starget_to_tgt(scsi_target(sc->device));
+ if ((snic_tgt_chkready(tgt) != 0) && (tgt->tdata.typ == SNIC_TGT_SAN))
+ tmf = SNIC_ITMF_ABTS_TASK_TERM;
+ else
+ tmf = SNIC_ITMF_ABTS_TASK;
+
+ /* Now queue the abort command to firmware */
+ ret = snic_queue_abort_req(snic, rqi, sc, tmf);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "clean_single_req_err:sc %p, tag %d abt failed. tm_tag %d flags 0x%llx\n",
+ sc, tag, rqi->tm_tag, CMD_FLAGS(sc));
+
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (rqi)
+ rqi->abts_done = NULL;
+
+ /* Restore Command State */
+ if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING)
+ CMD_STATE(sc) = sv_state;
+
+ ret = 1;
+ goto skip_clean;
+ }
+
+ spin_lock_irqsave(io_lock, flags);
+ if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET)
+ CMD_FLAGS(sc) |= SNIC_DEV_RST_TERM_ISSUED;
+
+ CMD_FLAGS(sc) |= SNIC_IO_INTERNAL_TERM_ISSUED;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ wait_for_completion_timeout(&tm_done, SNIC_ABTS_TIMEOUT);
+
+ /* Recheck cmd state to check if it now aborted. */
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (!rqi) {
+ CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL;
+ goto skip_clean;
+ }
+ rqi->abts_done = NULL;
+
+ /* if abort is still pending w/ fw, fail */
+ if (CMD_ABTS_STATUS(sc) == SNIC_INVALID_CODE) {
+ SNIC_HOST_ERR(snic->shost,
+ "clean_single_req_err:sc %p tag %d abt still pending w/ fw, tm_tag %d flags 0x%llx\n",
+ sc, tag, rqi->tm_tag, CMD_FLAGS(sc));
+
+ CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_DONE;
+ ret = 1;
+
+ goto skip_clean;
+ }
+
+ CMD_STATE(sc) = SNIC_IOREQ_ABTS_COMPLETE;
+ CMD_SP(sc) = NULL;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ snic_release_req_buf(snic, rqi, sc);
+
+ ret = 0;
+
+ return ret;
+
+skip_clean:
+ spin_unlock_irqrestore(io_lock, flags);
+
+ return ret;
+} /* end of snic_dr_clean_single_req */
+
+static int
+snic_dr_clean_pending_req(struct snic *snic, struct scsi_cmnd *lr_sc)
+{
+ struct scsi_device *lr_sdev = lr_sc->device;
+ u32 tag = 0;
+ int ret = FAILED;
+
+ for (tag = 0; tag < snic->max_tag_id; tag++) {
+ if (tag == snic_cmd_tag(lr_sc))
+ continue;
+
+ ret = snic_dr_clean_single_req(snic, tag, lr_sdev);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost, "clean_err:tag = %d\n", tag);
+
+ goto clean_err;
+ }
+ }
+
+ schedule_timeout(msecs_to_jiffies(100));
+
+ /* Walk through all the cmds and check abts status. */
+ if (snic_is_abts_pending(snic, lr_sc)) {
+ ret = FAILED;
+
+ goto clean_err;
+ }
+
+ ret = 0;
+ SNIC_SCSI_DBG(snic->shost, "clean_pending_req: Success.\n");
+
+ return ret;
+
+clean_err:
+ ret = FAILED;
+ SNIC_HOST_ERR(snic->shost,
+ "Failed to Clean Pending IOs on %s device.\n",
+ dev_name(&lr_sdev->sdev_gendev));
+
+ return ret;
+
+} /* end of snic_dr_clean_pending_req */
+
+/*
+ * snic_dr_finish : Called by snic_device_reset
+ */
+static int
+snic_dr_finish(struct snic *snic, struct scsi_cmnd *sc)
+{
+ struct snic_req_info *rqi = NULL;
+ spinlock_t *io_lock = NULL;
+ unsigned long flags;
+ int lr_res = 0;
+ int ret = FAILED;
+
+ io_lock = snic_io_lock_hash(snic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (!rqi) {
+ spin_unlock_irqrestore(io_lock, flags);
+ SNIC_SCSI_DBG(snic->shost,
+ "dr_fini: rqi is null tag 0x%x sc 0x%p flags 0x%llx\n",
+ snic_cmd_tag(sc), sc, CMD_FLAGS(sc));
+
+ ret = FAILED;
+ goto dr_fini_end;
+ }
+
+ rqi->dr_done = NULL;
+
+ lr_res = CMD_LR_STATUS(sc);
+
+ switch (lr_res) {
+ case SNIC_INVALID_CODE:
+ /* stats */
+ SNIC_SCSI_DBG(snic->shost,
+ "dr_fini: Tag %x Dev Reset Timedout. flags 0x%llx\n",
+ snic_cmd_tag(sc), CMD_FLAGS(sc));
+
+ CMD_FLAGS(sc) |= SNIC_DEV_RST_TIMEDOUT;
+ ret = FAILED;
+
+ goto dr_failed;
+
+ case SNIC_STAT_IO_SUCCESS:
+ SNIC_SCSI_DBG(snic->shost,
+ "dr_fini: Tag %x Dev Reset cmpl\n",
+ snic_cmd_tag(sc));
+ ret = 0;
+ break;
+
+ default:
+ SNIC_HOST_ERR(snic->shost,
+ "dr_fini:Device Reset completed& failed.Tag = %x lr_status %s flags 0x%llx\n",
+ snic_cmd_tag(sc),
+ snic_io_status_to_str(lr_res), CMD_FLAGS(sc));
+ ret = FAILED;
+ goto dr_failed;
+ }
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /*
+ * Cleanup any IOs on this LUN that have still not completed.
+ * If any of these fail, then LUN Reset fails.
+ * Cleanup cleans all commands on this LUN except
+ * the lun reset command. If all cmds get cleaned, the LUN Reset
+ * succeeds.
+ */
+
+ ret = snic_dr_clean_pending_req(snic, sc);
+ if (ret) {
+ spin_lock_irqsave(io_lock, flags);
+ SNIC_SCSI_DBG(snic->shost,
+ "dr_fini: Device Reset Failed since could not abort all IOs. Tag = %x.\n",
+ snic_cmd_tag(sc));
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+
+ goto dr_failed;
+ } else {
+ /* Cleanup LUN Reset Command */
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (rqi)
+ ret = SUCCESS; /* Completed Successfully */
+ else
+ ret = FAILED;
+ }
+
+dr_failed:
+ SNIC_BUG_ON(!spin_is_locked(io_lock));
+ if (rqi)
+ CMD_SP(sc) = NULL;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ if (rqi)
+ snic_release_req_buf(snic, rqi, sc);
+
+dr_fini_end:
+ return ret;
+} /* end of snic_dr_finish */
+
+static int
+snic_queue_dr_req(struct snic *snic,
+ struct snic_req_info *rqi,
+ struct scsi_cmnd *sc)
+{
+ /* Add special tag for device reset */
+ rqi->tm_tag |= SNIC_TAG_DEV_RST;
+
+ return snic_issue_tm_req(snic, rqi, sc, SNIC_ITMF_LUN_RESET);
+}
+
+static int
+snic_send_dr_and_wait(struct snic *snic, struct scsi_cmnd *sc)
+{
+ struct snic_req_info *rqi = NULL;
+ enum snic_ioreq_state sv_state;
+ spinlock_t *io_lock = NULL;
+ unsigned long flags;
+ DECLARE_COMPLETION_ONSTACK(tm_done);
+ int ret = FAILED, tag = snic_cmd_tag(sc);
+
+ io_lock = snic_io_lock_hash(snic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ CMD_FLAGS(sc) |= SNIC_DEVICE_RESET;
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (!rqi) {
+ SNIC_HOST_ERR(snic->shost,
+ "send_dr: rqi is null, Tag 0x%x flags 0x%llx\n",
+ tag, CMD_FLAGS(sc));
+ spin_unlock_irqrestore(io_lock, flags);
+
+ ret = FAILED;
+ goto send_dr_end;
+ }
+
+ /* Save Command state to restore in case Queuing failed. */
+ sv_state = CMD_STATE(sc);
+
+ CMD_STATE(sc) = SNIC_IOREQ_LR_PENDING;
+ CMD_LR_STATUS(sc) = SNIC_INVALID_CODE;
+
+ SNIC_SCSI_DBG(snic->shost, "dr: TAG = %x\n", tag);
+
+ rqi->dr_done = &tm_done;
+ SNIC_BUG_ON(!rqi->dr_done);
+
+ spin_unlock_irqrestore(io_lock, flags);
+ /*
+ * The Command state is changed to IOREQ_PENDING,
+ * in this case, if the command is completed, the icmnd_cmpl will
+ * mark the cmd as completed.
+ * This logic still makes LUN Reset is inevitable.
+ */
+
+ ret = snic_queue_dr_req(snic, rqi, sc);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "send_dr: IO w/ Tag 0x%x Failed err = %d. flags 0x%llx\n",
+ tag, ret, CMD_FLAGS(sc));
+
+ spin_lock_irqsave(io_lock, flags);
+ /* Restore State */
+ CMD_STATE(sc) = sv_state;
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (rqi)
+ rqi->dr_done = NULL;
+ /* rqi is freed in caller. */
+ spin_unlock_irqrestore(io_lock, flags);
+ ret = FAILED;
+
+ goto send_dr_end;
+ }
+
+ spin_lock_irqsave(io_lock, flags);
+ CMD_FLAGS(sc) |= SNIC_DEV_RST_ISSUED;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ ret = 0;
+
+ wait_for_completion_timeout(&tm_done, SNIC_LUN_RESET_TIMEOUT);
+
+send_dr_end:
+ return ret;
+}
+
+/*
+ * auxillary funciton to check lun reset op is supported or not
+ * Not supported if returns 0
+ */
+static int
+snic_dev_reset_supported(struct scsi_device *sdev)
+{
+ struct snic_tgt *tgt = starget_to_tgt(scsi_target(sdev));
+
+ if (tgt->tdata.typ == SNIC_TGT_DAS)
+ return 0;
+
+ return 1;
+}
+
+static void
+snic_unlink_and_release_req(struct snic *snic, struct scsi_cmnd *sc, int flag)
+{
+ struct snic_req_info *rqi = NULL;
+ spinlock_t *io_lock = NULL;
+ unsigned long flags;
+ u32 start_time = jiffies;
+
+ io_lock = snic_io_lock_hash(snic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (rqi) {
+ start_time = rqi->start_time;
+ CMD_SP(sc) = NULL;
+ }
+
+ CMD_FLAGS(sc) |= flag;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ if (rqi)
+ snic_release_req_buf(snic, rqi, sc);
+
+ SNIC_TRC(snic->shost->host_no, snic_cmd_tag(sc), (ulong) sc,
+ jiffies_to_msecs(jiffies - start_time), (ulong) rqi,
+ SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+}
+
+/*
+ * SCSI Eh thread issues a LUN Reset when one or more commands on a LUN
+ * fail to get aborted. It calls driver's eh_device_reset with a SCSI
+ * command on the LUN.
+ */
+int
+snic_device_reset(struct scsi_cmnd *sc)
+{
+ struct Scsi_Host *shost = sc->device->host;
+ struct snic *snic = shost_priv(shost);
+ struct snic_req_info *rqi = NULL;
+ int tag = snic_cmd_tag(sc);
+ int start_time = jiffies;
+ int ret = FAILED;
+ int dr_supp = 0;
+
+ SNIC_SCSI_DBG(shost, "dev_reset:sc %p :0x%x :req = %p :tag = %d\n",
+ sc, sc->cmnd[0], sc->request,
+ snic_cmd_tag(sc));
+ dr_supp = snic_dev_reset_supported(sc->device);
+ if (!dr_supp) {
+ /* device reset op is not supported */
+ SNIC_HOST_INFO(shost, "LUN Reset Op not supported.\n");
+ snic_unlink_and_release_req(snic, sc, SNIC_DEV_RST_NOTSUP);
+
+ goto dev_rst_end;
+ }
+
+ if (unlikely(snic_get_state(snic) != SNIC_ONLINE)) {
+ snic_unlink_and_release_req(snic, sc, 0);
+ SNIC_HOST_ERR(shost, "Devrst: Parent Devs are not online.\n");
+
+ goto dev_rst_end;
+ }
+
+ /* There is no tag when lun reset is issue through ioctl. */
+ if (unlikely(tag <= SNIC_NO_TAG)) {
+ SNIC_HOST_INFO(snic->shost,
+ "Devrst: LUN Reset Recvd thru IOCTL.\n");
+
+ rqi = snic_req_init(snic, 0);
+ if (!rqi)
+ goto dev_rst_end;
+
+ memset(scsi_cmd_priv(sc), 0,
+ sizeof(struct snic_internal_io_state));
+ CMD_SP(sc) = (char *)rqi;
+ CMD_FLAGS(sc) = SNIC_NO_FLAGS;
+
+ /* Add special tag for dr coming from user spc */
+ rqi->tm_tag = SNIC_TAG_IOCTL_DEV_RST;
+ rqi->sc = sc;
+ }
+
+ ret = snic_send_dr_and_wait(snic, sc);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "Devrst: IO w/ Tag %x Failed w/ err = %d\n",
+ tag, ret);
+
+ snic_unlink_and_release_req(snic, sc, 0);
+
+ goto dev_rst_end;
+ }
+
+ ret = snic_dr_finish(snic, sc);
+
+dev_rst_end:
+ SNIC_TRC(snic->shost->host_no, tag, (ulong) sc,
+ jiffies_to_msecs(jiffies - start_time),
+ 0, SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+ SNIC_SCSI_DBG(snic->shost,
+ "Devrst: Returning from Device Reset : %s\n",
+ (ret == SUCCESS) ? "SUCCESS" : "FAILED");
+
+ return ret;
+} /* end of snic_device_reset */
+
+/*
+ * SCSI Error handling calls driver's eh_host_reset if all prior
+ * error handling levels return FAILED.
+ *
+ * Host Reset is the highest level of error recovery. If this fails, then
+ * host is offlined by SCSI.
+ */
+/*
+ * snic_issue_hba_reset : Queues FW Reset Request.
+ */
+static int
+snic_issue_hba_reset(struct snic *snic, struct scsi_cmnd *sc)
+{
+ struct snic_req_info *rqi = NULL;
+ struct snic_host_req *req = NULL;
+ spinlock_t *io_lock = NULL;
+ DECLARE_COMPLETION_ONSTACK(wait);
+ unsigned long flags;
+ int ret = -ENOMEM;
+
+ rqi = snic_req_init(snic, 0);
+ if (!rqi) {
+ ret = -ENOMEM;
+
+ goto hba_rst_end;
+ }
+
+ if (snic_cmd_tag(sc) == SCSI_NO_TAG) {
+ memset(scsi_cmd_priv(sc), 0,
+ sizeof(struct snic_internal_io_state));
+ SNIC_HOST_INFO(snic->shost, "issu_hr:Host reset thru ioctl.\n");
+ rqi->sc = sc;
+ }
+
+ req = rqi_to_req(rqi);
+
+ io_lock = snic_io_lock_hash(snic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ SNIC_BUG_ON(CMD_SP(sc) != NULL);
+ CMD_STATE(sc) = SNIC_IOREQ_PENDING;
+ CMD_SP(sc) = (char *) rqi;
+ CMD_FLAGS(sc) |= SNIC_IO_INITIALIZED;
+ snic->remove_wait = &wait;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ /* Initialize Request */
+ snic_io_hdr_enc(&req->hdr, SNIC_REQ_HBA_RESET, 0, snic_cmd_tag(sc),
+ snic->config.hid, 0, (ulong) rqi);
+
+ req->u.reset.flags = 0;
+
+ ret = snic_queue_wq_desc(snic, req, sizeof(*req));
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "issu_hr:Queuing HBA Reset Failed. w err %d\n",
+ ret);
+
+ goto hba_rst_err;
+ }
+
+ spin_lock_irqsave(io_lock, flags);
+ CMD_FLAGS(sc) |= SNIC_HOST_RESET_ISSUED;
+ spin_unlock_irqrestore(io_lock, flags);
+ atomic64_inc(&snic->s_stats.reset.hba_resets);
+ SNIC_HOST_INFO(snic->shost, "Queued HBA Reset Successfully.\n");
+
+ wait_for_completion_timeout(snic->remove_wait,
+ SNIC_HOST_RESET_TIMEOUT);
+
+ if (snic_get_state(snic) == SNIC_FWRESET) {
+ SNIC_HOST_ERR(snic->shost, "reset_cmpl: Reset Timedout.\n");
+ ret = -ETIMEDOUT;
+
+ goto hba_rst_err;
+ }
+
+ spin_lock_irqsave(io_lock, flags);
+ snic->remove_wait = NULL;
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ CMD_SP(sc) = NULL;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ if (rqi)
+ snic_req_free(snic, rqi);
+
+ ret = 0;
+
+ return ret;
+
+hba_rst_err:
+ spin_lock_irqsave(io_lock, flags);
+ snic->remove_wait = NULL;
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ CMD_SP(sc) = NULL;
+ spin_unlock_irqrestore(io_lock, flags);
+
+ if (rqi)
+ snic_req_free(snic, rqi);
+
+hba_rst_end:
+ SNIC_HOST_ERR(snic->shost,
+ "reset:HBA Reset Failed w/ err = %d.\n",
+ ret);
+
+ return ret;
+} /* end of snic_issue_hba_reset */
+
+int
+snic_reset(struct Scsi_Host *shost, struct scsi_cmnd *sc)
+{
+ struct snic *snic = shost_priv(shost);
+ enum snic_state sv_state;
+ unsigned long flags;
+ int ret = FAILED;
+
+ /* Set snic state as SNIC_FWRESET*/
+ sv_state = snic_get_state(snic);
+
+ spin_lock_irqsave(&snic->snic_lock, flags);
+ if (snic_get_state(snic) == SNIC_FWRESET) {
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+ SNIC_HOST_INFO(shost, "reset:prev reset is in progres\n");
+
+ msleep(SNIC_HOST_RESET_TIMEOUT);
+ ret = SUCCESS;
+
+ goto reset_end;
+ }
+
+ snic_set_state(snic, SNIC_FWRESET);
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+
+ /* Wait for all the IOs that are entered in Qcmd */
+ while (atomic_read(&snic->ios_inflight))
+ schedule_timeout(msecs_to_jiffies(1));
+
+ ret = snic_issue_hba_reset(snic, sc);
+ if (ret) {
+ SNIC_HOST_ERR(shost,
+ "reset:Host Reset Failed w/ err %d.\n",
+ ret);
+ spin_lock_irqsave(&snic->snic_lock, flags);
+ snic_set_state(snic, sv_state);
+ spin_unlock_irqrestore(&snic->snic_lock, flags);
+ atomic64_inc(&snic->s_stats.reset.hba_reset_fail);
+ ret = FAILED;
+
+ goto reset_end;
+ }
+
+ ret = SUCCESS;
+
+reset_end:
+ return ret;
+} /* end of snic_reset */
+
+/*
+ * SCSI Error handling calls driver's eh_host_reset if all prior
+ * error handling levels return FAILED.
+ *
+ * Host Reset is the highest level of error recovery. If this fails, then
+ * host is offlined by SCSI.
+ */
+int
+snic_host_reset(struct scsi_cmnd *sc)
+{
+ struct Scsi_Host *shost = sc->device->host;
+ u32 start_time = jiffies;
+ int ret = FAILED;
+
+ SNIC_SCSI_DBG(shost,
+ "host reset:sc %p sc_cmd 0x%x req %p tag %d flags 0x%llx\n",
+ sc, sc->cmnd[0], sc->request,
+ snic_cmd_tag(sc), CMD_FLAGS(sc));
+
+ ret = snic_reset(shost, sc);
+
+ SNIC_TRC(shost->host_no, snic_cmd_tag(sc), (ulong) sc,
+ jiffies_to_msecs(jiffies - start_time),
+ 0, SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+ return ret;
+} /* end of snic_host_reset */
+
+/*
+ * snic_cmpl_pending_tmreq : Caller should hold io_lock
+ */
+static void
+snic_cmpl_pending_tmreq(struct snic *snic, struct scsi_cmnd *sc)
+{
+ struct snic_req_info *rqi = NULL;
+
+ SNIC_SCSI_DBG(snic->shost,
+ "Completing Pending TM Req sc %p, state %s flags 0x%llx\n",
+ sc, snic_io_status_to_str(CMD_STATE(sc)), CMD_FLAGS(sc));
+
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (!rqi)
+ return;
+
+ if (rqi->dr_done)
+ complete(rqi->dr_done);
+ else if (rqi->abts_done)
+ complete(rqi->abts_done);
+}
+
+/*
+ * snic_scsi_cleanup: Walks through tag map and releases the reqs
+ */
+static void
+snic_scsi_cleanup(struct snic *snic, int ex_tag)
+{
+ struct snic_req_info *rqi = NULL;
+ struct scsi_cmnd *sc = NULL;
+ spinlock_t *io_lock = NULL;
+ unsigned long flags;
+ int tag;
+ u64 st_time = 0;
+
+ SNIC_SCSI_DBG(snic->shost, "sc_clean: scsi cleanup.\n");
+
+ for (tag = 0; tag < snic->max_tag_id; tag++) {
+ /* Skip ex_tag */
+ if (tag == ex_tag)
+ continue;
+
+ io_lock = snic_io_lock_tag(snic, tag);
+ spin_lock_irqsave(io_lock, flags);
+ sc = scsi_host_find_tag(snic->shost, tag);
+ if (!sc) {
+ spin_unlock_irqrestore(io_lock, flags);
+
+ continue;
+ }
+
+ if (unlikely(snic_tmreq_pending(sc))) {
+ /*
+ * When FW Completes reset w/o sending completions
+ * for outstanding ios.
+ */
+ snic_cmpl_pending_tmreq(snic, sc);
+ spin_unlock_irqrestore(io_lock, flags);
+
+ continue;
+ }
+
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (!rqi) {
+ spin_unlock_irqrestore(io_lock, flags);
+
+ goto cleanup;
+ }
+
+ SNIC_SCSI_DBG(snic->shost,
+ "sc_clean: sc %p, rqi %p, tag %d flags 0x%llx\n",
+ sc, rqi, tag, CMD_FLAGS(sc));
+
+ CMD_SP(sc) = NULL;
+ CMD_FLAGS(sc) |= SNIC_SCSI_CLEANUP;
+ spin_unlock_irqrestore(io_lock, flags);
+ st_time = rqi->start_time;
+
+ SNIC_HOST_INFO(snic->shost,
+ "sc_clean: Releasing rqi %p : flags 0x%llx\n",
+ rqi, CMD_FLAGS(sc));
+
+ snic_release_req_buf(snic, rqi, sc);
+
+cleanup:
+ sc->result = DID_TRANSPORT_DISRUPTED << 16;
+ SNIC_HOST_INFO(snic->shost,
+ "sc_clean: DID_TRANSPORT_DISRUPTED for sc %p. rqi %p duration %llu msecs\n",
+ sc, rqi, (jiffies - st_time));
+
+ /* Update IO stats */
+ snic_stats_update_io_cmpl(&snic->s_stats);
+
+ if (sc->scsi_done) {
+ SNIC_TRC(snic->shost->host_no, tag, (ulong) sc,
+ jiffies_to_msecs(jiffies - st_time), 0,
+ SNIC_TRC_CMD(sc),
+ SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+ sc->scsi_done(sc);
+ }
+ }
+} /* end of snic_scsi_cleanup */
+
+void
+snic_shutdown_scsi_cleanup(struct snic *snic)
+{
+ SNIC_HOST_INFO(snic->shost, "Shutdown time SCSI Cleanup.\n");
+
+ snic_scsi_cleanup(snic, SCSI_NO_TAG);
+} /* end of snic_shutdown_scsi_cleanup */
+
+/*
+ * snic_internal_abort_io
+ * called by : snic_tgt_scsi_abort_io
+ */
+static int
+snic_internal_abort_io(struct snic *snic, struct scsi_cmnd *sc, int tmf)
+{
+ struct snic_req_info *rqi = NULL;
+ spinlock_t *io_lock = NULL;
+ unsigned long flags;
+ u32 sv_state = 0;
+ int ret = 0;
+
+ io_lock = snic_io_lock_hash(snic, sc);
+ spin_lock_irqsave(io_lock, flags);
+ rqi = (struct snic_req_info *) CMD_SP(sc);
+ if (!rqi)
+ goto skip_internal_abts;
+
+ if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING)
+ goto skip_internal_abts;
+
+ if ((CMD_FLAGS(sc) & SNIC_DEVICE_RESET) &&
+ (!(CMD_FLAGS(sc) & SNIC_DEV_RST_ISSUED))) {
+
+ SNIC_SCSI_DBG(snic->shost,
+ "internal_abts: dev rst not pending sc 0x%p\n",
+ sc);
+
+ goto skip_internal_abts;
+ }
+
+
+ if (!(CMD_FLAGS(sc) & SNIC_IO_ISSUED)) {
+ SNIC_SCSI_DBG(snic->shost,
+ "internal_abts: IO not yet issued sc 0x%p tag 0x%x flags 0x%llx state %d\n",
+ sc, snic_cmd_tag(sc), CMD_FLAGS(sc), CMD_STATE(sc));
+
+ goto skip_internal_abts;
+ }
+
+ sv_state = CMD_STATE(sc);
+ CMD_STATE(sc) = SNIC_IOREQ_ABTS_PENDING;
+ CMD_ABTS_STATUS(sc) = SNIC_INVALID_CODE;
+ CMD_FLAGS(sc) |= SNIC_IO_INTERNAL_TERM_PENDING;
+
+ if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET) {
+ /* stats */
+ rqi->tm_tag = SNIC_TAG_DEV_RST;
+ SNIC_SCSI_DBG(snic->shost, "internal_abts:dev rst sc %p\n", sc);
+ }
+
+ SNIC_SCSI_DBG(snic->shost, "internal_abts: Issuing abts tag %x\n",
+ snic_cmd_tag(sc));
+ SNIC_BUG_ON(rqi->abts_done);
+ spin_unlock_irqrestore(io_lock, flags);
+
+ ret = snic_queue_abort_req(snic, rqi, sc, tmf);
+ if (ret) {
+ SNIC_HOST_ERR(snic->shost,
+ "internal_abts: Tag = %x , Failed w/ err = %d\n",
+ snic_cmd_tag(sc), ret);
+
+ spin_lock_irqsave(io_lock, flags);
+
+ if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING)
+ CMD_STATE(sc) = sv_state;
+
+ goto skip_internal_abts;
+ }
+
+ spin_lock_irqsave(io_lock, flags);
+ if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET)
+ CMD_FLAGS(sc) |= SNIC_DEV_RST_TERM_ISSUED;
+ else
+ CMD_FLAGS(sc) |= SNIC_IO_INTERNAL_TERM_ISSUED;
+
+ ret = SUCCESS;
+
+skip_internal_abts:
+ SNIC_BUG_ON(!spin_is_locked(io_lock));
+ spin_unlock_irqrestore(io_lock, flags);
+
+ return ret;
+} /* end of snic_internal_abort_io */
+
+/*
+ * snic_tgt_scsi_abort_io : called by snic_tgt_del
+ */
+int
+snic_tgt_scsi_abort_io(struct snic_tgt *tgt)
+{
+ struct snic *snic = NULL;
+ struct scsi_cmnd *sc = NULL;
+ struct snic_tgt *sc_tgt = NULL;
+ spinlock_t *io_lock = NULL;
+ unsigned long flags;
+ int ret = 0, tag, abt_cnt = 0, tmf = 0;
+
+ if (!tgt)
+ return -1;
+
+ snic = shost_priv(snic_tgt_to_shost(tgt));
+ SNIC_SCSI_DBG(snic->shost, "tgt_abt_io: Cleaning Pending IOs.\n");
+
+ if (tgt->tdata.typ == SNIC_TGT_DAS)
+ tmf = SNIC_ITMF_ABTS_TASK;
+ else
+ tmf = SNIC_ITMF_ABTS_TASK_TERM;
+
+ for (tag = 0; tag < snic->max_tag_id; tag++) {
+ io_lock = snic_io_lock_tag(snic, tag);
+
+ spin_lock_irqsave(io_lock, flags);
+ sc = scsi_host_find_tag(snic->shost, tag);
+ if (!sc) {
+ spin_unlock_irqrestore(io_lock, flags);
+
+ continue;
+ }
+
+ sc_tgt = starget_to_tgt(scsi_target(sc->device));
+ if (sc_tgt != tgt) {
+ spin_unlock_irqrestore(io_lock, flags);
+
+ continue;
+ }
+ spin_unlock_irqrestore(io_lock, flags);
+
+ ret = snic_internal_abort_io(snic, sc, tmf);
+ if (ret < 0) {
+ SNIC_HOST_ERR(snic->shost,
+ "tgt_abt_io: Tag %x, Failed w err = %d\n",
+ tag, ret);
+
+ continue;
+ }
+
+ if (ret == SUCCESS)
+ abt_cnt++;
+ }
+
+ SNIC_SCSI_DBG(snic->shost, "tgt_abt_io: abt_cnt = %d\n", abt_cnt);
+
+ return 0;
+} /* end of snic_tgt_scsi_abort_io */
diff --git a/drivers/scsi/snic/snic_stats.h b/drivers/scsi/snic/snic_stats.h
new file mode 100644
index 000000000000..11e614849a82
--- /dev/null
+++ b/drivers/scsi/snic/snic_stats.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __SNIC_STATS_H
+#define __SNIC_STATS_H
+
+struct snic_io_stats {
+ atomic64_t active; /* Active IOs */
+ atomic64_t max_active; /* Max # active IOs */
+ atomic64_t max_sgl; /* Max # SGLs for any IO */
+ atomic64_t max_time; /* Max time to process IO */
+ atomic64_t max_qtime; /* Max time to Queue the IO */
+ atomic64_t max_cmpl_time; /* Max time to complete the IO */
+ atomic64_t sgl_cnt[SNIC_MAX_SG_DESC_CNT]; /* SGL Counters */
+ atomic64_t max_io_sz; /* Max IO Size */
+ atomic64_t compl; /* IO Completions */
+ atomic64_t fail; /* IO Failures */
+ atomic64_t req_null; /* req or req info is NULL */
+ atomic64_t alloc_fail; /* Alloc Failures */
+ atomic64_t sc_null;
+ atomic64_t io_not_found; /* IO Not Found */
+ atomic64_t num_ios; /* Number of IOs */
+};
+
+struct snic_abort_stats {
+ atomic64_t num; /* Abort counter */
+ atomic64_t fail; /* Abort Failure Counter */
+ atomic64_t drv_tmo; /* Abort Driver Timeouts */
+ atomic64_t fw_tmo; /* Abort Firmware Timeouts */
+ atomic64_t io_not_found;/* Abort IO Not Found */
+};
+
+struct snic_reset_stats {
+ atomic64_t dev_resets; /* Device Reset Counter */
+ atomic64_t dev_reset_fail; /* Device Reset Failures */
+ atomic64_t dev_reset_aborts; /* Device Reset Aborts */
+ atomic64_t dev_reset_tmo; /* Device Reset Timeout */
+ atomic64_t dev_reset_terms; /* Device Reset terminate */
+ atomic64_t hba_resets; /* hba/firmware resets */
+ atomic64_t hba_reset_cmpl; /* hba/firmware reset completions */
+ atomic64_t hba_reset_fail; /* hba/firmware failures */
+ atomic64_t snic_resets; /* snic resets */
+ atomic64_t snic_reset_compl; /* snic reset completions */
+ atomic64_t snic_reset_fail; /* snic reset failures */
+};
+
+struct snic_fw_stats {
+ atomic64_t actv_reqs; /* Active Requests */
+ atomic64_t max_actv_reqs; /* Max Active Requests */
+ atomic64_t out_of_res; /* Firmware Out Of Resources */
+ atomic64_t io_errs; /* Firmware IO Firmware Errors */
+ atomic64_t scsi_errs; /* Target hits check condition */
+};
+
+struct snic_misc_stats {
+ u64 last_isr_time;
+ u64 last_ack_time;
+ atomic64_t isr_cnt;
+ atomic64_t max_cq_ents; /* Max CQ Entries */
+ atomic64_t data_cnt_mismat; /* Data Count Mismatch */
+ atomic64_t io_tmo;
+ atomic64_t io_aborted;
+ atomic64_t sgl_inval; /* SGL Invalid */
+ atomic64_t abts_wq_alloc_fail; /* Abort Path WQ desc alloc failure */
+ atomic64_t devrst_wq_alloc_fail;/* Device Reset - WQ desc alloc fail */
+ atomic64_t wq_alloc_fail; /* IO WQ desc alloc failure */
+ atomic64_t no_icmnd_itmf_cmpls;
+ atomic64_t io_under_run;
+ atomic64_t qfull;
+ atomic64_t tgt_not_rdy;
+};
+
+struct snic_stats {
+ struct snic_io_stats io;
+ struct snic_abort_stats abts;
+ struct snic_reset_stats reset;
+ struct snic_fw_stats fw;
+ struct snic_misc_stats misc;
+ atomic64_t io_cmpl_skip;
+};
+
+int snic_stats_debugfs_init(struct snic *);
+void snic_stats_debugfs_remove(struct snic *);
+
+/* Auxillary function to update active IO counter */
+static inline void
+snic_stats_update_active_ios(struct snic_stats *s_stats)
+{
+ struct snic_io_stats *io = &s_stats->io;
+ u32 nr_active_ios;
+
+ nr_active_ios = atomic64_inc_return(&io->active);
+ if (atomic64_read(&io->max_active) < nr_active_ios)
+ atomic64_set(&io->max_active, nr_active_ios);
+
+ atomic64_inc(&io->num_ios);
+}
+
+/* Auxillary function to update IO completion counter */
+static inline void
+snic_stats_update_io_cmpl(struct snic_stats *s_stats)
+{
+ atomic64_dec(&s_stats->io.active);
+ if (unlikely(atomic64_read(&s_stats->io_cmpl_skip)))
+ atomic64_dec(&s_stats->io_cmpl_skip);
+ else
+ atomic64_inc(&s_stats->io.compl);
+}
+#endif /* __SNIC_STATS_H */
diff --git a/drivers/scsi/snic/snic_trc.c b/drivers/scsi/snic/snic_trc.c
new file mode 100644
index 000000000000..28a40a7ade38
--- /dev/null
+++ b/drivers/scsi/snic/snic_trc.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/mempool.h>
+#include <linux/errno.h>
+#include <linux/vmalloc.h>
+
+#include "snic_io.h"
+#include "snic.h"
+
+/*
+ * snic_get_trc_buf : Allocates a trace record and returns.
+ */
+struct snic_trc_data *
+snic_get_trc_buf(void)
+{
+ struct snic_trc *trc = &snic_glob->trc;
+ struct snic_trc_data *td = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&trc->lock, flags);
+ td = &trc->buf[trc->wr_idx];
+ trc->wr_idx++;
+
+ if (trc->wr_idx == trc->max_idx)
+ trc->wr_idx = 0;
+
+ if (trc->wr_idx != trc->rd_idx) {
+ spin_unlock_irqrestore(&trc->lock, flags);
+
+ goto end;
+ }
+
+ trc->rd_idx++;
+ if (trc->rd_idx == trc->max_idx)
+ trc->rd_idx = 0;
+
+ td->ts = 0; /* Marker for checking the record, for complete data*/
+ spin_unlock_irqrestore(&trc->lock, flags);
+
+end:
+
+ return td;
+} /* end of snic_get_trc_buf */
+
+/*
+ * snic_fmt_trc_data : Formats trace data for printing.
+ */
+static int
+snic_fmt_trc_data(struct snic_trc_data *td, char *buf, int buf_sz)
+{
+ int len = 0;
+ struct timespec tmspec;
+
+ jiffies_to_timespec(td->ts, &tmspec);
+
+ len += snprintf(buf, buf_sz,
+ "%lu.%10lu %-25s %3d %4x %16llx %16llx %16llx %16llx %16llx\n",
+ tmspec.tv_sec,
+ tmspec.tv_nsec,
+ td->fn,
+ td->hno,
+ td->tag,
+ td->data[0], td->data[1], td->data[2], td->data[3],
+ td->data[4]);
+
+ return len;
+} /* end of snic_fmt_trc_data */
+
+/*
+ * snic_get_trc_data : Returns a formatted trace buffer.
+ */
+int
+snic_get_trc_data(char *buf, int buf_sz)
+{
+ struct snic_trc_data *td = NULL;
+ struct snic_trc *trc = &snic_glob->trc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&trc->lock, flags);
+ if (trc->rd_idx == trc->wr_idx) {
+ spin_unlock_irqrestore(&trc->lock, flags);
+
+ return -1;
+ }
+ td = &trc->buf[trc->rd_idx];
+
+ if (td->ts == 0) {
+ /* write in progress. */
+ spin_unlock_irqrestore(&trc->lock, flags);
+
+ return -1;
+ }
+
+ trc->rd_idx++;
+ if (trc->rd_idx == trc->max_idx)
+ trc->rd_idx = 0;
+ spin_unlock_irqrestore(&trc->lock, flags);
+
+ return snic_fmt_trc_data(td, buf, buf_sz);
+} /* end of snic_get_trc_data */
+
+/*
+ * snic_trc_init() : Configures Trace Functionality for snic.
+ */
+int
+snic_trc_init(void)
+{
+ struct snic_trc *trc = &snic_glob->trc;
+ void *tbuf = NULL;
+ int tbuf_sz = 0, ret;
+
+ tbuf_sz = (snic_trace_max_pages * PAGE_SIZE);
+ tbuf = vmalloc(tbuf_sz);
+ if (!tbuf) {
+ SNIC_ERR("Failed to Allocate Trace Buffer Size. %d\n", tbuf_sz);
+ SNIC_ERR("Trace Facility not enabled.\n");
+ ret = -ENOMEM;
+
+ return ret;
+ }
+
+ memset(tbuf, 0, tbuf_sz);
+ trc->buf = (struct snic_trc_data *) tbuf;
+ spin_lock_init(&trc->lock);
+
+ ret = snic_trc_debugfs_init();
+ if (ret) {
+ SNIC_ERR("Failed to create Debugfs Files.\n");
+
+ goto error;
+ }
+
+ trc->max_idx = (tbuf_sz / SNIC_TRC_ENTRY_SZ);
+ trc->rd_idx = trc->wr_idx = 0;
+ trc->enable = 1;
+ SNIC_INFO("Trace Facility Enabled.\n Trace Buffer SZ %lu Pages.\n",
+ tbuf_sz / PAGE_SIZE);
+ ret = 0;
+
+ return ret;
+
+error:
+ snic_trc_free();
+
+ return ret;
+} /* end of snic_trc_init */
+
+/*
+ * snic_trc_free : Releases the trace buffer and disables the tracing.
+ */
+void
+snic_trc_free(void)
+{
+ struct snic_trc *trc = &snic_glob->trc;
+
+ trc->enable = 0;
+ snic_trc_debugfs_term();
+
+ if (trc->buf) {
+ vfree(trc->buf);
+ trc->buf = NULL;
+ }
+
+ SNIC_INFO("Trace Facility Disabled.\n");
+} /* end of snic_trc_free */
diff --git a/drivers/scsi/snic/snic_trc.h b/drivers/scsi/snic/snic_trc.h
new file mode 100644
index 000000000000..427faee5f97e
--- /dev/null
+++ b/drivers/scsi/snic/snic_trc.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __SNIC_TRC_H
+#define __SNIC_TRC_H
+
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+
+extern ssize_t simple_read_from_buffer(void __user *to,
+ size_t count,
+ loff_t *ppos,
+ const void *from,
+ size_t available);
+
+extern unsigned int snic_trace_max_pages;
+
+/* Global Data structure for trace to manage trace functionality */
+struct snic_trc_data {
+ u64 ts; /* Time Stamp */
+ char *fn; /* Ptr to Function Name */
+ u32 hno; /* SCSI Host ID */
+ u32 tag; /* Command Tag */
+ u64 data[5];
+} __attribute__((__packed__));
+
+#define SNIC_TRC_ENTRY_SZ 64 /* in Bytes */
+
+struct snic_trc {
+ spinlock_t lock;
+ struct snic_trc_data *buf; /* Trace Buffer */
+ u32 max_idx; /* Max Index into trace buffer */
+ u32 rd_idx;
+ u32 wr_idx;
+ u32 enable; /* Control Variable for Tracing */
+
+ struct dentry *trc_enable; /* debugfs file object */
+ struct dentry *trc_file;
+};
+
+int snic_trc_init(void);
+void snic_trc_free(void);
+int snic_trc_debugfs_init(void);
+void snic_trc_debugfs_term(void);
+struct snic_trc_data *snic_get_trc_buf(void);
+int snic_get_trc_data(char *buf, int buf_sz);
+
+int snic_debugfs_init(void);
+void snic_debugfs_term(void);
+
+static inline void
+snic_trace(char *fn, u16 hno, u32 tag, u64 d1, u64 d2, u64 d3, u64 d4, u64 d5)
+{
+ struct snic_trc_data *tr_rec = snic_get_trc_buf();
+
+ if (!tr_rec)
+ return;
+
+ tr_rec->fn = (char *)fn;
+ tr_rec->hno = hno;
+ tr_rec->tag = tag;
+ tr_rec->data[0] = d1;
+ tr_rec->data[1] = d2;
+ tr_rec->data[2] = d3;
+ tr_rec->data[3] = d4;
+ tr_rec->data[4] = d5;
+ tr_rec->ts = jiffies; /* Update time stamp at last */
+}
+
+#define SNIC_TRC(_hno, _tag, d1, d2, d3, d4, d5) \
+ do { \
+ if (unlikely(snic_glob->trc.enable)) \
+ snic_trace((char *)__func__, \
+ (u16)(_hno), \
+ (u32)(_tag), \
+ (u64)(d1), \
+ (u64)(d2), \
+ (u64)(d3), \
+ (u64)(d4), \
+ (u64)(d5)); \
+ } while (0)
+#else
+
+#define SNIC_TRC(_hno, _tag, d1, d2, d3, d4, d5) \
+ do { \
+ if (unlikely(snic_log_level & 0x2)) \
+ SNIC_DBG("SnicTrace: %s %2u %2u %llx %llx %llx %llx %llx", \
+ (char *)__func__, \
+ (u16)(_hno), \
+ (u32)(_tag), \
+ (u64)(d1), \
+ (u64)(d2), \
+ (u64)(d3), \
+ (u64)(d4), \
+ (u64)(d5)); \
+ } while (0)
+#endif /* end of CONFIG_SCSI_SNIC_DEBUG_FS */
+
+#define SNIC_TRC_CMD(sc) \
+ ((u64)sc->cmnd[0] << 56 | (u64)sc->cmnd[7] << 40 | \
+ (u64)sc->cmnd[8] << 32 | (u64)sc->cmnd[2] << 24 | \
+ (u64)sc->cmnd[3] << 16 | (u64)sc->cmnd[4] << 8 | \
+ (u64)sc->cmnd[5])
+
+#define SNIC_TRC_CMD_STATE_FLAGS(sc) \
+ ((u64) CMD_FLAGS(sc) << 32 | CMD_STATE(sc))
+
+#endif /* end of __SNIC_TRC_H */
diff --git a/drivers/scsi/snic/vnic_cq.c b/drivers/scsi/snic/vnic_cq.c
new file mode 100644
index 000000000000..4c8e64e4fba6
--- /dev/null
+++ b/drivers/scsi/snic/vnic_cq.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include "vnic_dev.h"
+#include "vnic_cq.h"
+
+void svnic_cq_free(struct vnic_cq *cq)
+{
+ svnic_dev_free_desc_ring(cq->vdev, &cq->ring);
+
+ cq->ctrl = NULL;
+}
+
+int svnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq,
+ unsigned int index, unsigned int desc_count, unsigned int desc_size)
+{
+ int err;
+
+ cq->index = index;
+ cq->vdev = vdev;
+
+ cq->ctrl = svnic_dev_get_res(vdev, RES_TYPE_CQ, index);
+ if (!cq->ctrl) {
+ pr_err("Failed to hook CQ[%d] resource\n", index);
+
+ return -EINVAL;
+ }
+
+ err = svnic_dev_alloc_desc_ring(vdev, &cq->ring, desc_count, desc_size);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+void svnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable,
+ unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail,
+ unsigned int cq_tail_color, unsigned int interrupt_enable,
+ unsigned int cq_entry_enable, unsigned int cq_message_enable,
+ unsigned int interrupt_offset, u64 cq_message_addr)
+{
+ u64 paddr;
+
+ paddr = (u64)cq->ring.base_addr | VNIC_PADDR_TARGET;
+ writeq(paddr, &cq->ctrl->ring_base);
+ iowrite32(cq->ring.desc_count, &cq->ctrl->ring_size);
+ iowrite32(flow_control_enable, &cq->ctrl->flow_control_enable);
+ iowrite32(color_enable, &cq->ctrl->color_enable);
+ iowrite32(cq_head, &cq->ctrl->cq_head);
+ iowrite32(cq_tail, &cq->ctrl->cq_tail);
+ iowrite32(cq_tail_color, &cq->ctrl->cq_tail_color);
+ iowrite32(interrupt_enable, &cq->ctrl->interrupt_enable);
+ iowrite32(cq_entry_enable, &cq->ctrl->cq_entry_enable);
+ iowrite32(cq_message_enable, &cq->ctrl->cq_message_enable);
+ iowrite32(interrupt_offset, &cq->ctrl->interrupt_offset);
+ writeq(cq_message_addr, &cq->ctrl->cq_message_addr);
+}
+
+void svnic_cq_clean(struct vnic_cq *cq)
+{
+ cq->to_clean = 0;
+ cq->last_color = 0;
+
+ iowrite32(0, &cq->ctrl->cq_head);
+ iowrite32(0, &cq->ctrl->cq_tail);
+ iowrite32(1, &cq->ctrl->cq_tail_color);
+
+ svnic_dev_clear_desc_ring(&cq->ring);
+}
diff --git a/drivers/scsi/snic/vnic_cq.h b/drivers/scsi/snic/vnic_cq.h
new file mode 100644
index 000000000000..6e651c3e16f7
--- /dev/null
+++ b/drivers/scsi/snic/vnic_cq.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VNIC_CQ_H_
+#define _VNIC_CQ_H_
+
+#include "cq_desc.h"
+#include "vnic_dev.h"
+
+/* Completion queue control */
+struct vnic_cq_ctrl {
+ u64 ring_base; /* 0x00 */
+ u32 ring_size; /* 0x08 */
+ u32 pad0;
+ u32 flow_control_enable; /* 0x10 */
+ u32 pad1;
+ u32 color_enable; /* 0x18 */
+ u32 pad2;
+ u32 cq_head; /* 0x20 */
+ u32 pad3;
+ u32 cq_tail; /* 0x28 */
+ u32 pad4;
+ u32 cq_tail_color; /* 0x30 */
+ u32 pad5;
+ u32 interrupt_enable; /* 0x38 */
+ u32 pad6;
+ u32 cq_entry_enable; /* 0x40 */
+ u32 pad7;
+ u32 cq_message_enable; /* 0x48 */
+ u32 pad8;
+ u32 interrupt_offset; /* 0x50 */
+ u32 pad9;
+ u64 cq_message_addr; /* 0x58 */
+ u32 pad10;
+};
+
+struct vnic_cq {
+ unsigned int index;
+ struct vnic_dev *vdev;
+ struct vnic_cq_ctrl __iomem *ctrl; /* memory-mapped */
+ struct vnic_dev_ring ring;
+ unsigned int to_clean;
+ unsigned int last_color;
+};
+
+static inline unsigned int svnic_cq_service(struct vnic_cq *cq,
+ unsigned int work_to_do,
+ int (*q_service)(struct vnic_dev *vdev, struct cq_desc *cq_desc,
+ u8 type, u16 q_number, u16 completed_index, void *opaque),
+ void *opaque)
+{
+ struct cq_desc *cq_desc;
+ unsigned int work_done = 0;
+ u16 q_number, completed_index;
+ u8 type, color;
+
+ cq_desc = (struct cq_desc *)((u8 *)cq->ring.descs +
+ cq->ring.desc_size * cq->to_clean);
+ cq_desc_dec(cq_desc, &type, &color,
+ &q_number, &completed_index);
+
+ while (color != cq->last_color) {
+
+ if ((*q_service)(cq->vdev, cq_desc, type,
+ q_number, completed_index, opaque))
+ break;
+
+ cq->to_clean++;
+ if (cq->to_clean == cq->ring.desc_count) {
+ cq->to_clean = 0;
+ cq->last_color = cq->last_color ? 0 : 1;
+ }
+
+ cq_desc = (struct cq_desc *)((u8 *)cq->ring.descs +
+ cq->ring.desc_size * cq->to_clean);
+ cq_desc_dec(cq_desc, &type, &color,
+ &q_number, &completed_index);
+
+ work_done++;
+ if (work_done >= work_to_do)
+ break;
+ }
+
+ return work_done;
+}
+
+void svnic_cq_free(struct vnic_cq *cq);
+int svnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq,
+ unsigned int index, unsigned int desc_count, unsigned int desc_size);
+void svnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable,
+ unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail,
+ unsigned int cq_tail_color, unsigned int interrupt_enable,
+ unsigned int cq_entry_enable, unsigned int message_enable,
+ unsigned int interrupt_offset, u64 message_addr);
+void svnic_cq_clean(struct vnic_cq *cq);
+#endif /* _VNIC_CQ_H_ */
diff --git a/drivers/scsi/snic/vnic_cq_fw.h b/drivers/scsi/snic/vnic_cq_fw.h
new file mode 100644
index 000000000000..c2d1bbd44bd1
--- /dev/null
+++ b/drivers/scsi/snic/vnic_cq_fw.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VNIC_CQ_FW_H_
+#define _VNIC_CQ_FW_H_
+
+#include "snic_fwint.h"
+
+static inline unsigned int
+vnic_cq_fw_service(struct vnic_cq *cq,
+ int (*q_service)(struct vnic_dev *vdev,
+ unsigned int index,
+ struct snic_fw_req *desc),
+ unsigned int work_to_do)
+
+{
+ struct snic_fw_req *desc;
+ unsigned int work_done = 0;
+ u8 color;
+
+ desc = (struct snic_fw_req *)((u8 *)cq->ring.descs +
+ cq->ring.desc_size * cq->to_clean);
+ snic_color_dec(desc, &color);
+
+ while (color != cq->last_color) {
+
+ if ((*q_service)(cq->vdev, cq->index, desc))
+ break;
+
+ cq->to_clean++;
+ if (cq->to_clean == cq->ring.desc_count) {
+ cq->to_clean = 0;
+ cq->last_color = cq->last_color ? 0 : 1;
+ }
+
+ desc = (struct snic_fw_req *)((u8 *)cq->ring.descs +
+ cq->ring.desc_size * cq->to_clean);
+ snic_color_dec(desc, &color);
+
+ work_done++;
+ if (work_done >= work_to_do)
+ break;
+ }
+
+ return work_done;
+}
+
+#endif /* _VNIC_CQ_FW_H_ */
diff --git a/drivers/scsi/snic/vnic_dev.c b/drivers/scsi/snic/vnic_dev.c
new file mode 100644
index 000000000000..e0b5549bc9fb
--- /dev/null
+++ b/drivers/scsi/snic/vnic_dev.c
@@ -0,0 +1,748 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/if_ether.h>
+#include <linux/slab.h>
+#include "vnic_resource.h"
+#include "vnic_devcmd.h"
+#include "vnic_dev.h"
+#include "vnic_stats.h"
+#include "vnic_wq.h"
+
+#define VNIC_DVCMD_TMO 10000 /* Devcmd Timeout value */
+#define VNIC_NOTIFY_INTR_MASK 0x0000ffff00000000ULL
+
+struct devcmd2_controller {
+ struct vnic_wq_ctrl __iomem *wq_ctrl;
+ struct vnic_dev_ring results_ring;
+ struct vnic_wq wq;
+ struct vnic_devcmd2 *cmd_ring;
+ struct devcmd2_result *result;
+ u16 next_result;
+ u16 result_size;
+ int color;
+};
+
+struct vnic_res {
+ void __iomem *vaddr;
+ unsigned int count;
+};
+
+struct vnic_dev {
+ void *priv;
+ struct pci_dev *pdev;
+ struct vnic_res res[RES_TYPE_MAX];
+ enum vnic_dev_intr_mode intr_mode;
+ struct vnic_devcmd __iomem *devcmd;
+ struct vnic_devcmd_notify *notify;
+ struct vnic_devcmd_notify notify_copy;
+ dma_addr_t notify_pa;
+ u32 *linkstatus;
+ dma_addr_t linkstatus_pa;
+ struct vnic_stats *stats;
+ dma_addr_t stats_pa;
+ struct vnic_devcmd_fw_info *fw_info;
+ dma_addr_t fw_info_pa;
+ u64 args[VNIC_DEVCMD_NARGS];
+ struct devcmd2_controller *devcmd2;
+
+ int (*devcmd_rtn)(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ int wait);
+};
+
+#define VNIC_MAX_RES_HDR_SIZE \
+ (sizeof(struct vnic_resource_header) + \
+ sizeof(struct vnic_resource) * RES_TYPE_MAX)
+#define VNIC_RES_STRIDE 128
+
+void *svnic_dev_priv(struct vnic_dev *vdev)
+{
+ return vdev->priv;
+}
+
+static int vnic_dev_discover_res(struct vnic_dev *vdev,
+ struct vnic_dev_bar *bar, unsigned int num_bars)
+{
+ struct vnic_resource_header __iomem *rh;
+ struct vnic_resource __iomem *r;
+ u8 type;
+
+ if (num_bars == 0)
+ return -EINVAL;
+
+ if (bar->len < VNIC_MAX_RES_HDR_SIZE) {
+ pr_err("vNIC BAR0 res hdr length error\n");
+
+ return -EINVAL;
+ }
+
+ rh = bar->vaddr;
+ if (!rh) {
+ pr_err("vNIC BAR0 res hdr not mem-mapped\n");
+
+ return -EINVAL;
+ }
+
+ if (ioread32(&rh->magic) != VNIC_RES_MAGIC ||
+ ioread32(&rh->version) != VNIC_RES_VERSION) {
+ pr_err("vNIC BAR0 res magic/version error exp (%lx/%lx) curr (%x/%x)\n",
+ VNIC_RES_MAGIC, VNIC_RES_VERSION,
+ ioread32(&rh->magic), ioread32(&rh->version));
+
+ return -EINVAL;
+ }
+
+ r = (struct vnic_resource __iomem *)(rh + 1);
+
+ while ((type = ioread8(&r->type)) != RES_TYPE_EOL) {
+
+ u8 bar_num = ioread8(&r->bar);
+ u32 bar_offset = ioread32(&r->bar_offset);
+ u32 count = ioread32(&r->count);
+ u32 len;
+
+ r++;
+
+ if (bar_num >= num_bars)
+ continue;
+
+ if (!bar[bar_num].len || !bar[bar_num].vaddr)
+ continue;
+
+ switch (type) {
+ case RES_TYPE_WQ:
+ case RES_TYPE_RQ:
+ case RES_TYPE_CQ:
+ case RES_TYPE_INTR_CTRL:
+ /* each count is stride bytes long */
+ len = count * VNIC_RES_STRIDE;
+ if (len + bar_offset > bar->len) {
+ pr_err("vNIC BAR0 resource %d out-of-bounds, offset 0x%x + size 0x%x > bar len 0x%lx\n",
+ type, bar_offset,
+ len,
+ bar->len);
+
+ return -EINVAL;
+ }
+ break;
+
+ case RES_TYPE_INTR_PBA_LEGACY:
+ case RES_TYPE_DEVCMD:
+ case RES_TYPE_DEVCMD2:
+ len = count;
+ break;
+
+ default:
+ continue;
+ }
+
+ vdev->res[type].count = count;
+ vdev->res[type].vaddr = (char __iomem *)bar->vaddr + bar_offset;
+ }
+
+ return 0;
+}
+
+unsigned int svnic_dev_get_res_count(struct vnic_dev *vdev,
+ enum vnic_res_type type)
+{
+ return vdev->res[type].count;
+}
+
+void __iomem *svnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type,
+ unsigned int index)
+{
+ if (!vdev->res[type].vaddr)
+ return NULL;
+
+ switch (type) {
+ case RES_TYPE_WQ:
+ case RES_TYPE_RQ:
+ case RES_TYPE_CQ:
+ case RES_TYPE_INTR_CTRL:
+ return (char __iomem *)vdev->res[type].vaddr +
+ index * VNIC_RES_STRIDE;
+
+ default:
+ return (char __iomem *)vdev->res[type].vaddr;
+ }
+}
+
+unsigned int svnic_dev_desc_ring_size(struct vnic_dev_ring *ring,
+ unsigned int desc_count,
+ unsigned int desc_size)
+{
+ /* The base address of the desc rings must be 512 byte aligned.
+ * Descriptor count is aligned to groups of 32 descriptors. A
+ * count of 0 means the maximum 4096 descriptors. Descriptor
+ * size is aligned to 16 bytes.
+ */
+
+ unsigned int count_align = 32;
+ unsigned int desc_align = 16;
+
+ ring->base_align = 512;
+
+ if (desc_count == 0)
+ desc_count = 4096;
+
+ ring->desc_count = ALIGN(desc_count, count_align);
+
+ ring->desc_size = ALIGN(desc_size, desc_align);
+
+ ring->size = ring->desc_count * ring->desc_size;
+ ring->size_unaligned = ring->size + ring->base_align;
+
+ return ring->size_unaligned;
+}
+
+void svnic_dev_clear_desc_ring(struct vnic_dev_ring *ring)
+{
+ memset(ring->descs, 0, ring->size);
+}
+
+int svnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ svnic_dev_desc_ring_size(ring, desc_count, desc_size);
+
+ ring->descs_unaligned = pci_alloc_consistent(vdev->pdev,
+ ring->size_unaligned,
+ &ring->base_addr_unaligned);
+
+ if (!ring->descs_unaligned) {
+ pr_err("Failed to allocate ring (size=%d), aborting\n",
+ (int)ring->size);
+
+ return -ENOMEM;
+ }
+
+ ring->base_addr = ALIGN(ring->base_addr_unaligned,
+ ring->base_align);
+ ring->descs = (u8 *)ring->descs_unaligned +
+ (ring->base_addr - ring->base_addr_unaligned);
+
+ svnic_dev_clear_desc_ring(ring);
+
+ ring->desc_avail = ring->desc_count - 1;
+
+ return 0;
+}
+
+void svnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring)
+{
+ if (ring->descs) {
+ pci_free_consistent(vdev->pdev,
+ ring->size_unaligned,
+ ring->descs_unaligned,
+ ring->base_addr_unaligned);
+ ring->descs = NULL;
+ }
+}
+
+static int _svnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ int wait)
+{
+ struct devcmd2_controller *dc2c = vdev->devcmd2;
+ struct devcmd2_result *result = dc2c->result + dc2c->next_result;
+ unsigned int i;
+ int delay;
+ int err;
+ u32 posted;
+ u32 new_posted;
+
+ posted = ioread32(&dc2c->wq_ctrl->posted_index);
+
+ if (posted == 0xFFFFFFFF) { /* check for hardware gone */
+ /* Hardware surprise removal: return error */
+ return -ENODEV;
+ }
+
+ new_posted = (posted + 1) % DEVCMD2_RING_SIZE;
+ dc2c->cmd_ring[posted].cmd = cmd;
+ dc2c->cmd_ring[posted].flags = 0;
+
+ if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT))
+ dc2c->cmd_ring[posted].flags |= DEVCMD2_FNORESULT;
+
+ if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) {
+ for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
+ dc2c->cmd_ring[posted].args[i] = vdev->args[i];
+ }
+ /* Adding write memory barrier prevents compiler and/or CPU
+ * reordering, thus avoiding descriptor posting before
+ * descriptor is initialized. Otherwise, hardware can read
+ * stale descriptor fields.
+ */
+ wmb();
+ iowrite32(new_posted, &dc2c->wq_ctrl->posted_index);
+
+ if (dc2c->cmd_ring[posted].flags & DEVCMD2_FNORESULT)
+ return 0;
+
+ for (delay = 0; delay < wait; delay++) {
+ udelay(100);
+ if (result->color == dc2c->color) {
+ dc2c->next_result++;
+ if (dc2c->next_result == dc2c->result_size) {
+ dc2c->next_result = 0;
+ dc2c->color = dc2c->color ? 0 : 1;
+ }
+ if (result->error) {
+ err = (int) result->error;
+ if (err != ERR_ECMDUNKNOWN ||
+ cmd != CMD_CAPABILITY)
+ pr_err("Error %d devcmd %d\n",
+ err, _CMD_N(cmd));
+
+ return err;
+ }
+ if (_CMD_DIR(cmd) & _CMD_DIR_READ) {
+ /*
+ * Adding the rmb() prevents the compiler
+ * and/or CPU from reordering the reads which
+ * would potentially result in reading stale
+ * values.
+ */
+ rmb();
+ for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
+ vdev->args[i] = result->results[i];
+ }
+
+ return 0;
+ }
+ }
+
+ pr_err("Timed out devcmd %d\n", _CMD_N(cmd));
+
+ return -ETIMEDOUT;
+}
+
+static int svnic_dev_init_devcmd2(struct vnic_dev *vdev)
+{
+ struct devcmd2_controller *dc2c = NULL;
+ unsigned int fetch_idx;
+ int ret;
+ void __iomem *p;
+
+ if (vdev->devcmd2)
+ return 0;
+
+ p = svnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0);
+ if (!p)
+ return -ENODEV;
+
+ dc2c = kzalloc(sizeof(*dc2c), GFP_ATOMIC);
+ if (!dc2c)
+ return -ENOMEM;
+
+ vdev->devcmd2 = dc2c;
+
+ dc2c->color = 1;
+ dc2c->result_size = DEVCMD2_RING_SIZE;
+
+ ret = vnic_wq_devcmd2_alloc(vdev,
+ &dc2c->wq,
+ DEVCMD2_RING_SIZE,
+ DEVCMD2_DESC_SIZE);
+ if (ret)
+ goto err_free_devcmd2;
+
+ fetch_idx = ioread32(&dc2c->wq.ctrl->fetch_index);
+ if (fetch_idx == 0xFFFFFFFF) { /* check for hardware gone */
+ /* Hardware surprise removal: reset fetch_index */
+ fetch_idx = 0;
+ }
+
+ /*
+ * Don't change fetch_index ever and
+ * set posted_index same as fetch_index
+ * when setting up the WQ for devcmd2.
+ */
+ vnic_wq_init_start(&dc2c->wq, 0, fetch_idx, fetch_idx, 0, 0);
+ svnic_wq_enable(&dc2c->wq);
+ ret = svnic_dev_alloc_desc_ring(vdev,
+ &dc2c->results_ring,
+ DEVCMD2_RING_SIZE,
+ DEVCMD2_DESC_SIZE);
+ if (ret)
+ goto err_free_wq;
+
+ dc2c->result = (struct devcmd2_result *) dc2c->results_ring.descs;
+ dc2c->cmd_ring = (struct vnic_devcmd2 *) dc2c->wq.ring.descs;
+ dc2c->wq_ctrl = dc2c->wq.ctrl;
+ vdev->args[0] = (u64) dc2c->results_ring.base_addr | VNIC_PADDR_TARGET;
+ vdev->args[1] = DEVCMD2_RING_SIZE;
+
+ ret = _svnic_dev_cmd2(vdev, CMD_INITIALIZE_DEVCMD2, VNIC_DVCMD_TMO);
+ if (ret < 0)
+ goto err_free_desc_ring;
+
+ vdev->devcmd_rtn = &_svnic_dev_cmd2;
+ pr_info("DEVCMD2 Initialized.\n");
+
+ return ret;
+
+err_free_desc_ring:
+ svnic_dev_free_desc_ring(vdev, &dc2c->results_ring);
+
+err_free_wq:
+ svnic_wq_disable(&dc2c->wq);
+ svnic_wq_free(&dc2c->wq);
+
+err_free_devcmd2:
+ kfree(dc2c);
+ vdev->devcmd2 = NULL;
+
+ return ret;
+} /* end of svnic_dev_init_devcmd2 */
+
+static void vnic_dev_deinit_devcmd2(struct vnic_dev *vdev)
+{
+ struct devcmd2_controller *dc2c = vdev->devcmd2;
+
+ vdev->devcmd2 = NULL;
+ vdev->devcmd_rtn = NULL;
+
+ svnic_dev_free_desc_ring(vdev, &dc2c->results_ring);
+ svnic_wq_disable(&dc2c->wq);
+ svnic_wq_free(&dc2c->wq);
+ kfree(dc2c);
+}
+
+int svnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ u64 *a0, u64 *a1, int wait)
+{
+ int err;
+
+ memset(vdev->args, 0, sizeof(vdev->args));
+ vdev->args[0] = *a0;
+ vdev->args[1] = *a1;
+
+ err = (*vdev->devcmd_rtn)(vdev, cmd, wait);
+
+ *a0 = vdev->args[0];
+ *a1 = vdev->args[1];
+
+ return err;
+}
+
+int svnic_dev_fw_info(struct vnic_dev *vdev,
+ struct vnic_devcmd_fw_info **fw_info)
+{
+ u64 a0, a1 = 0;
+ int wait = VNIC_DVCMD_TMO;
+ int err = 0;
+
+ if (!vdev->fw_info) {
+ vdev->fw_info = pci_alloc_consistent(vdev->pdev,
+ sizeof(struct vnic_devcmd_fw_info),
+ &vdev->fw_info_pa);
+ if (!vdev->fw_info)
+ return -ENOMEM;
+
+ a0 = vdev->fw_info_pa;
+
+ /* only get fw_info once and cache it */
+ err = svnic_dev_cmd(vdev, CMD_MCPU_FW_INFO, &a0, &a1, wait);
+ }
+
+ *fw_info = vdev->fw_info;
+
+ return err;
+}
+
+int svnic_dev_spec(struct vnic_dev *vdev, unsigned int offset,
+ unsigned int size, void *value)
+{
+ u64 a0, a1;
+ int wait = VNIC_DVCMD_TMO;
+ int err;
+
+ a0 = offset;
+ a1 = size;
+
+ err = svnic_dev_cmd(vdev, CMD_DEV_SPEC, &a0, &a1, wait);
+
+ switch (size) {
+ case 1:
+ *(u8 *)value = (u8)a0;
+ break;
+ case 2:
+ *(u16 *)value = (u16)a0;
+ break;
+ case 4:
+ *(u32 *)value = (u32)a0;
+ break;
+ case 8:
+ *(u64 *)value = a0;
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ return err;
+}
+
+int svnic_dev_stats_clear(struct vnic_dev *vdev)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = VNIC_DVCMD_TMO;
+
+ return svnic_dev_cmd(vdev, CMD_STATS_CLEAR, &a0, &a1, wait);
+}
+
+int svnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats)
+{
+ u64 a0, a1;
+ int wait = VNIC_DVCMD_TMO;
+
+ if (!vdev->stats) {
+ vdev->stats = pci_alloc_consistent(vdev->pdev,
+ sizeof(struct vnic_stats), &vdev->stats_pa);
+ if (!vdev->stats)
+ return -ENOMEM;
+ }
+
+ *stats = vdev->stats;
+ a0 = vdev->stats_pa;
+ a1 = sizeof(struct vnic_stats);
+
+ return svnic_dev_cmd(vdev, CMD_STATS_DUMP, &a0, &a1, wait);
+}
+
+int svnic_dev_close(struct vnic_dev *vdev)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = VNIC_DVCMD_TMO;
+
+ return svnic_dev_cmd(vdev, CMD_CLOSE, &a0, &a1, wait);
+}
+
+int svnic_dev_enable_wait(struct vnic_dev *vdev)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = VNIC_DVCMD_TMO;
+ int err = 0;
+
+ err = svnic_dev_cmd(vdev, CMD_ENABLE_WAIT, &a0, &a1, wait);
+ if (err == ERR_ECMDUNKNOWN)
+ return svnic_dev_cmd(vdev, CMD_ENABLE, &a0, &a1, wait);
+
+ return err;
+}
+
+int svnic_dev_disable(struct vnic_dev *vdev)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = VNIC_DVCMD_TMO;
+
+ return svnic_dev_cmd(vdev, CMD_DISABLE, &a0, &a1, wait);
+}
+
+int svnic_dev_open(struct vnic_dev *vdev, int arg)
+{
+ u64 a0 = (u32)arg, a1 = 0;
+ int wait = VNIC_DVCMD_TMO;
+
+ return svnic_dev_cmd(vdev, CMD_OPEN, &a0, &a1, wait);
+}
+
+int svnic_dev_open_done(struct vnic_dev *vdev, int *done)
+{
+ u64 a0 = 0, a1 = 0;
+ int wait = VNIC_DVCMD_TMO;
+ int err;
+
+ *done = 0;
+
+ err = svnic_dev_cmd(vdev, CMD_OPEN_STATUS, &a0, &a1, wait);
+ if (err)
+ return err;
+
+ *done = (a0 == 0);
+
+ return 0;
+}
+
+int svnic_dev_notify_set(struct vnic_dev *vdev, u16 intr)
+{
+ u64 a0, a1;
+ int wait = VNIC_DVCMD_TMO;
+
+ if (!vdev->notify) {
+ vdev->notify = pci_alloc_consistent(vdev->pdev,
+ sizeof(struct vnic_devcmd_notify),
+ &vdev->notify_pa);
+ if (!vdev->notify)
+ return -ENOMEM;
+ }
+
+ a0 = vdev->notify_pa;
+ a1 = ((u64)intr << 32) & VNIC_NOTIFY_INTR_MASK;
+ a1 += sizeof(struct vnic_devcmd_notify);
+
+ return svnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
+}
+
+void svnic_dev_notify_unset(struct vnic_dev *vdev)
+{
+ u64 a0, a1;
+ int wait = VNIC_DVCMD_TMO;
+
+ a0 = 0; /* paddr = 0 to unset notify buffer */
+ a1 = VNIC_NOTIFY_INTR_MASK; /* intr num = -1 to unreg for intr */
+ a1 += sizeof(struct vnic_devcmd_notify);
+
+ svnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
+}
+
+static int vnic_dev_notify_ready(struct vnic_dev *vdev)
+{
+ u32 *words;
+ unsigned int nwords = sizeof(struct vnic_devcmd_notify) / 4;
+ unsigned int i;
+ u32 csum;
+
+ if (!vdev->notify)
+ return 0;
+
+ do {
+ csum = 0;
+ memcpy(&vdev->notify_copy, vdev->notify,
+ sizeof(struct vnic_devcmd_notify));
+ words = (u32 *)&vdev->notify_copy;
+ for (i = 1; i < nwords; i++)
+ csum += words[i];
+ } while (csum != words[0]);
+
+ return 1;
+}
+
+int svnic_dev_init(struct vnic_dev *vdev, int arg)
+{
+ u64 a0 = (u32)arg, a1 = 0;
+ int wait = VNIC_DVCMD_TMO;
+
+ return svnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait);
+}
+
+int svnic_dev_link_status(struct vnic_dev *vdev)
+{
+ if (vdev->linkstatus)
+ return *vdev->linkstatus;
+
+ if (!vnic_dev_notify_ready(vdev))
+ return 0;
+
+ return vdev->notify_copy.link_state;
+}
+
+u32 svnic_dev_link_down_cnt(struct vnic_dev *vdev)
+{
+ if (!vnic_dev_notify_ready(vdev))
+ return 0;
+
+ return vdev->notify_copy.link_down_cnt;
+}
+
+void svnic_dev_set_intr_mode(struct vnic_dev *vdev,
+ enum vnic_dev_intr_mode intr_mode)
+{
+ vdev->intr_mode = intr_mode;
+}
+
+enum vnic_dev_intr_mode svnic_dev_get_intr_mode(struct vnic_dev *vdev)
+{
+ return vdev->intr_mode;
+}
+
+void svnic_dev_unregister(struct vnic_dev *vdev)
+{
+ if (vdev) {
+ if (vdev->notify)
+ pci_free_consistent(vdev->pdev,
+ sizeof(struct vnic_devcmd_notify),
+ vdev->notify,
+ vdev->notify_pa);
+ if (vdev->linkstatus)
+ pci_free_consistent(vdev->pdev,
+ sizeof(u32),
+ vdev->linkstatus,
+ vdev->linkstatus_pa);
+ if (vdev->stats)
+ pci_free_consistent(vdev->pdev,
+ sizeof(struct vnic_stats),
+ vdev->stats, vdev->stats_pa);
+ if (vdev->fw_info)
+ pci_free_consistent(vdev->pdev,
+ sizeof(struct vnic_devcmd_fw_info),
+ vdev->fw_info, vdev->fw_info_pa);
+ if (vdev->devcmd2)
+ vnic_dev_deinit_devcmd2(vdev);
+ kfree(vdev);
+ }
+}
+
+struct vnic_dev *svnic_dev_alloc_discover(struct vnic_dev *vdev,
+ void *priv,
+ struct pci_dev *pdev,
+ struct vnic_dev_bar *bar,
+ unsigned int num_bars)
+{
+ if (!vdev) {
+ vdev = kzalloc(sizeof(struct vnic_dev), GFP_ATOMIC);
+ if (!vdev)
+ return NULL;
+ }
+
+ vdev->priv = priv;
+ vdev->pdev = pdev;
+
+ if (vnic_dev_discover_res(vdev, bar, num_bars))
+ goto err_out;
+
+ return vdev;
+
+err_out:
+ svnic_dev_unregister(vdev);
+
+ return NULL;
+} /* end of svnic_dev_alloc_discover */
+
+/*
+ * fallback option is left to keep the interface common for other vnics.
+ */
+int svnic_dev_cmd_init(struct vnic_dev *vdev, int fallback)
+{
+ int err = -ENODEV;
+ void __iomem *p;
+
+ p = svnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0);
+ if (p)
+ err = svnic_dev_init_devcmd2(vdev);
+ else
+ pr_err("DEVCMD2 resource not found.\n");
+
+ return err;
+} /* end of svnic_dev_cmd_init */
diff --git a/drivers/scsi/snic/vnic_dev.h b/drivers/scsi/snic/vnic_dev.h
new file mode 100644
index 000000000000..e65726da6504
--- /dev/null
+++ b/drivers/scsi/snic/vnic_dev.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VNIC_DEV_H_
+#define _VNIC_DEV_H_
+
+#include "vnic_resource.h"
+#include "vnic_devcmd.h"
+
+#ifndef VNIC_PADDR_TARGET
+#define VNIC_PADDR_TARGET 0x0000000000000000ULL
+#endif
+
+#ifndef readq
+static inline u64 readq(void __iomem *reg)
+{
+ return ((u64)readl(reg + 0x4UL) << 32) | (u64)readl(reg);
+}
+
+static inline void writeq(u64 val, void __iomem *reg)
+{
+ writel(lower_32_bits(val), reg);
+ writel(upper_32_bits(val), reg + 0x4UL);
+}
+#endif
+
+enum vnic_dev_intr_mode {
+ VNIC_DEV_INTR_MODE_UNKNOWN,
+ VNIC_DEV_INTR_MODE_INTX,
+ VNIC_DEV_INTR_MODE_MSI,
+ VNIC_DEV_INTR_MODE_MSIX,
+};
+
+struct vnic_dev_bar {
+ void __iomem *vaddr;
+ dma_addr_t bus_addr;
+ unsigned long len;
+};
+
+struct vnic_dev_ring {
+ void *descs;
+ size_t size;
+ dma_addr_t base_addr;
+ size_t base_align;
+ void *descs_unaligned;
+ size_t size_unaligned;
+ dma_addr_t base_addr_unaligned;
+ unsigned int desc_size;
+ unsigned int desc_count;
+ unsigned int desc_avail;
+};
+
+struct vnic_dev;
+struct vnic_stats;
+
+void *svnic_dev_priv(struct vnic_dev *vdev);
+unsigned int svnic_dev_get_res_count(struct vnic_dev *vdev,
+ enum vnic_res_type type);
+void __iomem *svnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type,
+ unsigned int index);
+unsigned int svnic_dev_desc_ring_size(struct vnic_dev_ring *ring,
+ unsigned int desc_count,
+ unsigned int desc_size);
+void svnic_dev_clear_desc_ring(struct vnic_dev_ring *ring);
+int svnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring,
+ unsigned int desc_count, unsigned int desc_size);
+void svnic_dev_free_desc_ring(struct vnic_dev *vdev,
+ struct vnic_dev_ring *ring);
+int svnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+ u64 *a0, u64 *a1, int wait);
+int svnic_dev_fw_info(struct vnic_dev *vdev,
+ struct vnic_devcmd_fw_info **fw_info);
+int svnic_dev_spec(struct vnic_dev *vdev, unsigned int offset,
+ unsigned int size, void *value);
+int svnic_dev_stats_clear(struct vnic_dev *vdev);
+int svnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats);
+int svnic_dev_notify_set(struct vnic_dev *vdev, u16 intr);
+void svnic_dev_notify_unset(struct vnic_dev *vdev);
+int svnic_dev_link_status(struct vnic_dev *vdev);
+u32 svnic_dev_link_down_cnt(struct vnic_dev *vdev);
+int svnic_dev_close(struct vnic_dev *vdev);
+int svnic_dev_enable_wait(struct vnic_dev *vdev);
+int svnic_dev_disable(struct vnic_dev *vdev);
+int svnic_dev_open(struct vnic_dev *vdev, int arg);
+int svnic_dev_open_done(struct vnic_dev *vdev, int *done);
+int svnic_dev_init(struct vnic_dev *vdev, int arg);
+struct vnic_dev *svnic_dev_alloc_discover(struct vnic_dev *vdev,
+ void *priv, struct pci_dev *pdev,
+ struct vnic_dev_bar *bar,
+ unsigned int num_bars);
+void svnic_dev_set_intr_mode(struct vnic_dev *vdev,
+ enum vnic_dev_intr_mode intr_mode);
+enum vnic_dev_intr_mode svnic_dev_get_intr_mode(struct vnic_dev *vdev);
+void svnic_dev_unregister(struct vnic_dev *vdev);
+int svnic_dev_cmd_init(struct vnic_dev *vdev, int fallback);
+#endif /* _VNIC_DEV_H_ */
diff --git a/drivers/scsi/snic/vnic_devcmd.h b/drivers/scsi/snic/vnic_devcmd.h
new file mode 100644
index 000000000000..d81b4f0ceaaa
--- /dev/null
+++ b/drivers/scsi/snic/vnic_devcmd.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VNIC_DEVCMD_H_
+#define _VNIC_DEVCMD_H_
+
+#define _CMD_NBITS 14
+#define _CMD_VTYPEBITS 10
+#define _CMD_FLAGSBITS 6
+#define _CMD_DIRBITS 2
+
+#define _CMD_NMASK ((1 << _CMD_NBITS)-1)
+#define _CMD_VTYPEMASK ((1 << _CMD_VTYPEBITS)-1)
+#define _CMD_FLAGSMASK ((1 << _CMD_FLAGSBITS)-1)
+#define _CMD_DIRMASK ((1 << _CMD_DIRBITS)-1)
+
+#define _CMD_NSHIFT 0
+#define _CMD_VTYPESHIFT (_CMD_NSHIFT+_CMD_NBITS)
+#define _CMD_FLAGSSHIFT (_CMD_VTYPESHIFT+_CMD_VTYPEBITS)
+#define _CMD_DIRSHIFT (_CMD_FLAGSSHIFT+_CMD_FLAGSBITS)
+
+/*
+ * Direction bits (from host perspective).
+ */
+#define _CMD_DIR_NONE 0U
+#define _CMD_DIR_WRITE 1U
+#define _CMD_DIR_READ 2U
+#define _CMD_DIR_RW (_CMD_DIR_WRITE | _CMD_DIR_READ)
+
+/*
+ * Flag bits.
+ */
+#define _CMD_FLAGS_NONE 0U
+#define _CMD_FLAGS_NOWAIT 1U
+
+/*
+ * vNIC type bits.
+ */
+#define _CMD_VTYPE_NONE 0U
+#define _CMD_VTYPE_ENET 1U
+#define _CMD_VTYPE_FC 2U
+#define _CMD_VTYPE_SCSI 4U
+#define _CMD_VTYPE_ALL (_CMD_VTYPE_ENET | _CMD_VTYPE_FC | _CMD_VTYPE_SCSI)
+
+/*
+ * Used to create cmds..
+*/
+#define _CMDCF(dir, flags, vtype, nr) \
+ (((dir) << _CMD_DIRSHIFT) | \
+ ((flags) << _CMD_FLAGSSHIFT) | \
+ ((vtype) << _CMD_VTYPESHIFT) | \
+ ((nr) << _CMD_NSHIFT))
+#define _CMDC(dir, vtype, nr) _CMDCF(dir, 0, vtype, nr)
+#define _CMDCNW(dir, vtype, nr) _CMDCF(dir, _CMD_FLAGS_NOWAIT, vtype, nr)
+
+/*
+ * Used to decode cmds..
+*/
+#define _CMD_DIR(cmd) (((cmd) >> _CMD_DIRSHIFT) & _CMD_DIRMASK)
+#define _CMD_FLAGS(cmd) (((cmd) >> _CMD_FLAGSSHIFT) & _CMD_FLAGSMASK)
+#define _CMD_VTYPE(cmd) (((cmd) >> _CMD_VTYPESHIFT) & _CMD_VTYPEMASK)
+#define _CMD_N(cmd) (((cmd) >> _CMD_NSHIFT) & _CMD_NMASK)
+
+enum vnic_devcmd_cmd {
+ CMD_NONE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_NONE, 0),
+
+ /* mcpu fw info in mem: (u64)a0=paddr to struct vnic_devcmd_fw_info */
+ CMD_MCPU_FW_INFO = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 1),
+
+ /* dev-specific block member:
+ * in: (u16)a0=offset,(u8)a1=size
+ * out: a0=value */
+ CMD_DEV_SPEC = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 2),
+
+ /* stats clear */
+ CMD_STATS_CLEAR = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 3),
+
+ /* stats dump in mem: (u64)a0=paddr to stats area,
+ * (u16)a1=sizeof stats area */
+ CMD_STATS_DUMP = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 4),
+
+ /* nic_cfg in (u32)a0 */
+ CMD_NIC_CFG = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 16),
+
+ /* set struct vnic_devcmd_notify buffer in mem:
+ * in:
+ * (u64)a0=paddr to notify (set paddr=0 to unset)
+ * (u32)a1 & 0x00000000ffffffff=sizeof(struct vnic_devcmd_notify)
+ * (u16)a1 & 0x0000ffff00000000=intr num (-1 for no intr)
+ * out:
+ * (u32)a1 = effective size
+ */
+ CMD_NOTIFY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 21),
+
+ /* initiate open sequence (u32)a0=flags (see CMD_OPENF_*) */
+ CMD_OPEN = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 23),
+
+ /* open status:
+ * out: a0=0 open complete, a0=1 open in progress */
+ CMD_OPEN_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 24),
+
+ /* close vnic */
+ CMD_CLOSE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 25),
+
+ /* initialize virtual link: (u32)a0=flags (see CMD_INITF_*) */
+ CMD_INIT = _CMDCNW(_CMD_DIR_READ, _CMD_VTYPE_ALL, 26),
+
+ /* enable virtual link */
+ CMD_ENABLE = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 28),
+
+ /* enable virtual link, waiting variant. */
+ CMD_ENABLE_WAIT = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 28),
+
+ /* disable virtual link */
+ CMD_DISABLE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 29),
+
+ /* stats dump all vnics on uplink in mem: (u64)a0=paddr (u32)a1=uif */
+ CMD_STATS_DUMP_ALL = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 30),
+
+ /* init status:
+ * out: a0=0 init complete, a0=1 init in progress
+ * if a0=0, a1=errno */
+ CMD_INIT_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 31),
+
+ /* undo initialize of virtual link */
+ CMD_DEINIT = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 34),
+
+ /* check fw capability of a cmd:
+ * in: (u32)a0=cmd
+ * out: (u32)a0=errno, 0:valid cmd, a1=supported VNIC_STF_* bits */
+ CMD_CAPABILITY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 36),
+
+ /*
+ * Initialization for the devcmd2 interface.
+ * in: (u64) a0=host result buffer physical address
+ * in: (u16) a1=number of entries in result buffer
+ */
+ CMD_INITIALIZE_DEVCMD2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 57)
+};
+
+/* flags for CMD_OPEN */
+#define CMD_OPENF_OPROM 0x1 /* open coming from option rom */
+
+/* flags for CMD_INIT */
+#define CMD_INITF_DEFAULT_MAC 0x1 /* init with default mac addr */
+
+/* flags for CMD_PACKET_FILTER */
+#define CMD_PFILTER_DIRECTED 0x01
+#define CMD_PFILTER_MULTICAST 0x02
+#define CMD_PFILTER_BROADCAST 0x04
+#define CMD_PFILTER_PROMISCUOUS 0x08
+#define CMD_PFILTER_ALL_MULTICAST 0x10
+
+enum vnic_devcmd_status {
+ STAT_NONE = 0,
+ STAT_BUSY = 1 << 0, /* cmd in progress */
+ STAT_ERROR = 1 << 1, /* last cmd caused error (code in a0) */
+};
+
+enum vnic_devcmd_error {
+ ERR_SUCCESS = 0,
+ ERR_EINVAL = 1,
+ ERR_EFAULT = 2,
+ ERR_EPERM = 3,
+ ERR_EBUSY = 4,
+ ERR_ECMDUNKNOWN = 5,
+ ERR_EBADSTATE = 6,
+ ERR_ENOMEM = 7,
+ ERR_ETIMEDOUT = 8,
+ ERR_ELINKDOWN = 9,
+};
+
+struct vnic_devcmd_fw_info {
+ char fw_version[32];
+ char fw_build[32];
+ char hw_version[32];
+ char hw_serial_number[32];
+};
+
+struct vnic_devcmd_notify {
+ u32 csum; /* checksum over following words */
+
+ u32 link_state; /* link up == 1 */
+ u32 port_speed; /* effective port speed (rate limit) */
+ u32 mtu; /* MTU */
+ u32 msglvl; /* requested driver msg lvl */
+ u32 uif; /* uplink interface */
+ u32 status; /* status bits (see VNIC_STF_*) */
+ u32 error; /* error code (see ERR_*) for first ERR */
+ u32 link_down_cnt; /* running count of link down transitions */
+};
+#define VNIC_STF_FATAL_ERR 0x0001 /* fatal fw error */
+
+struct vnic_devcmd_provinfo {
+ u8 oui[3];
+ u8 type;
+ u8 data[0];
+};
+
+/*
+ * Writing cmd register causes STAT_BUSY to get set in status register.
+ * When cmd completes, STAT_BUSY will be cleared.
+ *
+ * If cmd completed successfully STAT_ERROR will be clear
+ * and args registers contain cmd-specific results.
+ *
+ * If cmd error, STAT_ERROR will be set and args[0] contains error code.
+ *
+ * status register is read-only. While STAT_BUSY is set,
+ * all other register contents are read-only.
+ */
+
+/* Make sizeof(vnic_devcmd) a power-of-2 for I/O BAR. */
+#define VNIC_DEVCMD_NARGS 15
+struct vnic_devcmd {
+ u32 status; /* RO */
+ u32 cmd; /* RW */
+ u64 args[VNIC_DEVCMD_NARGS]; /* RW cmd args (little-endian) */
+};
+
+
+/*
+ * Version 2 of the interface.
+ *
+ * Some things are carried over, notably the vnic_devcmd_cmd enum.
+ */
+
+/*
+ * Flags for vnic_devcmd2.flags
+ */
+
+#define DEVCMD2_FNORESULT 0x1 /* Don't copy result to host */
+
+#define VNIC_DEVCMD2_NARGS VNIC_DEVCMD_NARGS
+struct vnic_devcmd2 {
+ u16 pad;
+ u16 flags;
+ u32 cmd; /* same command #defines as original */
+ u64 args[VNIC_DEVCMD2_NARGS];
+};
+
+#define VNIC_DEVCMD2_NRESULTS VNIC_DEVCMD_NARGS
+struct devcmd2_result {
+ u64 results[VNIC_DEVCMD2_NRESULTS];
+ u32 pad;
+ u16 completed_index; /* into copy WQ */
+ u8 error; /* same error codes as original */
+ u8 color; /* 0 or 1 as with completion queues */
+};
+
+#define DEVCMD2_RING_SIZE 32
+#define DEVCMD2_DESC_SIZE 128
+
+#define DEVCMD2_RESULTS_SIZE_MAX ((1 << 16) - 1)
+
+#endif /* _VNIC_DEVCMD_H_ */
diff --git a/drivers/scsi/snic/vnic_intr.c b/drivers/scsi/snic/vnic_intr.c
new file mode 100644
index 000000000000..a7d54806787d
--- /dev/null
+++ b/drivers/scsi/snic/vnic_intr.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include "vnic_dev.h"
+#include "vnic_intr.h"
+
+void svnic_intr_free(struct vnic_intr *intr)
+{
+ intr->ctrl = NULL;
+}
+
+int svnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
+ unsigned int index)
+{
+ intr->index = index;
+ intr->vdev = vdev;
+
+ intr->ctrl = svnic_dev_get_res(vdev, RES_TYPE_INTR_CTRL, index);
+ if (!intr->ctrl) {
+ pr_err("Failed to hook INTR[%d].ctrl resource\n",
+ index);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void svnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer,
+ unsigned int coalescing_type, unsigned int mask_on_assertion)
+{
+ iowrite32(coalescing_timer, &intr->ctrl->coalescing_timer);
+ iowrite32(coalescing_type, &intr->ctrl->coalescing_type);
+ iowrite32(mask_on_assertion, &intr->ctrl->mask_on_assertion);
+ iowrite32(0, &intr->ctrl->int_credits);
+}
+
+void svnic_intr_clean(struct vnic_intr *intr)
+{
+ iowrite32(0, &intr->ctrl->int_credits);
+}
diff --git a/drivers/scsi/snic/vnic_intr.h b/drivers/scsi/snic/vnic_intr.h
new file mode 100644
index 000000000000..4547f603fe5e
--- /dev/null
+++ b/drivers/scsi/snic/vnic_intr.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VNIC_INTR_H_
+#define _VNIC_INTR_H_
+
+#include <linux/pci.h>
+#include "vnic_dev.h"
+
+#define VNIC_INTR_TIMER_MAX 0xffff
+
+#define VNIC_INTR_TIMER_TYPE_ABS 0
+#define VNIC_INTR_TIMER_TYPE_QUIET 1
+
+/* Interrupt control */
+struct vnic_intr_ctrl {
+ u32 coalescing_timer; /* 0x00 */
+ u32 pad0;
+ u32 coalescing_value; /* 0x08 */
+ u32 pad1;
+ u32 coalescing_type; /* 0x10 */
+ u32 pad2;
+ u32 mask_on_assertion; /* 0x18 */
+ u32 pad3;
+ u32 mask; /* 0x20 */
+ u32 pad4;
+ u32 int_credits; /* 0x28 */
+ u32 pad5;
+ u32 int_credit_return; /* 0x30 */
+ u32 pad6;
+};
+
+struct vnic_intr {
+ unsigned int index;
+ struct vnic_dev *vdev;
+ struct vnic_intr_ctrl __iomem *ctrl; /* memory-mapped */
+};
+
+static inline void
+svnic_intr_unmask(struct vnic_intr *intr)
+{
+ iowrite32(0, &intr->ctrl->mask);
+}
+
+static inline void
+svnic_intr_mask(struct vnic_intr *intr)
+{
+ iowrite32(1, &intr->ctrl->mask);
+}
+
+static inline void
+svnic_intr_return_credits(struct vnic_intr *intr,
+ unsigned int credits,
+ int unmask,
+ int reset_timer)
+{
+#define VNIC_INTR_UNMASK_SHIFT 16
+#define VNIC_INTR_RESET_TIMER_SHIFT 17
+
+ u32 int_credit_return = (credits & 0xffff) |
+ (unmask ? (1 << VNIC_INTR_UNMASK_SHIFT) : 0) |
+ (reset_timer ? (1 << VNIC_INTR_RESET_TIMER_SHIFT) : 0);
+
+ iowrite32(int_credit_return, &intr->ctrl->int_credit_return);
+}
+
+static inline unsigned int
+svnic_intr_credits(struct vnic_intr *intr)
+{
+ return ioread32(&intr->ctrl->int_credits);
+}
+
+static inline void
+svnic_intr_return_all_credits(struct vnic_intr *intr)
+{
+ unsigned int credits = svnic_intr_credits(intr);
+ int unmask = 1;
+ int reset_timer = 1;
+
+ svnic_intr_return_credits(intr, credits, unmask, reset_timer);
+}
+
+void svnic_intr_free(struct vnic_intr *);
+int svnic_intr_alloc(struct vnic_dev *, struct vnic_intr *, unsigned int);
+void svnic_intr_init(struct vnic_intr *intr,
+ unsigned int coalescing_timer,
+ unsigned int coalescing_type,
+ unsigned int mask_on_assertion);
+void svnic_intr_clean(struct vnic_intr *);
+
+#endif /* _VNIC_INTR_H_ */
diff --git a/drivers/scsi/snic/vnic_resource.h b/drivers/scsi/snic/vnic_resource.h
new file mode 100644
index 000000000000..9713d6835db3
--- /dev/null
+++ b/drivers/scsi/snic/vnic_resource.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VNIC_RESOURCE_H_
+#define _VNIC_RESOURCE_H_
+
+#define VNIC_RES_MAGIC 0x766E6963L /* 'vnic' */
+#define VNIC_RES_VERSION 0x00000000L
+
+/* vNIC resource types */
+enum vnic_res_type {
+ RES_TYPE_EOL, /* End-of-list */
+ RES_TYPE_WQ, /* Work queues */
+ RES_TYPE_RQ, /* Receive queues */
+ RES_TYPE_CQ, /* Completion queues */
+ RES_TYPE_RSVD1,
+ RES_TYPE_NIC_CFG, /* Enet NIC config registers */
+ RES_TYPE_RSVD2,
+ RES_TYPE_RSVD3,
+ RES_TYPE_RSVD4,
+ RES_TYPE_RSVD5,
+ RES_TYPE_INTR_CTRL, /* Interrupt ctrl table */
+ RES_TYPE_INTR_TABLE, /* MSI/MSI-X Interrupt table */
+ RES_TYPE_INTR_PBA, /* MSI/MSI-X PBA table */
+ RES_TYPE_INTR_PBA_LEGACY, /* Legacy intr status */
+ RES_TYPE_RSVD6,
+ RES_TYPE_RSVD7,
+ RES_TYPE_DEVCMD, /* Device command region */
+ RES_TYPE_PASS_THRU_PAGE, /* Pass-thru page */
+ RES_TYPE_SUBVNIC, /* subvnic resource type */
+ RES_TYPE_MQ_WQ, /* MQ Work queues */
+ RES_TYPE_MQ_RQ, /* MQ Receive queues */
+ RES_TYPE_MQ_CQ, /* MQ Completion queues */
+ RES_TYPE_DEPRECATED1, /* Old version of devcmd 2 */
+ RES_TYPE_DEPRECATED2, /* Old version of devcmd 2 */
+ RES_TYPE_DEVCMD2, /* Device control region */
+
+ RES_TYPE_MAX, /* Count of resource types */
+};
+
+struct vnic_resource_header {
+ u32 magic;
+ u32 version;
+};
+
+struct vnic_resource {
+ u8 type;
+ u8 bar;
+ u8 pad[2];
+ u32 bar_offset;
+ u32 count;
+};
+
+#endif /* _VNIC_RESOURCE_H_ */
diff --git a/drivers/scsi/snic/vnic_snic.h b/drivers/scsi/snic/vnic_snic.h
new file mode 100644
index 000000000000..514d39f5cf00
--- /dev/null
+++ b/drivers/scsi/snic/vnic_snic.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VNIC_SNIC_H_
+#define _VNIC_SNIC_H_
+
+#define VNIC_SNIC_WQ_DESCS_MIN 64
+#define VNIC_SNIC_WQ_DESCS_MAX 1024
+
+#define VNIC_SNIC_MAXDATAFIELDSIZE_MIN 256
+#define VNIC_SNIC_MAXDATAFIELDSIZE_MAX 2112
+
+#define VNIC_SNIC_IO_THROTTLE_COUNT_MIN 1
+#define VNIC_SNIC_IO_THROTTLE_COUNT_MAX 1024
+
+#define VNIC_SNIC_PORT_DOWN_TIMEOUT_MIN 0
+#define VNIC_SNIC_PORT_DOWN_TIMEOUT_MAX 240000
+
+#define VNIC_SNIC_PORT_DOWN_IO_RETRIES_MIN 0
+#define VNIC_SNIC_PORT_DOWN_IO_RETRIES_MAX 255
+
+#define VNIC_SNIC_LUNS_PER_TARGET_MIN 1
+#define VNIC_SNIC_LUNS_PER_TARGET_MAX 1024
+
+/* Device-specific region: scsi configuration */
+struct vnic_snic_config {
+ u32 flags;
+ u32 wq_enet_desc_count;
+ u32 io_throttle_count;
+ u32 port_down_timeout;
+ u32 port_down_io_retries;
+ u32 luns_per_tgt;
+ u16 maxdatafieldsize;
+ u16 intr_timer;
+ u8 intr_timer_type;
+ u8 _resvd2;
+ u8 xpt_type;
+ u8 hid;
+};
+#endif /* _VNIC_SNIC_H_ */
diff --git a/drivers/scsi/snic/vnic_stats.h b/drivers/scsi/snic/vnic_stats.h
new file mode 100644
index 000000000000..370a37c97748
--- /dev/null
+++ b/drivers/scsi/snic/vnic_stats.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VNIC_STATS_H_
+#define _VNIC_STATS_H_
+
+/* Tx statistics */
+struct vnic_tx_stats {
+ u64 tx_frames_ok;
+ u64 tx_unicast_frames_ok;
+ u64 tx_multicast_frames_ok;
+ u64 tx_broadcast_frames_ok;
+ u64 tx_bytes_ok;
+ u64 tx_unicast_bytes_ok;
+ u64 tx_multicast_bytes_ok;
+ u64 tx_broadcast_bytes_ok;
+ u64 tx_drops;
+ u64 tx_errors;
+ u64 tx_tso;
+ u64 rsvd[16];
+};
+
+/* Rx statistics */
+struct vnic_rx_stats {
+ u64 rx_frames_ok;
+ u64 rx_frames_total;
+ u64 rx_unicast_frames_ok;
+ u64 rx_multicast_frames_ok;
+ u64 rx_broadcast_frames_ok;
+ u64 rx_bytes_ok;
+ u64 rx_unicast_bytes_ok;
+ u64 rx_multicast_bytes_ok;
+ u64 rx_broadcast_bytes_ok;
+ u64 rx_drop;
+ u64 rx_no_bufs;
+ u64 rx_errors;
+ u64 rx_rss;
+ u64 rx_crc_errors;
+ u64 rx_frames_64;
+ u64 rx_frames_127;
+ u64 rx_frames_255;
+ u64 rx_frames_511;
+ u64 rx_frames_1023;
+ u64 rx_frames_1518;
+ u64 rx_frames_to_max;
+ u64 rsvd[16];
+};
+
+struct vnic_stats {
+ struct vnic_tx_stats tx;
+ struct vnic_rx_stats rx;
+};
+
+#endif /* _VNIC_STATS_H_ */
diff --git a/drivers/scsi/snic/vnic_wq.c b/drivers/scsi/snic/vnic_wq.c
new file mode 100644
index 000000000000..1e91d432089e
--- /dev/null
+++ b/drivers/scsi/snic/vnic_wq.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include "vnic_dev.h"
+#include "vnic_wq.h"
+
+static inline int vnic_wq_get_ctrl(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int index, enum vnic_res_type res_type)
+{
+ wq->ctrl = svnic_dev_get_res(vdev, res_type, index);
+ if (!wq->ctrl)
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline int vnic_wq_alloc_ring(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int index, unsigned int desc_count, unsigned int desc_size)
+{
+ return svnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count,
+ desc_size);
+}
+
+static int vnic_wq_alloc_bufs(struct vnic_wq *wq)
+{
+ struct vnic_wq_buf *buf;
+ unsigned int i, j, count = wq->ring.desc_count;
+ unsigned int blks = VNIC_WQ_BUF_BLKS_NEEDED(count);
+
+ for (i = 0; i < blks; i++) {
+ wq->bufs[i] = kzalloc(VNIC_WQ_BUF_BLK_SZ, GFP_ATOMIC);
+ if (!wq->bufs[i]) {
+ pr_err("Failed to alloc wq_bufs\n");
+
+ return -ENOMEM;
+ }
+ }
+
+ for (i = 0; i < blks; i++) {
+ buf = wq->bufs[i];
+ for (j = 0; j < VNIC_WQ_BUF_DFLT_BLK_ENTRIES; j++) {
+ buf->index = i * VNIC_WQ_BUF_DFLT_BLK_ENTRIES + j;
+ buf->desc = (u8 *)wq->ring.descs +
+ wq->ring.desc_size * buf->index;
+ if (buf->index + 1 == count) {
+ buf->next = wq->bufs[0];
+ break;
+ } else if (j + 1 == VNIC_WQ_BUF_DFLT_BLK_ENTRIES) {
+ buf->next = wq->bufs[i + 1];
+ } else {
+ buf->next = buf + 1;
+ buf++;
+ }
+ }
+ }
+
+ wq->to_use = wq->to_clean = wq->bufs[0];
+
+ return 0;
+}
+
+void svnic_wq_free(struct vnic_wq *wq)
+{
+ struct vnic_dev *vdev;
+ unsigned int i;
+
+ vdev = wq->vdev;
+
+ svnic_dev_free_desc_ring(vdev, &wq->ring);
+
+ for (i = 0; i < VNIC_WQ_BUF_BLKS_MAX; i++) {
+ kfree(wq->bufs[i]);
+ wq->bufs[i] = NULL;
+ }
+
+ wq->ctrl = NULL;
+
+}
+
+int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int desc_count, unsigned int desc_size)
+{
+ int err;
+
+ wq->index = 0;
+ wq->vdev = vdev;
+
+ err = vnic_wq_get_ctrl(vdev, wq, 0, RES_TYPE_DEVCMD2);
+ if (err) {
+ pr_err("Failed to get devcmd2 resource\n");
+
+ return err;
+ }
+
+ svnic_wq_disable(wq);
+
+ err = vnic_wq_alloc_ring(vdev, wq, 0, desc_count, desc_size);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int svnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int index, unsigned int desc_count, unsigned int desc_size)
+{
+ int err;
+
+ wq->index = index;
+ wq->vdev = vdev;
+
+ err = vnic_wq_get_ctrl(vdev, wq, index, RES_TYPE_WQ);
+ if (err) {
+ pr_err("Failed to hook WQ[%d] resource\n", index);
+
+ return err;
+ }
+
+ svnic_wq_disable(wq);
+
+ err = vnic_wq_alloc_ring(vdev, wq, index, desc_count, desc_size);
+ if (err)
+ return err;
+
+ err = vnic_wq_alloc_bufs(wq);
+ if (err) {
+ svnic_wq_free(wq);
+
+ return err;
+ }
+
+ return 0;
+}
+
+void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index,
+ unsigned int fetch_index, unsigned int posted_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset)
+{
+ u64 paddr;
+ unsigned int count = wq->ring.desc_count;
+
+ paddr = (u64)wq->ring.base_addr | VNIC_PADDR_TARGET;
+ writeq(paddr, &wq->ctrl->ring_base);
+ iowrite32(count, &wq->ctrl->ring_size);
+ iowrite32(fetch_index, &wq->ctrl->fetch_index);
+ iowrite32(posted_index, &wq->ctrl->posted_index);
+ iowrite32(cq_index, &wq->ctrl->cq_index);
+ iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable);
+ iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset);
+ iowrite32(0, &wq->ctrl->error_status);
+
+ wq->to_use = wq->to_clean =
+ &wq->bufs[fetch_index / VNIC_WQ_BUF_BLK_ENTRIES(count)]
+ [fetch_index % VNIC_WQ_BUF_BLK_ENTRIES(count)];
+}
+
+void svnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset)
+{
+ vnic_wq_init_start(wq, cq_index, 0, 0, error_interrupt_enable,
+ error_interrupt_offset);
+}
+
+unsigned int svnic_wq_error_status(struct vnic_wq *wq)
+{
+ return ioread32(&wq->ctrl->error_status);
+}
+
+void svnic_wq_enable(struct vnic_wq *wq)
+{
+ iowrite32(1, &wq->ctrl->enable);
+}
+
+int svnic_wq_disable(struct vnic_wq *wq)
+{
+ unsigned int wait;
+
+ iowrite32(0, &wq->ctrl->enable);
+
+ /* Wait for HW to ACK disable request */
+ for (wait = 0; wait < 100; wait++) {
+ if (!(ioread32(&wq->ctrl->running)))
+ return 0;
+ udelay(1);
+ }
+
+ pr_err("Failed to disable WQ[%d]\n", wq->index);
+
+ return -ETIMEDOUT;
+}
+
+void svnic_wq_clean(struct vnic_wq *wq,
+ void (*buf_clean)(struct vnic_wq *wq, struct vnic_wq_buf *buf))
+{
+ struct vnic_wq_buf *buf;
+
+ BUG_ON(ioread32(&wq->ctrl->enable));
+
+ buf = wq->to_clean;
+
+ while (svnic_wq_desc_used(wq) > 0) {
+
+ (*buf_clean)(wq, buf);
+
+ buf = wq->to_clean = buf->next;
+ wq->ring.desc_avail++;
+ }
+
+ wq->to_use = wq->to_clean = wq->bufs[0];
+
+ iowrite32(0, &wq->ctrl->fetch_index);
+ iowrite32(0, &wq->ctrl->posted_index);
+ iowrite32(0, &wq->ctrl->error_status);
+
+ svnic_dev_clear_desc_ring(&wq->ring);
+}
diff --git a/drivers/scsi/snic/vnic_wq.h b/drivers/scsi/snic/vnic_wq.h
new file mode 100644
index 000000000000..7cc031c7ceba
--- /dev/null
+++ b/drivers/scsi/snic/vnic_wq.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _VNIC_WQ_H_
+#define _VNIC_WQ_H_
+
+#include <linux/pci.h>
+#include "vnic_dev.h"
+#include "vnic_cq.h"
+
+/* Work queue control */
+struct vnic_wq_ctrl {
+ u64 ring_base; /* 0x00 */
+ u32 ring_size; /* 0x08 */
+ u32 pad0;
+ u32 posted_index; /* 0x10 */
+ u32 pad1;
+ u32 cq_index; /* 0x18 */
+ u32 pad2;
+ u32 enable; /* 0x20 */
+ u32 pad3;
+ u32 running; /* 0x28 */
+ u32 pad4;
+ u32 fetch_index; /* 0x30 */
+ u32 pad5;
+ u32 dca_value; /* 0x38 */
+ u32 pad6;
+ u32 error_interrupt_enable; /* 0x40 */
+ u32 pad7;
+ u32 error_interrupt_offset; /* 0x48 */
+ u32 pad8;
+ u32 error_status; /* 0x50 */
+ u32 pad9;
+};
+
+struct vnic_wq_buf {
+ struct vnic_wq_buf *next;
+ dma_addr_t dma_addr;
+ void *os_buf;
+ unsigned int len;
+ unsigned int index;
+ int sop;
+ void *desc;
+};
+
+/* Break the vnic_wq_buf allocations into blocks of 64 entries */
+#define VNIC_WQ_BUF_MIN_BLK_ENTRIES 32
+#define VNIC_WQ_BUF_DFLT_BLK_ENTRIES 64
+#define VNIC_WQ_BUF_BLK_ENTRIES(entries) \
+ ((unsigned int)(entries < VNIC_WQ_BUF_DFLT_BLK_ENTRIES) ? \
+ VNIC_WQ_BUF_MIN_BLK_ENTRIES : VNIC_WQ_BUF_DFLT_BLK_ENTRIES)
+#define VNIC_WQ_BUF_BLK_SZ \
+ (VNIC_WQ_BUF_DFLT_BLK_ENTRIES * sizeof(struct vnic_wq_buf))
+#define VNIC_WQ_BUF_BLKS_NEEDED(entries) \
+ DIV_ROUND_UP(entries, VNIC_WQ_BUF_DFLT_BLK_ENTRIES)
+#define VNIC_WQ_BUF_BLKS_NEEDED(entries) \
+ DIV_ROUND_UP(entries, VNIC_WQ_BUF_DFLT_BLK_ENTRIES)
+#define VNIC_WQ_BUF_BLKS_MAX VNIC_WQ_BUF_BLKS_NEEDED(4096)
+
+struct vnic_wq {
+ unsigned int index;
+ struct vnic_dev *vdev;
+ struct vnic_wq_ctrl __iomem *ctrl; /* memory-mapped */
+ struct vnic_dev_ring ring;
+ struct vnic_wq_buf *bufs[VNIC_WQ_BUF_BLKS_MAX];
+ struct vnic_wq_buf *to_use;
+ struct vnic_wq_buf *to_clean;
+ unsigned int pkts_outstanding;
+};
+
+static inline unsigned int svnic_wq_desc_avail(struct vnic_wq *wq)
+{
+ /* how many does SW own? */
+ return wq->ring.desc_avail;
+}
+
+static inline unsigned int svnic_wq_desc_used(struct vnic_wq *wq)
+{
+ /* how many does HW own? */
+ return wq->ring.desc_count - wq->ring.desc_avail - 1;
+}
+
+static inline void *svnic_wq_next_desc(struct vnic_wq *wq)
+{
+ return wq->to_use->desc;
+}
+
+static inline void svnic_wq_post(struct vnic_wq *wq,
+ void *os_buf, dma_addr_t dma_addr,
+ unsigned int len, int sop, int eop)
+{
+ struct vnic_wq_buf *buf = wq->to_use;
+
+ buf->sop = sop;
+ buf->os_buf = eop ? os_buf : NULL;
+ buf->dma_addr = dma_addr;
+ buf->len = len;
+
+ buf = buf->next;
+ if (eop) {
+ /* Adding write memory barrier prevents compiler and/or CPU
+ * reordering, thus avoiding descriptor posting before
+ * descriptor is initialized. Otherwise, hardware can read
+ * stale descriptor fields.
+ */
+ wmb();
+ iowrite32(buf->index, &wq->ctrl->posted_index);
+ }
+ wq->to_use = buf;
+
+ wq->ring.desc_avail--;
+}
+
+static inline void svnic_wq_service(struct vnic_wq *wq,
+ struct cq_desc *cq_desc, u16 completed_index,
+ void (*buf_service)(struct vnic_wq *wq,
+ struct cq_desc *cq_desc, struct vnic_wq_buf *buf, void *opaque),
+ void *opaque)
+{
+ struct vnic_wq_buf *buf;
+
+ buf = wq->to_clean;
+ while (1) {
+
+ (*buf_service)(wq, cq_desc, buf, opaque);
+
+ wq->ring.desc_avail++;
+
+ wq->to_clean = buf->next;
+
+ if (buf->index == completed_index)
+ break;
+
+ buf = wq->to_clean;
+ }
+}
+
+void svnic_wq_free(struct vnic_wq *wq);
+int svnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int index, unsigned int desc_count, unsigned int desc_size);
+int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+ unsigned int desc_count, unsigned int desc_size);
+void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index,
+ unsigned int fetch_index, unsigned int post_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset);
+
+void svnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
+ unsigned int error_interrupt_enable,
+ unsigned int error_interrupt_offset);
+unsigned int svnic_wq_error_status(struct vnic_wq *wq);
+void svnic_wq_enable(struct vnic_wq *wq);
+int svnic_wq_disable(struct vnic_wq *wq);
+void svnic_wq_clean(struct vnic_wq *wq,
+ void (*buf_clean)(struct vnic_wq *wq, struct vnic_wq_buf *buf));
+#endif /* _VNIC_WQ_H_ */
diff --git a/drivers/scsi/snic/wq_enet_desc.h b/drivers/scsi/snic/wq_enet_desc.h
new file mode 100644
index 000000000000..68f62b6d105b
--- /dev/null
+++ b/drivers/scsi/snic/wq_enet_desc.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _WQ_ENET_DESC_H_
+#define _WQ_ENET_DESC_H_
+
+/* Ethernet work queue descriptor: 16B */
+struct wq_enet_desc {
+ __le64 address;
+ __le16 length;
+ __le16 mss_loopback;
+ __le16 header_length_flags;
+ __le16 vlan_tag;
+};
+
+#define WQ_ENET_ADDR_BITS 64
+#define WQ_ENET_LEN_BITS 14
+#define WQ_ENET_LEN_MASK ((1 << WQ_ENET_LEN_BITS) - 1)
+#define WQ_ENET_MSS_BITS 14
+#define WQ_ENET_MSS_MASK ((1 << WQ_ENET_MSS_BITS) - 1)
+#define WQ_ENET_MSS_SHIFT 2
+#define WQ_ENET_LOOPBACK_SHIFT 1
+#define WQ_ENET_HDRLEN_BITS 10
+#define WQ_ENET_HDRLEN_MASK ((1 << WQ_ENET_HDRLEN_BITS) - 1)
+#define WQ_ENET_FLAGS_OM_BITS 2
+#define WQ_ENET_FLAGS_OM_MASK ((1 << WQ_ENET_FLAGS_OM_BITS) - 1)
+#define WQ_ENET_FLAGS_EOP_SHIFT 12
+#define WQ_ENET_FLAGS_CQ_ENTRY_SHIFT 13
+#define WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT 14
+#define WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT 15
+
+#define WQ_ENET_OFFLOAD_MODE_CSUM 0
+#define WQ_ENET_OFFLOAD_MODE_RESERVED 1
+#define WQ_ENET_OFFLOAD_MODE_CSUM_L4 2
+#define WQ_ENET_OFFLOAD_MODE_TSO 3
+
+static inline void wq_enet_desc_enc(struct wq_enet_desc *desc,
+ u64 address, u16 length, u16 mss, u16 header_length,
+ u8 offload_mode, u8 eop, u8 cq_entry, u8 fcoe_encap,
+ u8 vlan_tag_insert, u16 vlan_tag, u8 loopback)
+{
+ desc->address = cpu_to_le64(address);
+ desc->length = cpu_to_le16(length & WQ_ENET_LEN_MASK);
+ desc->mss_loopback = cpu_to_le16((mss & WQ_ENET_MSS_MASK) <<
+ WQ_ENET_MSS_SHIFT | (loopback & 1) << WQ_ENET_LOOPBACK_SHIFT);
+ desc->header_length_flags = cpu_to_le16(
+ (header_length & WQ_ENET_HDRLEN_MASK) |
+ (offload_mode & WQ_ENET_FLAGS_OM_MASK) << WQ_ENET_HDRLEN_BITS |
+ (eop & 1) << WQ_ENET_FLAGS_EOP_SHIFT |
+ (cq_entry & 1) << WQ_ENET_FLAGS_CQ_ENTRY_SHIFT |
+ (fcoe_encap & 1) << WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT |
+ (vlan_tag_insert & 1) << WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT);
+ desc->vlan_tag = cpu_to_le16(vlan_tag);
+}
+
+static inline void wq_enet_desc_dec(struct wq_enet_desc *desc,
+ u64 *address, u16 *length, u16 *mss, u16 *header_length,
+ u8 *offload_mode, u8 *eop, u8 *cq_entry, u8 *fcoe_encap,
+ u8 *vlan_tag_insert, u16 *vlan_tag, u8 *loopback)
+{
+ *address = le64_to_cpu(desc->address);
+ *length = le16_to_cpu(desc->length) & WQ_ENET_LEN_MASK;
+ *mss = (le16_to_cpu(desc->mss_loopback) >> WQ_ENET_MSS_SHIFT) &
+ WQ_ENET_MSS_MASK;
+ *loopback = (u8)((le16_to_cpu(desc->mss_loopback) >>
+ WQ_ENET_LOOPBACK_SHIFT) & 1);
+ *header_length = le16_to_cpu(desc->header_length_flags) &
+ WQ_ENET_HDRLEN_MASK;
+ *offload_mode = (u8)((le16_to_cpu(desc->header_length_flags) >>
+ WQ_ENET_HDRLEN_BITS) & WQ_ENET_FLAGS_OM_MASK);
+ *eop = (u8)((le16_to_cpu(desc->header_length_flags) >>
+ WQ_ENET_FLAGS_EOP_SHIFT) & 1);
+ *cq_entry = (u8)((le16_to_cpu(desc->header_length_flags) >>
+ WQ_ENET_FLAGS_CQ_ENTRY_SHIFT) & 1);
+ *fcoe_encap = (u8)((le16_to_cpu(desc->header_length_flags) >>
+ WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT) & 1);
+ *vlan_tag_insert = (u8)((le16_to_cpu(desc->header_length_flags) >>
+ WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT) & 1);
+ *vlan_tag = le16_to_cpu(desc->vlan_tag);
+}
+
+#endif /* _WQ_ENET_DESC_H_ */
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 9a1c34205254..3f25b8fa921d 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -471,6 +471,47 @@ static void st_release_request(struct st_request *streq)
kfree(streq);
}
+static void st_do_stats(struct scsi_tape *STp, struct request *req)
+{
+ ktime_t now;
+
+ now = ktime_get();
+ if (req->cmd[0] == WRITE_6) {
+ now = ktime_sub(now, STp->stats->write_time);
+ atomic64_add(ktime_to_ns(now), &STp->stats->tot_write_time);
+ atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
+ atomic64_inc(&STp->stats->write_cnt);
+ if (req->errors) {
+ atomic64_add(atomic_read(&STp->stats->last_write_size)
+ - STp->buffer->cmdstat.residual,
+ &STp->stats->write_byte_cnt);
+ if (STp->buffer->cmdstat.residual > 0)
+ atomic64_inc(&STp->stats->resid_cnt);
+ } else
+ atomic64_add(atomic_read(&STp->stats->last_write_size),
+ &STp->stats->write_byte_cnt);
+ } else if (req->cmd[0] == READ_6) {
+ now = ktime_sub(now, STp->stats->read_time);
+ atomic64_add(ktime_to_ns(now), &STp->stats->tot_read_time);
+ atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
+ atomic64_inc(&STp->stats->read_cnt);
+ if (req->errors) {
+ atomic64_add(atomic_read(&STp->stats->last_read_size)
+ - STp->buffer->cmdstat.residual,
+ &STp->stats->read_byte_cnt);
+ if (STp->buffer->cmdstat.residual > 0)
+ atomic64_inc(&STp->stats->resid_cnt);
+ } else
+ atomic64_add(atomic_read(&STp->stats->last_read_size),
+ &STp->stats->read_byte_cnt);
+ } else {
+ now = ktime_sub(now, STp->stats->other_time);
+ atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
+ atomic64_inc(&STp->stats->other_cnt);
+ }
+ atomic64_dec(&STp->stats->in_flight);
+}
+
static void st_scsi_execute_end(struct request *req, int uptodate)
{
struct st_request *SRpnt = req->end_io_data;
@@ -480,6 +521,8 @@ static void st_scsi_execute_end(struct request *req, int uptodate)
STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors;
STp->buffer->cmdstat.residual = req->resid_len;
+ st_do_stats(STp, req);
+
tmp = SRpnt->bio;
if (SRpnt->waiting)
complete(SRpnt->waiting);
@@ -496,6 +539,7 @@ static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd,
struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data;
int err = 0;
int write = (data_direction == DMA_TO_DEVICE);
+ struct scsi_tape *STp = SRpnt->stp;
req = blk_get_request(SRpnt->stp->device->request_queue, write,
GFP_KERNEL);
@@ -516,6 +560,17 @@ static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd,
}
}
+ atomic64_inc(&STp->stats->in_flight);
+ if (cmd[0] == WRITE_6) {
+ atomic_set(&STp->stats->last_write_size, bufflen);
+ STp->stats->write_time = ktime_get();
+ } else if (cmd[0] == READ_6) {
+ atomic_set(&STp->stats->last_read_size, bufflen);
+ STp->stats->read_time = ktime_get();
+ } else {
+ STp->stats->other_time = ktime_get();
+ }
+
SRpnt->bio = req->bio;
req->cmd_len = COMMAND_SIZE(cmd[0]);
memset(req->cmd, 0, BLK_MAX_CDB);
@@ -4222,6 +4277,12 @@ static int st_probe(struct device *dev)
}
tpnt->index = error;
sprintf(disk->disk_name, "st%d", tpnt->index);
+ tpnt->stats = kzalloc(sizeof(struct scsi_tape_stats), GFP_KERNEL);
+ if (tpnt->stats == NULL) {
+ sdev_printk(KERN_ERR, SDp,
+ "st: Can't allocate statistics.\n");
+ goto out_idr_remove;
+ }
dev_set_drvdata(dev, tpnt);
@@ -4241,6 +4302,8 @@ static int st_probe(struct device *dev)
out_remove_devs:
remove_cdevs(tpnt);
+ kfree(tpnt->stats);
+out_idr_remove:
spin_lock(&st_index_lock);
idr_remove(&st_index_idr, tpnt->index);
spin_unlock(&st_index_lock);
@@ -4298,6 +4361,7 @@ static void scsi_tape_release(struct kref *kref)
disk->private_data = NULL;
put_disk(disk);
+ kfree(tpnt->stats);
kfree(tpnt);
return;
}
@@ -4513,6 +4577,184 @@ options_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR_RO(options);
+/* Support for tape stats */
+
+/**
+ * read_cnt_show - return read count - count of reads made from tape drive
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t read_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->read_cnt));
+}
+static DEVICE_ATTR_RO(read_cnt);
+
+/**
+ * read_byte_cnt_show - return read byte count - tape drives
+ * may use blocks less than 512 bytes this gives the raw byte count of
+ * of data read from the tape drive.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t read_byte_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->read_byte_cnt));
+}
+static DEVICE_ATTR_RO(read_byte_cnt);
+
+/**
+ * read_us_show - return read us - overall time spent waiting on reads in ns.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t read_ns_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->tot_read_time));
+}
+static DEVICE_ATTR_RO(read_ns);
+
+/**
+ * write_cnt_show - write count - number of user calls
+ * to write(2) that have written data to tape.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t write_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->write_cnt));
+}
+static DEVICE_ATTR_RO(write_cnt);
+
+/**
+ * write_byte_cnt_show - write byte count - raw count of
+ * bytes written to tape.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t write_byte_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->write_byte_cnt));
+}
+static DEVICE_ATTR_RO(write_byte_cnt);
+
+/**
+ * write_ns_show - write ns - number of nanoseconds waiting on write
+ * requests to complete.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t write_ns_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->tot_write_time));
+}
+static DEVICE_ATTR_RO(write_ns);
+
+/**
+ * in_flight_show - number of I/Os currently in flight -
+ * in most cases this will be either 0 or 1. It may be higher if someone
+ * has also issued other SCSI commands such as via an ioctl.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t in_flight_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->in_flight));
+}
+static DEVICE_ATTR_RO(in_flight);
+
+/**
+ * io_ns_show - io wait ns - this is the number of ns spent
+ * waiting on all I/O to complete. This includes tape movement commands
+ * such as rewinding, seeking to end of file or tape, it also includes
+ * read and write. To determine the time spent on tape movement
+ * subtract the read and write ns from this value.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t io_ns_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->tot_io_time));
+}
+static DEVICE_ATTR_RO(io_ns);
+
+/**
+ * other_cnt_show - other io count - this is the number of
+ * I/O requests other than read and write requests.
+ * Typically these are tape movement requests but will include driver
+ * tape movement. This includes only requests issued by the st driver.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t other_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->other_cnt));
+}
+static DEVICE_ATTR_RO(other_cnt);
+
+/**
+ * resid_cnt_show - A count of the number of times we get a residual
+ * count - this should indicate someone issuing reads larger than the
+ * block size on tape.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t resid_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->resid_cnt));
+}
+static DEVICE_ATTR_RO(resid_cnt);
+
static struct attribute *st_dev_attrs[] = {
&dev_attr_defined.attr,
&dev_attr_default_blksize.attr,
@@ -4521,7 +4763,35 @@ static struct attribute *st_dev_attrs[] = {
&dev_attr_options.attr,
NULL,
};
-ATTRIBUTE_GROUPS(st_dev);
+
+static struct attribute *st_stats_attrs[] = {
+ &dev_attr_read_cnt.attr,
+ &dev_attr_read_byte_cnt.attr,
+ &dev_attr_read_ns.attr,
+ &dev_attr_write_cnt.attr,
+ &dev_attr_write_byte_cnt.attr,
+ &dev_attr_write_ns.attr,
+ &dev_attr_in_flight.attr,
+ &dev_attr_io_ns.attr,
+ &dev_attr_other_cnt.attr,
+ &dev_attr_resid_cnt.attr,
+ NULL,
+};
+
+static struct attribute_group stats_group = {
+ .name = "stats",
+ .attrs = st_stats_attrs,
+};
+
+static struct attribute_group st_group = {
+ .attrs = st_dev_attrs,
+};
+
+static const struct attribute_group *st_dev_groups[] = {
+ &st_group,
+ &stats_group,
+ NULL,
+};
/* The following functions may be useful for a larger audience. */
static int sgl_map_user_pages(struct st_buffer *STbp,
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index f3eee0f9f40c..b6486b5d8681 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -92,6 +92,27 @@ struct st_partstat {
int drv_file;
};
+/* Tape statistics */
+struct scsi_tape_stats {
+ atomic64_t read_byte_cnt; /* bytes read */
+ atomic64_t write_byte_cnt; /* bytes written */
+ atomic64_t in_flight; /* Number of I/Os in flight */
+ atomic64_t read_cnt; /* Count of read requests */
+ atomic64_t write_cnt; /* Count of write requests */
+ atomic64_t other_cnt; /* Count of other requests either
+ * implicit or from user space
+ * ioctl. */
+ atomic64_t resid_cnt; /* Count of resid_len > 0 */
+ atomic64_t tot_read_time; /* ktime spent completing reads */
+ atomic64_t tot_write_time; /* ktime spent completing writes */
+ atomic64_t tot_io_time; /* ktime spent doing any I/O */
+ ktime_t read_time; /* holds ktime request was queued */
+ ktime_t write_time; /* holds ktime request was queued */
+ ktime_t other_time; /* holds ktime request was queued */
+ atomic_t last_read_size; /* Number of bytes issued for last read */
+ atomic_t last_write_size; /* Number of bytes issued for last write */
+};
+
#define ST_NBR_PARTITIONS 4
/* The tape drive descriptor */
@@ -171,6 +192,7 @@ struct scsi_tape {
#endif
struct gendisk *disk;
struct kref kref;
+ struct scsi_tape_stats *stats;
};
/* Bit masks for use_pf */
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index d9dad90344d5..3c6584ff65c1 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -1600,8 +1600,7 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
break;
default:
vm_srb->data_in = UNKNOWN_TYPE;
- vm_srb->win8_extension.srb_flags |= (SRB_FLAGS_DATA_IN |
- SRB_FLAGS_DATA_OUT);
+ vm_srb->win8_extension.srb_flags |= SRB_FLAGS_NO_DATA_TRANSFER;
break;
}
diff --git a/drivers/scsi/sym53c416.c b/drivers/scsi/sym53c416.c
index 0b7819f3e09b..5bdcbe8fa958 100644
--- a/drivers/scsi/sym53c416.c
+++ b/drivers/scsi/sym53c416.c
@@ -838,7 +838,6 @@ static struct scsi_host_template driver_template = {
.can_queue = 1,
.this_id = SYM53C416_SCSI_ID,
.sg_tablesize = 32,
- .cmd_per_lun = 1,
.unchecked_isa_dma = 1,
.use_clustering = ENABLE_CLUSTERING,
};
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index 8a1f4b355416..e94538362536 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -73,7 +73,7 @@ config SCSI_UFSHCD_PLATFORM
config SCSI_UFS_QCOM
bool "QCOM specific hooks to UFS controller platform driver"
- depends on SCSI_UFSHCD_PLATFORM && ARCH_MSM
+ depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM
select PHY_QCOM_UFS
help
This selects the QCOM specific additions to UFSHCD platform driver.
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 6652a8171de6..4cdffa46d401 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -307,6 +307,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, bool status)
static unsigned long
ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate)
{
+ struct ufs_qcom_host *host = hba->priv;
struct ufs_clk_info *clki;
u32 core_clk_period_in_ns;
u32 tx_clk_cycles_per_us = 0;
@@ -330,6 +331,16 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate)
{UFS_HS_G2, 0x49},
};
+ /*
+ * The Qunipro controller does not use following registers:
+ * SYS1CLK_1US_REG, TX_SYMBOL_CLK_1US_REG, CLK_NS_REG &
+ * UFS_REG_PA_LINK_STARTUP_TIMER
+ * But UTP controller uses SYS1CLK_1US_REG register for Interrupt
+ * Aggregation logic.
+ */
+ if (ufs_qcom_cap_qunipro(host) && !ufshcd_is_intr_aggr_allowed(hba))
+ goto out;
+
if (gear == 0) {
dev_err(hba->dev, "%s: invalid gear = %d\n", __func__, gear);
goto out_error;
@@ -683,6 +694,16 @@ out:
return ret;
}
+static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba)
+{
+ struct ufs_qcom_host *host = hba->priv;
+
+ if (host->hw_ver.major == 0x1)
+ return UFSHCI_VERSION_11;
+ else
+ return UFSHCI_VERSION_20;
+}
+
/**
* ufs_qcom_advertise_quirks - advertise the known QCOM UFS controller quirks
* @hba: host controller instance
@@ -696,13 +717,24 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = hba->priv;
- if (host->hw_ver.major == 0x1)
- hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS;
+ if (host->hw_ver.major == 0x01) {
+ hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS
+ | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP
+ | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE;
+
+ if (host->hw_ver.minor == 0x0001 && host->hw_ver.step == 0x0001)
+ hba->quirks |= UFSHCD_QUIRK_BROKEN_INTR_AGGR;
+ }
if (host->hw_ver.major >= 0x2) {
+ hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC;
+ hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION;
+
if (!ufs_qcom_cap_qunipro(host))
/* Legacy UniPro mode still need following quirks */
- hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS;
+ hba->quirks |= (UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS
+ | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE
+ | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP);
}
}
@@ -1005,6 +1037,7 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
.name = "qcom",
.init = ufs_qcom_init,
.exit = ufs_qcom_exit,
+ .get_ufs_hci_version = ufs_qcom_get_ufs_hci_version,
.clk_scale_notify = ufs_qcom_clk_scale_notify,
.setup_clocks = ufs_qcom_setup_clocks,
.hce_enable_notify = ufs_qcom_hce_enable_notify,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 648a44675880..b0ade73f8c6a 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -188,6 +188,8 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
static irqreturn_t ufshcd_intr(int irq, void *__hba);
static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
struct ufs_pa_layer_attr *desired_pwr_mode);
+static int ufshcd_change_power_mode(struct ufs_hba *hba,
+ struct ufs_pa_layer_attr *pwr_mode);
static inline int ufshcd_enable_irq(struct ufs_hba *hba)
{
@@ -269,6 +271,11 @@ static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba)
*/
static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
{
+ if (hba->quirks & UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION) {
+ if (hba->vops && hba->vops->get_ufs_hci_version)
+ return hba->vops->get_ufs_hci_version(hba);
+ }
+
return ufshcd_readl(hba, REG_UFS_VERSION);
}
@@ -481,6 +488,15 @@ ufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 tmout)
}
/**
+ * ufshcd_disable_intr_aggr - Disables interrupt aggregation.
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_disable_intr_aggr(struct ufs_hba *hba)
+{
+ ufshcd_writel(hba, 0, REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
+}
+
+/**
* ufshcd_enable_run_stop_reg - Enable run-stop registers,
* When run-stop registers are set to 1, it indicates the
* host controller that it can process the requests
@@ -1326,7 +1342,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
lrbp->sense_buffer = cmd->sense_buffer;
lrbp->task_tag = tag;
lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);
- lrbp->intr_cmd = false;
+ lrbp->intr_cmd = !ufshcd_is_intr_aggr_allowed(hba) ? true : false;
lrbp->command_type = UTP_CMD_TYPE_SCSI;
/* form UPIU before issuing the command */
@@ -2147,6 +2163,31 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
};
const char *get = action[!!peer];
int ret;
+ struct ufs_pa_layer_attr orig_pwr_info;
+ struct ufs_pa_layer_attr temp_pwr_info;
+ bool pwr_mode_change = false;
+
+ if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE)) {
+ orig_pwr_info = hba->pwr_info;
+ temp_pwr_info = orig_pwr_info;
+
+ if (orig_pwr_info.pwr_tx == FAST_MODE ||
+ orig_pwr_info.pwr_rx == FAST_MODE) {
+ temp_pwr_info.pwr_tx = FASTAUTO_MODE;
+ temp_pwr_info.pwr_rx = FASTAUTO_MODE;
+ pwr_mode_change = true;
+ } else if (orig_pwr_info.pwr_tx == SLOW_MODE ||
+ orig_pwr_info.pwr_rx == SLOW_MODE) {
+ temp_pwr_info.pwr_tx = SLOWAUTO_MODE;
+ temp_pwr_info.pwr_rx = SLOWAUTO_MODE;
+ pwr_mode_change = true;
+ }
+ if (pwr_mode_change) {
+ ret = ufshcd_change_power_mode(hba, &temp_pwr_info);
+ if (ret)
+ goto out;
+ }
+ }
uic_cmd.command = peer ?
UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
@@ -2161,6 +2202,10 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
if (mib_val)
*mib_val = uic_cmd.argument3;
+
+ if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE)
+ && pwr_mode_change)
+ ufshcd_change_power_mode(hba, &orig_pwr_info);
out:
return ret;
}
@@ -2249,6 +2294,16 @@ static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
struct uic_command uic_cmd = {0};
int ret;
+ if (hba->quirks & UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP) {
+ ret = ufshcd_dme_set(hba,
+ UIC_ARG_MIB_SEL(PA_RXHSUNTERMCAP, 0), 1);
+ if (ret) {
+ dev_err(hba->dev, "%s: failed to enable PA_RXHSUNTERMCAP ret %d\n",
+ __func__, ret);
+ goto out;
+ }
+ }
+
uic_cmd.command = UIC_CMD_DME_SET;
uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
uic_cmd.argument3 = mode;
@@ -2256,6 +2311,7 @@ static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
ufshcd_release(hba);
+out:
return ret;
}
@@ -2522,7 +2578,10 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
/* Configure interrupt aggregation */
- ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
+ if (ufshcd_is_intr_aggr_allowed(hba))
+ ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
+ else
+ ufshcd_disable_intr_aggr(hba);
/* Configure UTRL and UTMRL base address registers */
ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
@@ -2628,6 +2687,42 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
return 0;
}
+static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer)
+{
+ int tx_lanes, i, err = 0;
+
+ if (!peer)
+ ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
+ &tx_lanes);
+ else
+ ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
+ &tx_lanes);
+ for (i = 0; i < tx_lanes; i++) {
+ if (!peer)
+ err = ufshcd_dme_set(hba,
+ UIC_ARG_MIB_SEL(TX_LCC_ENABLE,
+ UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
+ 0);
+ else
+ err = ufshcd_dme_peer_set(hba,
+ UIC_ARG_MIB_SEL(TX_LCC_ENABLE,
+ UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
+ 0);
+ if (err) {
+ dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d",
+ __func__, peer, i, err);
+ break;
+ }
+ }
+
+ return err;
+}
+
+static inline int ufshcd_disable_device_tx_lcc(struct ufs_hba *hba)
+{
+ return ufshcd_disable_tx_lcc(hba, true);
+}
+
/**
* ufshcd_link_startup - Initialize unipro link startup
* @hba: per adapter instance
@@ -2665,6 +2760,12 @@ static int ufshcd_link_startup(struct ufs_hba *hba)
/* failed to get the link up... retire */
goto out;
+ if (hba->quirks & UFSHCD_QUIRK_BROKEN_LCC) {
+ ret = ufshcd_disable_device_tx_lcc(hba);
+ if (ret)
+ goto out;
+ }
+
/* Include any host controller configuration via UIC commands */
if (hba->vops && hba->vops->link_startup_notify) {
ret = hba->vops->link_startup_notify(hba, POST_CHANGE);
@@ -3073,7 +3174,8 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
* false interrupt if device completes another request after resetting
* aggregation and before reading the DB.
*/
- ufshcd_reset_intr_aggr(hba);
+ if (ufshcd_is_intr_aggr_allowed(hba))
+ ufshcd_reset_intr_aggr(hba);
tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index b47ff07698e8..c40a0e78a6c4 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -246,6 +246,7 @@ struct ufs_pwr_mode_info {
* @name: variant name
* @init: called when the driver is initialized
* @exit: called to cleanup everything done in init
+ * @get_ufs_hci_version: called to get UFS HCI version
* @clk_scale_notify: notifies that clks are scaled up/down
* @setup_clocks: called before touching any of the controller registers
* @setup_regulators: called before accessing the host controller
@@ -263,6 +264,7 @@ struct ufs_hba_variant_ops {
const char *name;
int (*init)(struct ufs_hba *);
void (*exit)(struct ufs_hba *);
+ u32 (*get_ufs_hci_version)(struct ufs_hba *);
void (*clk_scale_notify)(struct ufs_hba *);
int (*setup_clocks)(struct ufs_hba *, bool);
int (*setup_regulators)(struct ufs_hba *, bool);
@@ -417,11 +419,45 @@ struct ufs_hba {
unsigned int irq;
bool is_irq_enabled;
+ /* Interrupt aggregation support is broken */
+ #define UFSHCD_QUIRK_BROKEN_INTR_AGGR UFS_BIT(0)
+
/*
* delay before each dme command is required as the unipro
* layer has shown instabilities
*/
- #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS UFS_BIT(0)
+ #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS UFS_BIT(1)
+
+ /*
+ * If UFS host controller is having issue in processing LCC (Line
+ * Control Command) coming from device then enable this quirk.
+ * When this quirk is enabled, host controller driver should disable
+ * the LCC transmission on UFS device (by clearing TX_LCC_ENABLE
+ * attribute of device to 0).
+ */
+ #define UFSHCD_QUIRK_BROKEN_LCC UFS_BIT(2)
+
+ /*
+ * The attribute PA_RXHSUNTERMCAP specifies whether or not the
+ * inbound Link supports unterminated line in HS mode. Setting this
+ * attribute to 1 fixes moving to HS gear.
+ */
+ #define UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP UFS_BIT(3)
+
+ /*
+ * This quirk needs to be enabled if the host contoller only allows
+ * accessing the peer dme attributes in AUTO mode (FAST AUTO or
+ * SLOW AUTO).
+ */
+ #define UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE UFS_BIT(4)
+
+ /*
+ * This quirk needs to be enabled if the host contoller doesn't
+ * advertise the correct version in UFS_VER register. If this quirk
+ * is enabled, standard UFS host driver will call the vendor specific
+ * ops (get_ufs_hci_version) to get the correct version.
+ */
+ #define UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION UFS_BIT(5)
unsigned int quirks; /* Deviations from standard UFSHCI spec. */
@@ -478,6 +514,12 @@ struct ufs_hba {
#define UFSHCD_CAP_CLK_SCALING (1 << 2)
/* Allow auto bkops to enabled during runtime suspend */
#define UFSHCD_CAP_AUTO_BKOPS_SUSPEND (1 << 3)
+ /*
+ * This capability allows host controller driver to use the UFS HCI's
+ * interrupt aggregation capability.
+ * CAUTION: Enabling this might reduce overall UFS throughput.
+ */
+#define UFSHCD_CAP_INTR_AGGR (1 << 4)
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
@@ -502,6 +544,15 @@ static inline bool ufshcd_can_autobkops_during_suspend(struct ufs_hba *hba)
return hba->caps & UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
}
+static inline bool ufshcd_is_intr_aggr_allowed(struct ufs_hba *hba)
+{
+ if ((hba->caps & UFSHCD_CAP_INTR_AGGR) &&
+ !(hba->quirks & UFSHCD_QUIRK_BROKEN_INTR_AGGR))
+ return true;
+ else
+ return false;
+}
+
#define ufshcd_writel(hba, val, reg) \
writel((val), (hba)->mmio_base + (reg))
#define ufshcd_readl(hba, reg) \
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
index d5721199e9cc..0ae0967aaed8 100644
--- a/drivers/scsi/ufs/ufshci.h
+++ b/drivers/scsi/ufs/ufshci.h
@@ -89,8 +89,9 @@ enum {
/* Controller UFSHCI version */
enum {
- UFSHCI_VERSION_10 = 0x00010000,
- UFSHCI_VERSION_11 = 0x00010100,
+ UFSHCI_VERSION_10 = 0x00010000, /* 1.0 */
+ UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */
+ UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */
};
/*
@@ -206,6 +207,9 @@ enum {
#define CONFIG_RESULT_CODE_MASK 0xFF
#define GENERIC_ERROR_CODE_MASK 0xFF
+/* GenSelectorIndex calculation macros for M-PHY attributes */
+#define UIC_ARG_MPHY_TX_GEN_SEL_INDEX(lane) (lane)
+
#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\
((sel) & 0xFFFF))
#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0)
diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h
index 3fc3e21b746b..816a8a46efb8 100644
--- a/drivers/scsi/ufs/unipro.h
+++ b/drivers/scsi/ufs/unipro.h
@@ -198,6 +198,14 @@ enum ufs_hs_gear_tag {
#define T_TC0TXMAXSDUSIZE 0x4060
#define T_TC1TXMAXSDUSIZE 0x4061
+#ifdef FALSE
+#undef FALSE
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+
/* Boolean attribute values */
enum {
FALSE = 0,
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index f164f24a4a55..285f77544c36 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -501,6 +501,7 @@ static void virtio_scsi_init_hdr(struct virtio_device *vdev,
cmd->crn = 0;
}
+#ifdef CONFIG_BLK_DEV_INTEGRITY
static void virtio_scsi_init_hdr_pi(struct virtio_device *vdev,
struct virtio_scsi_cmd_req_pi *cmd_pi,
struct scsi_cmnd *sc)
@@ -524,6 +525,7 @@ static void virtio_scsi_init_hdr_pi(struct virtio_device *vdev,
blk_rq_sectors(rq) *
bi->tuple_size);
}
+#endif
static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
struct virtio_scsi_vq *req_vq,
@@ -546,11 +548,14 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE);
+#ifdef CONFIG_BLK_DEV_INTEGRITY
if (virtio_has_feature(vscsi->vdev, VIRTIO_SCSI_F_T10_PI)) {
virtio_scsi_init_hdr_pi(vscsi->vdev, &cmd->req.cmd_pi, sc);
memcpy(cmd->req.cmd_pi.cdb, sc->cmnd, sc->cmd_len);
req_size = sizeof(cmd->req.cmd_pi);
- } else {
+ } else
+#endif
+ {
virtio_scsi_init_hdr(vscsi->vdev, &cmd->req.cmd, sc);
memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
req_size = sizeof(cmd->req.cmd);
@@ -1002,6 +1007,7 @@ static int virtscsi_probe(struct virtio_device *vdev)
shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE;
shost->nr_hw_queues = num_queues;
+#ifdef CONFIG_BLK_DEV_INTEGRITY
if (virtio_has_feature(vdev, VIRTIO_SCSI_F_T10_PI)) {
host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION |
SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE1_PROTECTION |
@@ -1010,6 +1016,7 @@ static int virtscsi_probe(struct virtio_device *vdev)
scsi_host_set_prot(shost, host_prot);
scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
}
+#endif
err = scsi_add_host(shost, &vdev->dev);
if (err)
@@ -1090,7 +1097,9 @@ static struct virtio_device_id id_table[] = {
static unsigned int features[] = {
VIRTIO_SCSI_F_HOTPLUG,
VIRTIO_SCSI_F_CHANGE,
+#ifdef CONFIG_BLK_DEV_INTEGRITY
VIRTIO_SCSI_F_T10_PI,
+#endif
};
static struct virtio_driver virtio_scsi_driver = {
diff --git a/drivers/scsi/wd719x.c b/drivers/scsi/wd719x.c
index 289ad016d925..61346aa73178 100644
--- a/drivers/scsi/wd719x.c
+++ b/drivers/scsi/wd719x.c
@@ -882,7 +882,6 @@ static struct scsi_host_template wd719x_template = {
.can_queue = 255,
.this_id = 7,
.sg_tablesize = WD719X_SG,
- .cmd_per_lun = WD719X_CMD_PER_LUN,
.use_clustering = ENABLE_CLUSTERING,
};
diff --git a/drivers/scsi/wd719x.h b/drivers/scsi/wd719x.h
index 185e30e4eb93..9c6dd45f95f5 100644
--- a/drivers/scsi/wd719x.h
+++ b/drivers/scsi/wd719x.h
@@ -2,8 +2,6 @@
#define _WD719X_H_
#define WD719X_SG 255 /* Scatter/gather size */
-#define WD719X_CMD_PER_LUN 1 /* We should be able to do linked commands, but
- * this is 1 for now to be safe. */
struct wd719x_sglist {
__le32 ptr;
diff --git a/drivers/sh/pm_runtime.c b/drivers/sh/pm_runtime.c
index cd4c293f0dd0..d3d1891cda3c 100644
--- a/drivers/sh/pm_runtime.c
+++ b/drivers/sh/pm_runtime.c
@@ -20,58 +20,15 @@
#include <linux/bitmap.h>
#include <linux/slab.h>
-#ifdef CONFIG_PM
-static int sh_pm_runtime_suspend(struct device *dev)
-{
- int ret;
-
- ret = pm_generic_runtime_suspend(dev);
- if (ret) {
- dev_err(dev, "failed to suspend device\n");
- return ret;
- }
-
- ret = pm_clk_suspend(dev);
- if (ret) {
- dev_err(dev, "failed to suspend clock\n");
- pm_generic_runtime_resume(dev);
- return ret;
- }
-
- return 0;
-}
-
-static int sh_pm_runtime_resume(struct device *dev)
-{
- int ret;
-
- ret = pm_clk_resume(dev);
- if (ret) {
- dev_err(dev, "failed to resume clock\n");
- return ret;
- }
-
- return pm_generic_runtime_resume(dev);
-}
-
static struct dev_pm_domain default_pm_domain = {
.ops = {
- .runtime_suspend = sh_pm_runtime_suspend,
- .runtime_resume = sh_pm_runtime_resume,
+ USE_PM_CLK_RUNTIME_OPS
USE_PLATFORM_PM_SLEEP_OPS
},
};
-#define DEFAULT_PM_DOMAIN_PTR (&default_pm_domain)
-
-#else
-
-#define DEFAULT_PM_DOMAIN_PTR NULL
-
-#endif /* CONFIG_PM */
-
static struct pm_clk_notifier_block platform_bus_notifier = {
- .pm_domain = DEFAULT_PM_DOMAIN_PTR,
+ .pm_domain = &default_pm_domain,
.con_ids = { NULL, },
};
@@ -80,9 +37,10 @@ static int __init sh_pm_runtime_init(void)
if (IS_ENABLED(CONFIG_ARCH_SHMOBILE_MULTI)) {
if (!of_machine_is_compatible("renesas,emev2") &&
!of_machine_is_compatible("renesas,r7s72100") &&
- !of_machine_is_compatible("renesas,r8a73a4") &&
#ifndef CONFIG_PM_GENERIC_DOMAINS_OF
+ !of_machine_is_compatible("renesas,r8a73a4") &&
!of_machine_is_compatible("renesas,r8a7740") &&
+ !of_machine_is_compatible("renesas,sh73a0") &&
#endif
!of_machine_is_compatible("renesas,r8a7778") &&
!of_machine_is_compatible("renesas,r8a7779") &&
@@ -90,9 +48,7 @@ static int __init sh_pm_runtime_init(void)
!of_machine_is_compatible("renesas,r8a7791") &&
!of_machine_is_compatible("renesas,r8a7792") &&
!of_machine_is_compatible("renesas,r8a7793") &&
- !of_machine_is_compatible("renesas,r8a7794") &&
- !of_machine_is_compatible("renesas,sh7372") &&
- !of_machine_is_compatible("renesas,sh73a0"))
+ !of_machine_is_compatible("renesas,r8a7794"))
return 0;
}
diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
index bcdb22d5e215..3c1850332a90 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -4,6 +4,7 @@
config MTK_PMIC_WRAP
tristate "MediaTek PMIC Wrapper Support"
depends on ARCH_MEDIATEK
+ depends on RESET_CONTROLLER
select REGMAP
help
Say yes here to add support for MediaTek PMIC Wrapper found
diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
index db5be1eec54c..f432291feee9 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -443,11 +443,6 @@ static int pwrap_wait_for_state(struct pmic_wrapper *wrp,
static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
{
int ret;
- u32 val;
-
- val = pwrap_readl(wrp, PWRAP_WACS2_RDATA);
- if (PWRAP_GET_WACS_FSM(val) == PWRAP_WACS_FSM_WFVLDCLR)
- pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR);
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
if (ret)
@@ -462,11 +457,6 @@ static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
{
int ret;
- u32 val;
-
- val = pwrap_readl(wrp, PWRAP_WACS2_RDATA);
- if (PWRAP_GET_WACS_FSM(val) == PWRAP_WACS_FSM_WFVLDCLR)
- pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR);
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
if (ret)
@@ -480,6 +470,8 @@ static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
*rdata = PWRAP_GET_WACS_RDATA(pwrap_readl(wrp, PWRAP_WACS2_RDATA));
+ pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR);
+
return 0;
}
@@ -563,45 +555,17 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp)
static int pwrap_init_reg_clock(struct pmic_wrapper *wrp)
{
- unsigned long rate_spi;
- int ck_mhz;
-
- rate_spi = clk_get_rate(wrp->clk_spi);
-
- if (rate_spi > 26000000)
- ck_mhz = 26;
- else if (rate_spi > 18000000)
- ck_mhz = 18;
- else
- ck_mhz = 0;
-
- switch (ck_mhz) {
- case 18:
- if (pwrap_is_mt8135(wrp))
- pwrap_writel(wrp, 0xc, PWRAP_CSHEXT);
- pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_WRITE);
- pwrap_writel(wrp, 0xc, PWRAP_CSHEXT_READ);
- pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_START);
- pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_END);
- break;
- case 26:
- if (pwrap_is_mt8135(wrp))
- pwrap_writel(wrp, 0x4, PWRAP_CSHEXT);
+ if (pwrap_is_mt8135(wrp)) {
+ pwrap_writel(wrp, 0x4, PWRAP_CSHEXT);
pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE);
pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ);
pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_START);
pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_END);
- break;
- case 0:
- if (pwrap_is_mt8135(wrp))
- pwrap_writel(wrp, 0xf, PWRAP_CSHEXT);
- pwrap_writel(wrp, 0xf, PWRAP_CSHEXT_WRITE);
- pwrap_writel(wrp, 0xf, PWRAP_CSHEXT_READ);
- pwrap_writel(wrp, 0xf, PWRAP_CSLEXT_START);
- pwrap_writel(wrp, 0xf, PWRAP_CSLEXT_END);
- break;
- default:
- return -EINVAL;
+ } else {
+ pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE);
+ pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ);
+ pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START);
+ pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END);
}
return 0;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 198f96b7fb45..0cae1694014d 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -77,7 +77,9 @@ config SPI_ATMEL
config SPI_BCM2835
tristate "BCM2835 SPI controller"
+ depends on GPIOLIB
depends on ARCH_BCM2835 || COMPILE_TEST
+ depends on GPIOLIB
help
This selects a driver for the Broadcom BCM2835 SPI master.
@@ -220,7 +222,7 @@ config SPI_FALCON
config SPI_GPIO
tristate "GPIO-based bitbanging SPI Master"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
select SPI_BITBANG
help
This simple GPIO bitbanging SPI master uses the arch-neutral GPIO
@@ -302,7 +304,7 @@ config SPI_FSL_SPI
config SPI_FSL_DSPI
tristate "Freescale DSPI controller"
select REGMAP_MMIO
- depends on SOC_VF610 || COMPILE_TEST
+ depends on SOC_VF610 || SOC_LS1021A || COMPILE_TEST
help
This enables support for the Freescale DSPI controller in master
mode. VF610 platform uses the controller.
@@ -326,7 +328,7 @@ config SPI_MESON_SPIFC
config SPI_OC_TINY
tristate "OpenCores tiny SPI"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
select SPI_BITBANG
help
This is the driver for OpenCores tiny SPI master controller.
@@ -393,16 +395,9 @@ config SPI_PPC4xx
help
This selects a driver for the PPC4xx SPI Controller.
-config SPI_PXA2XX_PXADMA
- bool "PXA2xx SSP legacy PXA DMA API support"
- depends on SPI_PXA2XX && ARCH_PXA
- help
- Enable PXA private legacy DMA API support. Note that this is
- deprecated in favor of generic DMA engine API.
-
config SPI_PXA2XX_DMA
def_bool y
- depends on SPI_PXA2XX && !SPI_PXA2XX_PXADMA
+ depends on SPI_PXA2XX
config SPI_PXA2XX
tristate "PXA2xx SSP SPI master"
@@ -428,6 +423,12 @@ config SPI_ROCKCHIP
The main usecase of this controller is to use spi flash as boot
device.
+config SPI_RB4XX
+ tristate "Mikrotik RB4XX SPI master"
+ depends on SPI_MASTER && ATH79
+ help
+ SPI controller driver for the Mikrotik RB4xx series boards.
+
config SPI_RSPI
tristate "Renesas RSPI/QSPI controller"
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
@@ -609,6 +610,12 @@ config SPI_XTENSA_XTFPGA
16 bit words in SPI mode 0, automatically asserting CS on transfer
start and deasserting on end.
+config SPI_ZYNQMP_GQSPI
+ tristate "Xilinx ZynqMP GQSPI controller"
+ depends on SPI_MASTER
+ help
+ Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
+
config SPI_NUC900
tristate "Nuvoton NUC900 series SPI"
depends on ARCH_W90X900
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index d8cbf654976b..1154dbac8f2c 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -60,12 +60,12 @@ obj-$(CONFIG_SPI_ORION) += spi-orion.o
obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
spi-pxa2xx-platform-objs := spi-pxa2xx.o
-spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o
spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
obj-$(CONFIG_SPI_QUP) += spi-qup.o
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
+obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y := spi-s3c24xx.o
@@ -89,3 +89,4 @@ obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
+obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c
index b02eb4ac0218..bf1f9b32c597 100644
--- a/drivers/spi/spi-ath79.c
+++ b/drivers/spi/spi-ath79.c
@@ -79,10 +79,8 @@ static void ath79_spi_chipselect(struct spi_device *spi, int is_active)
}
if (spi->chip_select) {
- struct ath79_spi_controller_data *cdata = spi->controller_data;
-
/* SPI is normally active-low */
- gpio_set_value(cdata->gpio, cs_high);
+ gpio_set_value(spi->cs_gpio, cs_high);
} else {
if (cs_high)
sp->ioc_base |= AR71XX_SPI_IOC_CS0;
@@ -117,11 +115,10 @@ static void ath79_spi_disable(struct ath79_spi *sp)
static int ath79_spi_setup_cs(struct spi_device *spi)
{
- struct ath79_spi_controller_data *cdata;
+ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
int status;
- cdata = spi->controller_data;
- if (spi->chip_select && !cdata)
+ if (spi->chip_select && !gpio_is_valid(spi->cs_gpio))
return -EINVAL;
status = 0;
@@ -134,8 +131,15 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
else
flags |= GPIOF_INIT_HIGH;
- status = gpio_request_one(cdata->gpio, flags,
+ status = gpio_request_one(spi->cs_gpio, flags,
dev_name(&spi->dev));
+ } else {
+ if (spi->mode & SPI_CS_HIGH)
+ sp->ioc_base &= ~AR71XX_SPI_IOC_CS0;
+ else
+ sp->ioc_base |= AR71XX_SPI_IOC_CS0;
+
+ ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
}
return status;
@@ -144,8 +148,7 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
static void ath79_spi_cleanup_cs(struct spi_device *spi)
{
if (spi->chip_select) {
- struct ath79_spi_controller_data *cdata = spi->controller_data;
- gpio_free(cdata->gpio);
+ gpio_free(spi->cs_gpio);
}
}
@@ -217,6 +220,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
}
sp = spi_master_get_devdata(master);
+ master->dev.of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, sp);
pdata = dev_get_platdata(&pdev->dev);
@@ -253,7 +257,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
goto err_put_master;
}
- ret = clk_enable(sp->clk);
+ ret = clk_prepare_enable(sp->clk);
if (ret)
goto err_put_master;
@@ -277,7 +281,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
err_disable:
ath79_spi_disable(sp);
err_clk_disable:
- clk_disable(sp->clk);
+ clk_disable_unprepare(sp->clk);
err_put_master:
spi_master_put(sp->bitbang.master);
@@ -290,7 +294,7 @@ static int ath79_spi_remove(struct platform_device *pdev)
spi_bitbang_stop(&sp->bitbang);
ath79_spi_disable(sp);
- clk_disable(sp->clk);
+ clk_disable_unprepare(sp->clk);
spi_master_put(sp->bitbang.master);
return 0;
@@ -301,12 +305,18 @@ static void ath79_spi_shutdown(struct platform_device *pdev)
ath79_spi_remove(pdev);
}
+static const struct of_device_id ath79_spi_of_match[] = {
+ { .compatible = "qca,ar7100-spi", },
+ { },
+};
+
static struct platform_driver ath79_spi_driver = {
.probe = ath79_spi_probe,
.remove = ath79_spi_remove,
.shutdown = ath79_spi_shutdown,
.driver = {
.name = DRV_NAME,
+ .of_match_table = ath79_spi_of_match,
},
};
module_platform_driver(ath79_spi_driver);
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index a2f40b1b2225..c9eca347787d 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -41,6 +41,8 @@
#define SPI_CSR1 0x0034
#define SPI_CSR2 0x0038
#define SPI_CSR3 0x003c
+#define SPI_FMR 0x0040
+#define SPI_FLR 0x0044
#define SPI_VERSION 0x00fc
#define SPI_RPR 0x0100
#define SPI_RCR 0x0104
@@ -62,6 +64,14 @@
#define SPI_SWRST_SIZE 1
#define SPI_LASTXFER_OFFSET 24
#define SPI_LASTXFER_SIZE 1
+#define SPI_TXFCLR_OFFSET 16
+#define SPI_TXFCLR_SIZE 1
+#define SPI_RXFCLR_OFFSET 17
+#define SPI_RXFCLR_SIZE 1
+#define SPI_FIFOEN_OFFSET 30
+#define SPI_FIFOEN_SIZE 1
+#define SPI_FIFODIS_OFFSET 31
+#define SPI_FIFODIS_SIZE 1
/* Bitfields in MR */
#define SPI_MSTR_OFFSET 0
@@ -114,6 +124,22 @@
#define SPI_TXEMPTY_SIZE 1
#define SPI_SPIENS_OFFSET 16
#define SPI_SPIENS_SIZE 1
+#define SPI_TXFEF_OFFSET 24
+#define SPI_TXFEF_SIZE 1
+#define SPI_TXFFF_OFFSET 25
+#define SPI_TXFFF_SIZE 1
+#define SPI_TXFTHF_OFFSET 26
+#define SPI_TXFTHF_SIZE 1
+#define SPI_RXFEF_OFFSET 27
+#define SPI_RXFEF_SIZE 1
+#define SPI_RXFFF_OFFSET 28
+#define SPI_RXFFF_SIZE 1
+#define SPI_RXFTHF_OFFSET 29
+#define SPI_RXFTHF_SIZE 1
+#define SPI_TXFPTEF_OFFSET 30
+#define SPI_TXFPTEF_SIZE 1
+#define SPI_RXFPTEF_OFFSET 31
+#define SPI_RXFPTEF_SIZE 1
/* Bitfields in CSR0 */
#define SPI_CPOL_OFFSET 0
@@ -157,6 +183,22 @@
#define SPI_TXTDIS_OFFSET 9
#define SPI_TXTDIS_SIZE 1
+/* Bitfields in FMR */
+#define SPI_TXRDYM_OFFSET 0
+#define SPI_TXRDYM_SIZE 2
+#define SPI_RXRDYM_OFFSET 4
+#define SPI_RXRDYM_SIZE 2
+#define SPI_TXFTHRES_OFFSET 16
+#define SPI_TXFTHRES_SIZE 6
+#define SPI_RXFTHRES_OFFSET 24
+#define SPI_RXFTHRES_SIZE 6
+
+/* Bitfields in FLR */
+#define SPI_TXFL_OFFSET 0
+#define SPI_TXFL_SIZE 6
+#define SPI_RXFL_OFFSET 16
+#define SPI_RXFL_SIZE 6
+
/* Constants for BITS */
#define SPI_BITS_8_BPT 0
#define SPI_BITS_9_BPT 1
@@ -167,6 +209,9 @@
#define SPI_BITS_14_BPT 6
#define SPI_BITS_15_BPT 7
#define SPI_BITS_16_BPT 8
+#define SPI_ONE_DATA 0
+#define SPI_TWO_DATA 1
+#define SPI_FOUR_DATA 2
/* Bit manipulation macros */
#define SPI_BIT(name) \
@@ -185,11 +230,31 @@
__raw_readl((port)->regs + SPI_##reg)
#define spi_writel(port, reg, value) \
__raw_writel((value), (port)->regs + SPI_##reg)
+
+#define spi_readw(port, reg) \
+ __raw_readw((port)->regs + SPI_##reg)
+#define spi_writew(port, reg, value) \
+ __raw_writew((value), (port)->regs + SPI_##reg)
+
+#define spi_readb(port, reg) \
+ __raw_readb((port)->regs + SPI_##reg)
+#define spi_writeb(port, reg, value) \
+ __raw_writeb((value), (port)->regs + SPI_##reg)
#else
#define spi_readl(port, reg) \
readl_relaxed((port)->regs + SPI_##reg)
#define spi_writel(port, reg, value) \
writel_relaxed((value), (port)->regs + SPI_##reg)
+
+#define spi_readw(port, reg) \
+ readw_relaxed((port)->regs + SPI_##reg)
+#define spi_writew(port, reg, value) \
+ writew_relaxed((value), (port)->regs + SPI_##reg)
+
+#define spi_readb(port, reg) \
+ readb_relaxed((port)->regs + SPI_##reg)
+#define spi_writeb(port, reg, value) \
+ writeb_relaxed((value), (port)->regs + SPI_##reg)
#endif
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
* cache operations; better heuristics consider wordsize and bitrate.
@@ -246,11 +311,14 @@ struct atmel_spi {
bool use_dma;
bool use_pdc;
+ bool use_cs_gpios;
/* dmaengine data */
struct atmel_spi_dma dma;
bool keep_cs;
bool cs_active;
+
+ u32 fifo_size;
};
/* Controller-specific per-slave state */
@@ -321,7 +389,8 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
}
mr = spi_readl(as, MR);
- gpio_set_value(asd->npcs_pin, active);
+ if (as->use_cs_gpios)
+ gpio_set_value(asd->npcs_pin, active);
} else {
u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
int i;
@@ -337,7 +406,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
mr = spi_readl(as, MR);
mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
- if (spi->chip_select != 0)
+ if (as->use_cs_gpios && spi->chip_select != 0)
gpio_set_value(asd->npcs_pin, active);
spi_writel(as, MR, mr);
}
@@ -366,7 +435,9 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
asd->npcs_pin, active ? " (low)" : "",
mr);
- if (atmel_spi_is_v2(as) || spi->chip_select != 0)
+ if (!as->use_cs_gpios)
+ spi_writel(as, CR, SPI_BIT(LASTXFER));
+ else if (atmel_spi_is_v2(as) || spi->chip_select != 0)
gpio_set_value(asd->npcs_pin, !active);
}
@@ -406,6 +477,20 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
slave_config->dst_maxburst = 1;
slave_config->device_fc = false;
+ /*
+ * This driver uses fixed peripheral select mode (PS bit set to '0' in
+ * the Mode Register).
+ * So according to the datasheet, when FIFOs are available (and
+ * enabled), the Transmit FIFO operates in Multiple Data Mode.
+ * In this mode, up to 2 data, not 4, can be written into the Transmit
+ * Data Register in a single access.
+ * However, the first data has to be written into the lowest 16 bits and
+ * the second data into the highest 16 bits of the Transmit
+ * Data Register. For 8bit data (the most frequent case), it would
+ * require to rework tx_buf so each data would actualy fit 16 bits.
+ * So we'd rather write only one data at the time. Hence the transmit
+ * path works the same whether FIFOs are available (and enabled) or not.
+ */
slave_config->direction = DMA_MEM_TO_DEV;
if (dmaengine_slave_config(as->dma.chan_tx, slave_config)) {
dev_err(&as->pdev->dev,
@@ -413,6 +498,14 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
err = -EINVAL;
}
+ /*
+ * This driver configures the spi controller for master mode (MSTR bit
+ * set to '1' in the Mode Register).
+ * So according to the datasheet, when FIFOs are available (and
+ * enabled), the Receive FIFO operates in Single Data Mode.
+ * So the receive path works the same whether FIFOs are available (and
+ * enabled) or not.
+ */
slave_config->direction = DMA_DEV_TO_MEM;
if (dmaengine_slave_config(as->dma.chan_rx, slave_config)) {
dev_err(&as->pdev->dev,
@@ -502,10 +595,10 @@ static void dma_callback(void *data)
}
/*
- * Next transfer using PIO.
+ * Next transfer using PIO without FIFO.
*/
-static void atmel_spi_next_xfer_pio(struct spi_master *master,
- struct spi_transfer *xfer)
+static void atmel_spi_next_xfer_single(struct spi_master *master,
+ struct spi_transfer *xfer)
{
struct atmel_spi *as = spi_master_get_devdata(master);
unsigned long xfer_pos = xfer->len - as->current_remaining_bytes;
@@ -538,6 +631,99 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master,
}
/*
+ * Next transfer using PIO with FIFO.
+ */
+static void atmel_spi_next_xfer_fifo(struct spi_master *master,
+ struct spi_transfer *xfer)
+{
+ struct atmel_spi *as = spi_master_get_devdata(master);
+ u32 current_remaining_data, num_data;
+ u32 offset = xfer->len - as->current_remaining_bytes;
+ const u16 *words = (const u16 *)((u8 *)xfer->tx_buf + offset);
+ const u8 *bytes = (const u8 *)((u8 *)xfer->tx_buf + offset);
+ u16 td0, td1;
+ u32 fifomr;
+
+ dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_fifo\n");
+
+ /* Compute the number of data to transfer in the current iteration */
+ current_remaining_data = ((xfer->bits_per_word > 8) ?
+ ((u32)as->current_remaining_bytes >> 1) :
+ (u32)as->current_remaining_bytes);
+ num_data = min(current_remaining_data, as->fifo_size);
+
+ /* Flush RX and TX FIFOs */
+ spi_writel(as, CR, SPI_BIT(RXFCLR) | SPI_BIT(TXFCLR));
+ while (spi_readl(as, FLR))
+ cpu_relax();
+
+ /* Set RX FIFO Threshold to the number of data to transfer */
+ fifomr = spi_readl(as, FMR);
+ spi_writel(as, FMR, SPI_BFINS(RXFTHRES, num_data, fifomr));
+
+ /* Clear FIFO flags in the Status Register, especially RXFTHF */
+ (void)spi_readl(as, SR);
+
+ /* Fill TX FIFO */
+ while (num_data >= 2) {
+ if (xfer->tx_buf) {
+ if (xfer->bits_per_word > 8) {
+ td0 = *words++;
+ td1 = *words++;
+ } else {
+ td0 = *bytes++;
+ td1 = *bytes++;
+ }
+ } else {
+ td0 = 0;
+ td1 = 0;
+ }
+
+ spi_writel(as, TDR, (td1 << 16) | td0);
+ num_data -= 2;
+ }
+
+ if (num_data) {
+ if (xfer->tx_buf) {
+ if (xfer->bits_per_word > 8)
+ td0 = *words++;
+ else
+ td0 = *bytes++;
+ } else {
+ td0 = 0;
+ }
+
+ spi_writew(as, TDR, td0);
+ num_data--;
+ }
+
+ dev_dbg(master->dev.parent,
+ " start fifo xfer %p: len %u tx %p rx %p bitpw %d\n",
+ xfer, xfer->len, xfer->tx_buf, xfer->rx_buf,
+ xfer->bits_per_word);
+
+ /*
+ * Enable RX FIFO Threshold Flag interrupt to be notified about
+ * transfer completion.
+ */
+ spi_writel(as, IER, SPI_BIT(RXFTHF) | SPI_BIT(OVRES));
+}
+
+/*
+ * Next transfer using PIO.
+ */
+static void atmel_spi_next_xfer_pio(struct spi_master *master,
+ struct spi_transfer *xfer)
+{
+ struct atmel_spi *as = spi_master_get_devdata(master);
+
+ if (as->fifo_size)
+ atmel_spi_next_xfer_fifo(master, xfer);
+ else
+ atmel_spi_next_xfer_single(master, xfer);
+}
+
+/*
* Submit next transfer for DMA.
*/
static int atmel_spi_next_xfer_dma_submit(struct spi_master *master,
@@ -839,13 +1025,8 @@ static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as)
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
}
-/* Called from IRQ
- *
- * Must update "current_remaining_bytes" to keep track of data
- * to transfer.
- */
static void
-atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
+atmel_spi_pump_single_data(struct atmel_spi *as, struct spi_transfer *xfer)
{
u8 *rxp;
u16 *rxp16;
@@ -872,6 +1053,57 @@ atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
}
}
+static void
+atmel_spi_pump_fifo_data(struct atmel_spi *as, struct spi_transfer *xfer)
+{
+ u32 fifolr = spi_readl(as, FLR);
+ u32 num_bytes, num_data = SPI_BFEXT(RXFL, fifolr);
+ u32 offset = xfer->len - as->current_remaining_bytes;
+ u16 *words = (u16 *)((u8 *)xfer->rx_buf + offset);
+ u8 *bytes = (u8 *)((u8 *)xfer->rx_buf + offset);
+ u16 rd; /* RD field is the lowest 16 bits of RDR */
+
+ /* Update the number of remaining bytes to transfer */
+ num_bytes = ((xfer->bits_per_word > 8) ?
+ (num_data << 1) :
+ num_data);
+
+ if (as->current_remaining_bytes > num_bytes)
+ as->current_remaining_bytes -= num_bytes;
+ else
+ as->current_remaining_bytes = 0;
+
+ /* Handle odd number of bytes when data are more than 8bit width */
+ if (xfer->bits_per_word > 8)
+ as->current_remaining_bytes &= ~0x1;
+
+ /* Read data */
+ while (num_data) {
+ rd = spi_readl(as, RDR);
+ if (xfer->rx_buf) {
+ if (xfer->bits_per_word > 8)
+ *words++ = rd;
+ else
+ *bytes++ = rd;
+ }
+ num_data--;
+ }
+}
+
+/* Called from IRQ
+ *
+ * Must update "current_remaining_bytes" to keep track of data
+ * to transfer.
+ */
+static void
+atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
+{
+ if (as->fifo_size)
+ atmel_spi_pump_fifo_data(as, xfer);
+ else
+ atmel_spi_pump_single_data(as, xfer);
+}
+
/* Interrupt
*
* No need for locking in this Interrupt handler: done_status is the
@@ -912,7 +1144,7 @@ atmel_spi_pio_interrupt(int irq, void *dev_id)
complete(&as->xfer_completion);
- } else if (pending & SPI_BIT(RDRF)) {
+ } else if (pending & (SPI_BIT(RDRF) | SPI_BIT(RXFTHF))) {
atmel_spi_lock(as);
if (as->current_remaining_bytes) {
@@ -996,6 +1228,8 @@ static int atmel_spi_setup(struct spi_device *spi)
csr |= SPI_BIT(CPOL);
if (!(spi->mode & SPI_CPHA))
csr |= SPI_BIT(NCPHA);
+ if (!as->use_cs_gpios)
+ csr |= SPI_BIT(CSAAT);
/* DLYBS is mostly irrelevant since we manage chipselect using GPIOs.
*
@@ -1009,7 +1243,9 @@ static int atmel_spi_setup(struct spi_device *spi)
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
npcs_pin = (unsigned long)spi->controller_data;
- if (gpio_is_valid(spi->cs_gpio))
+ if (!as->use_cs_gpios)
+ npcs_pin = spi->chip_select;
+ else if (gpio_is_valid(spi->cs_gpio))
npcs_pin = spi->cs_gpio;
asd = spi->controller_state;
@@ -1018,15 +1254,19 @@ static int atmel_spi_setup(struct spi_device *spi)
if (!asd)
return -ENOMEM;
- ret = gpio_request(npcs_pin, dev_name(&spi->dev));
- if (ret) {
- kfree(asd);
- return ret;
+ if (as->use_cs_gpios) {
+ ret = gpio_request(npcs_pin, dev_name(&spi->dev));
+ if (ret) {
+ kfree(asd);
+ return ret;
+ }
+
+ gpio_direction_output(npcs_pin,
+ !(spi->mode & SPI_CS_HIGH));
}
asd->npcs_pin = npcs_pin;
spi->controller_state = asd;
- gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
}
asd->csr = csr;
@@ -1338,6 +1578,13 @@ static int atmel_spi_probe(struct platform_device *pdev)
atmel_get_caps(as);
+ as->use_cs_gpios = true;
+ if (atmel_spi_is_v2(as) &&
+ !of_get_property(pdev->dev.of_node, "cs-gpios", NULL)) {
+ as->use_cs_gpios = false;
+ master->num_chipselect = 4;
+ }
+
as->use_dma = false;
as->use_pdc = false;
if (as->caps.has_dma_support) {
@@ -1380,6 +1627,13 @@ static int atmel_spi_probe(struct platform_device *pdev)
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
spi_writel(as, CR, SPI_BIT(SPIEN));
+ as->fifo_size = 0;
+ if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
+ &as->fifo_size)) {
+ dev_info(&pdev->dev, "Using FIFO (%u data)\n", as->fifo_size);
+ spi_writel(as, CR, SPI_BIT(FIFOEN));
+ }
+
/* go! */
dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n",
(unsigned long)regs->start, irq);
diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
index f63864a893c5..59705ab23577 100644
--- a/drivers/spi/spi-bcm2835.c
+++ b/drivers/spi/spi-bcm2835.c
@@ -20,18 +20,22 @@
* GNU General Public License for more details.
*/
+#include <asm/page.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_irq.h>
-#include <linux/of_gpio.h>
+#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
#include <linux/spi/spi.h>
/* SPI register offsets */
@@ -69,7 +73,8 @@
#define BCM2835_SPI_CS_CS_01 0x00000001
#define BCM2835_SPI_POLLING_LIMIT_US 30
-#define BCM2835_SPI_TIMEOUT_MS 30000
+#define BCM2835_SPI_POLLING_JIFFIES 2
+#define BCM2835_SPI_DMA_MIN_LENGTH 96
#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
| SPI_NO_CS | SPI_3WIRE)
@@ -83,6 +88,7 @@ struct bcm2835_spi {
u8 *rx_buf;
int tx_len;
int rx_len;
+ bool dma_pending;
};
static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
@@ -128,12 +134,15 @@ static void bcm2835_spi_reset_hw(struct spi_master *master)
/* Disable SPI interrupts and transfer */
cs &= ~(BCM2835_SPI_CS_INTR |
BCM2835_SPI_CS_INTD |
+ BCM2835_SPI_CS_DMAEN |
BCM2835_SPI_CS_TA);
/* and reset RX/TX FIFOS */
cs |= BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX;
/* and reset the SPI_HW */
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
+ /* as well as DLEN */
+ bcm2835_wr(bs, BCM2835_SPI_DLEN, 0);
}
static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
@@ -157,43 +166,6 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
- struct spi_device *spi,
- struct spi_transfer *tfr,
- u32 cs,
- unsigned long xfer_time_us)
-{
- struct bcm2835_spi *bs = spi_master_get_devdata(master);
- unsigned long timeout = jiffies +
- max(4 * xfer_time_us * HZ / 1000000, 2uL);
-
- /* enable HW block without interrupts */
- bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);
-
- /* set timeout to 4x the expected time, or 2 jiffies */
- /* loop until finished the transfer */
- while (bs->rx_len) {
- /* read from fifo as much as possible */
- bcm2835_rd_fifo(bs);
- /* fill in tx fifo as much as possible */
- bcm2835_wr_fifo(bs);
- /* if we still expect some data after the read,
- * check for a possible timeout
- */
- if (bs->rx_len && time_after(jiffies, timeout)) {
- /* Transfer complete - reset SPI HW */
- bcm2835_spi_reset_hw(master);
- /* and return timeout */
- return -ETIMEDOUT;
- }
- }
-
- /* Transfer complete - reset SPI HW */
- bcm2835_spi_reset_hw(master);
- /* and return without waiting for completion */
- return 0;
-}
-
static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr,
@@ -230,6 +202,329 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
return 1;
}
+/*
+ * DMA support
+ *
+ * this implementation has currently a few issues in so far as it does
+ * not work arrount limitations of the HW.
+ *
+ * the main one being that DMA transfers are limited to 16 bit
+ * (so 0 to 65535 bytes) by the SPI HW due to BCM2835_SPI_DLEN
+ *
+ * also we currently assume that the scatter-gather fragments are
+ * all multiple of 4 (except the last) - otherwise we would need
+ * to reset the FIFO before subsequent transfers...
+ * this also means that tx/rx transfers sg's need to be of equal size!
+ *
+ * there may be a few more border-cases we may need to address as well
+ * but unfortunately this would mean splitting up the scatter-gather
+ * list making it slightly unpractical...
+ */
+static void bcm2835_spi_dma_done(void *data)
+{
+ struct spi_master *master = data;
+ struct bcm2835_spi *bs = spi_master_get_devdata(master);
+
+ /* reset fifo and HW */
+ bcm2835_spi_reset_hw(master);
+
+ /* and terminate tx-dma as we do not have an irq for it
+ * because when the rx dma will terminate and this callback
+ * is called the tx-dma must have finished - can't get to this
+ * situation otherwise...
+ */
+ dmaengine_terminate_all(master->dma_tx);
+
+ /* mark as no longer pending */
+ bs->dma_pending = 0;
+
+ /* and mark as completed */;
+ complete(&master->xfer_completion);
+}
+
+static int bcm2835_spi_prepare_sg(struct spi_master *master,
+ struct spi_transfer *tfr,
+ bool is_tx)
+{
+ struct dma_chan *chan;
+ struct scatterlist *sgl;
+ unsigned int nents;
+ enum dma_transfer_direction dir;
+ unsigned long flags;
+
+ struct dma_async_tx_descriptor *desc;
+ dma_cookie_t cookie;
+
+ if (is_tx) {
+ dir = DMA_MEM_TO_DEV;
+ chan = master->dma_tx;
+ nents = tfr->tx_sg.nents;
+ sgl = tfr->tx_sg.sgl;
+ flags = 0 /* no tx interrupt */;
+
+ } else {
+ dir = DMA_DEV_TO_MEM;
+ chan = master->dma_rx;
+ nents = tfr->rx_sg.nents;
+ sgl = tfr->rx_sg.sgl;
+ flags = DMA_PREP_INTERRUPT;
+ }
+ /* prepare the channel */
+ desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
+ if (!desc)
+ return -EINVAL;
+
+ /* set callback for rx */
+ if (!is_tx) {
+ desc->callback = bcm2835_spi_dma_done;
+ desc->callback_param = master;
+ }
+
+ /* submit it to DMA-engine */
+ cookie = dmaengine_submit(desc);
+
+ return dma_submit_error(cookie);
+}
+
+static inline int bcm2835_check_sg_length(struct sg_table *sgt)
+{
+ int i;
+ struct scatterlist *sgl;
+
+ /* check that the sg entries are word-sized (except for last) */
+ for_each_sg(sgt->sgl, sgl, (int)sgt->nents - 1, i) {
+ if (sg_dma_len(sgl) % 4)
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int bcm2835_spi_transfer_one_dma(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *tfr,
+ u32 cs)
+{
+ struct bcm2835_spi *bs = spi_master_get_devdata(master);
+ int ret;
+
+ /* check that the scatter gather segments are all a multiple of 4 */
+ if (bcm2835_check_sg_length(&tfr->tx_sg) ||
+ bcm2835_check_sg_length(&tfr->rx_sg)) {
+ dev_warn_once(&spi->dev,
+ "scatter gather segment length is not a multiple of 4 - falling back to interrupt mode\n");
+ return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
+ }
+
+ /* setup tx-DMA */
+ ret = bcm2835_spi_prepare_sg(master, tfr, true);
+ if (ret)
+ return ret;
+
+ /* start TX early */
+ dma_async_issue_pending(master->dma_tx);
+
+ /* mark as dma pending */
+ bs->dma_pending = 1;
+
+ /* set the DMA length */
+ bcm2835_wr(bs, BCM2835_SPI_DLEN, tfr->len);
+
+ /* start the HW */
+ bcm2835_wr(bs, BCM2835_SPI_CS,
+ cs | BCM2835_SPI_CS_TA | BCM2835_SPI_CS_DMAEN);
+
+ /* setup rx-DMA late - to run transfers while
+ * mapping of the rx buffers still takes place
+ * this saves 10us or more.
+ */
+ ret = bcm2835_spi_prepare_sg(master, tfr, false);
+ if (ret) {
+ /* need to reset on errors */
+ dmaengine_terminate_all(master->dma_tx);
+ bcm2835_spi_reset_hw(master);
+ return ret;
+ }
+
+ /* start rx dma late */
+ dma_async_issue_pending(master->dma_rx);
+
+ /* wait for wakeup in framework */
+ return 1;
+}
+
+static bool bcm2835_spi_can_dma(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *tfr)
+{
+ /* only run for gpio_cs */
+ if (!gpio_is_valid(spi->cs_gpio))
+ return false;
+
+ /* we start DMA efforts only on bigger transfers */
+ if (tfr->len < BCM2835_SPI_DMA_MIN_LENGTH)
+ return false;
+
+ /* BCM2835_SPI_DLEN has defined a max transfer size as
+ * 16 bit, so max is 65535
+ * we can revisit this by using an alternative transfer
+ * method - ideally this would get done without any more
+ * interaction...
+ */
+ if (tfr->len > 65535) {
+ dev_warn_once(&spi->dev,
+ "transfer size of %d too big for dma-transfer\n",
+ tfr->len);
+ return false;
+ }
+
+ /* if we run rx/tx_buf with word aligned addresses then we are OK */
+ if ((((size_t)tfr->rx_buf & 3) == 0) &&
+ (((size_t)tfr->tx_buf & 3) == 0))
+ return true;
+
+ /* otherwise we only allow transfers within the same page
+ * to avoid wasting time on dma_mapping when it is not practical
+ */
+ if (((size_t)tfr->tx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) {
+ dev_warn_once(&spi->dev,
+ "Unaligned spi tx-transfer bridging page\n");
+ return false;
+ }
+ if (((size_t)tfr->rx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) {
+ dev_warn_once(&spi->dev,
+ "Unaligned spi tx-transfer bridging page\n");
+ return false;
+ }
+
+ /* return OK */
+ return true;
+}
+
+static void bcm2835_dma_release(struct spi_master *master)
+{
+ if (master->dma_tx) {
+ dmaengine_terminate_all(master->dma_tx);
+ dma_release_channel(master->dma_tx);
+ master->dma_tx = NULL;
+ }
+ if (master->dma_rx) {
+ dmaengine_terminate_all(master->dma_rx);
+ dma_release_channel(master->dma_rx);
+ master->dma_rx = NULL;
+ }
+}
+
+static void bcm2835_dma_init(struct spi_master *master, struct device *dev)
+{
+ struct dma_slave_config slave_config;
+ const __be32 *addr;
+ dma_addr_t dma_reg_base;
+ int ret;
+
+ /* base address in dma-space */
+ addr = of_get_address(master->dev.of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(dev, "could not get DMA-register address - not using dma mode\n");
+ goto err;
+ }
+ dma_reg_base = be32_to_cpup(addr);
+
+ /* get tx/rx dma */
+ master->dma_tx = dma_request_slave_channel(dev, "tx");
+ if (!master->dma_tx) {
+ dev_err(dev, "no tx-dma configuration found - not using dma mode\n");
+ goto err;
+ }
+ master->dma_rx = dma_request_slave_channel(dev, "rx");
+ if (!master->dma_rx) {
+ dev_err(dev, "no rx-dma configuration found - not using dma mode\n");
+ goto err_release;
+ }
+
+ /* configure DMAs */
+ slave_config.direction = DMA_MEM_TO_DEV;
+ slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
+ slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ ret = dmaengine_slave_config(master->dma_tx, &slave_config);
+ if (ret)
+ goto err_config;
+
+ slave_config.direction = DMA_DEV_TO_MEM;
+ slave_config.src_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
+ slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ ret = dmaengine_slave_config(master->dma_rx, &slave_config);
+ if (ret)
+ goto err_config;
+
+ /* all went well, so set can_dma */
+ master->can_dma = bcm2835_spi_can_dma;
+ master->max_dma_len = 65535; /* limitation by BCM2835_SPI_DLEN */
+ /* need to do TX AND RX DMA, so we need dummy buffers */
+ master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
+
+ return;
+
+err_config:
+ dev_err(dev, "issue configuring dma: %d - not using DMA mode\n",
+ ret);
+err_release:
+ bcm2835_dma_release(master);
+err:
+ return;
+}
+
+static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *tfr,
+ u32 cs,
+ unsigned long xfer_time_us)
+{
+ struct bcm2835_spi *bs = spi_master_get_devdata(master);
+ unsigned long timeout;
+
+ /* enable HW block without interrupts */
+ bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);
+
+ /* fill in the fifo before timeout calculations
+ * if we are interrupted here, then the data is
+ * getting transferred by the HW while we are interrupted
+ */
+ bcm2835_wr_fifo(bs);
+
+ /* set the timeout */
+ timeout = jiffies + BCM2835_SPI_POLLING_JIFFIES;
+
+ /* loop until finished the transfer */
+ while (bs->rx_len) {
+ /* fill in tx fifo with remaining data */
+ bcm2835_wr_fifo(bs);
+
+ /* read from fifo as much as possible */
+ bcm2835_rd_fifo(bs);
+
+ /* if there is still data pending to read
+ * then check the timeout
+ */
+ if (bs->rx_len && time_after(jiffies, timeout)) {
+ dev_dbg_ratelimited(&spi->dev,
+ "timeout period reached: jiffies: %lu remaining tx/rx: %d/%d - falling back to interrupt mode\n",
+ jiffies - timeout,
+ bs->tx_len, bs->rx_len);
+ /* fall back to interrupt mode */
+ return bcm2835_spi_transfer_one_irq(master, spi,
+ tfr, cs);
+ }
+ }
+
+ /* Transfer complete - reset SPI HW */
+ bcm2835_spi_reset_hw(master);
+ /* and return without waiting for completion */
+ return 0;
+}
+
static int bcm2835_spi_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr)
@@ -289,12 +584,26 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
return bcm2835_spi_transfer_one_poll(master, spi, tfr,
cs, xfer_time_us);
+ /* run in dma mode if conditions are right */
+ if (master->can_dma && bcm2835_spi_can_dma(master, spi, tfr))
+ return bcm2835_spi_transfer_one_dma(master, spi, tfr, cs);
+
+ /* run in interrupt-mode */
return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
}
static void bcm2835_spi_handle_err(struct spi_master *master,
struct spi_message *msg)
{
+ struct bcm2835_spi *bs = spi_master_get_devdata(master);
+
+ /* if an error occurred and we have an active dma, then terminate */
+ if (bs->dma_pending) {
+ dmaengine_terminate_all(master->dma_tx);
+ dmaengine_terminate_all(master->dma_rx);
+ bs->dma_pending = 0;
+ }
+ /* and reset */
bcm2835_spi_reset_hw(master);
}
@@ -464,6 +773,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
goto out_clk_disable;
}
+ bcm2835_dma_init(master, &pdev->dev);
+
/* initialise the hardware with the default polarities */
bcm2835_wr(bs, BCM2835_SPI_CS,
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
@@ -494,6 +805,8 @@ static int bcm2835_spi_remove(struct platform_device *pdev)
clk_disable_unprepare(bs->clk);
+ bcm2835_dma_release(master);
+
return 0;
}
diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c
index 5ef6638d5e8a..840a4984d365 100644
--- a/drivers/spi/spi-bitbang.c
+++ b/drivers/spi/spi-bitbang.c
@@ -180,7 +180,6 @@ int spi_bitbang_setup(struct spi_device *spi)
{
struct spi_bitbang_cs *cs = spi->controller_state;
struct spi_bitbang *bitbang;
- int retval;
unsigned long flags;
bitbang = spi_master_get_devdata(spi->master);
@@ -197,9 +196,11 @@ int spi_bitbang_setup(struct spi_device *spi)
if (!cs->txrx_word)
return -EINVAL;
- retval = bitbang->setup_transfer(spi, NULL);
- if (retval < 0)
- return retval;
+ if (bitbang->setup_transfer) {
+ int retval = bitbang->setup_transfer(spi, NULL);
+ if (retval < 0)
+ return retval;
+ }
dev_dbg(&spi->dev, "%s, %u nsec/bit\n", __func__, 2 * cs->nsecs);
@@ -295,9 +296,11 @@ static int spi_bitbang_transfer_one(struct spi_master *master,
/* init (-1) or override (1) transfer params */
if (do_setup != 0) {
- status = bitbang->setup_transfer(spi, t);
- if (status < 0)
- break;
+ if (bitbang->setup_transfer) {
+ status = bitbang->setup_transfer(spi, t);
+ if (status < 0)
+ break;
+ }
if (do_setup == -1)
do_setup = 0;
}
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index 5e991065f5b0..987afebea093 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -265,7 +265,7 @@ static inline int davinci_spi_get_prescale(struct davinci_spi *dspi,
ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz);
- if (ret < 3 || ret > 256)
+ if (ret < 1 || ret > 256)
return -EINVAL;
return ret - 1;
diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c
index 9c46a3058743..896add8cfd3b 100644
--- a/drivers/spi/spi-fsl-cpm.c
+++ b/drivers/spi/spi-fsl-cpm.c
@@ -24,6 +24,7 @@
#include <linux/of_address.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
+#include <linux/platform_device.h>
#include "spi-fsl-cpm.h"
#include "spi-fsl-lib.h"
@@ -269,17 +270,6 @@ static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
if (mspi->flags & SPI_CPM2) {
pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
out_be16(spi_base, pram_ofs);
- } else {
- struct spi_pram __iomem *pram = spi_base;
- u16 rpbase = in_be16(&pram->rpbase);
-
- /* Microcode relocation patch applied? */
- if (rpbase) {
- pram_ofs = rpbase;
- } else {
- pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
- out_be16(spi_base, pram_ofs);
- }
}
iounmap(spi_base);
@@ -292,7 +282,6 @@ int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
struct device_node *np = dev->of_node;
const u32 *iprop;
int size;
- unsigned long pram_ofs;
unsigned long bds_ofs;
if (!(mspi->flags & SPI_CPM_MODE))
@@ -319,8 +308,26 @@ int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
}
}
- pram_ofs = fsl_spi_cpm_get_pram(mspi);
- if (IS_ERR_VALUE(pram_ofs)) {
+ if (mspi->flags & SPI_CPM1) {
+ struct resource *res;
+ void *pram;
+
+ res = platform_get_resource(to_platform_device(dev),
+ IORESOURCE_MEM, 1);
+ pram = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pram))
+ mspi->pram = NULL;
+ else
+ mspi->pram = pram;
+ } else {
+ unsigned long pram_ofs = fsl_spi_cpm_get_pram(mspi);
+
+ if (IS_ERR_VALUE(pram_ofs))
+ mspi->pram = NULL;
+ else
+ mspi->pram = cpm_muram_addr(pram_ofs);
+ }
+ if (mspi->pram == NULL) {
dev_err(dev, "can't allocate spi parameter ram\n");
goto err_pram;
}
@@ -346,8 +353,6 @@ int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
goto err_dummy_rx;
}
- mspi->pram = cpm_muram_addr(pram_ofs);
-
mspi->tx_bd = cpm_muram_addr(bds_ofs);
mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
@@ -375,7 +380,8 @@ err_dummy_rx:
err_dummy_tx:
cpm_muram_free(bds_ofs);
err_bds:
- cpm_muram_free(pram_ofs);
+ if (!(mspi->flags & SPI_CPM1))
+ cpm_muram_free(cpm_muram_offset(mspi->pram));
err_pram:
fsl_spi_free_dummy_rx();
return -ENOMEM;
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 5fe54cda309f..86bcdd68c1fe 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -47,6 +48,7 @@
#define SPI_MCR_CLR_RXF (1 << 10)
#define SPI_TCR 0x08
+#define SPI_TCR_GET_TCNT(x) (((x) & 0xffff0000) >> 16)
#define SPI_CTAR(x) (0x0c + (((x) & 0x3) * 4))
#define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27)
@@ -67,9 +69,11 @@
#define SPI_SR 0x2c
#define SPI_SR_EOQF 0x10000000
+#define SPI_SR_TCFQF 0x80000000
#define SPI_RSER 0x30
#define SPI_RSER_EOQFE 0x10000000
+#define SPI_RSER_TCFQE 0x80000000
#define SPI_PUSHR 0x34
#define SPI_PUSHR_CONT (1 << 31)
@@ -102,12 +106,35 @@
#define SPI_CS_ASSERT 0x02
#define SPI_CS_DROP 0x04
+#define SPI_TCR_TCNT_MAX 0x10000
+
struct chip_data {
u32 mcr_val;
u32 ctar_val;
u16 void_write_data;
};
+enum dspi_trans_mode {
+ DSPI_EOQ_MODE = 0,
+ DSPI_TCFQ_MODE,
+};
+
+struct fsl_dspi_devtype_data {
+ enum dspi_trans_mode trans_mode;
+};
+
+static const struct fsl_dspi_devtype_data vf610_data = {
+ .trans_mode = DSPI_EOQ_MODE,
+};
+
+static const struct fsl_dspi_devtype_data ls1021a_v1_data = {
+ .trans_mode = DSPI_TCFQ_MODE,
+};
+
+static const struct fsl_dspi_devtype_data ls2085a_data = {
+ .trans_mode = DSPI_TCFQ_MODE,
+};
+
struct fsl_dspi {
struct spi_master *master;
struct platform_device *pdev;
@@ -128,9 +155,12 @@ struct fsl_dspi {
u8 cs;
u16 void_write_data;
u32 cs_change;
+ struct fsl_dspi_devtype_data *devtype_data;
wait_queue_head_t waitq;
u32 waitflags;
+
+ u32 spi_tcnt;
};
static inline int is_double_byte_mode(struct fsl_dspi *dspi)
@@ -213,63 +243,60 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns,
}
}
-static int dspi_transfer_write(struct fsl_dspi *dspi)
+static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word)
{
- int tx_count = 0;
- int tx_word;
u16 d16;
- u8 d8;
- u32 dspi_pushr = 0;
- int first = 1;
- tx_word = is_double_byte_mode(dspi);
+ if (!(dspi->dataflags & TRAN_STATE_TX_VOID))
+ d16 = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx;
+ else
+ d16 = dspi->void_write_data;
- /* If we are in word mode, but only have a single byte to transfer
- * then switch to byte mode temporarily. Will switch back at the
- * end of the transfer.
- */
- if (tx_word && (dspi->len == 1)) {
- dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
- regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
- SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
- tx_word = 0;
- }
+ dspi->tx += tx_word + 1;
+ dspi->len -= tx_word + 1;
- while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) {
- if (tx_word) {
- if (dspi->len == 1)
- break;
+ return SPI_PUSHR_TXDATA(d16) |
+ SPI_PUSHR_PCS(dspi->cs) |
+ SPI_PUSHR_CTAS(dspi->cs) |
+ SPI_PUSHR_CONT;
+}
- if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) {
- d16 = *(u16 *)dspi->tx;
- dspi->tx += 2;
- } else {
- d16 = dspi->void_write_data;
- }
+static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word)
+{
+ u16 d;
+ unsigned int val;
- dspi_pushr = SPI_PUSHR_TXDATA(d16) |
- SPI_PUSHR_PCS(dspi->cs) |
- SPI_PUSHR_CTAS(dspi->cs) |
- SPI_PUSHR_CONT;
+ regmap_read(dspi->regmap, SPI_POPR, &val);
+ d = SPI_POPR_RXDATA(val);
- dspi->len -= 2;
- } else {
- if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) {
+ if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
+ rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d);
- d8 = *(u8 *)dspi->tx;
- dspi->tx++;
- } else {
- d8 = (u8)dspi->void_write_data;
- }
+ dspi->rx += rx_word + 1;
+}
- dspi_pushr = SPI_PUSHR_TXDATA(d8) |
- SPI_PUSHR_PCS(dspi->cs) |
- SPI_PUSHR_CTAS(dspi->cs) |
- SPI_PUSHR_CONT;
+static int dspi_eoq_write(struct fsl_dspi *dspi)
+{
+ int tx_count = 0;
+ int tx_word;
+ u32 dspi_pushr = 0;
+
+ tx_word = is_double_byte_mode(dspi);
- dspi->len--;
+ while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) {
+ /* If we are in word mode, only have a single byte to transfer
+ * switch to byte mode temporarily. Will switch back at the
+ * end of the transfer.
+ */
+ if (tx_word && (dspi->len == 1)) {
+ dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
+ regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
+ SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
+ tx_word = 0;
}
+ dspi_pushr = dspi_data_to_pushr(dspi, tx_word);
+
if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) {
/* last transfer in the transfer */
dspi_pushr |= SPI_PUSHR_EOQ;
@@ -278,11 +305,6 @@ static int dspi_transfer_write(struct fsl_dspi *dspi)
} else if (tx_word && (dspi->len == 1))
dspi_pushr |= SPI_PUSHR_EOQ;
- if (first) {
- first = 0;
- dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */
- }
-
regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr);
tx_count++;
@@ -291,40 +313,55 @@ static int dspi_transfer_write(struct fsl_dspi *dspi)
return tx_count * (tx_word + 1);
}
-static int dspi_transfer_read(struct fsl_dspi *dspi)
+static int dspi_eoq_read(struct fsl_dspi *dspi)
{
int rx_count = 0;
int rx_word = is_double_byte_mode(dspi);
- u16 d;
while ((dspi->rx < dspi->rx_end)
&& (rx_count < DSPI_FIFO_SIZE)) {
- if (rx_word) {
- unsigned int val;
+ if (rx_word && (dspi->rx_end - dspi->rx) == 1)
+ rx_word = 0;
- if ((dspi->rx_end - dspi->rx) == 1)
- break;
+ dspi_data_from_popr(dspi, rx_word);
+ rx_count++;
+ }
- regmap_read(dspi->regmap, SPI_POPR, &val);
- d = SPI_POPR_RXDATA(val);
+ return rx_count;
+}
- if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
- *(u16 *)dspi->rx = d;
- dspi->rx += 2;
+static int dspi_tcfq_write(struct fsl_dspi *dspi)
+{
+ int tx_word;
+ u32 dspi_pushr = 0;
- } else {
- unsigned int val;
+ tx_word = is_double_byte_mode(dspi);
- regmap_read(dspi->regmap, SPI_POPR, &val);
- d = SPI_POPR_RXDATA(val);
- if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
- *(u8 *)dspi->rx = d;
- dspi->rx++;
- }
- rx_count++;
+ if (tx_word && (dspi->len == 1)) {
+ dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
+ regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
+ SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
+ tx_word = 0;
}
- return rx_count;
+ dspi_pushr = dspi_data_to_pushr(dspi, tx_word);
+
+ if ((dspi->cs_change) && (!dspi->len))
+ dspi_pushr &= ~SPI_PUSHR_CONT;
+
+ regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr);
+
+ return tx_word + 1;
+}
+
+static void dspi_tcfq_read(struct fsl_dspi *dspi)
+{
+ int rx_word = is_double_byte_mode(dspi);
+
+ if (rx_word && (dspi->rx_end - dspi->rx) == 1)
+ rx_word = 0;
+
+ dspi_data_from_popr(dspi, rx_word);
}
static int dspi_transfer_one_message(struct spi_master *master,
@@ -334,6 +371,12 @@ static int dspi_transfer_one_message(struct spi_master *master,
struct spi_device *spi = message->spi;
struct spi_transfer *transfer;
int status = 0;
+ enum dspi_trans_mode trans_mode;
+ u32 spi_tcr;
+
+ regmap_read(dspi->regmap, SPI_TCR, &spi_tcr);
+ dspi->spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr);
+
message->actual_length = 0;
list_for_each_entry(transfer, &message->transfers, transfer_list) {
@@ -341,10 +384,10 @@ static int dspi_transfer_one_message(struct spi_master *master,
dspi->cur_msg = message;
dspi->cur_chip = spi_get_ctldata(spi);
dspi->cs = spi->chip_select;
+ dspi->cs_change = 0;
if (dspi->cur_transfer->transfer_list.next
== &dspi->cur_msg->transfers)
- transfer->cs_change = 1;
- dspi->cs_change = transfer->cs_change;
+ dspi->cs_change = 1;
dspi->void_write_data = dspi->cur_chip->void_write_data;
dspi->dataflags = 0;
@@ -370,8 +413,22 @@ static int dspi_transfer_one_message(struct spi_master *master,
regmap_write(dspi->regmap, SPI_CTAR(dspi->cs),
dspi->cur_chip->ctar_val);
- regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE);
- message->actual_length += dspi_transfer_write(dspi);
+ trans_mode = dspi->devtype_data->trans_mode;
+ switch (trans_mode) {
+ case DSPI_EOQ_MODE:
+ regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE);
+ dspi_eoq_write(dspi);
+ break;
+ case DSPI_TCFQ_MODE:
+ regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE);
+ dspi_tcfq_write(dspi);
+ break;
+ default:
+ dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
+ trans_mode);
+ status = -EINVAL;
+ goto out;
+ }
if (wait_event_interruptible(dspi->waitq, dspi->waitflags))
dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n");
@@ -381,6 +438,7 @@ static int dspi_transfer_one_message(struct spi_master *master,
udelay(transfer->delay_usecs);
}
+out:
message->status = status;
spi_finalize_current_message(master);
@@ -460,27 +518,89 @@ static void dspi_cleanup(struct spi_device *spi)
static irqreturn_t dspi_interrupt(int irq, void *dev_id)
{
struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id;
-
struct spi_message *msg = dspi->cur_msg;
+ enum dspi_trans_mode trans_mode;
+ u32 spi_sr, spi_tcr;
+ u32 spi_tcnt, tcnt_diff;
+ int tx_word;
- regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF);
- dspi_transfer_read(dspi);
-
- if (!dspi->len) {
+ regmap_read(dspi->regmap, SPI_SR, &spi_sr);
+ regmap_write(dspi->regmap, SPI_SR, spi_sr);
+
+
+ if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) {
+ tx_word = is_double_byte_mode(dspi);
+
+ regmap_read(dspi->regmap, SPI_TCR, &spi_tcr);
+ spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr);
+ /*
+ * The width of SPI Transfer Counter in SPI_TCR is 16bits,
+ * so the max couner is 65535. When the counter reach 65535,
+ * it will wrap around, counter reset to zero.
+ * spi_tcnt my be less than dspi->spi_tcnt, it means the
+ * counter already wrapped around.
+ * SPI Transfer Counter is a counter of transmitted frames.
+ * The size of frame maybe two bytes.
+ */
+ tcnt_diff = ((spi_tcnt + SPI_TCR_TCNT_MAX) - dspi->spi_tcnt)
+ % SPI_TCR_TCNT_MAX;
+ tcnt_diff *= (tx_word + 1);
if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM)
- regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
- SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16));
+ tcnt_diff--;
+
+ msg->actual_length += tcnt_diff;
+
+ dspi->spi_tcnt = spi_tcnt;
+
+ trans_mode = dspi->devtype_data->trans_mode;
+ switch (trans_mode) {
+ case DSPI_EOQ_MODE:
+ dspi_eoq_read(dspi);
+ break;
+ case DSPI_TCFQ_MODE:
+ dspi_tcfq_read(dspi);
+ break;
+ default:
+ dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
+ trans_mode);
+ return IRQ_HANDLED;
+ }
- dspi->waitflags = 1;
- wake_up_interruptible(&dspi->waitq);
- } else
- msg->actual_length += dspi_transfer_write(dspi);
+ if (!dspi->len) {
+ if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) {
+ regmap_update_bits(dspi->regmap,
+ SPI_CTAR(dspi->cs),
+ SPI_FRAME_BITS_MASK,
+ SPI_FRAME_BITS(16));
+ dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM;
+ }
+
+ dspi->waitflags = 1;
+ wake_up_interruptible(&dspi->waitq);
+ } else {
+ switch (trans_mode) {
+ case DSPI_EOQ_MODE:
+ dspi_eoq_write(dspi);
+ break;
+ case DSPI_TCFQ_MODE:
+ dspi_tcfq_write(dspi);
+ break;
+ default:
+ dev_err(&dspi->pdev->dev,
+ "unsupported trans_mode %u\n",
+ trans_mode);
+ }
+ }
+ }
return IRQ_HANDLED;
}
static const struct of_device_id fsl_dspi_dt_ids[] = {
- { .compatible = "fsl,vf610-dspi", .data = NULL, },
+ { .compatible = "fsl,vf610-dspi", .data = (void *)&vf610_data, },
+ { .compatible = "fsl,ls1021a-v1.0-dspi",
+ .data = (void *)&ls1021a_v1_data, },
+ { .compatible = "fsl,ls2085a-dspi", .data = (void *)&ls2085a_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids);
@@ -494,6 +614,8 @@ static int dspi_suspend(struct device *dev)
spi_master_suspend(master);
clk_disable_unprepare(dspi->clk);
+ pinctrl_pm_select_sleep_state(dev);
+
return 0;
}
@@ -502,6 +624,8 @@ static int dspi_resume(struct device *dev)
struct spi_master *master = dev_get_drvdata(dev);
struct fsl_dspi *dspi = spi_master_get_devdata(master);
+ pinctrl_pm_select_default_state(dev);
+
clk_prepare_enable(dspi->clk);
spi_master_resume(master);
@@ -526,6 +650,8 @@ static int dspi_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *base;
int ret = 0, cs_num, bus_num;
+ const struct of_device_id *of_id =
+ of_match_device(fsl_dspi_dt_ids, &pdev->dev);
master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
if (!master)
@@ -559,6 +685,13 @@ static int dspi_probe(struct platform_device *pdev)
}
master->bus_num = bus_num;
+ dspi->devtype_data = (struct fsl_dspi_devtype_data *)of_id->data;
+ if (!dspi->devtype_data) {
+ dev_err(&pdev->dev, "can't get devtype_data\n");
+ ret = -EFAULT;
+ goto out_master_put;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base)) {
@@ -566,7 +699,7 @@ static int dspi_probe(struct platform_device *pdev)
goto out_master_put;
}
- dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base,
+ dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
&dspi_regmap_config);
if (IS_ERR(dspi->regmap)) {
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index d0a73a09a9bd..d3f05a0525a4 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -359,14 +359,16 @@ static void fsl_espi_rw_trans(struct spi_message *m,
struct fsl_espi_transfer *trans, u8 *rx_buff)
{
struct fsl_espi_transfer *espi_trans = trans;
- unsigned int n_tx = espi_trans->n_tx;
- unsigned int n_rx = espi_trans->n_rx;
+ unsigned int total_len = espi_trans->len;
struct spi_transfer *t;
u8 *local_buf;
u8 *rx_buf = rx_buff;
unsigned int trans_len;
unsigned int addr;
- int i, pos, loop;
+ unsigned int tx_only;
+ unsigned int rx_pos = 0;
+ unsigned int pos;
+ int i, loop;
local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
if (!local_buf) {
@@ -374,36 +376,48 @@ static void fsl_espi_rw_trans(struct spi_message *m,
return;
}
- for (pos = 0, loop = 0; pos < n_rx; pos += trans_len, loop++) {
- trans_len = n_rx - pos;
- if (trans_len > SPCOM_TRANLEN_MAX - n_tx)
- trans_len = SPCOM_TRANLEN_MAX - n_tx;
+ for (pos = 0, loop = 0; pos < total_len; pos += trans_len, loop++) {
+ trans_len = total_len - pos;
i = 0;
+ tx_only = 0;
list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->tx_buf) {
memcpy(local_buf + i, t->tx_buf, t->len);
i += t->len;
+ if (!t->rx_buf)
+ tx_only += t->len;
}
}
+ /* Add additional TX bytes to compensate SPCOM_TRANLEN_MAX */
+ if (loop > 0)
+ trans_len += tx_only;
+
+ if (trans_len > SPCOM_TRANLEN_MAX)
+ trans_len = SPCOM_TRANLEN_MAX;
+
+ /* Update device offset */
if (pos > 0) {
addr = fsl_espi_cmd2addr(local_buf);
- addr += pos;
+ addr += rx_pos;
fsl_espi_addr2cmd(addr, local_buf);
}
- espi_trans->n_tx = n_tx;
- espi_trans->n_rx = trans_len;
- espi_trans->len = trans_len + n_tx;
+ espi_trans->len = trans_len;
espi_trans->tx_buf = local_buf;
espi_trans->rx_buf = local_buf;
fsl_espi_do_trans(m, espi_trans);
- memcpy(rx_buf + pos, espi_trans->rx_buf + n_tx, trans_len);
+ /* If there is at least one RX byte then copy it to rx_buf */
+ if (tx_only < SPCOM_TRANLEN_MAX)
+ memcpy(rx_buf + rx_pos, espi_trans->rx_buf + tx_only,
+ trans_len - tx_only);
+
+ rx_pos += trans_len - tx_only;
if (loop > 0)
- espi_trans->actual_length += espi_trans->len - n_tx;
+ espi_trans->actual_length += espi_trans->len - tx_only;
else
espi_trans->actual_length += espi_trans->len;
}
@@ -418,6 +432,7 @@ static int fsl_espi_do_one_msg(struct spi_master *master,
u8 *rx_buf = NULL;
unsigned int n_tx = 0;
unsigned int n_rx = 0;
+ unsigned int xfer_len = 0;
struct fsl_espi_transfer espi_trans;
list_for_each_entry(t, &m->transfers, transfer_list) {
@@ -427,11 +442,13 @@ static int fsl_espi_do_one_msg(struct spi_master *master,
n_rx += t->len;
rx_buf = t->rx_buf;
}
+ if ((t->tx_buf) || (t->rx_buf))
+ xfer_len += t->len;
}
espi_trans.n_tx = n_tx;
espi_trans.n_rx = n_rx;
- espi_trans.len = n_tx + n_rx;
+ espi_trans.len = xfer_len;
espi_trans.actual_length = 0;
espi_trans.status = 0;
@@ -544,9 +561,13 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
/* spin until TX is done */
ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg(
- &reg_base->event)) & SPIE_NF) == 0, 1000, 0);
+ &reg_base->event)) & SPIE_NF), 1000, 0);
if (!ret) {
dev_err(mspi->dev, "tired waiting for SPIE_NF\n");
+
+ /* Clear the SPIE bits */
+ mpc8xxx_spi_write_reg(&reg_base->event, events);
+ complete(&mspi->done);
return;
}
}
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index f08e812b2984..eb7d3a6fb14c 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -674,7 +674,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
.devtype = IMX51_ECSPI,
};
-static struct platform_device_id spi_imx_devtype[] = {
+static const struct platform_device_id spi_imx_devtype[] = {
{
.name = "imx1-cspi",
.driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data,
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 4df8942058de..58673841286c 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -35,6 +35,7 @@
#include <linux/gcd.h>
#include <linux/spi/spi.h>
+#include <linux/gpio.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
@@ -242,17 +243,27 @@ static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable)
mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0);
}
-static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active)
+static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
{
u32 l;
- l = mcspi_cached_chconf0(spi);
- if (cs_active)
- l |= OMAP2_MCSPI_CHCONF_FORCE;
- else
- l &= ~OMAP2_MCSPI_CHCONF_FORCE;
+ /* The controller handles the inverted chip selects
+ * using the OMAP2_MCSPI_CHCONF_EPOL bit so revert
+ * the inversion from the core spi_set_cs function.
+ */
+ if (spi->mode & SPI_CS_HIGH)
+ enable = !enable;
- mcspi_write_chconf0(spi, l);
+ if (spi->controller_state) {
+ l = mcspi_cached_chconf0(spi);
+
+ if (enable)
+ l &= ~OMAP2_MCSPI_CHCONF_FORCE;
+ else
+ l |= OMAP2_MCSPI_CHCONF_FORCE;
+
+ mcspi_write_chconf0(spi, l);
+ }
}
static void omap2_mcspi_set_master_mode(struct spi_master *master)
@@ -1011,6 +1022,15 @@ static int omap2_mcspi_setup(struct spi_device *spi)
return ret;
}
+ if (gpio_is_valid(spi->cs_gpio)) {
+ ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
+ if (ret) {
+ dev_err(&spi->dev, "failed to request gpio\n");
+ return ret;
+ }
+ gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
+ }
+
ret = pm_runtime_get_sync(mcspi->dev);
if (ret < 0)
return ret;
@@ -1050,9 +1070,13 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
mcspi_dma->dma_tx = NULL;
}
}
+
+ if (gpio_is_valid(spi->cs_gpio))
+ gpio_free(spi->cs_gpio);
}
-static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
+static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi,
+ struct spi_device *spi, struct spi_transfer *t)
{
/* We only enable one channel at a time -- the one whose message is
@@ -1062,18 +1086,14 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
* chipselect with the FORCE bit ... CS != channel enable.
*/
- struct spi_device *spi;
- struct spi_transfer *t = NULL;
struct spi_master *master;
struct omap2_mcspi_dma *mcspi_dma;
- int cs_active = 0;
struct omap2_mcspi_cs *cs;
struct omap2_mcspi_device_config *cd;
int par_override = 0;
int status = 0;
u32 chconf;
- spi = m->spi;
master = spi->master;
mcspi_dma = mcspi->dma_channels + spi->chip_select;
cs = spi->controller_state;
@@ -1090,103 +1110,84 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
par_override = 1;
omap2_mcspi_set_enable(spi, 0);
- list_for_each_entry(t, &m->transfers, transfer_list) {
- if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
- status = -EINVAL;
- break;
- }
- if (par_override ||
- (t->speed_hz != spi->max_speed_hz) ||
- (t->bits_per_word != spi->bits_per_word)) {
- par_override = 1;
- status = omap2_mcspi_setup_transfer(spi, t);
- if (status < 0)
- break;
- if (t->speed_hz == spi->max_speed_hz &&
- t->bits_per_word == spi->bits_per_word)
- par_override = 0;
- }
- if (cd && cd->cs_per_word) {
- chconf = mcspi->ctx.modulctrl;
- chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE;
- mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf);
- mcspi->ctx.modulctrl =
- mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL);
- }
+ if (gpio_is_valid(spi->cs_gpio))
+ omap2_mcspi_set_cs(spi, spi->mode & SPI_CS_HIGH);
- if (!cs_active) {
- omap2_mcspi_force_cs(spi, 1);
- cs_active = 1;
- }
-
- chconf = mcspi_cached_chconf0(spi);
- chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
- chconf &= ~OMAP2_MCSPI_CHCONF_TURBO;
+ if (par_override ||
+ (t->speed_hz != spi->max_speed_hz) ||
+ (t->bits_per_word != spi->bits_per_word)) {
+ par_override = 1;
+ status = omap2_mcspi_setup_transfer(spi, t);
+ if (status < 0)
+ goto out;
+ if (t->speed_hz == spi->max_speed_hz &&
+ t->bits_per_word == spi->bits_per_word)
+ par_override = 0;
+ }
+ if (cd && cd->cs_per_word) {
+ chconf = mcspi->ctx.modulctrl;
+ chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE;
+ mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf);
+ mcspi->ctx.modulctrl =
+ mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL);
+ }
- if (t->tx_buf == NULL)
- chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;
- else if (t->rx_buf == NULL)
- chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;
-
- if (cd && cd->turbo_mode && t->tx_buf == NULL) {
- /* Turbo mode is for more than one word */
- if (t->len > ((cs->word_len + 7) >> 3))
- chconf |= OMAP2_MCSPI_CHCONF_TURBO;
- }
+ chconf = mcspi_cached_chconf0(spi);
+ chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
+ chconf &= ~OMAP2_MCSPI_CHCONF_TURBO;
+
+ if (t->tx_buf == NULL)
+ chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;
+ else if (t->rx_buf == NULL)
+ chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;
+
+ if (cd && cd->turbo_mode && t->tx_buf == NULL) {
+ /* Turbo mode is for more than one word */
+ if (t->len > ((cs->word_len + 7) >> 3))
+ chconf |= OMAP2_MCSPI_CHCONF_TURBO;
+ }
- mcspi_write_chconf0(spi, chconf);
+ mcspi_write_chconf0(spi, chconf);
- if (t->len) {
- unsigned count;
+ if (t->len) {
+ unsigned count;
- if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
- (m->is_dma_mapped || t->len >= DMA_MIN_BYTES))
- omap2_mcspi_set_fifo(spi, t, 1);
+ if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
+ (t->len >= DMA_MIN_BYTES))
+ omap2_mcspi_set_fifo(spi, t, 1);
- omap2_mcspi_set_enable(spi, 1);
+ omap2_mcspi_set_enable(spi, 1);
- /* RX_ONLY mode needs dummy data in TX reg */
- if (t->tx_buf == NULL)
- writel_relaxed(0, cs->base
- + OMAP2_MCSPI_TX0);
+ /* RX_ONLY mode needs dummy data in TX reg */
+ if (t->tx_buf == NULL)
+ writel_relaxed(0, cs->base
+ + OMAP2_MCSPI_TX0);
- if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
- (m->is_dma_mapped || t->len >= DMA_MIN_BYTES))
- count = omap2_mcspi_txrx_dma(spi, t);
- else
- count = omap2_mcspi_txrx_pio(spi, t);
- m->actual_length += count;
+ if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
+ (t->len >= DMA_MIN_BYTES))
+ count = omap2_mcspi_txrx_dma(spi, t);
+ else
+ count = omap2_mcspi_txrx_pio(spi, t);
- if (count != t->len) {
- status = -EIO;
- break;
- }
+ if (count != t->len) {
+ status = -EIO;
+ goto out;
}
+ }
- if (t->delay_usecs)
- udelay(t->delay_usecs);
-
- /* ignore the "leave it on after last xfer" hint */
- if (t->cs_change) {
- omap2_mcspi_force_cs(spi, 0);
- cs_active = 0;
- }
+ omap2_mcspi_set_enable(spi, 0);
- omap2_mcspi_set_enable(spi, 0);
+ if (mcspi->fifo_depth > 0)
+ omap2_mcspi_set_fifo(spi, t, 0);
- if (mcspi->fifo_depth > 0)
- omap2_mcspi_set_fifo(spi, t, 0);
- }
+out:
/* Restore defaults if they were overriden */
if (par_override) {
par_override = 0;
status = omap2_mcspi_setup_transfer(spi, NULL);
}
- if (cs_active)
- omap2_mcspi_force_cs(spi, 0);
-
if (cd && cd->cs_per_word) {
chconf = mcspi->ctx.modulctrl;
chconf |= OMAP2_MCSPI_MODULCTRL_SINGLE;
@@ -1197,70 +1198,64 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
omap2_mcspi_set_enable(spi, 0);
+ if (gpio_is_valid(spi->cs_gpio))
+ omap2_mcspi_set_cs(spi, !(spi->mode & SPI_CS_HIGH));
+
if (mcspi->fifo_depth > 0 && t)
omap2_mcspi_set_fifo(spi, t, 0);
- m->status = status;
+ return status;
}
-static int omap2_mcspi_transfer_one_message(struct spi_master *master,
- struct spi_message *m)
+static int omap2_mcspi_transfer_one(struct spi_master *master,
+ struct spi_device *spi, struct spi_transfer *t)
{
- struct spi_device *spi;
struct omap2_mcspi *mcspi;
struct omap2_mcspi_dma *mcspi_dma;
- struct spi_transfer *t;
+ const void *tx_buf = t->tx_buf;
+ void *rx_buf = t->rx_buf;
+ unsigned len = t->len;
- spi = m->spi;
mcspi = spi_master_get_devdata(master);
mcspi_dma = mcspi->dma_channels + spi->chip_select;
- m->actual_length = 0;
- m->status = 0;
-
- list_for_each_entry(t, &m->transfers, transfer_list) {
- const void *tx_buf = t->tx_buf;
- void *rx_buf = t->rx_buf;
- unsigned len = t->len;
-
- if ((len && !(rx_buf || tx_buf))) {
- dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
- t->speed_hz,
- len,
- tx_buf ? "tx" : "",
- rx_buf ? "rx" : "",
- t->bits_per_word);
- return -EINVAL;
- }
- if (m->is_dma_mapped || len < DMA_MIN_BYTES)
- continue;
+ if ((len && !(rx_buf || tx_buf))) {
+ dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
+ t->speed_hz,
+ len,
+ tx_buf ? "tx" : "",
+ rx_buf ? "rx" : "",
+ t->bits_per_word);
+ return -EINVAL;
+ }
- if (mcspi_dma->dma_tx && tx_buf != NULL) {
- t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf,
- len, DMA_TO_DEVICE);
- if (dma_mapping_error(mcspi->dev, t->tx_dma)) {
- dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
- 'T', len);
- return -EINVAL;
- }
+ if (len < DMA_MIN_BYTES)
+ goto skip_dma_map;
+
+ if (mcspi_dma->dma_tx && tx_buf != NULL) {
+ t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf,
+ len, DMA_TO_DEVICE);
+ if (dma_mapping_error(mcspi->dev, t->tx_dma)) {
+ dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
+ 'T', len);
+ return -EINVAL;
}
- if (mcspi_dma->dma_rx && rx_buf != NULL) {
- t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(mcspi->dev, t->rx_dma)) {
- dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
- 'R', len);
- if (tx_buf != NULL)
- dma_unmap_single(mcspi->dev, t->tx_dma,
- len, DMA_TO_DEVICE);
- return -EINVAL;
- }
+ }
+ if (mcspi_dma->dma_rx && rx_buf != NULL) {
+ t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(mcspi->dev, t->rx_dma)) {
+ dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
+ 'R', len);
+ if (tx_buf != NULL)
+ dma_unmap_single(mcspi->dev, t->tx_dma,
+ len, DMA_TO_DEVICE);
+ return -EINVAL;
}
}
- omap2_mcspi_work(mcspi, m);
- spi_finalize_current_message(master);
- return 0;
+skip_dma_map:
+ return omap2_mcspi_work_one(mcspi, spi, t);
}
static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
@@ -1339,7 +1334,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
master->setup = omap2_mcspi_setup;
master->auto_runtime_pm = true;
- master->transfer_one_message = omap2_mcspi_transfer_one_message;
+ master->transfer_one = omap2_mcspi_transfer_one;
+ master->set_cs = omap2_mcspi_set_cs;
master->cleanup = omap2_mcspi_cleanup;
master->dev.of_node = node;
master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ;
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index 861664776672..8cad107a5b3f 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -61,6 +61,12 @@ enum orion_spi_type {
struct orion_spi_dev {
enum orion_spi_type typ;
+ /*
+ * min_divisor and max_hz should be exclusive, the only we can
+ * have both is for managing the armada-370-spi case with old
+ * device tree
+ */
+ unsigned long max_hz;
unsigned int min_divisor;
unsigned int max_divisor;
u32 prescale_mask;
@@ -385,16 +391,54 @@ static const struct orion_spi_dev orion_spi_dev_data = {
.prescale_mask = ORION_SPI_CLK_PRESCALE_MASK,
};
-static const struct orion_spi_dev armada_spi_dev_data = {
+static const struct orion_spi_dev armada_370_spi_dev_data = {
.typ = ARMADA_SPI,
- .min_divisor = 1,
+ .min_divisor = 4,
+ .max_divisor = 1920,
+ .max_hz = 50000000,
+ .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
+};
+
+static const struct orion_spi_dev armada_xp_spi_dev_data = {
+ .typ = ARMADA_SPI,
+ .max_hz = 50000000,
+ .max_divisor = 1920,
+ .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
+};
+
+static const struct orion_spi_dev armada_375_spi_dev_data = {
+ .typ = ARMADA_SPI,
+ .min_divisor = 15,
.max_divisor = 1920,
.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
};
static const struct of_device_id orion_spi_of_match_table[] = {
- { .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, },
- { .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, },
+ {
+ .compatible = "marvell,orion-spi",
+ .data = &orion_spi_dev_data,
+ },
+ {
+ .compatible = "marvell,armada-370-spi",
+ .data = &armada_370_spi_dev_data,
+ },
+ {
+ .compatible = "marvell,armada-375-spi",
+ .data = &armada_375_spi_dev_data,
+ },
+ {
+ .compatible = "marvell,armada-380-spi",
+ .data = &armada_xp_spi_dev_data,
+ },
+ {
+ .compatible = "marvell,armada-390-spi",
+ .data = &armada_xp_spi_dev_data,
+ },
+ {
+ .compatible = "marvell,armada-xp-spi",
+ .data = &armada_xp_spi_dev_data,
+ },
+
{}
};
MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
@@ -454,7 +498,23 @@ static int orion_spi_probe(struct platform_device *pdev)
goto out;
tclk_hz = clk_get_rate(spi->clk);
- master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
+
+ /*
+ * With old device tree, armada-370-spi could be used with
+ * Armada XP, however for this SoC the maximum frequency is
+ * 50MHz instead of tclk/4. On Armada 370, tclk cannot be
+ * higher than 200MHz. So, in order to be able to handle both
+ * SoCs, we can take the minimum of 50MHz and tclk/4.
+ */
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "marvell,armada-370-spi"))
+ master->max_speed_hz = min(devdata->max_hz,
+ DIV_ROUND_UP(tclk_hz, devdata->min_divisor));
+ else if (devdata->min_divisor)
+ master->max_speed_hz =
+ DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
+ else
+ master->max_speed_hz = devdata->max_hz;
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c
index fa7399e84bbb..3cfd4357489a 100644
--- a/drivers/spi/spi-pxa2xx-pci.c
+++ b/drivers/spi/spi-pxa2xx-pci.c
@@ -62,7 +62,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.max_clk_rate = 3686400,
},
[PORT_BYT] = {
- .type = LPSS_SSP,
+ .type = LPSS_BYT_SSP,
.port_id = 0,
.num_chipselect = 1,
.max_clk_rate = 50000000,
@@ -70,7 +70,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.rx_param = &byt_rx_param,
},
[PORT_BSW0] = {
- .type = LPSS_SSP,
+ .type = LPSS_BYT_SSP,
.port_id = 0,
.num_chipselect = 1,
.max_clk_rate = 50000000,
@@ -78,7 +78,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.rx_param = &bsw0_rx_param,
},
[PORT_BSW1] = {
- .type = LPSS_SSP,
+ .type = LPSS_BYT_SSP,
.port_id = 1,
.num_chipselect = 1,
.max_clk_rate = 50000000,
@@ -86,7 +86,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.rx_param = &bsw1_rx_param,
},
[PORT_BSW2] = {
- .type = LPSS_SSP,
+ .type = LPSS_BYT_SSP,
.port_id = 2,
.num_chipselect = 1,
.max_clk_rate = 50000000,
diff --git a/drivers/spi/spi-pxa2xx-pxadma.c b/drivers/spi/spi-pxa2xx-pxadma.c
deleted file mode 100644
index 2e0796a0003f..000000000000
--- a/drivers/spi/spi-pxa2xx-pxadma.c
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * PXA2xx SPI private DMA support.
- *
- * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/pxa2xx_ssp.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/pxa2xx_spi.h>
-
-#include <mach/dma.h>
-#include "spi-pxa2xx.h"
-
-#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR)
-#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK)
-
-bool pxa2xx_spi_dma_is_possible(size_t len)
-{
- /* Try to map dma buffer and do a dma transfer if successful, but
- * only if the length is non-zero and less than MAX_DMA_LEN.
- *
- * Zero-length non-descriptor DMA is illegal on PXA2xx; force use
- * of PIO instead. Care is needed above because the transfer may
- * have have been passed with buffers that are already dma mapped.
- * A zero-length transfer in PIO mode will not try to write/read
- * to/from the buffers
- *
- * REVISIT large transfers are exactly where we most want to be
- * using DMA. If this happens much, split those transfers into
- * multiple DMA segments rather than forcing PIO.
- */
- return len > 0 && len <= MAX_DMA_LEN;
-}
-
-int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data)
-{
- struct spi_message *msg = drv_data->cur_msg;
- struct device *dev = &msg->spi->dev;
-
- if (!drv_data->cur_chip->enable_dma)
- return 0;
-
- if (msg->is_dma_mapped)
- return drv_data->rx_dma && drv_data->tx_dma;
-
- if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx))
- return 0;
-
- /* Modify setup if rx buffer is null */
- if (drv_data->rx == NULL) {
- *drv_data->null_dma_buf = 0;
- drv_data->rx = drv_data->null_dma_buf;
- drv_data->rx_map_len = 4;
- } else
- drv_data->rx_map_len = drv_data->len;
-
-
- /* Modify setup if tx buffer is null */
- if (drv_data->tx == NULL) {
- *drv_data->null_dma_buf = 0;
- drv_data->tx = drv_data->null_dma_buf;
- drv_data->tx_map_len = 4;
- } else
- drv_data->tx_map_len = drv_data->len;
-
- /* Stream map the tx buffer. Always do DMA_TO_DEVICE first
- * so we flush the cache *before* invalidating it, in case
- * the tx and rx buffers overlap.
- */
- drv_data->tx_dma = dma_map_single(dev, drv_data->tx,
- drv_data->tx_map_len, DMA_TO_DEVICE);
- if (dma_mapping_error(dev, drv_data->tx_dma))
- return 0;
-
- /* Stream map the rx buffer */
- drv_data->rx_dma = dma_map_single(dev, drv_data->rx,
- drv_data->rx_map_len, DMA_FROM_DEVICE);
- if (dma_mapping_error(dev, drv_data->rx_dma)) {
- dma_unmap_single(dev, drv_data->tx_dma,
- drv_data->tx_map_len, DMA_TO_DEVICE);
- return 0;
- }
-
- return 1;
-}
-
-static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data)
-{
- struct device *dev;
-
- if (!drv_data->dma_mapped)
- return;
-
- if (!drv_data->cur_msg->is_dma_mapped) {
- dev = &drv_data->cur_msg->spi->dev;
- dma_unmap_single(dev, drv_data->rx_dma,
- drv_data->rx_map_len, DMA_FROM_DEVICE);
- dma_unmap_single(dev, drv_data->tx_dma,
- drv_data->tx_map_len, DMA_TO_DEVICE);
- }
-
- drv_data->dma_mapped = 0;
-}
-
-static int wait_ssp_rx_stall(struct driver_data *drv_data)
-{
- unsigned long limit = loops_per_jiffy << 1;
-
- while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY) && --limit)
- cpu_relax();
-
- return limit;
-}
-
-static int wait_dma_channel_stop(int channel)
-{
- unsigned long limit = loops_per_jiffy << 1;
-
- while (!(DCSR(channel) & DCSR_STOPSTATE) && --limit)
- cpu_relax();
-
- return limit;
-}
-
-static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data,
- const char *msg)
-{
- /* Stop and reset */
- DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
- DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
- write_SSSR_CS(drv_data, drv_data->clear_sr);
- pxa2xx_spi_write(drv_data, SSCR1,
- pxa2xx_spi_read(drv_data, SSCR1)
- & ~drv_data->dma_cr1);
- if (!pxa25x_ssp_comp(drv_data))
- pxa2xx_spi_write(drv_data, SSTO, 0);
- pxa2xx_spi_flush(drv_data);
- pxa2xx_spi_write(drv_data, SSCR0,
- pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
-
- pxa2xx_spi_unmap_dma_buffers(drv_data);
-
- dev_err(&drv_data->pdev->dev, "%s\n", msg);
-
- drv_data->cur_msg->state = ERROR_STATE;
- tasklet_schedule(&drv_data->pump_transfers);
-}
-
-static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data)
-{
- struct spi_message *msg = drv_data->cur_msg;
-
- /* Clear and disable interrupts on SSP and DMA channels*/
- pxa2xx_spi_write(drv_data, SSCR1,
- pxa2xx_spi_read(drv_data, SSCR1)
- & ~drv_data->dma_cr1);
- write_SSSR_CS(drv_data, drv_data->clear_sr);
- DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
- DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
-
- if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
- dev_err(&drv_data->pdev->dev,
- "dma_handler: dma rx channel stop failed\n");
-
- if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)
- dev_err(&drv_data->pdev->dev,
- "dma_transfer: ssp rx stall failed\n");
-
- pxa2xx_spi_unmap_dma_buffers(drv_data);
-
- /* update the buffer pointer for the amount completed in dma */
- drv_data->rx += drv_data->len -
- (DCMD(drv_data->rx_channel) & DCMD_LENGTH);
-
- /* read trailing data from fifo, it does not matter how many
- * bytes are in the fifo just read until buffer is full
- * or fifo is empty, which ever occurs first */
- drv_data->read(drv_data);
-
- /* return count of what was actually read */
- msg->actual_length += drv_data->len -
- (drv_data->rx_end - drv_data->rx);
-
- /* Transfer delays and chip select release are
- * handled in pump_transfers or giveback
- */
-
- /* Move to next transfer */
- msg->state = pxa2xx_spi_next_transfer(drv_data);
-
- /* Schedule transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
-}
-
-void pxa2xx_spi_dma_handler(int channel, void *data)
-{
- struct driver_data *drv_data = data;
- u32 irq_status = DCSR(channel) & DMA_INT_MASK;
-
- if (irq_status & DCSR_BUSERR) {
-
- if (channel == drv_data->tx_channel)
- pxa2xx_spi_dma_error_stop(drv_data,
- "dma_handler: bad bus address on tx channel");
- else
- pxa2xx_spi_dma_error_stop(drv_data,
- "dma_handler: bad bus address on rx channel");
- return;
- }
-
- /* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */
- if ((channel == drv_data->tx_channel)
- && (irq_status & DCSR_ENDINTR)
- && (drv_data->ssp_type == PXA25x_SSP)) {
-
- /* Wait for rx to stall */
- if (wait_ssp_rx_stall(drv_data) == 0)
- dev_err(&drv_data->pdev->dev,
- "dma_handler: ssp rx stall failed\n");
-
- /* finish this transfer, start the next */
- pxa2xx_spi_dma_transfer_complete(drv_data);
- }
-}
-
-irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data)
-{
- u32 irq_status;
-
- irq_status = pxa2xx_spi_read(drv_data, SSSR) & drv_data->mask_sr;
- if (irq_status & SSSR_ROR) {
- pxa2xx_spi_dma_error_stop(drv_data,
- "dma_transfer: fifo overrun");
- return IRQ_HANDLED;
- }
-
- /* Check for false positive timeout */
- if ((irq_status & SSSR_TINT)
- && (DCSR(drv_data->tx_channel) & DCSR_RUN)) {
- pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT);
- return IRQ_HANDLED;
- }
-
- if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) {
-
- /* Clear and disable timeout interrupt, do the rest in
- * dma_transfer_complete */
- if (!pxa25x_ssp_comp(drv_data))
- pxa2xx_spi_write(drv_data, SSTO, 0);
-
- /* finish this transfer, start the next */
- pxa2xx_spi_dma_transfer_complete(drv_data);
-
- return IRQ_HANDLED;
- }
-
- /* Opps problem detected */
- return IRQ_NONE;
-}
-
-int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst)
-{
- u32 dma_width;
-
- switch (drv_data->n_bytes) {
- case 1:
- dma_width = DCMD_WIDTH1;
- break;
- case 2:
- dma_width = DCMD_WIDTH2;
- break;
- default:
- dma_width = DCMD_WIDTH4;
- break;
- }
-
- /* Setup rx DMA Channel */
- DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
- DSADR(drv_data->rx_channel) = drv_data->ssdr_physical;
- DTADR(drv_data->rx_channel) = drv_data->rx_dma;
- if (drv_data->rx == drv_data->null_dma_buf)
- /* No target address increment */
- DCMD(drv_data->rx_channel) = DCMD_FLOWSRC
- | dma_width
- | dma_burst
- | drv_data->len;
- else
- DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR
- | DCMD_FLOWSRC
- | dma_width
- | dma_burst
- | drv_data->len;
-
- /* Setup tx DMA Channel */
- DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
- DSADR(drv_data->tx_channel) = drv_data->tx_dma;
- DTADR(drv_data->tx_channel) = drv_data->ssdr_physical;
- if (drv_data->tx == drv_data->null_dma_buf)
- /* No source address increment */
- DCMD(drv_data->tx_channel) = DCMD_FLOWTRG
- | dma_width
- | dma_burst
- | drv_data->len;
- else
- DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR
- | DCMD_FLOWTRG
- | dma_width
- | dma_burst
- | drv_data->len;
-
- /* Enable dma end irqs on SSP to detect end of transfer */
- if (drv_data->ssp_type == PXA25x_SSP)
- DCMD(drv_data->tx_channel) |= DCMD_ENDIRQEN;
-
- return 0;
-}
-
-void pxa2xx_spi_dma_start(struct driver_data *drv_data)
-{
- DCSR(drv_data->rx_channel) |= DCSR_RUN;
- DCSR(drv_data->tx_channel) |= DCSR_RUN;
-}
-
-int pxa2xx_spi_dma_setup(struct driver_data *drv_data)
-{
- struct device *dev = &drv_data->pdev->dev;
- struct ssp_device *ssp = drv_data->ssp;
-
- /* Get two DMA channels (rx and tx) */
- drv_data->rx_channel = pxa_request_dma("pxa2xx_spi_ssp_rx",
- DMA_PRIO_HIGH,
- pxa2xx_spi_dma_handler,
- drv_data);
- if (drv_data->rx_channel < 0) {
- dev_err(dev, "problem (%d) requesting rx channel\n",
- drv_data->rx_channel);
- return -ENODEV;
- }
- drv_data->tx_channel = pxa_request_dma("pxa2xx_spi_ssp_tx",
- DMA_PRIO_MEDIUM,
- pxa2xx_spi_dma_handler,
- drv_data);
- if (drv_data->tx_channel < 0) {
- dev_err(dev, "problem (%d) requesting tx channel\n",
- drv_data->tx_channel);
- pxa_free_dma(drv_data->rx_channel);
- return -ENODEV;
- }
-
- DRCMR(ssp->drcmr_rx) = DRCMR_MAPVLD | drv_data->rx_channel;
- DRCMR(ssp->drcmr_tx) = DRCMR_MAPVLD | drv_data->tx_channel;
-
- return 0;
-}
-
-void pxa2xx_spi_dma_release(struct driver_data *drv_data)
-{
- struct ssp_device *ssp = drv_data->ssp;
-
- DRCMR(ssp->drcmr_rx) = 0;
- DRCMR(ssp->drcmr_tx) = 0;
-
- if (drv_data->tx_channel != 0)
- pxa_free_dma(drv_data->tx_channel);
- if (drv_data->rx_channel != 0)
- pxa_free_dma(drv_data->rx_channel);
-}
-
-void pxa2xx_spi_dma_resume(struct driver_data *drv_data)
-{
- if (drv_data->rx_channel != -1)
- DRCMR(drv_data->ssp->drcmr_rx) =
- DRCMR_MAPVLD | drv_data->rx_channel;
- if (drv_data->tx_channel != -1)
- DRCMR(drv_data->ssp->drcmr_tx) =
- DRCMR_MAPVLD | drv_data->tx_channel;
-}
-
-int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip,
- struct spi_device *spi,
- u8 bits_per_word, u32 *burst_code,
- u32 *threshold)
-{
- struct pxa2xx_spi_chip *chip_info =
- (struct pxa2xx_spi_chip *)spi->controller_data;
- int bytes_per_word;
- int burst_bytes;
- int thresh_words;
- int req_burst_size;
- int retval = 0;
-
- /* Set the threshold (in registers) to equal the same amount of data
- * as represented by burst size (in bytes). The computation below
- * is (burst_size rounded up to nearest 8 byte, word or long word)
- * divided by (bytes/register); the tx threshold is the inverse of
- * the rx, so that there will always be enough data in the rx fifo
- * to satisfy a burst, and there will always be enough space in the
- * tx fifo to accept a burst (a tx burst will overwrite the fifo if
- * there is not enough space), there must always remain enough empty
- * space in the rx fifo for any data loaded to the tx fifo.
- * Whenever burst_size (in bytes) equals bits/word, the fifo threshold
- * will be 8, or half the fifo;
- * The threshold can only be set to 2, 4 or 8, but not 16, because
- * to burst 16 to the tx fifo, the fifo would have to be empty;
- * however, the minimum fifo trigger level is 1, and the tx will
- * request service when the fifo is at this level, with only 15 spaces.
- */
-
- /* find bytes/word */
- if (bits_per_word <= 8)
- bytes_per_word = 1;
- else if (bits_per_word <= 16)
- bytes_per_word = 2;
- else
- bytes_per_word = 4;
-
- /* use struct pxa2xx_spi_chip->dma_burst_size if available */
- if (chip_info)
- req_burst_size = chip_info->dma_burst_size;
- else {
- switch (chip->dma_burst_size) {
- default:
- /* if the default burst size is not set,
- * do it now */
- chip->dma_burst_size = DCMD_BURST8;
- case DCMD_BURST8:
- req_burst_size = 8;
- break;
- case DCMD_BURST16:
- req_burst_size = 16;
- break;
- case DCMD_BURST32:
- req_burst_size = 32;
- break;
- }
- }
- if (req_burst_size <= 8) {
- *burst_code = DCMD_BURST8;
- burst_bytes = 8;
- } else if (req_burst_size <= 16) {
- if (bytes_per_word == 1) {
- /* don't burst more than 1/2 the fifo */
- *burst_code = DCMD_BURST8;
- burst_bytes = 8;
- retval = 1;
- } else {
- *burst_code = DCMD_BURST16;
- burst_bytes = 16;
- }
- } else {
- if (bytes_per_word == 1) {
- /* don't burst more than 1/2 the fifo */
- *burst_code = DCMD_BURST8;
- burst_bytes = 8;
- retval = 1;
- } else if (bytes_per_word == 2) {
- /* don't burst more than 1/2 the fifo */
- *burst_code = DCMD_BURST16;
- burst_bytes = 16;
- retval = 1;
- } else {
- *burst_code = DCMD_BURST32;
- burst_bytes = 32;
- }
- }
-
- thresh_words = burst_bytes / bytes_per_word;
-
- /* thresh_words will be between 2 and 8 */
- *threshold = (SSCR1_RxTresh(thresh_words) & SSCR1_RFT)
- | (SSCR1_TxTresh(16-thresh_words) & SSCR1_TFT);
-
- return retval;
-}
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index e3223ac75a7c..7293d6d875c5 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -60,21 +60,60 @@ MODULE_ALIAS("platform:pxa2xx-spi");
| QUARK_X1000_SSCR1_TFT \
| SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
-#define LPSS_RX_THRESH_DFLT 64
-#define LPSS_TX_LOTHRESH_DFLT 160
-#define LPSS_TX_HITHRESH_DFLT 224
-
-/* Offset from drv_data->lpss_base */
-#define GENERAL_REG 0x08
#define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
-#define SSP_REG 0x0c
-#define SPI_CS_CONTROL 0x18
#define SPI_CS_CONTROL_SW_MODE BIT(0)
#define SPI_CS_CONTROL_CS_HIGH BIT(1)
+struct lpss_config {
+ /* LPSS offset from drv_data->ioaddr */
+ unsigned offset;
+ /* Register offsets from drv_data->lpss_base or -1 */
+ int reg_general;
+ int reg_ssp;
+ int reg_cs_ctrl;
+ /* FIFO thresholds */
+ u32 rx_threshold;
+ u32 tx_threshold_lo;
+ u32 tx_threshold_hi;
+};
+
+/* Keep these sorted with enum pxa_ssp_type */
+static const struct lpss_config lpss_platforms[] = {
+ { /* LPSS_LPT_SSP */
+ .offset = 0x800,
+ .reg_general = 0x08,
+ .reg_ssp = 0x0c,
+ .reg_cs_ctrl = 0x18,
+ .rx_threshold = 64,
+ .tx_threshold_lo = 160,
+ .tx_threshold_hi = 224,
+ },
+ { /* LPSS_BYT_SSP */
+ .offset = 0x400,
+ .reg_general = 0x08,
+ .reg_ssp = 0x0c,
+ .reg_cs_ctrl = 0x18,
+ .rx_threshold = 64,
+ .tx_threshold_lo = 160,
+ .tx_threshold_hi = 224,
+ },
+};
+
+static inline const struct lpss_config
+*lpss_get_config(const struct driver_data *drv_data)
+{
+ return &lpss_platforms[drv_data->ssp_type - LPSS_LPT_SSP];
+}
+
static bool is_lpss_ssp(const struct driver_data *drv_data)
{
- return drv_data->ssp_type == LPSS_SSP;
+ switch (drv_data->ssp_type) {
+ case LPSS_LPT_SSP:
+ case LPSS_BYT_SSP:
+ return true;
+ default:
+ return false;
+ }
}
static bool is_quark_x1000_ssp(const struct driver_data *drv_data)
@@ -192,63 +231,43 @@ static void __lpss_ssp_write_priv(struct driver_data *drv_data,
*/
static void lpss_ssp_setup(struct driver_data *drv_data)
{
- unsigned offset = 0x400;
- u32 value, orig;
-
- /*
- * Perform auto-detection of the LPSS SSP private registers. They
- * can be either at 1k or 2k offset from the base address.
- */
- orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
-
- /* Test SPI_CS_CONTROL_SW_MODE bit enabling */
- value = orig | SPI_CS_CONTROL_SW_MODE;
- writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL);
- value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
- if (value != (orig | SPI_CS_CONTROL_SW_MODE)) {
- offset = 0x800;
- goto detection_done;
- }
-
- orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
-
- /* Test SPI_CS_CONTROL_SW_MODE bit disabling */
- value = orig & ~SPI_CS_CONTROL_SW_MODE;
- writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL);
- value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
- if (value != (orig & ~SPI_CS_CONTROL_SW_MODE)) {
- offset = 0x800;
- goto detection_done;
- }
+ const struct lpss_config *config;
+ u32 value;
-detection_done:
- /* Now set the LPSS base */
- drv_data->lpss_base = drv_data->ioaddr + offset;
+ config = lpss_get_config(drv_data);
+ drv_data->lpss_base = drv_data->ioaddr + config->offset;
/* Enable software chip select control */
value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH;
- __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value);
+ __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
/* Enable multiblock DMA transfers */
if (drv_data->master_info->enable_dma) {
- __lpss_ssp_write_priv(drv_data, SSP_REG, 1);
-
- value = __lpss_ssp_read_priv(drv_data, GENERAL_REG);
- value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE;
- __lpss_ssp_write_priv(drv_data, GENERAL_REG, value);
+ __lpss_ssp_write_priv(drv_data, config->reg_ssp, 1);
+
+ if (config->reg_general >= 0) {
+ value = __lpss_ssp_read_priv(drv_data,
+ config->reg_general);
+ value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE;
+ __lpss_ssp_write_priv(drv_data,
+ config->reg_general, value);
+ }
}
}
static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
{
+ const struct lpss_config *config;
u32 value;
- value = __lpss_ssp_read_priv(drv_data, SPI_CS_CONTROL);
+ config = lpss_get_config(drv_data);
+
+ value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
if (enable)
value &= ~SPI_CS_CONTROL_CS_HIGH;
else
value |= SPI_CS_CONTROL_CS_HIGH;
- __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value);
+ __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
}
static void cs_assert(struct driver_data *drv_data)
@@ -1075,6 +1094,7 @@ static int setup(struct spi_device *spi)
{
struct pxa2xx_spi_chip *chip_info = NULL;
struct chip_data *chip;
+ const struct lpss_config *config;
struct driver_data *drv_data = spi_master_get_devdata(spi->master);
unsigned int clk_div;
uint tx_thres, tx_hi_thres, rx_thres;
@@ -1085,10 +1105,12 @@ static int setup(struct spi_device *spi)
tx_hi_thres = 0;
rx_thres = RX_THRESH_QUARK_X1000_DFLT;
break;
- case LPSS_SSP:
- tx_thres = LPSS_TX_LOTHRESH_DFLT;
- tx_hi_thres = LPSS_TX_HITHRESH_DFLT;
- rx_thres = LPSS_RX_THRESH_DFLT;
+ case LPSS_LPT_SSP:
+ case LPSS_BYT_SSP:
+ config = lpss_get_config(drv_data);
+ tx_thres = config->tx_threshold_lo;
+ tx_hi_thres = config->tx_threshold_hi;
+ rx_thres = config->rx_threshold;
break;
default:
tx_thres = TX_THRESH_DFLT;
@@ -1242,6 +1264,18 @@ static void cleanup(struct spi_device *spi)
}
#ifdef CONFIG_ACPI
+
+static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
+ { "INT33C0", LPSS_LPT_SSP },
+ { "INT33C1", LPSS_LPT_SSP },
+ { "INT3430", LPSS_LPT_SSP },
+ { "INT3431", LPSS_LPT_SSP },
+ { "80860F0E", LPSS_BYT_SSP },
+ { "8086228E", LPSS_BYT_SSP },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
+
static struct pxa2xx_spi_master *
pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
{
@@ -1249,12 +1283,19 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
struct acpi_device *adev;
struct ssp_device *ssp;
struct resource *res;
- int devid;
+ const struct acpi_device_id *id;
+ int devid, type;
if (!ACPI_HANDLE(&pdev->dev) ||
acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev))
return NULL;
+ id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
+ if (id)
+ type = (int)id->driver_data;
+ else
+ return NULL;
+
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
@@ -1272,7 +1313,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
ssp->clk = devm_clk_get(&pdev->dev, NULL);
ssp->irq = platform_get_irq(pdev, 0);
- ssp->type = LPSS_SSP;
+ ssp->type = type;
ssp->pdev = pdev;
ssp->port_id = -1;
@@ -1285,16 +1326,6 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
return pdata;
}
-static struct acpi_device_id pxa2xx_spi_acpi_match[] = {
- { "INT33C0", 0 },
- { "INT33C1", 0 },
- { "INT3430", 0 },
- { "INT3431", 0 },
- { "80860F0E", 0 },
- { "8086228E", 0 },
- { },
-};
-MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
#else
static inline struct pxa2xx_spi_master *
pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h
index 85a58c906869..9f01e9c9aa75 100644
--- a/drivers/spi/spi-pxa2xx.h
+++ b/drivers/spi/spi-pxa2xx.h
@@ -162,11 +162,7 @@ extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data);
/*
* Select the right DMA implementation.
*/
-#if defined(CONFIG_SPI_PXA2XX_PXADMA)
-#define SPI_PXA2XX_USE_DMA 1
-#define MAX_DMA_LEN 8191
-#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TINTE)
-#elif defined(CONFIG_SPI_PXA2XX_DMA)
+#if defined(CONFIG_SPI_PXA2XX_DMA)
#define SPI_PXA2XX_USE_DMA 1
#define MAX_DMA_LEN SZ_64K
#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL)
diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c
new file mode 100644
index 000000000000..3641d0e20135
--- /dev/null
+++ b/drivers/spi/spi-rb4xx.c
@@ -0,0 +1,210 @@
+/*
+ * SPI controller driver for the Mikrotik RB4xx boards
+ *
+ * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
+ *
+ * This file was based on the patches for Linux 2.6.27.39 published by
+ * MikroTik for their RouterBoard 4xx series devices.
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+
+#include <asm/mach-ath79/ar71xx_regs.h>
+
+struct rb4xx_spi {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static inline u32 rb4xx_read(struct rb4xx_spi *rbspi, u32 reg)
+{
+ return __raw_readl(rbspi->base + reg);
+}
+
+static inline void rb4xx_write(struct rb4xx_spi *rbspi, u32 reg, u32 value)
+{
+ __raw_writel(value, rbspi->base + reg);
+}
+
+static inline void do_spi_clk(struct rb4xx_spi *rbspi, u32 spi_ioc, int value)
+{
+ u32 regval;
+
+ regval = spi_ioc;
+ if (value & BIT(0))
+ regval |= AR71XX_SPI_IOC_DO;
+
+ rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
+ rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
+}
+
+static void do_spi_byte(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
+{
+ int i;
+
+ for (i = 7; i >= 0; i--)
+ do_spi_clk(rbspi, spi_ioc, byte >> i);
+}
+
+/* The CS2 pin is used to clock in a second bit per clock cycle. */
+static inline void do_spi_clk_two(struct rb4xx_spi *rbspi, u32 spi_ioc,
+ u8 value)
+{
+ u32 regval;
+
+ regval = spi_ioc;
+ if (value & BIT(1))
+ regval |= AR71XX_SPI_IOC_DO;
+ if (value & BIT(0))
+ regval |= AR71XX_SPI_IOC_CS2;
+
+ rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
+ rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
+}
+
+/* Two bits at a time, msb first */
+static void do_spi_byte_two(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
+{
+ do_spi_clk_two(rbspi, spi_ioc, byte >> 6);
+ do_spi_clk_two(rbspi, spi_ioc, byte >> 4);
+ do_spi_clk_two(rbspi, spi_ioc, byte >> 2);
+ do_spi_clk_two(rbspi, spi_ioc, byte >> 0);
+}
+
+static void rb4xx_set_cs(struct spi_device *spi, bool enable)
+{
+ struct rb4xx_spi *rbspi = spi_master_get_devdata(spi->master);
+
+ /*
+ * Setting CS is done along with bitbanging the actual values,
+ * since it's all on the same hardware register. However the
+ * CPLD needs CS deselected after every command.
+ */
+ if (enable)
+ rb4xx_write(rbspi, AR71XX_SPI_REG_IOC,
+ AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1);
+}
+
+static int rb4xx_transfer_one(struct spi_master *master,
+ struct spi_device *spi, struct spi_transfer *t)
+{
+ struct rb4xx_spi *rbspi = spi_master_get_devdata(master);
+ int i;
+ u32 spi_ioc;
+ u8 *rx_buf;
+ const u8 *tx_buf;
+
+ /*
+ * Prime the SPI register with the SPI device selected. The m25p80 boot
+ * flash and CPLD share the CS0 pin. This works because the CPLD's
+ * command set was designed to almost not clash with that of the
+ * boot flash.
+ */
+ if (spi->chip_select == 2)
+ /* MMC */
+ spi_ioc = AR71XX_SPI_IOC_CS0;
+ else
+ /* Boot flash and CPLD */
+ spi_ioc = AR71XX_SPI_IOC_CS1;
+
+ tx_buf = t->tx_buf;
+ rx_buf = t->rx_buf;
+ for (i = 0; i < t->len; ++i) {
+ if (t->tx_nbits == SPI_NBITS_DUAL)
+ /* CPLD can use two-wire transfers */
+ do_spi_byte_two(rbspi, spi_ioc, tx_buf[i]);
+ else
+ do_spi_byte(rbspi, spi_ioc, tx_buf[i]);
+ if (!rx_buf)
+ continue;
+ rx_buf[i] = rb4xx_read(rbspi, AR71XX_SPI_REG_RDS);
+ }
+ spi_finalize_current_transfer(master);
+
+ return 0;
+}
+
+static int rb4xx_spi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct clk *ahb_clk;
+ struct rb4xx_spi *rbspi;
+ struct resource *r;
+ int err;
+ void __iomem *spi_base;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ spi_base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(spi_base))
+ return PTR_ERR(spi_base);
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*rbspi));
+ if (!master)
+ return -ENOMEM;
+
+ ahb_clk = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(ahb_clk))
+ return PTR_ERR(ahb_clk);
+
+ master->bus_num = 0;
+ master->num_chipselect = 3;
+ master->mode_bits = SPI_TX_DUAL;
+ master->bits_per_word_mask = BIT(7);
+ master->flags = SPI_MASTER_MUST_TX;
+ master->transfer_one = rb4xx_transfer_one;
+ master->set_cs = rb4xx_set_cs;
+
+ err = devm_spi_register_master(&pdev->dev, master);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register SPI master\n");
+ return err;
+ }
+
+ err = clk_prepare_enable(ahb_clk);
+ if (err)
+ return err;
+
+ rbspi = spi_master_get_devdata(master);
+ rbspi->base = spi_base;
+ rbspi->clk = ahb_clk;
+ platform_set_drvdata(pdev, rbspi);
+
+ /* Enable SPI */
+ rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
+
+ return 0;
+}
+
+static int rb4xx_spi_remove(struct platform_device *pdev)
+{
+ struct rb4xx_spi *rbspi = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(rbspi->clk);
+
+ return 0;
+}
+
+static struct platform_driver rb4xx_spi_drv = {
+ .probe = rb4xx_spi_probe,
+ .remove = rb4xx_spi_remove,
+ .driver = {
+ .name = "rb4xx-spi",
+ },
+};
+
+module_platform_driver(rb4xx_spi_drv);
+
+MODULE_DESCRIPTION("Mikrotik RB4xx SPI controller driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_AUTHOR("Bert Vermeulen <bert@biot.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
index f6bac9e77d06..f9189a0c8cec 100644
--- a/drivers/spi/spi-rspi.c
+++ b/drivers/spi/spi-rspi.c
@@ -665,15 +665,12 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi,
static int rspi_dma_check_then_transfer(struct rspi_data *rspi,
struct spi_transfer *xfer)
{
- if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
- /* rx_buf can be NULL on RSPI on SH in TX-only Mode */
- int ret = rspi_dma_transfer(rspi, &xfer->tx_sg,
- xfer->rx_buf ? &xfer->rx_sg : NULL);
- if (ret != -EAGAIN)
- return 0;
- }
+ if (!rspi->master->can_dma || !__rspi_can_dma(rspi, xfer))
+ return -EAGAIN;
- return -EAGAIN;
+ /* rx_buf can be NULL on RSPI on SH in TX-only Mode */
+ return rspi_dma_transfer(rspi, &xfer->tx_sg,
+ xfer->rx_buf ? &xfer->rx_sg : NULL);
}
static int rspi_common_transfer(struct rspi_data *rspi,
@@ -724,7 +721,7 @@ static int rspi_rz_transfer_one(struct spi_master *master,
return rspi_common_transfer(rspi, xfer);
}
-static int qspi_trigger_transfer_out_int(struct rspi_data *rspi, const u8 *tx,
+static int qspi_trigger_transfer_out_in(struct rspi_data *rspi, const u8 *tx,
u8 *rx, unsigned int len)
{
int i, n, ret;
@@ -771,12 +768,8 @@ static int qspi_transfer_out_in(struct rspi_data *rspi,
if (ret != -EAGAIN)
return ret;
- ret = qspi_trigger_transfer_out_int(rspi, xfer->tx_buf,
+ return qspi_trigger_transfer_out_in(rspi, xfer->tx_buf,
xfer->rx_buf, xfer->len);
- if (ret < 0)
- return ret;
-
- return 0;
}
static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
@@ -1300,7 +1293,7 @@ error1:
return ret;
}
-static struct platform_device_id spi_driver_ids[] = {
+static const struct platform_device_id spi_driver_ids[] = {
{ "rspi", (kernel_ulong_t)&rspi_ops },
{ "rspi-rz", (kernel_ulong_t)&rspi_rz_ops },
{ "qspi", (kernel_ulong_t)&qspi_ops },
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index b1c6731fbf27..2a8c513c4d07 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -1347,7 +1347,7 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = {
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
};
-static struct platform_device_id s3c64xx_spi_driver_ids[] = {
+static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
{
.name = "s3c2443-spi",
.driver_data = (kernel_ulong_t)&s3c2443_spi_port_config,
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index bcc7c635d8e7..d3370a612d84 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -1263,7 +1263,7 @@ static int sh_msiof_spi_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id spi_driver_ids[] = {
+static const struct platform_device_id spi_driver_ids[] = {
{ "spi_sh_msiof", (kernel_ulong_t)&sh_data },
{ "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data },
{ "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data },
diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c
index f5715c9f68b0..7072276ad354 100644
--- a/drivers/spi/spi-sirf.c
+++ b/drivers/spi/spi-sirf.c
@@ -26,28 +26,6 @@
#include <linux/reset.h>
#define DRIVER_NAME "sirfsoc_spi"
-
-#define SIRFSOC_SPI_CTRL 0x0000
-#define SIRFSOC_SPI_CMD 0x0004
-#define SIRFSOC_SPI_TX_RX_EN 0x0008
-#define SIRFSOC_SPI_INT_EN 0x000C
-#define SIRFSOC_SPI_INT_STATUS 0x0010
-#define SIRFSOC_SPI_TX_DMA_IO_CTRL 0x0100
-#define SIRFSOC_SPI_TX_DMA_IO_LEN 0x0104
-#define SIRFSOC_SPI_TXFIFO_CTRL 0x0108
-#define SIRFSOC_SPI_TXFIFO_LEVEL_CHK 0x010C
-#define SIRFSOC_SPI_TXFIFO_OP 0x0110
-#define SIRFSOC_SPI_TXFIFO_STATUS 0x0114
-#define SIRFSOC_SPI_TXFIFO_DATA 0x0118
-#define SIRFSOC_SPI_RX_DMA_IO_CTRL 0x0120
-#define SIRFSOC_SPI_RX_DMA_IO_LEN 0x0124
-#define SIRFSOC_SPI_RXFIFO_CTRL 0x0128
-#define SIRFSOC_SPI_RXFIFO_LEVEL_CHK 0x012C
-#define SIRFSOC_SPI_RXFIFO_OP 0x0130
-#define SIRFSOC_SPI_RXFIFO_STATUS 0x0134
-#define SIRFSOC_SPI_RXFIFO_DATA 0x0138
-#define SIRFSOC_SPI_DUMMY_DELAY_CTL 0x0144
-
/* SPI CTRL register defines */
#define SIRFSOC_SPI_SLV_MODE BIT(16)
#define SIRFSOC_SPI_CMD_MODE BIT(17)
@@ -80,8 +58,6 @@
#define SIRFSOC_SPI_TXFIFO_THD_INT_EN BIT(9)
#define SIRFSOC_SPI_FRM_END_INT_EN BIT(10)
-#define SIRFSOC_SPI_INT_MASK_ALL 0x1FFF
-
/* Interrupt status */
#define SIRFSOC_SPI_RX_DONE BIT(0)
#define SIRFSOC_SPI_TX_DONE BIT(1)
@@ -110,20 +86,66 @@
#define SIRFSOC_SPI_FIFO_WIDTH_BYTE (0 << 0)
#define SIRFSOC_SPI_FIFO_WIDTH_WORD (1 << 0)
#define SIRFSOC_SPI_FIFO_WIDTH_DWORD (2 << 0)
-
-/* FIFO Status */
-#define SIRFSOC_SPI_FIFO_LEVEL_MASK 0xFF
-#define SIRFSOC_SPI_FIFO_FULL BIT(8)
-#define SIRFSOC_SPI_FIFO_EMPTY BIT(9)
-
-/* 256 bytes rx/tx FIFO */
-#define SIRFSOC_SPI_FIFO_SIZE 256
-#define SIRFSOC_SPI_DAT_FRM_LEN_MAX (64 * 1024)
-
-#define SIRFSOC_SPI_FIFO_SC(x) ((x) & 0x3F)
-#define SIRFSOC_SPI_FIFO_LC(x) (((x) & 0x3F) << 10)
-#define SIRFSOC_SPI_FIFO_HC(x) (((x) & 0x3F) << 20)
-#define SIRFSOC_SPI_FIFO_THD(x) (((x) & 0xFF) << 2)
+/* USP related */
+#define SIRFSOC_USP_SYNC_MODE BIT(0)
+#define SIRFSOC_USP_SLV_MODE BIT(1)
+#define SIRFSOC_USP_LSB BIT(4)
+#define SIRFSOC_USP_EN BIT(5)
+#define SIRFSOC_USP_RXD_FALLING_EDGE BIT(6)
+#define SIRFSOC_USP_TXD_FALLING_EDGE BIT(7)
+#define SIRFSOC_USP_CS_HIGH_VALID BIT(9)
+#define SIRFSOC_USP_SCLK_IDLE_STAT BIT(11)
+#define SIRFSOC_USP_TFS_IO_MODE BIT(14)
+#define SIRFSOC_USP_TFS_IO_INPUT BIT(19)
+
+#define SIRFSOC_USP_RXD_DELAY_LEN_MASK 0xFF
+#define SIRFSOC_USP_TXD_DELAY_LEN_MASK 0xFF
+#define SIRFSOC_USP_RXD_DELAY_OFFSET 0
+#define SIRFSOC_USP_TXD_DELAY_OFFSET 8
+#define SIRFSOC_USP_RXD_DELAY_LEN 1
+#define SIRFSOC_USP_TXD_DELAY_LEN 1
+#define SIRFSOC_USP_CLK_DIVISOR_OFFSET 21
+#define SIRFSOC_USP_CLK_DIVISOR_MASK 0x3FF
+#define SIRFSOC_USP_CLK_10_11_MASK 0x3
+#define SIRFSOC_USP_CLK_10_11_OFFSET 30
+#define SIRFSOC_USP_CLK_12_15_MASK 0xF
+#define SIRFSOC_USP_CLK_12_15_OFFSET 24
+
+#define SIRFSOC_USP_TX_DATA_OFFSET 0
+#define SIRFSOC_USP_TX_SYNC_OFFSET 8
+#define SIRFSOC_USP_TX_FRAME_OFFSET 16
+#define SIRFSOC_USP_TX_SHIFTER_OFFSET 24
+
+#define SIRFSOC_USP_TX_DATA_MASK 0xFF
+#define SIRFSOC_USP_TX_SYNC_MASK 0xFF
+#define SIRFSOC_USP_TX_FRAME_MASK 0xFF
+#define SIRFSOC_USP_TX_SHIFTER_MASK 0x1F
+
+#define SIRFSOC_USP_RX_DATA_OFFSET 0
+#define SIRFSOC_USP_RX_FRAME_OFFSET 8
+#define SIRFSOC_USP_RX_SHIFTER_OFFSET 16
+
+#define SIRFSOC_USP_RX_DATA_MASK 0xFF
+#define SIRFSOC_USP_RX_FRAME_MASK 0xFF
+#define SIRFSOC_USP_RX_SHIFTER_MASK 0x1F
+#define SIRFSOC_USP_CS_HIGH_VALUE BIT(1)
+
+#define SIRFSOC_SPI_FIFO_SC_OFFSET 0
+#define SIRFSOC_SPI_FIFO_LC_OFFSET 10
+#define SIRFSOC_SPI_FIFO_HC_OFFSET 20
+
+#define SIRFSOC_SPI_FIFO_FULL_MASK(s) (1 << ((s)->fifo_full_offset))
+#define SIRFSOC_SPI_FIFO_EMPTY_MASK(s) (1 << ((s)->fifo_full_offset + 1))
+#define SIRFSOC_SPI_FIFO_THD_MASK(s) ((s)->fifo_size - 1)
+#define SIRFSOC_SPI_FIFO_THD_OFFSET 2
+#define SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(s, val) \
+ ((val) & (s)->fifo_level_chk_mask)
+
+enum sirf_spi_type {
+ SIRF_REAL_SPI,
+ SIRF_USP_SPI_P2,
+ SIRF_USP_SPI_A7,
+};
/*
* only if the rx/tx buffer and transfer size are 4-bytes aligned, we use dma
@@ -137,6 +159,95 @@
#define SIRFSOC_MAX_CMD_BYTES 4
#define SIRFSOC_SPI_DEFAULT_FRQ 1000000
+struct sirf_spi_register {
+ /*SPI and USP-SPI common*/
+ u32 tx_rx_en;
+ u32 int_en;
+ u32 int_st;
+ u32 tx_dma_io_ctrl;
+ u32 tx_dma_io_len;
+ u32 txfifo_ctrl;
+ u32 txfifo_level_chk;
+ u32 txfifo_op;
+ u32 txfifo_st;
+ u32 txfifo_data;
+ u32 rx_dma_io_ctrl;
+ u32 rx_dma_io_len;
+ u32 rxfifo_ctrl;
+ u32 rxfifo_level_chk;
+ u32 rxfifo_op;
+ u32 rxfifo_st;
+ u32 rxfifo_data;
+ /*SPI self*/
+ u32 spi_ctrl;
+ u32 spi_cmd;
+ u32 spi_dummy_delay_ctrl;
+ /*USP-SPI self*/
+ u32 usp_mode1;
+ u32 usp_mode2;
+ u32 usp_tx_frame_ctrl;
+ u32 usp_rx_frame_ctrl;
+ u32 usp_pin_io_data;
+ u32 usp_risc_dsp_mode;
+ u32 usp_async_param_reg;
+ u32 usp_irda_x_mode_div;
+ u32 usp_sm_cfg;
+ u32 usp_int_en_clr;
+};
+
+static const struct sirf_spi_register real_spi_register = {
+ .tx_rx_en = 0x8,
+ .int_en = 0xc,
+ .int_st = 0x10,
+ .tx_dma_io_ctrl = 0x100,
+ .tx_dma_io_len = 0x104,
+ .txfifo_ctrl = 0x108,
+ .txfifo_level_chk = 0x10c,
+ .txfifo_op = 0x110,
+ .txfifo_st = 0x114,
+ .txfifo_data = 0x118,
+ .rx_dma_io_ctrl = 0x120,
+ .rx_dma_io_len = 0x124,
+ .rxfifo_ctrl = 0x128,
+ .rxfifo_level_chk = 0x12c,
+ .rxfifo_op = 0x130,
+ .rxfifo_st = 0x134,
+ .rxfifo_data = 0x138,
+ .spi_ctrl = 0x0,
+ .spi_cmd = 0x4,
+ .spi_dummy_delay_ctrl = 0x144,
+};
+
+static const struct sirf_spi_register usp_spi_register = {
+ .tx_rx_en = 0x10,
+ .int_en = 0x14,
+ .int_st = 0x18,
+ .tx_dma_io_ctrl = 0x100,
+ .tx_dma_io_len = 0x104,
+ .txfifo_ctrl = 0x108,
+ .txfifo_level_chk = 0x10c,
+ .txfifo_op = 0x110,
+ .txfifo_st = 0x114,
+ .txfifo_data = 0x118,
+ .rx_dma_io_ctrl = 0x120,
+ .rx_dma_io_len = 0x124,
+ .rxfifo_ctrl = 0x128,
+ .rxfifo_level_chk = 0x12c,
+ .rxfifo_op = 0x130,
+ .rxfifo_st = 0x134,
+ .rxfifo_data = 0x138,
+ .usp_mode1 = 0x0,
+ .usp_mode2 = 0x4,
+ .usp_tx_frame_ctrl = 0x8,
+ .usp_rx_frame_ctrl = 0xc,
+ .usp_pin_io_data = 0x1c,
+ .usp_risc_dsp_mode = 0x20,
+ .usp_async_param_reg = 0x24,
+ .usp_irda_x_mode_div = 0x28,
+ .usp_sm_cfg = 0x2c,
+ .usp_int_en_clr = 0x140,
+};
+
struct sirfsoc_spi {
struct spi_bitbang bitbang;
struct completion rx_done;
@@ -164,7 +275,6 @@ struct sirfsoc_spi {
struct dma_chan *tx_chan;
dma_addr_t src_start;
dma_addr_t dst_start;
- void *dummypage;
int word_width; /* in bytes */
/*
@@ -173,14 +283,39 @@ struct sirfsoc_spi {
*/
bool tx_by_cmd;
bool hw_cs;
+ enum sirf_spi_type type;
+ const struct sirf_spi_register *regs;
+ unsigned int fifo_size;
+ /* fifo empty offset is (fifo full offset + 1)*/
+ unsigned int fifo_full_offset;
+ /* fifo_level_chk_mask is (fifo_size/4 - 1) */
+ unsigned int fifo_level_chk_mask;
+ unsigned int dat_max_frm_len;
+};
+
+struct sirf_spi_comp_data {
+ const struct sirf_spi_register *regs;
+ enum sirf_spi_type type;
+ unsigned int dat_max_frm_len;
+ unsigned int fifo_size;
+ void (*hwinit)(struct sirfsoc_spi *sspi);
};
+static void sirfsoc_usp_hwinit(struct sirfsoc_spi *sspi)
+{
+ /* reset USP and let USP can operate */
+ writel(readl(sspi->base + sspi->regs->usp_mode1) &
+ ~SIRFSOC_USP_EN, sspi->base + sspi->regs->usp_mode1);
+ writel(readl(sspi->base + sspi->regs->usp_mode1) |
+ SIRFSOC_USP_EN, sspi->base + sspi->regs->usp_mode1);
+}
+
static void spi_sirfsoc_rx_word_u8(struct sirfsoc_spi *sspi)
{
u32 data;
u8 *rx = sspi->rx;
- data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA);
+ data = readl(sspi->base + sspi->regs->rxfifo_data);
if (rx) {
*rx++ = (u8) data;
@@ -199,8 +334,7 @@ static void spi_sirfsoc_tx_word_u8(struct sirfsoc_spi *sspi)
data = *tx++;
sspi->tx = tx;
}
-
- writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
+ writel(data, sspi->base + sspi->regs->txfifo_data);
sspi->left_tx_word--;
}
@@ -209,7 +343,7 @@ static void spi_sirfsoc_rx_word_u16(struct sirfsoc_spi *sspi)
u32 data;
u16 *rx = sspi->rx;
- data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA);
+ data = readl(sspi->base + sspi->regs->rxfifo_data);
if (rx) {
*rx++ = (u16) data;
@@ -229,7 +363,7 @@ static void spi_sirfsoc_tx_word_u16(struct sirfsoc_spi *sspi)
sspi->tx = tx;
}
- writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
+ writel(data, sspi->base + sspi->regs->txfifo_data);
sspi->left_tx_word--;
}
@@ -238,7 +372,7 @@ static void spi_sirfsoc_rx_word_u32(struct sirfsoc_spi *sspi)
u32 data;
u32 *rx = sspi->rx;
- data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA);
+ data = readl(sspi->base + sspi->regs->rxfifo_data);
if (rx) {
*rx++ = (u32) data;
@@ -259,41 +393,59 @@ static void spi_sirfsoc_tx_word_u32(struct sirfsoc_spi *sspi)
sspi->tx = tx;
}
- writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
+ writel(data, sspi->base + sspi->regs->txfifo_data);
sspi->left_tx_word--;
}
static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id)
{
struct sirfsoc_spi *sspi = dev_id;
- u32 spi_stat = readl(sspi->base + SIRFSOC_SPI_INT_STATUS);
- if (sspi->tx_by_cmd && (spi_stat & SIRFSOC_SPI_FRM_END)) {
+ u32 spi_stat;
+
+ spi_stat = readl(sspi->base + sspi->regs->int_st);
+ if (sspi->tx_by_cmd && sspi->type == SIRF_REAL_SPI
+ && (spi_stat & SIRFSOC_SPI_FRM_END)) {
complete(&sspi->tx_done);
- writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
- writel(SIRFSOC_SPI_INT_MASK_ALL,
- sspi->base + SIRFSOC_SPI_INT_STATUS);
+ writel(0x0, sspi->base + sspi->regs->int_en);
+ writel(readl(sspi->base + sspi->regs->int_st),
+ sspi->base + sspi->regs->int_st);
return IRQ_HANDLED;
}
-
/* Error Conditions */
if (spi_stat & SIRFSOC_SPI_RX_OFLOW ||
spi_stat & SIRFSOC_SPI_TX_UFLOW) {
complete(&sspi->tx_done);
complete(&sspi->rx_done);
- writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
- writel(SIRFSOC_SPI_INT_MASK_ALL,
- sspi->base + SIRFSOC_SPI_INT_STATUS);
+ switch (sspi->type) {
+ case SIRF_REAL_SPI:
+ case SIRF_USP_SPI_P2:
+ writel(0x0, sspi->base + sspi->regs->int_en);
+ break;
+ case SIRF_USP_SPI_A7:
+ writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr);
+ break;
+ }
+ writel(readl(sspi->base + sspi->regs->int_st),
+ sspi->base + sspi->regs->int_st);
return IRQ_HANDLED;
}
if (spi_stat & SIRFSOC_SPI_TXFIFO_EMPTY)
complete(&sspi->tx_done);
- while (!(readl(sspi->base + SIRFSOC_SPI_INT_STATUS) &
+ while (!(readl(sspi->base + sspi->regs->int_st) &
SIRFSOC_SPI_RX_IO_DMA))
cpu_relax();
complete(&sspi->rx_done);
- writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
- writel(SIRFSOC_SPI_INT_MASK_ALL,
- sspi->base + SIRFSOC_SPI_INT_STATUS);
+ switch (sspi->type) {
+ case SIRF_REAL_SPI:
+ case SIRF_USP_SPI_P2:
+ writel(0x0, sspi->base + sspi->regs->int_en);
+ break;
+ case SIRF_USP_SPI_A7:
+ writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr);
+ break;
+ }
+ writel(readl(sspi->base + sspi->regs->int_st),
+ sspi->base + sspi->regs->int_st);
return IRQ_HANDLED;
}
@@ -313,8 +465,8 @@ static void spi_sirfsoc_cmd_transfer(struct spi_device *spi,
u32 cmd;
sspi = spi_master_get_devdata(spi->master);
- writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
+ writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->txfifo_op);
+ writel(SIRFSOC_SPI_FIFO_START, sspi->base + sspi->regs->txfifo_op);
memcpy(&cmd, sspi->tx, t->len);
if (sspi->word_width == 1 && !(spi->mode & SPI_LSB_FIRST))
cmd = cpu_to_be32(cmd) >>
@@ -322,11 +474,11 @@ static void spi_sirfsoc_cmd_transfer(struct spi_device *spi,
if (sspi->word_width == 2 && t->len == 4 &&
(!(spi->mode & SPI_LSB_FIRST)))
cmd = ((cmd & 0xffff) << 16) | (cmd >> 16);
- writel(cmd, sspi->base + SIRFSOC_SPI_CMD);
+ writel(cmd, sspi->base + sspi->regs->spi_cmd);
writel(SIRFSOC_SPI_FRM_END_INT_EN,
- sspi->base + SIRFSOC_SPI_INT_EN);
+ sspi->base + sspi->regs->int_en);
writel(SIRFSOC_SPI_CMD_TX_EN,
- sspi->base + SIRFSOC_SPI_TX_RX_EN);
+ sspi->base + sspi->regs->tx_rx_en);
if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) {
dev_err(&spi->dev, "cmd transfer timeout\n");
return;
@@ -342,25 +494,56 @@ static void spi_sirfsoc_dma_transfer(struct spi_device *spi,
int timeout = t->len * 10;
sspi = spi_master_get_devdata(spi->master);
- writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
- writel(0, sspi->base + SIRFSOC_SPI_INT_EN);
- writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS);
- if (sspi->left_tx_word < SIRFSOC_SPI_DAT_FRM_LEN_MAX) {
- writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
- SIRFSOC_SPI_ENA_AUTO_CLR | SIRFSOC_SPI_MUL_DAT_MODE,
- sspi->base + SIRFSOC_SPI_CTRL);
- writel(sspi->left_tx_word - 1,
- sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
- writel(sspi->left_tx_word - 1,
- sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
+ writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->rxfifo_op);
+ writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->txfifo_op);
+ switch (sspi->type) {
+ case SIRF_REAL_SPI:
+ writel(SIRFSOC_SPI_FIFO_START,
+ sspi->base + sspi->regs->rxfifo_op);
+ writel(SIRFSOC_SPI_FIFO_START,
+ sspi->base + sspi->regs->txfifo_op);
+ writel(0, sspi->base + sspi->regs->int_en);
+ break;
+ case SIRF_USP_SPI_P2:
+ writel(0x0, sspi->base + sspi->regs->rxfifo_op);
+ writel(0x0, sspi->base + sspi->regs->txfifo_op);
+ writel(0, sspi->base + sspi->regs->int_en);
+ break;
+ case SIRF_USP_SPI_A7:
+ writel(0x0, sspi->base + sspi->regs->rxfifo_op);
+ writel(0x0, sspi->base + sspi->regs->txfifo_op);
+ writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr);
+ break;
+ }
+ writel(readl(sspi->base + sspi->regs->int_st),
+ sspi->base + sspi->regs->int_st);
+ if (sspi->left_tx_word < sspi->dat_max_frm_len) {
+ switch (sspi->type) {
+ case SIRF_REAL_SPI:
+ writel(readl(sspi->base + sspi->regs->spi_ctrl) |
+ SIRFSOC_SPI_ENA_AUTO_CLR |
+ SIRFSOC_SPI_MUL_DAT_MODE,
+ sspi->base + sspi->regs->spi_ctrl);
+ writel(sspi->left_tx_word - 1,
+ sspi->base + sspi->regs->tx_dma_io_len);
+ writel(sspi->left_tx_word - 1,
+ sspi->base + sspi->regs->rx_dma_io_len);
+ break;
+ case SIRF_USP_SPI_P2:
+ case SIRF_USP_SPI_A7:
+ /*USP simulate SPI, tx/rx_dma_io_len indicates bytes*/
+ writel(sspi->left_tx_word * sspi->word_width,
+ sspi->base + sspi->regs->tx_dma_io_len);
+ writel(sspi->left_tx_word * sspi->word_width,
+ sspi->base + sspi->regs->rx_dma_io_len);
+ break;
+ }
} else {
- writel(readl(sspi->base + SIRFSOC_SPI_CTRL),
- sspi->base + SIRFSOC_SPI_CTRL);
- writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
- writel(0, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
+ if (sspi->type == SIRF_REAL_SPI)
+ writel(readl(sspi->base + sspi->regs->spi_ctrl),
+ sspi->base + sspi->regs->spi_ctrl);
+ writel(0, sspi->base + sspi->regs->tx_dma_io_len);
+ writel(0, sspi->base + sspi->regs->rx_dma_io_len);
}
sspi->dst_start = dma_map_single(&spi->dev, sspi->rx, t->len,
(t->tx_buf != t->rx_buf) ?
@@ -385,7 +568,14 @@ static void spi_sirfsoc_dma_transfer(struct spi_device *spi,
dma_async_issue_pending(sspi->tx_chan);
dma_async_issue_pending(sspi->rx_chan);
writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN,
- sspi->base + SIRFSOC_SPI_TX_RX_EN);
+ sspi->base + sspi->regs->tx_rx_en);
+ if (sspi->type == SIRF_USP_SPI_P2 ||
+ sspi->type == SIRF_USP_SPI_A7) {
+ writel(SIRFSOC_SPI_FIFO_START,
+ sspi->base + sspi->regs->rxfifo_op);
+ writel(SIRFSOC_SPI_FIFO_START,
+ sspi->base + sspi->regs->txfifo_op);
+ }
if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) {
dev_err(&spi->dev, "transfer timeout\n");
dmaengine_terminate_all(sspi->rx_chan);
@@ -398,15 +588,21 @@ static void spi_sirfsoc_dma_transfer(struct spi_device *spi,
*/
if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) {
dev_err(&spi->dev, "transfer timeout\n");
+ if (sspi->type == SIRF_USP_SPI_P2 ||
+ sspi->type == SIRF_USP_SPI_A7)
+ writel(0, sspi->base + sspi->regs->tx_rx_en);
dmaengine_terminate_all(sspi->tx_chan);
}
dma_unmap_single(&spi->dev, sspi->src_start, t->len, DMA_TO_DEVICE);
dma_unmap_single(&spi->dev, sspi->dst_start, t->len, DMA_FROM_DEVICE);
/* TX, RX FIFO stop */
- writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
- writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
- if (sspi->left_tx_word >= SIRFSOC_SPI_DAT_FRM_LEN_MAX)
- writel(0, sspi->base + SIRFSOC_SPI_TX_RX_EN);
+ writel(0, sspi->base + sspi->regs->rxfifo_op);
+ writel(0, sspi->base + sspi->regs->txfifo_op);
+ if (sspi->left_tx_word >= sspi->dat_max_frm_len)
+ writel(0, sspi->base + sspi->regs->tx_rx_en);
+ if (sspi->type == SIRF_USP_SPI_P2 ||
+ sspi->type == SIRF_USP_SPI_A7)
+ writel(0, sspi->base + sspi->regs->tx_rx_en);
}
static void spi_sirfsoc_pio_transfer(struct spi_device *spi,
@@ -414,57 +610,105 @@ static void spi_sirfsoc_pio_transfer(struct spi_device *spi,
{
struct sirfsoc_spi *sspi;
int timeout = t->len * 10;
+ unsigned int data_units;
sspi = spi_master_get_devdata(spi->master);
do {
writel(SIRFSOC_SPI_FIFO_RESET,
- sspi->base + SIRFSOC_SPI_RXFIFO_OP);
+ sspi->base + sspi->regs->rxfifo_op);
writel(SIRFSOC_SPI_FIFO_RESET,
- sspi->base + SIRFSOC_SPI_TXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_START,
- sspi->base + SIRFSOC_SPI_RXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_START,
- sspi->base + SIRFSOC_SPI_TXFIFO_OP);
- writel(0, sspi->base + SIRFSOC_SPI_INT_EN);
- writel(SIRFSOC_SPI_INT_MASK_ALL,
- sspi->base + SIRFSOC_SPI_INT_STATUS);
- writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
- SIRFSOC_SPI_MUL_DAT_MODE | SIRFSOC_SPI_ENA_AUTO_CLR,
- sspi->base + SIRFSOC_SPI_CTRL);
- writel(min(sspi->left_tx_word, (u32)(256 / sspi->word_width))
- - 1, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
- writel(min(sspi->left_rx_word, (u32)(256 / sspi->word_width))
- - 1, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
- while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS)
- & SIRFSOC_SPI_FIFO_FULL)) && sspi->left_tx_word)
+ sspi->base + sspi->regs->txfifo_op);
+ switch (sspi->type) {
+ case SIRF_USP_SPI_P2:
+ writel(0x0, sspi->base + sspi->regs->rxfifo_op);
+ writel(0x0, sspi->base + sspi->regs->txfifo_op);
+ writel(0, sspi->base + sspi->regs->int_en);
+ writel(readl(sspi->base + sspi->regs->int_st),
+ sspi->base + sspi->regs->int_st);
+ writel(min((sspi->left_tx_word * sspi->word_width),
+ sspi->fifo_size),
+ sspi->base + sspi->regs->tx_dma_io_len);
+ writel(min((sspi->left_rx_word * sspi->word_width),
+ sspi->fifo_size),
+ sspi->base + sspi->regs->rx_dma_io_len);
+ break;
+ case SIRF_USP_SPI_A7:
+ writel(0x0, sspi->base + sspi->regs->rxfifo_op);
+ writel(0x0, sspi->base + sspi->regs->txfifo_op);
+ writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr);
+ writel(readl(sspi->base + sspi->regs->int_st),
+ sspi->base + sspi->regs->int_st);
+ writel(min((sspi->left_tx_word * sspi->word_width),
+ sspi->fifo_size),
+ sspi->base + sspi->regs->tx_dma_io_len);
+ writel(min((sspi->left_rx_word * sspi->word_width),
+ sspi->fifo_size),
+ sspi->base + sspi->regs->rx_dma_io_len);
+ break;
+ case SIRF_REAL_SPI:
+ writel(SIRFSOC_SPI_FIFO_START,
+ sspi->base + sspi->regs->rxfifo_op);
+ writel(SIRFSOC_SPI_FIFO_START,
+ sspi->base + sspi->regs->txfifo_op);
+ writel(0, sspi->base + sspi->regs->int_en);
+ writel(readl(sspi->base + sspi->regs->int_st),
+ sspi->base + sspi->regs->int_st);
+ writel(readl(sspi->base + sspi->regs->spi_ctrl) |
+ SIRFSOC_SPI_MUL_DAT_MODE |
+ SIRFSOC_SPI_ENA_AUTO_CLR,
+ sspi->base + sspi->regs->spi_ctrl);
+ data_units = sspi->fifo_size / sspi->word_width;
+ writel(min(sspi->left_tx_word, data_units) - 1,
+ sspi->base + sspi->regs->tx_dma_io_len);
+ writel(min(sspi->left_rx_word, data_units) - 1,
+ sspi->base + sspi->regs->rx_dma_io_len);
+ break;
+ }
+ while (!((readl(sspi->base + sspi->regs->txfifo_st)
+ & SIRFSOC_SPI_FIFO_FULL_MASK(sspi))) &&
+ sspi->left_tx_word)
sspi->tx_word(sspi);
writel(SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN |
SIRFSOC_SPI_TX_UFLOW_INT_EN |
SIRFSOC_SPI_RX_OFLOW_INT_EN |
SIRFSOC_SPI_RX_IO_DMA_INT_EN,
- sspi->base + SIRFSOC_SPI_INT_EN);
+ sspi->base + sspi->regs->int_en);
writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN,
- sspi->base + SIRFSOC_SPI_TX_RX_EN);
+ sspi->base + sspi->regs->tx_rx_en);
+ if (sspi->type == SIRF_USP_SPI_P2 ||
+ sspi->type == SIRF_USP_SPI_A7) {
+ writel(SIRFSOC_SPI_FIFO_START,
+ sspi->base + sspi->regs->rxfifo_op);
+ writel(SIRFSOC_SPI_FIFO_START,
+ sspi->base + sspi->regs->txfifo_op);
+ }
if (!wait_for_completion_timeout(&sspi->tx_done, timeout) ||
!wait_for_completion_timeout(&sspi->rx_done, timeout)) {
dev_err(&spi->dev, "transfer timeout\n");
+ if (sspi->type == SIRF_USP_SPI_P2 ||
+ sspi->type == SIRF_USP_SPI_A7)
+ writel(0, sspi->base + sspi->regs->tx_rx_en);
break;
}
- while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS)
- & SIRFSOC_SPI_FIFO_EMPTY)) && sspi->left_rx_word)
+ while (!((readl(sspi->base + sspi->regs->rxfifo_st)
+ & SIRFSOC_SPI_FIFO_EMPTY_MASK(sspi))) &&
+ sspi->left_rx_word)
sspi->rx_word(sspi);
- writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
- writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
+ if (sspi->type == SIRF_USP_SPI_P2 ||
+ sspi->type == SIRF_USP_SPI_A7)
+ writel(0, sspi->base + sspi->regs->tx_rx_en);
+ writel(0, sspi->base + sspi->regs->rxfifo_op);
+ writel(0, sspi->base + sspi->regs->txfifo_op);
} while (sspi->left_tx_word != 0 || sspi->left_rx_word != 0);
}
static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct sirfsoc_spi *sspi;
- sspi = spi_master_get_devdata(spi->master);
- sspi->tx = t->tx_buf ? t->tx_buf : sspi->dummypage;
- sspi->rx = t->rx_buf ? t->rx_buf : sspi->dummypage;
+ sspi = spi_master_get_devdata(spi->master);
+ sspi->tx = t->tx_buf;
+ sspi->rx = t->rx_buf;
sspi->left_tx_word = sspi->left_rx_word = t->len / sspi->word_width;
reinit_completion(&sspi->rx_done);
reinit_completion(&sspi->tx_done);
@@ -473,7 +717,7 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
* null, just fill command data into command register and wait for its
* completion.
*/
- if (sspi->tx_by_cmd)
+ if (sspi->type == SIRF_REAL_SPI && sspi->tx_by_cmd)
spi_sirfsoc_cmd_transfer(spi, t);
else if (IS_DMA_VALID(t))
spi_sirfsoc_dma_transfer(spi, t);
@@ -488,22 +732,49 @@ static void spi_sirfsoc_chipselect(struct spi_device *spi, int value)
struct sirfsoc_spi *sspi = spi_master_get_devdata(spi->master);
if (sspi->hw_cs) {
- u32 regval = readl(sspi->base + SIRFSOC_SPI_CTRL);
- switch (value) {
- case BITBANG_CS_ACTIVE:
- if (spi->mode & SPI_CS_HIGH)
- regval |= SIRFSOC_SPI_CS_IO_OUT;
- else
- regval &= ~SIRFSOC_SPI_CS_IO_OUT;
+ u32 regval;
+
+ switch (sspi->type) {
+ case SIRF_REAL_SPI:
+ regval = readl(sspi->base + sspi->regs->spi_ctrl);
+ switch (value) {
+ case BITBANG_CS_ACTIVE:
+ if (spi->mode & SPI_CS_HIGH)
+ regval |= SIRFSOC_SPI_CS_IO_OUT;
+ else
+ regval &= ~SIRFSOC_SPI_CS_IO_OUT;
+ break;
+ case BITBANG_CS_INACTIVE:
+ if (spi->mode & SPI_CS_HIGH)
+ regval &= ~SIRFSOC_SPI_CS_IO_OUT;
+ else
+ regval |= SIRFSOC_SPI_CS_IO_OUT;
+ break;
+ }
+ writel(regval, sspi->base + sspi->regs->spi_ctrl);
break;
- case BITBANG_CS_INACTIVE:
- if (spi->mode & SPI_CS_HIGH)
- regval &= ~SIRFSOC_SPI_CS_IO_OUT;
- else
- regval |= SIRFSOC_SPI_CS_IO_OUT;
+ case SIRF_USP_SPI_P2:
+ case SIRF_USP_SPI_A7:
+ regval = readl(sspi->base +
+ sspi->regs->usp_pin_io_data);
+ switch (value) {
+ case BITBANG_CS_ACTIVE:
+ if (spi->mode & SPI_CS_HIGH)
+ regval |= SIRFSOC_USP_CS_HIGH_VALUE;
+ else
+ regval &= ~(SIRFSOC_USP_CS_HIGH_VALUE);
+ break;
+ case BITBANG_CS_INACTIVE:
+ if (spi->mode & SPI_CS_HIGH)
+ regval &= ~(SIRFSOC_USP_CS_HIGH_VALUE);
+ else
+ regval |= SIRFSOC_USP_CS_HIGH_VALUE;
+ break;
+ }
+ writel(regval,
+ sspi->base + sspi->regs->usp_pin_io_data);
break;
}
- writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
} else {
switch (value) {
case BITBANG_CS_ACTIVE:
@@ -518,27 +789,102 @@ static void spi_sirfsoc_chipselect(struct spi_device *spi, int value)
}
}
+static int spi_sirfsoc_config_mode(struct spi_device *spi)
+{
+ struct sirfsoc_spi *sspi;
+ u32 regval, usp_mode1;
+
+ sspi = spi_master_get_devdata(spi->master);
+ regval = readl(sspi->base + sspi->regs->spi_ctrl);
+ usp_mode1 = readl(sspi->base + sspi->regs->usp_mode1);
+ if (!(spi->mode & SPI_CS_HIGH)) {
+ regval |= SIRFSOC_SPI_CS_IDLE_STAT;
+ usp_mode1 &= ~SIRFSOC_USP_CS_HIGH_VALID;
+ } else {
+ regval &= ~SIRFSOC_SPI_CS_IDLE_STAT;
+ usp_mode1 |= SIRFSOC_USP_CS_HIGH_VALID;
+ }
+ if (!(spi->mode & SPI_LSB_FIRST)) {
+ regval |= SIRFSOC_SPI_TRAN_MSB;
+ usp_mode1 &= ~SIRFSOC_USP_LSB;
+ } else {
+ regval &= ~SIRFSOC_SPI_TRAN_MSB;
+ usp_mode1 |= SIRFSOC_USP_LSB;
+ }
+ if (spi->mode & SPI_CPOL) {
+ regval |= SIRFSOC_SPI_CLK_IDLE_STAT;
+ usp_mode1 |= SIRFSOC_USP_SCLK_IDLE_STAT;
+ } else {
+ regval &= ~SIRFSOC_SPI_CLK_IDLE_STAT;
+ usp_mode1 &= ~SIRFSOC_USP_SCLK_IDLE_STAT;
+ }
+ /*
+ * Data should be driven at least 1/2 cycle before the fetch edge
+ * to make sure that data gets stable at the fetch edge.
+ */
+ if (((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA)) ||
+ (!(spi->mode & SPI_CPOL) && !(spi->mode & SPI_CPHA))) {
+ regval &= ~SIRFSOC_SPI_DRV_POS_EDGE;
+ usp_mode1 |= (SIRFSOC_USP_TXD_FALLING_EDGE |
+ SIRFSOC_USP_RXD_FALLING_EDGE);
+ } else {
+ regval |= SIRFSOC_SPI_DRV_POS_EDGE;
+ usp_mode1 &= ~(SIRFSOC_USP_RXD_FALLING_EDGE |
+ SIRFSOC_USP_TXD_FALLING_EDGE);
+ }
+ writel((SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size - 2) <<
+ SIRFSOC_SPI_FIFO_SC_OFFSET) |
+ (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size / 2) <<
+ SIRFSOC_SPI_FIFO_LC_OFFSET) |
+ (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, 2) <<
+ SIRFSOC_SPI_FIFO_HC_OFFSET),
+ sspi->base + sspi->regs->txfifo_level_chk);
+ writel((SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, 2) <<
+ SIRFSOC_SPI_FIFO_SC_OFFSET) |
+ (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size / 2) <<
+ SIRFSOC_SPI_FIFO_LC_OFFSET) |
+ (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size - 2) <<
+ SIRFSOC_SPI_FIFO_HC_OFFSET),
+ sspi->base + sspi->regs->rxfifo_level_chk);
+ /*
+ * it should never set to hardware cs mode because in hardware cs mode,
+ * cs signal can't controlled by driver.
+ */
+ switch (sspi->type) {
+ case SIRF_REAL_SPI:
+ regval |= SIRFSOC_SPI_CS_IO_MODE;
+ writel(regval, sspi->base + sspi->regs->spi_ctrl);
+ break;
+ case SIRF_USP_SPI_P2:
+ case SIRF_USP_SPI_A7:
+ usp_mode1 |= SIRFSOC_USP_SYNC_MODE;
+ usp_mode1 |= SIRFSOC_USP_TFS_IO_MODE;
+ usp_mode1 &= ~SIRFSOC_USP_TFS_IO_INPUT;
+ writel(usp_mode1, sspi->base + sspi->regs->usp_mode1);
+ break;
+ }
+
+ return 0;
+}
+
static int
spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct sirfsoc_spi *sspi;
u8 bits_per_word = 0;
int hz = 0;
- u32 regval;
- u32 txfifo_ctrl, rxfifo_ctrl;
- u32 fifo_size = SIRFSOC_SPI_FIFO_SIZE / 4;
+ u32 regval, txfifo_ctrl, rxfifo_ctrl, tx_frm_ctl, rx_frm_ctl, usp_mode2;
sspi = spi_master_get_devdata(spi->master);
bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
hz = t && t->speed_hz ? t->speed_hz : spi->max_speed_hz;
- regval = (sspi->ctrl_freq / (2 * hz)) - 1;
+ usp_mode2 = regval = (sspi->ctrl_freq / (2 * hz)) - 1;
if (regval > 0xFFFF || regval < 0) {
dev_err(&spi->dev, "Speed %d not supported\n", hz);
return -EINVAL;
}
-
switch (bits_per_word) {
case 8:
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_8;
@@ -559,94 +905,177 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
sspi->tx_word = spi_sirfsoc_tx_word_u32;
break;
default:
- BUG();
+ dev_err(&spi->dev, "bpw %d not supported\n", bits_per_word);
+ return -EINVAL;
}
-
sspi->word_width = DIV_ROUND_UP(bits_per_word, 8);
- txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
- (sspi->word_width >> 1);
- rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
- (sspi->word_width >> 1);
-
- if (!(spi->mode & SPI_CS_HIGH))
- regval |= SIRFSOC_SPI_CS_IDLE_STAT;
- if (!(spi->mode & SPI_LSB_FIRST))
- regval |= SIRFSOC_SPI_TRAN_MSB;
- if (spi->mode & SPI_CPOL)
- regval |= SIRFSOC_SPI_CLK_IDLE_STAT;
-
- /*
- * Data should be driven at least 1/2 cycle before the fetch edge
- * to make sure that data gets stable at the fetch edge.
- */
- if (((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA)) ||
- (!(spi->mode & SPI_CPOL) && !(spi->mode & SPI_CPHA)))
- regval &= ~SIRFSOC_SPI_DRV_POS_EDGE;
- else
- regval |= SIRFSOC_SPI_DRV_POS_EDGE;
-
- writel(SIRFSOC_SPI_FIFO_SC(fifo_size - 2) |
- SIRFSOC_SPI_FIFO_LC(fifo_size / 2) |
- SIRFSOC_SPI_FIFO_HC(2),
- sspi->base + SIRFSOC_SPI_TXFIFO_LEVEL_CHK);
- writel(SIRFSOC_SPI_FIFO_SC(2) |
- SIRFSOC_SPI_FIFO_LC(fifo_size / 2) |
- SIRFSOC_SPI_FIFO_HC(fifo_size - 2),
- sspi->base + SIRFSOC_SPI_RXFIFO_LEVEL_CHK);
- writel(txfifo_ctrl, sspi->base + SIRFSOC_SPI_TXFIFO_CTRL);
- writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL);
-
- if (t && t->tx_buf && !t->rx_buf && (t->len <= SIRFSOC_MAX_CMD_BYTES)) {
- regval |= (SIRFSOC_SPI_CMD_BYTE_NUM((t->len - 1)) |
- SIRFSOC_SPI_CMD_MODE);
- sspi->tx_by_cmd = true;
- } else {
- regval &= ~SIRFSOC_SPI_CMD_MODE;
- sspi->tx_by_cmd = false;
+ txfifo_ctrl = (((sspi->fifo_size / 2) &
+ SIRFSOC_SPI_FIFO_THD_MASK(sspi))
+ << SIRFSOC_SPI_FIFO_THD_OFFSET) |
+ (sspi->word_width >> 1);
+ rxfifo_ctrl = (((sspi->fifo_size / 2) &
+ SIRFSOC_SPI_FIFO_THD_MASK(sspi))
+ << SIRFSOC_SPI_FIFO_THD_OFFSET) |
+ (sspi->word_width >> 1);
+ writel(txfifo_ctrl, sspi->base + sspi->regs->txfifo_ctrl);
+ writel(rxfifo_ctrl, sspi->base + sspi->regs->rxfifo_ctrl);
+ if (sspi->type == SIRF_USP_SPI_P2 ||
+ sspi->type == SIRF_USP_SPI_A7) {
+ tx_frm_ctl = 0;
+ tx_frm_ctl |= ((bits_per_word - 1) & SIRFSOC_USP_TX_DATA_MASK)
+ << SIRFSOC_USP_TX_DATA_OFFSET;
+ tx_frm_ctl |= ((bits_per_word + 1 + SIRFSOC_USP_TXD_DELAY_LEN
+ - 1) & SIRFSOC_USP_TX_SYNC_MASK) <<
+ SIRFSOC_USP_TX_SYNC_OFFSET;
+ tx_frm_ctl |= ((bits_per_word + 1 + SIRFSOC_USP_TXD_DELAY_LEN
+ + 2 - 1) & SIRFSOC_USP_TX_FRAME_MASK) <<
+ SIRFSOC_USP_TX_FRAME_OFFSET;
+ tx_frm_ctl |= ((bits_per_word - 1) &
+ SIRFSOC_USP_TX_SHIFTER_MASK) <<
+ SIRFSOC_USP_TX_SHIFTER_OFFSET;
+ rx_frm_ctl = 0;
+ rx_frm_ctl |= ((bits_per_word - 1) & SIRFSOC_USP_RX_DATA_MASK)
+ << SIRFSOC_USP_RX_DATA_OFFSET;
+ rx_frm_ctl |= ((bits_per_word + 1 + SIRFSOC_USP_RXD_DELAY_LEN
+ + 2 - 1) & SIRFSOC_USP_RX_FRAME_MASK) <<
+ SIRFSOC_USP_RX_FRAME_OFFSET;
+ rx_frm_ctl |= ((bits_per_word - 1)
+ & SIRFSOC_USP_RX_SHIFTER_MASK) <<
+ SIRFSOC_USP_RX_SHIFTER_OFFSET;
+ writel(tx_frm_ctl | (((usp_mode2 >> 10) &
+ SIRFSOC_USP_CLK_10_11_MASK) <<
+ SIRFSOC_USP_CLK_10_11_OFFSET),
+ sspi->base + sspi->regs->usp_tx_frame_ctrl);
+ writel(rx_frm_ctl | (((usp_mode2 >> 12) &
+ SIRFSOC_USP_CLK_12_15_MASK) <<
+ SIRFSOC_USP_CLK_12_15_OFFSET),
+ sspi->base + sspi->regs->usp_rx_frame_ctrl);
+ writel(readl(sspi->base + sspi->regs->usp_mode2) |
+ ((usp_mode2 & SIRFSOC_USP_CLK_DIVISOR_MASK) <<
+ SIRFSOC_USP_CLK_DIVISOR_OFFSET) |
+ (SIRFSOC_USP_RXD_DELAY_LEN <<
+ SIRFSOC_USP_RXD_DELAY_OFFSET) |
+ (SIRFSOC_USP_TXD_DELAY_LEN <<
+ SIRFSOC_USP_TXD_DELAY_OFFSET),
+ sspi->base + sspi->regs->usp_mode2);
+ }
+ if (sspi->type == SIRF_REAL_SPI)
+ writel(regval, sspi->base + sspi->regs->spi_ctrl);
+ spi_sirfsoc_config_mode(spi);
+ if (sspi->type == SIRF_REAL_SPI) {
+ if (t && t->tx_buf && !t->rx_buf &&
+ (t->len <= SIRFSOC_MAX_CMD_BYTES)) {
+ sspi->tx_by_cmd = true;
+ writel(readl(sspi->base + sspi->regs->spi_ctrl) |
+ (SIRFSOC_SPI_CMD_BYTE_NUM((t->len - 1)) |
+ SIRFSOC_SPI_CMD_MODE),
+ sspi->base + sspi->regs->spi_ctrl);
+ } else {
+ sspi->tx_by_cmd = false;
+ writel(readl(sspi->base + sspi->regs->spi_ctrl) &
+ ~SIRFSOC_SPI_CMD_MODE,
+ sspi->base + sspi->regs->spi_ctrl);
+ }
}
- /*
- * it should never set to hardware cs mode because in hardware cs mode,
- * cs signal can't controlled by driver.
- */
- regval |= SIRFSOC_SPI_CS_IO_MODE;
- writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
-
if (IS_DMA_VALID(t)) {
/* Enable DMA mode for RX, TX */
- writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
+ writel(0, sspi->base + sspi->regs->tx_dma_io_ctrl);
writel(SIRFSOC_SPI_RX_DMA_FLUSH,
- sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
+ sspi->base + sspi->regs->rx_dma_io_ctrl);
} else {
/* Enable IO mode for RX, TX */
writel(SIRFSOC_SPI_IO_MODE_SEL,
- sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
+ sspi->base + sspi->regs->tx_dma_io_ctrl);
writel(SIRFSOC_SPI_IO_MODE_SEL,
- sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
+ sspi->base + sspi->regs->rx_dma_io_ctrl);
}
-
return 0;
}
static int spi_sirfsoc_setup(struct spi_device *spi)
{
struct sirfsoc_spi *sspi;
+ int ret = 0;
sspi = spi_master_get_devdata(spi->master);
-
if (spi->cs_gpio == -ENOENT)
sspi->hw_cs = true;
- else
+ else {
sspi->hw_cs = false;
- return spi_sirfsoc_setup_transfer(spi, NULL);
+ if (!spi_get_ctldata(spi)) {
+ void *cs = kmalloc(sizeof(int), GFP_KERNEL);
+ if (!cs) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ ret = gpio_is_valid(spi->cs_gpio);
+ if (!ret) {
+ dev_err(&spi->dev, "no valid gpio\n");
+ ret = -ENOENT;
+ goto exit;
+ }
+ ret = gpio_request(spi->cs_gpio, DRIVER_NAME);
+ if (ret) {
+ dev_err(&spi->dev, "failed to request gpio\n");
+ goto exit;
+ }
+ spi_set_ctldata(spi, cs);
+ }
+ }
+ spi_sirfsoc_config_mode(spi);
+ spi_sirfsoc_chipselect(spi, BITBANG_CS_INACTIVE);
+exit:
+ return ret;
+}
+
+static void spi_sirfsoc_cleanup(struct spi_device *spi)
+{
+ if (spi_get_ctldata(spi)) {
+ gpio_free(spi->cs_gpio);
+ kfree(spi_get_ctldata(spi));
+ }
}
+static const struct sirf_spi_comp_data sirf_real_spi = {
+ .regs = &real_spi_register,
+ .type = SIRF_REAL_SPI,
+ .dat_max_frm_len = 64 * 1024,
+ .fifo_size = 256,
+};
+
+static const struct sirf_spi_comp_data sirf_usp_spi_p2 = {
+ .regs = &usp_spi_register,
+ .type = SIRF_USP_SPI_P2,
+ .dat_max_frm_len = 1024 * 1024,
+ .fifo_size = 128,
+ .hwinit = sirfsoc_usp_hwinit,
+};
+
+static const struct sirf_spi_comp_data sirf_usp_spi_a7 = {
+ .regs = &usp_spi_register,
+ .type = SIRF_USP_SPI_A7,
+ .dat_max_frm_len = 1024 * 1024,
+ .fifo_size = 512,
+ .hwinit = sirfsoc_usp_hwinit,
+};
+
+static const struct of_device_id spi_sirfsoc_of_match[] = {
+ { .compatible = "sirf,prima2-spi", .data = &sirf_real_spi},
+ { .compatible = "sirf,prima2-usp-spi", .data = &sirf_usp_spi_p2},
+ { .compatible = "sirf,atlas7-usp-spi", .data = &sirf_usp_spi_a7},
+ {}
+};
+MODULE_DEVICE_TABLE(of, spi_sirfsoc_of_match);
+
static int spi_sirfsoc_probe(struct platform_device *pdev)
{
struct sirfsoc_spi *sspi;
struct spi_master *master;
struct resource *mem_res;
+ struct sirf_spi_comp_data *spi_comp_data;
int irq;
- int i, ret;
+ int ret;
+ const struct of_device_id *match;
ret = device_reset(&pdev->dev);
if (ret) {
@@ -659,16 +1088,22 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Unable to allocate SPI master\n");
return -ENOMEM;
}
+ match = of_match_node(spi_sirfsoc_of_match, pdev->dev.of_node);
platform_set_drvdata(pdev, master);
sspi = spi_master_get_devdata(master);
-
+ sspi->fifo_full_offset = ilog2(sspi->fifo_size);
+ spi_comp_data = (struct sirf_spi_comp_data *)match->data;
+ sspi->regs = spi_comp_data->regs;
+ sspi->type = spi_comp_data->type;
+ sspi->fifo_level_chk_mask = (sspi->fifo_size / 4) - 1;
+ sspi->dat_max_frm_len = spi_comp_data->dat_max_frm_len;
+ sspi->fifo_size = spi_comp_data->fifo_size;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sspi->base = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(sspi->base)) {
ret = PTR_ERR(sspi->base);
goto free_master;
}
-
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = -ENXIO;
@@ -684,11 +1119,13 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
sspi->bitbang.setup_transfer = spi_sirfsoc_setup_transfer;
sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer;
sspi->bitbang.master->setup = spi_sirfsoc_setup;
+ sspi->bitbang.master->cleanup = spi_sirfsoc_cleanup;
master->bus_num = pdev->id;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH;
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(12) |
SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
master->max_speed_hz = SIRFSOC_SPI_DEFAULT_FRQ;
+ master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
sspi->bitbang.master->dev.of_node = pdev->dev.of_node;
/* request DMA channels */
@@ -711,47 +1148,19 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
goto free_tx_dma;
}
clk_prepare_enable(sspi->clk);
+ if (spi_comp_data->hwinit)
+ spi_comp_data->hwinit(sspi);
sspi->ctrl_freq = clk_get_rate(sspi->clk);
init_completion(&sspi->rx_done);
init_completion(&sspi->tx_done);
- writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
- /* We are not using dummy delay between command and data */
- writel(0, sspi->base + SIRFSOC_SPI_DUMMY_DELAY_CTL);
-
- sspi->dummypage = kmalloc(2 * PAGE_SIZE, GFP_KERNEL);
- if (!sspi->dummypage) {
- ret = -ENOMEM;
- goto free_clk;
- }
-
ret = spi_bitbang_start(&sspi->bitbang);
if (ret)
- goto free_dummypage;
- for (i = 0; master->cs_gpios && i < master->num_chipselect; i++) {
- if (master->cs_gpios[i] == -ENOENT)
- continue;
- if (!gpio_is_valid(master->cs_gpios[i])) {
- dev_err(&pdev->dev, "no valid gpio\n");
- ret = -EINVAL;
- goto free_dummypage;
- }
- ret = devm_gpio_request(&pdev->dev,
- master->cs_gpios[i], DRIVER_NAME);
- if (ret) {
- dev_err(&pdev->dev, "failed to request gpio\n");
- goto free_dummypage;
- }
- }
+ goto free_clk;
dev_info(&pdev->dev, "registerred, bus number = %d\n", master->bus_num);
return 0;
-free_dummypage:
- kfree(sspi->dummypage);
free_clk:
clk_disable_unprepare(sspi->clk);
clk_put(sspi->clk);
@@ -772,9 +1181,7 @@ static int spi_sirfsoc_remove(struct platform_device *pdev)
master = platform_get_drvdata(pdev);
sspi = spi_master_get_devdata(master);
-
spi_bitbang_stop(&sspi->bitbang);
- kfree(sspi->dummypage);
clk_disable_unprepare(sspi->clk);
clk_put(sspi->clk);
dma_release_channel(sspi->rx_chan);
@@ -804,24 +1211,17 @@ static int spi_sirfsoc_resume(struct device *dev)
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
clk_enable(sspi->clk);
- writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
- writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-
- return spi_master_resume(master);
+ writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->txfifo_op);
+ writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->rxfifo_op);
+ writel(SIRFSOC_SPI_FIFO_START, sspi->base + sspi->regs->txfifo_op);
+ writel(SIRFSOC_SPI_FIFO_START, sspi->base + sspi->regs->rxfifo_op);
+ return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(spi_sirfsoc_pm_ops, spi_sirfsoc_suspend,
spi_sirfsoc_resume);
-static const struct of_device_id spi_sirfsoc_of_match[] = {
- { .compatible = "sirf,prima2-spi", },
- {}
-};
-MODULE_DEVICE_TABLE(of, spi_sirfsoc_of_match);
-
static struct platform_driver spi_sirfsoc_driver = {
.driver = {
.name = DRIVER_NAME,
@@ -835,4 +1235,5 @@ module_platform_driver(spi_sirfsoc_driver);
MODULE_DESCRIPTION("SiRF SoC SPI master driver");
MODULE_AUTHOR("Zhiwu Song <Zhiwu.Song@csr.com>");
MODULE_AUTHOR("Barry Song <Baohua.Song@csr.com>");
+MODULE_AUTHOR("Qipan Li <Qipan.Li@csr.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
new file mode 100644
index 000000000000..87b20a511a6b
--- /dev/null
+++ b/drivers/spi/spi-zynqmp-gqspi.c
@@ -0,0 +1,1122 @@
+/*
+ * Xilinx Zynq UltraScale+ MPSoC Quad-SPI (QSPI) controller driver
+ * (master mode only)
+ *
+ * Copyright (C) 2009 - 2015 Xilinx, 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+/* Generic QSPI register offsets */
+#define GQSPI_CONFIG_OFST 0x00000100
+#define GQSPI_ISR_OFST 0x00000104
+#define GQSPI_IDR_OFST 0x0000010C
+#define GQSPI_IER_OFST 0x00000108
+#define GQSPI_IMASK_OFST 0x00000110
+#define GQSPI_EN_OFST 0x00000114
+#define GQSPI_TXD_OFST 0x0000011C
+#define GQSPI_RXD_OFST 0x00000120
+#define GQSPI_TX_THRESHOLD_OFST 0x00000128
+#define GQSPI_RX_THRESHOLD_OFST 0x0000012C
+#define GQSPI_LPBK_DLY_ADJ_OFST 0x00000138
+#define GQSPI_GEN_FIFO_OFST 0x00000140
+#define GQSPI_SEL_OFST 0x00000144
+#define GQSPI_GF_THRESHOLD_OFST 0x00000150
+#define GQSPI_FIFO_CTRL_OFST 0x0000014C
+#define GQSPI_QSPIDMA_DST_CTRL_OFST 0x0000080C
+#define GQSPI_QSPIDMA_DST_SIZE_OFST 0x00000804
+#define GQSPI_QSPIDMA_DST_STS_OFST 0x00000808
+#define GQSPI_QSPIDMA_DST_I_STS_OFST 0x00000814
+#define GQSPI_QSPIDMA_DST_I_EN_OFST 0x00000818
+#define GQSPI_QSPIDMA_DST_I_DIS_OFST 0x0000081C
+#define GQSPI_QSPIDMA_DST_I_MASK_OFST 0x00000820
+#define GQSPI_QSPIDMA_DST_ADDR_OFST 0x00000800
+#define GQSPI_QSPIDMA_DST_ADDR_MSB_OFST 0x00000828
+
+/* GQSPI register bit masks */
+#define GQSPI_SEL_MASK 0x00000001
+#define GQSPI_EN_MASK 0x00000001
+#define GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK 0x00000020
+#define GQSPI_ISR_WR_TO_CLR_MASK 0x00000002
+#define GQSPI_IDR_ALL_MASK 0x00000FBE
+#define GQSPI_CFG_MODE_EN_MASK 0xC0000000
+#define GQSPI_CFG_GEN_FIFO_START_MODE_MASK 0x20000000
+#define GQSPI_CFG_ENDIAN_MASK 0x04000000
+#define GQSPI_CFG_EN_POLL_TO_MASK 0x00100000
+#define GQSPI_CFG_WP_HOLD_MASK 0x00080000
+#define GQSPI_CFG_BAUD_RATE_DIV_MASK 0x00000038
+#define GQSPI_CFG_CLK_PHA_MASK 0x00000004
+#define GQSPI_CFG_CLK_POL_MASK 0x00000002
+#define GQSPI_CFG_START_GEN_FIFO_MASK 0x10000000
+#define GQSPI_GENFIFO_IMM_DATA_MASK 0x000000FF
+#define GQSPI_GENFIFO_DATA_XFER 0x00000100
+#define GQSPI_GENFIFO_EXP 0x00000200
+#define GQSPI_GENFIFO_MODE_SPI 0x00000400
+#define GQSPI_GENFIFO_MODE_DUALSPI 0x00000800
+#define GQSPI_GENFIFO_MODE_QUADSPI 0x00000C00
+#define GQSPI_GENFIFO_MODE_MASK 0x00000C00
+#define GQSPI_GENFIFO_CS_LOWER 0x00001000
+#define GQSPI_GENFIFO_CS_UPPER 0x00002000
+#define GQSPI_GENFIFO_BUS_LOWER 0x00004000
+#define GQSPI_GENFIFO_BUS_UPPER 0x00008000
+#define GQSPI_GENFIFO_BUS_BOTH 0x0000C000
+#define GQSPI_GENFIFO_BUS_MASK 0x0000C000
+#define GQSPI_GENFIFO_TX 0x00010000
+#define GQSPI_GENFIFO_RX 0x00020000
+#define GQSPI_GENFIFO_STRIPE 0x00040000
+#define GQSPI_GENFIFO_POLL 0x00080000
+#define GQSPI_GENFIFO_EXP_START 0x00000100
+#define GQSPI_FIFO_CTRL_RST_RX_FIFO_MASK 0x00000004
+#define GQSPI_FIFO_CTRL_RST_TX_FIFO_MASK 0x00000002
+#define GQSPI_FIFO_CTRL_RST_GEN_FIFO_MASK 0x00000001
+#define GQSPI_ISR_RXEMPTY_MASK 0x00000800
+#define GQSPI_ISR_GENFIFOFULL_MASK 0x00000400
+#define GQSPI_ISR_GENFIFONOT_FULL_MASK 0x00000200
+#define GQSPI_ISR_TXEMPTY_MASK 0x00000100
+#define GQSPI_ISR_GENFIFOEMPTY_MASK 0x00000080
+#define GQSPI_ISR_RXFULL_MASK 0x00000020
+#define GQSPI_ISR_RXNEMPTY_MASK 0x00000010
+#define GQSPI_ISR_TXFULL_MASK 0x00000008
+#define GQSPI_ISR_TXNOT_FULL_MASK 0x00000004
+#define GQSPI_ISR_POLL_TIME_EXPIRE_MASK 0x00000002
+#define GQSPI_IER_TXNOT_FULL_MASK 0x00000004
+#define GQSPI_IER_RXEMPTY_MASK 0x00000800
+#define GQSPI_IER_POLL_TIME_EXPIRE_MASK 0x00000002
+#define GQSPI_IER_RXNEMPTY_MASK 0x00000010
+#define GQSPI_IER_GENFIFOEMPTY_MASK 0x00000080
+#define GQSPI_IER_TXEMPTY_MASK 0x00000100
+#define GQSPI_QSPIDMA_DST_INTR_ALL_MASK 0x000000FE
+#define GQSPI_QSPIDMA_DST_STS_WTC 0x0000E000
+#define GQSPI_CFG_MODE_EN_DMA_MASK 0x80000000
+#define GQSPI_ISR_IDR_MASK 0x00000994
+#define GQSPI_QSPIDMA_DST_I_EN_DONE_MASK 0x00000002
+#define GQSPI_QSPIDMA_DST_I_STS_DONE_MASK 0x00000002
+#define GQSPI_IRQ_MASK 0x00000980
+
+#define GQSPI_CFG_BAUD_RATE_DIV_SHIFT 3
+#define GQSPI_GENFIFO_CS_SETUP 0x4
+#define GQSPI_GENFIFO_CS_HOLD 0x3
+#define GQSPI_TXD_DEPTH 64
+#define GQSPI_RX_FIFO_THRESHOLD 32
+#define GQSPI_RX_FIFO_FILL (GQSPI_RX_FIFO_THRESHOLD * 4)
+#define GQSPI_TX_FIFO_THRESHOLD_RESET_VAL 32
+#define GQSPI_TX_FIFO_FILL (GQSPI_TXD_DEPTH -\
+ GQSPI_TX_FIFO_THRESHOLD_RESET_VAL)
+#define GQSPI_GEN_FIFO_THRESHOLD_RESET_VAL 0X10
+#define GQSPI_QSPIDMA_DST_CTRL_RESET_VAL 0x803FFA00
+#define GQSPI_SELECT_FLASH_CS_LOWER 0x1
+#define GQSPI_SELECT_FLASH_CS_UPPER 0x2
+#define GQSPI_SELECT_FLASH_CS_BOTH 0x3
+#define GQSPI_SELECT_FLASH_BUS_LOWER 0x1
+#define GQSPI_SELECT_FLASH_BUS_UPPER 0x2
+#define GQSPI_SELECT_FLASH_BUS_BOTH 0x3
+#define GQSPI_BAUD_DIV_MAX 7 /* Baud rate divisor maximum */
+#define GQSPI_BAUD_DIV_SHIFT 2 /* Baud rate divisor shift */
+#define GQSPI_SELECT_MODE_SPI 0x1
+#define GQSPI_SELECT_MODE_DUALSPI 0x2
+#define GQSPI_SELECT_MODE_QUADSPI 0x4
+#define GQSPI_DMA_UNALIGN 0x3
+#define GQSPI_DEFAULT_NUM_CS 1 /* Default number of chip selects */
+
+enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA};
+
+/**
+ * struct zynqmp_qspi - Defines qspi driver instance
+ * @regs: Virtual address of the QSPI controller registers
+ * @refclk: Pointer to the peripheral clock
+ * @pclk: Pointer to the APB clock
+ * @irq: IRQ number
+ * @dev: Pointer to struct device
+ * @txbuf: Pointer to the TX buffer
+ * @rxbuf: Pointer to the RX buffer
+ * @bytes_to_transfer: Number of bytes left to transfer
+ * @bytes_to_receive: Number of bytes left to receive
+ * @genfifocs: Used for chip select
+ * @genfifobus: Used to select the upper or lower bus
+ * @dma_rx_bytes: Remaining bytes to receive by DMA mode
+ * @dma_addr: DMA address after mapping the kernel buffer
+ * @genfifoentry: Used for storing the genfifoentry instruction.
+ * @mode: Defines the mode in which QSPI is operating
+ */
+struct zynqmp_qspi {
+ void __iomem *regs;
+ struct clk *refclk;
+ struct clk *pclk;
+ int irq;
+ struct device *dev;
+ const void *txbuf;
+ void *rxbuf;
+ int bytes_to_transfer;
+ int bytes_to_receive;
+ u32 genfifocs;
+ u32 genfifobus;
+ u32 dma_rx_bytes;
+ dma_addr_t dma_addr;
+ u32 genfifoentry;
+ enum mode_type mode;
+};
+
+/**
+ * zynqmp_gqspi_read: For GQSPI controller read operation
+ * @xqspi: Pointer to the zynqmp_qspi structure
+ * @offset: Offset from where to read
+ */
+static u32 zynqmp_gqspi_read(struct zynqmp_qspi *xqspi, u32 offset)
+{
+ return readl_relaxed(xqspi->regs + offset);
+}
+
+/**
+ * zynqmp_gqspi_write: For GQSPI controller write operation
+ * @xqspi: Pointer to the zynqmp_qspi structure
+ * @offset: Offset where to write
+ * @val: Value to be written
+ */
+static inline void zynqmp_gqspi_write(struct zynqmp_qspi *xqspi, u32 offset,
+ u32 val)
+{
+ writel_relaxed(val, (xqspi->regs + offset));
+}
+
+/**
+ * zynqmp_gqspi_selectslave: For selection of slave device
+ * @instanceptr: Pointer to the zynqmp_qspi structure
+ * @flashcs: For chip select
+ * @flashbus: To check which bus is selected- upper or lower
+ */
+static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr,
+ u8 slavecs, u8 slavebus)
+{
+ /*
+ * Bus and CS lines selected here will be updated in the instance and
+ * used for subsequent GENFIFO entries during transfer.
+ */
+
+ /* Choose slave select line */
+ switch (slavecs) {
+ case GQSPI_SELECT_FLASH_CS_BOTH:
+ instanceptr->genfifocs = GQSPI_GENFIFO_CS_LOWER |
+ GQSPI_GENFIFO_CS_UPPER;
+ case GQSPI_SELECT_FLASH_CS_UPPER:
+ instanceptr->genfifocs = GQSPI_GENFIFO_CS_UPPER;
+ break;
+ case GQSPI_SELECT_FLASH_CS_LOWER:
+ instanceptr->genfifocs = GQSPI_GENFIFO_CS_LOWER;
+ break;
+ default:
+ dev_warn(instanceptr->dev, "Invalid slave select\n");
+ }
+
+ /* Choose the bus */
+ switch (slavebus) {
+ case GQSPI_SELECT_FLASH_BUS_BOTH:
+ instanceptr->genfifobus = GQSPI_GENFIFO_BUS_LOWER |
+ GQSPI_GENFIFO_BUS_UPPER;
+ break;
+ case GQSPI_SELECT_FLASH_BUS_UPPER:
+ instanceptr->genfifobus = GQSPI_GENFIFO_BUS_UPPER;
+ break;
+ case GQSPI_SELECT_FLASH_BUS_LOWER:
+ instanceptr->genfifobus = GQSPI_GENFIFO_BUS_LOWER;
+ break;
+ default:
+ dev_warn(instanceptr->dev, "Invalid slave bus\n");
+ }
+}
+
+/**
+ * zynqmp_qspi_init_hw: Initialize the hardware
+ * @xqspi: Pointer to the zynqmp_qspi structure
+ *
+ * The default settings of the QSPI controller's configurable parameters on
+ * reset are
+ * - Master mode
+ * - TX threshold set to 1
+ * - RX threshold set to 1
+ * - Flash memory interface mode enabled
+ * This function performs the following actions
+ * - Disable and clear all the interrupts
+ * - Enable manual slave select
+ * - Enable manual start
+ * - Deselect all the chip select lines
+ * - Set the little endian mode of TX FIFO and
+ * - Enable the QSPI controller
+ */
+static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi)
+{
+ u32 config_reg;
+
+ /* Select the GQSPI mode */
+ zynqmp_gqspi_write(xqspi, GQSPI_SEL_OFST, GQSPI_SEL_MASK);
+ /* Clear and disable interrupts */
+ zynqmp_gqspi_write(xqspi, GQSPI_ISR_OFST,
+ zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST) |
+ GQSPI_ISR_WR_TO_CLR_MASK);
+ /* Clear the DMA STS */
+ zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST,
+ zynqmp_gqspi_read(xqspi,
+ GQSPI_QSPIDMA_DST_I_STS_OFST));
+ zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_STS_OFST,
+ zynqmp_gqspi_read(xqspi,
+ GQSPI_QSPIDMA_DST_STS_OFST) |
+ GQSPI_QSPIDMA_DST_STS_WTC);
+ zynqmp_gqspi_write(xqspi, GQSPI_IDR_OFST, GQSPI_IDR_ALL_MASK);
+ zynqmp_gqspi_write(xqspi,
+ GQSPI_QSPIDMA_DST_I_DIS_OFST,
+ GQSPI_QSPIDMA_DST_INTR_ALL_MASK);
+ /* Disable the GQSPI */
+ zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0);
+ config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);
+ config_reg &= ~GQSPI_CFG_MODE_EN_MASK;
+ /* Manual start */
+ config_reg |= GQSPI_CFG_GEN_FIFO_START_MODE_MASK;
+ /* Little endian by default */
+ config_reg &= ~GQSPI_CFG_ENDIAN_MASK;
+ /* Disable poll time out */
+ config_reg &= ~GQSPI_CFG_EN_POLL_TO_MASK;
+ /* Set hold bit */
+ config_reg |= GQSPI_CFG_WP_HOLD_MASK;
+ /* Clear pre-scalar by default */
+ config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK;
+ /* CPHA 0 */
+ config_reg &= ~GQSPI_CFG_CLK_PHA_MASK;
+ /* CPOL 0 */
+ config_reg &= ~GQSPI_CFG_CLK_POL_MASK;
+ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
+
+ /* Clear the TX and RX FIFO */
+ zynqmp_gqspi_write(xqspi, GQSPI_FIFO_CTRL_OFST,
+ GQSPI_FIFO_CTRL_RST_RX_FIFO_MASK |
+ GQSPI_FIFO_CTRL_RST_TX_FIFO_MASK |
+ GQSPI_FIFO_CTRL_RST_GEN_FIFO_MASK);
+ /* Set by default to allow for high frequencies */
+ zynqmp_gqspi_write(xqspi, GQSPI_LPBK_DLY_ADJ_OFST,
+ zynqmp_gqspi_read(xqspi, GQSPI_LPBK_DLY_ADJ_OFST) |
+ GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK);
+ /* Reset thresholds */
+ zynqmp_gqspi_write(xqspi, GQSPI_TX_THRESHOLD_OFST,
+ GQSPI_TX_FIFO_THRESHOLD_RESET_VAL);
+ zynqmp_gqspi_write(xqspi, GQSPI_RX_THRESHOLD_OFST,
+ GQSPI_RX_FIFO_THRESHOLD);
+ zynqmp_gqspi_write(xqspi, GQSPI_GF_THRESHOLD_OFST,
+ GQSPI_GEN_FIFO_THRESHOLD_RESET_VAL);
+ zynqmp_gqspi_selectslave(xqspi,
+ GQSPI_SELECT_FLASH_CS_LOWER,
+ GQSPI_SELECT_FLASH_BUS_LOWER);
+ /* Initialize DMA */
+ zynqmp_gqspi_write(xqspi,
+ GQSPI_QSPIDMA_DST_CTRL_OFST,
+ GQSPI_QSPIDMA_DST_CTRL_RESET_VAL);
+
+ /* Enable the GQSPI */
+ zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK);
+}
+
+/**
+ * zynqmp_qspi_copy_read_data: Copy data to RX buffer
+ * @xqspi: Pointer to the zynqmp_qspi structure
+ * @data: The variable where data is stored
+ * @size: Number of bytes to be copied from data to RX buffer
+ */
+static void zynqmp_qspi_copy_read_data(struct zynqmp_qspi *xqspi,
+ ulong data, u8 size)
+{
+ memcpy(xqspi->rxbuf, &data, size);
+ xqspi->rxbuf += size;
+ xqspi->bytes_to_receive -= size;
+}
+
+/**
+ * zynqmp_prepare_transfer_hardware: Prepares hardware for transfer.
+ * @master: Pointer to the spi_master structure which provides
+ * information about the controller.
+ *
+ * This function enables SPI master controller.
+ *
+ * Return: 0 on success; error value otherwise
+ */
+static int zynqmp_prepare_transfer_hardware(struct spi_master *master)
+{
+ struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+ int ret;
+
+ ret = clk_enable(xqspi->refclk);
+ if (ret)
+ goto clk_err;
+
+ ret = clk_enable(xqspi->pclk);
+ if (ret)
+ goto clk_err;
+
+ zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK);
+ return 0;
+clk_err:
+ return ret;
+}
+
+/**
+ * zynqmp_unprepare_transfer_hardware: Relaxes hardware after transfer
+ * @master: Pointer to the spi_master structure which provides
+ * information about the controller.
+ *
+ * This function disables the SPI master controller.
+ *
+ * Return: Always 0
+ */
+static int zynqmp_unprepare_transfer_hardware(struct spi_master *master)
+{
+ struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+
+ zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0);
+ clk_disable(xqspi->refclk);
+ clk_disable(xqspi->pclk);
+ return 0;
+}
+
+/**
+ * zynqmp_qspi_chipselect: Select or deselect the chip select line
+ * @qspi: Pointer to the spi_device structure
+ * @is_high: Select(0) or deselect (1) the chip select line
+ */
+static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high)
+{
+ struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master);
+ ulong timeout;
+ u32 genfifoentry = 0x0, statusreg;
+
+ genfifoentry |= GQSPI_GENFIFO_MODE_SPI;
+ genfifoentry |= xqspi->genfifobus;
+
+ if (!is_high) {
+ genfifoentry |= xqspi->genfifocs;
+ genfifoentry |= GQSPI_GENFIFO_CS_SETUP;
+ } else {
+ genfifoentry |= GQSPI_GENFIFO_CS_HOLD;
+ }
+
+ zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry);
+
+ /* Dummy generic FIFO entry */
+ zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0);
+
+ /* Manually start the generic FIFO command */
+ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
+ zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) |
+ GQSPI_CFG_START_GEN_FIFO_MASK);
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ /* Wait until the generic FIFO command is empty */
+ do {
+ statusreg = zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST);
+
+ if ((statusreg & GQSPI_ISR_GENFIFOEMPTY_MASK) &&
+ (statusreg & GQSPI_ISR_TXEMPTY_MASK))
+ break;
+ else
+ cpu_relax();
+ } while (!time_after_eq(jiffies, timeout));
+
+ if (time_after_eq(jiffies, timeout))
+ dev_err(xqspi->dev, "Chip select timed out\n");
+}
+
+/**
+ * zynqmp_qspi_setup_transfer: Configure QSPI controller for specified
+ * transfer
+ * @qspi: Pointer to the spi_device structure
+ * @transfer: Pointer to the spi_transfer structure which provides
+ * information about next transfer setup parameters
+ *
+ * Sets the operational mode of QSPI controller for the next QSPI transfer and
+ * sets the requested clock frequency.
+ *
+ * Return: Always 0
+ *
+ * Note:
+ * If the requested frequency is not an exact match with what can be
+ * obtained using the pre-scalar value, the driver sets the clock
+ * frequency which is lower than the requested frequency (maximum lower)
+ * for the transfer.
+ *
+ * If the requested frequency is higher or lower than that is supported
+ * by the QSPI controller the driver will set the highest or lowest
+ * frequency supported by controller.
+ */
+static int zynqmp_qspi_setup_transfer(struct spi_device *qspi,
+ struct spi_transfer *transfer)
+{
+ struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master);
+ ulong clk_rate;
+ u32 config_reg, req_hz, baud_rate_val = 0;
+
+ if (transfer)
+ req_hz = transfer->speed_hz;
+ else
+ req_hz = qspi->max_speed_hz;
+
+ /* Set the clock frequency */
+ /* If req_hz == 0, default to lowest speed */
+ clk_rate = clk_get_rate(xqspi->refclk);
+
+ while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) &&
+ (clk_rate /
+ (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > req_hz)
+ baud_rate_val++;
+
+ config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);
+
+ /* Set the QSPI clock phase and clock polarity */
+ config_reg &= (~GQSPI_CFG_CLK_PHA_MASK) & (~GQSPI_CFG_CLK_POL_MASK);
+
+ if (qspi->mode & SPI_CPHA)
+ config_reg |= GQSPI_CFG_CLK_PHA_MASK;
+ if (qspi->mode & SPI_CPOL)
+ config_reg |= GQSPI_CFG_CLK_POL_MASK;
+
+ config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK;
+ config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT);
+ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
+ return 0;
+}
+
+/**
+ * zynqmp_qspi_setup: Configure the QSPI controller
+ * @qspi: Pointer to the spi_device structure
+ *
+ * Sets the operational mode of QSPI controller for the next QSPI transfer,
+ * baud rate and divisor value to setup the requested qspi clock.
+ *
+ * Return: 0 on success; error value otherwise.
+ */
+static int zynqmp_qspi_setup(struct spi_device *qspi)
+{
+ if (qspi->master->busy)
+ return -EBUSY;
+ return 0;
+}
+
+/**
+ * zynqmp_qspi_filltxfifo: Fills the TX FIFO as long as there is room in
+ * the FIFO or the bytes required to be
+ * transmitted.
+ * @xqspi: Pointer to the zynqmp_qspi structure
+ * @size: Number of bytes to be copied from TX buffer to TX FIFO
+ */
+static void zynqmp_qspi_filltxfifo(struct zynqmp_qspi *xqspi, int size)
+{
+ u32 count = 0, intermediate;
+
+ while ((xqspi->bytes_to_transfer > 0) && (count < size)) {
+ memcpy(&intermediate, xqspi->txbuf, 4);
+ zynqmp_gqspi_write(xqspi, GQSPI_TXD_OFST, intermediate);
+
+ if (xqspi->bytes_to_transfer >= 4) {
+ xqspi->txbuf += 4;
+ xqspi->bytes_to_transfer -= 4;
+ } else {
+ xqspi->txbuf += xqspi->bytes_to_transfer;
+ xqspi->bytes_to_transfer = 0;
+ }
+ count++;
+ }
+}
+
+/**
+ * zynqmp_qspi_readrxfifo: Fills the RX FIFO as long as there is room in
+ * the FIFO.
+ * @xqspi: Pointer to the zynqmp_qspi structure
+ * @size: Number of bytes to be copied from RX buffer to RX FIFO
+ */
+static void zynqmp_qspi_readrxfifo(struct zynqmp_qspi *xqspi, u32 size)
+{
+ ulong data;
+ int count = 0;
+
+ while ((count < size) && (xqspi->bytes_to_receive > 0)) {
+ if (xqspi->bytes_to_receive >= 4) {
+ (*(u32 *) xqspi->rxbuf) =
+ zynqmp_gqspi_read(xqspi, GQSPI_RXD_OFST);
+ xqspi->rxbuf += 4;
+ xqspi->bytes_to_receive -= 4;
+ count += 4;
+ } else {
+ data = zynqmp_gqspi_read(xqspi, GQSPI_RXD_OFST);
+ count += xqspi->bytes_to_receive;
+ zynqmp_qspi_copy_read_data(xqspi, data,
+ xqspi->bytes_to_receive);
+ xqspi->bytes_to_receive = 0;
+ }
+ }
+}
+
+/**
+ * zynqmp_process_dma_irq: Handler for DMA done interrupt of QSPI
+ * controller
+ * @xqspi: zynqmp_qspi instance pointer
+ *
+ * This function handles DMA interrupt only.
+ */
+static void zynqmp_process_dma_irq(struct zynqmp_qspi *xqspi)
+{
+ u32 config_reg, genfifoentry;
+
+ dma_unmap_single(xqspi->dev, xqspi->dma_addr,
+ xqspi->dma_rx_bytes, DMA_FROM_DEVICE);
+ xqspi->rxbuf += xqspi->dma_rx_bytes;
+ xqspi->bytes_to_receive -= xqspi->dma_rx_bytes;
+ xqspi->dma_rx_bytes = 0;
+
+ /* Disabling the DMA interrupts */
+ zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_DIS_OFST,
+ GQSPI_QSPIDMA_DST_I_EN_DONE_MASK);
+
+ if (xqspi->bytes_to_receive > 0) {
+ /* Switch to IO mode,for remaining bytes to receive */
+ config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);
+ config_reg &= ~GQSPI_CFG_MODE_EN_MASK;
+ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
+
+ /* Initiate the transfer of remaining bytes */
+ genfifoentry = xqspi->genfifoentry;
+ genfifoentry |= xqspi->bytes_to_receive;
+ zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry);
+
+ /* Dummy generic FIFO entry */
+ zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0);
+
+ /* Manual start */
+ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
+ (zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) |
+ GQSPI_CFG_START_GEN_FIFO_MASK));
+
+ /* Enable the RX interrupts for IO mode */
+ zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST,
+ GQSPI_IER_GENFIFOEMPTY_MASK |
+ GQSPI_IER_RXNEMPTY_MASK |
+ GQSPI_IER_RXEMPTY_MASK);
+ }
+}
+
+/**
+ * zynqmp_qspi_irq: Interrupt service routine of the QSPI controller
+ * @irq: IRQ number
+ * @dev_id: Pointer to the xqspi structure
+ *
+ * This function handles TX empty only.
+ * On TX empty interrupt this function reads the received data from RX FIFO
+ * and fills the TX FIFO if there is any data remaining to be transferred.
+ *
+ * Return: IRQ_HANDLED when interrupt is handled
+ * IRQ_NONE otherwise.
+ */
+static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id)
+{
+ struct spi_master *master = dev_id;
+ struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+ int ret = IRQ_NONE;
+ u32 status, mask, dma_status = 0;
+
+ status = zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST);
+ zynqmp_gqspi_write(xqspi, GQSPI_ISR_OFST, status);
+ mask = (status & ~(zynqmp_gqspi_read(xqspi, GQSPI_IMASK_OFST)));
+
+ /* Read and clear DMA status */
+ if (xqspi->mode == GQSPI_MODE_DMA) {
+ dma_status =
+ zynqmp_gqspi_read(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST);
+ zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST,
+ dma_status);
+ }
+
+ if (mask & GQSPI_ISR_TXNOT_FULL_MASK) {
+ zynqmp_qspi_filltxfifo(xqspi, GQSPI_TX_FIFO_FILL);
+ ret = IRQ_HANDLED;
+ }
+
+ if (dma_status & GQSPI_QSPIDMA_DST_I_STS_DONE_MASK) {
+ zynqmp_process_dma_irq(xqspi);
+ ret = IRQ_HANDLED;
+ } else if (!(mask & GQSPI_IER_RXEMPTY_MASK) &&
+ (mask & GQSPI_IER_GENFIFOEMPTY_MASK)) {
+ zynqmp_qspi_readrxfifo(xqspi, GQSPI_RX_FIFO_FILL);
+ ret = IRQ_HANDLED;
+ }
+
+ if ((xqspi->bytes_to_receive == 0) && (xqspi->bytes_to_transfer == 0)
+ && ((status & GQSPI_IRQ_MASK) == GQSPI_IRQ_MASK)) {
+ zynqmp_gqspi_write(xqspi, GQSPI_IDR_OFST, GQSPI_ISR_IDR_MASK);
+ spi_finalize_current_transfer(master);
+ ret = IRQ_HANDLED;
+ }
+ return ret;
+}
+
+/**
+ * zynqmp_qspi_selectspimode: Selects SPI mode - x1 or x2 or x4.
+ * @xqspi: xqspi is a pointer to the GQSPI instance
+ * @spimode: spimode - SPI or DUAL or QUAD.
+ * Return: Mask to set desired SPI mode in GENFIFO entry.
+ */
+static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi,
+ u8 spimode)
+{
+ u32 mask = 0;
+
+ switch (spimode) {
+ case GQSPI_SELECT_MODE_DUALSPI:
+ mask = GQSPI_GENFIFO_MODE_DUALSPI;
+ break;
+ case GQSPI_SELECT_MODE_QUADSPI:
+ mask = GQSPI_GENFIFO_MODE_QUADSPI;
+ break;
+ case GQSPI_SELECT_MODE_SPI:
+ mask = GQSPI_GENFIFO_MODE_SPI;
+ break;
+ default:
+ dev_warn(xqspi->dev, "Invalid SPI mode\n");
+ }
+
+ return mask;
+}
+
+/**
+ * zynq_qspi_setuprxdma: This function sets up the RX DMA operation
+ * @xqspi: xqspi is a pointer to the GQSPI instance.
+ */
+static void zynq_qspi_setuprxdma(struct zynqmp_qspi *xqspi)
+{
+ u32 rx_bytes, rx_rem, config_reg;
+ dma_addr_t addr;
+ u64 dma_align = (u64)(uintptr_t)xqspi->rxbuf;
+
+ if ((xqspi->bytes_to_receive < 8) ||
+ ((dma_align & GQSPI_DMA_UNALIGN) != 0x0)) {
+ /* Setting to IO mode */
+ config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);
+ config_reg &= ~GQSPI_CFG_MODE_EN_MASK;
+ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
+ xqspi->mode = GQSPI_MODE_IO;
+ xqspi->dma_rx_bytes = 0;
+ return;
+ }
+
+ rx_rem = xqspi->bytes_to_receive % 4;
+ rx_bytes = (xqspi->bytes_to_receive - rx_rem);
+
+ addr = dma_map_single(xqspi->dev, (void *)xqspi->rxbuf,
+ rx_bytes, DMA_FROM_DEVICE);
+ if (dma_mapping_error(xqspi->dev, addr))
+ dev_err(xqspi->dev, "ERR:rxdma:memory not mapped\n");
+
+ xqspi->dma_rx_bytes = rx_bytes;
+ xqspi->dma_addr = addr;
+ zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_ADDR_OFST,
+ (u32)(addr & 0xffffffff));
+ addr = ((addr >> 16) >> 16);
+ zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_ADDR_MSB_OFST,
+ ((u32)addr) & 0xfff);
+
+ /* Enabling the DMA mode */
+ config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);
+ config_reg &= ~GQSPI_CFG_MODE_EN_MASK;
+ config_reg |= GQSPI_CFG_MODE_EN_DMA_MASK;
+ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
+
+ /* Switch to DMA mode */
+ xqspi->mode = GQSPI_MODE_DMA;
+
+ /* Write the number of bytes to transfer */
+ zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_SIZE_OFST, rx_bytes);
+}
+
+/**
+ * zynqmp_qspi_txrxsetup: This function checks the TX/RX buffers in
+ * the transfer and sets up the GENFIFO entries,
+ * TX FIFO as required.
+ * @xqspi: xqspi is a pointer to the GQSPI instance.
+ * @transfer: It is a pointer to the structure containing transfer data.
+ * @genfifoentry: genfifoentry is pointer to the variable in which
+ * GENFIFO mask is returned to calling function
+ */
+static void zynqmp_qspi_txrxsetup(struct zynqmp_qspi *xqspi,
+ struct spi_transfer *transfer,
+ u32 *genfifoentry)
+{
+ u32 config_reg;
+
+ /* Transmit */
+ if ((xqspi->txbuf != NULL) && (xqspi->rxbuf == NULL)) {
+ /* Setup data to be TXed */
+ *genfifoentry &= ~GQSPI_GENFIFO_RX;
+ *genfifoentry |= GQSPI_GENFIFO_DATA_XFER;
+ *genfifoentry |= GQSPI_GENFIFO_TX;
+ *genfifoentry |=
+ zynqmp_qspi_selectspimode(xqspi, transfer->tx_nbits);
+ xqspi->bytes_to_transfer = transfer->len;
+ if (xqspi->mode == GQSPI_MODE_DMA) {
+ config_reg = zynqmp_gqspi_read(xqspi,
+ GQSPI_CONFIG_OFST);
+ config_reg &= ~GQSPI_CFG_MODE_EN_MASK;
+ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
+ config_reg);
+ xqspi->mode = GQSPI_MODE_IO;
+ }
+ zynqmp_qspi_filltxfifo(xqspi, GQSPI_TXD_DEPTH);
+ /* Discard RX data */
+ xqspi->bytes_to_receive = 0;
+ } else if ((xqspi->txbuf == NULL) && (xqspi->rxbuf != NULL)) {
+ /* Receive */
+
+ /* TX auto fill */
+ *genfifoentry &= ~GQSPI_GENFIFO_TX;
+ /* Setup RX */
+ *genfifoentry |= GQSPI_GENFIFO_DATA_XFER;
+ *genfifoentry |= GQSPI_GENFIFO_RX;
+ *genfifoentry |=
+ zynqmp_qspi_selectspimode(xqspi, transfer->rx_nbits);
+ xqspi->bytes_to_transfer = 0;
+ xqspi->bytes_to_receive = transfer->len;
+ zynq_qspi_setuprxdma(xqspi);
+ }
+}
+
+/**
+ * zynqmp_qspi_start_transfer: Initiates the QSPI transfer
+ * @master: Pointer to the spi_master structure which provides
+ * information about the controller.
+ * @qspi: Pointer to the spi_device structure
+ * @transfer: Pointer to the spi_transfer structure which provide information
+ * about next transfer parameters
+ *
+ * This function fills the TX FIFO, starts the QSPI transfer, and waits for the
+ * transfer to be completed.
+ *
+ * Return: Number of bytes transferred in the last transfer
+ */
+static int zynqmp_qspi_start_transfer(struct spi_master *master,
+ struct spi_device *qspi,
+ struct spi_transfer *transfer)
+{
+ struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+ u32 genfifoentry = 0x0, transfer_len;
+
+ xqspi->txbuf = transfer->tx_buf;
+ xqspi->rxbuf = transfer->rx_buf;
+
+ zynqmp_qspi_setup_transfer(qspi, transfer);
+
+ genfifoentry |= xqspi->genfifocs;
+ genfifoentry |= xqspi->genfifobus;
+
+ zynqmp_qspi_txrxsetup(xqspi, transfer, &genfifoentry);
+
+ if (xqspi->mode == GQSPI_MODE_DMA)
+ transfer_len = xqspi->dma_rx_bytes;
+ else
+ transfer_len = transfer->len;
+
+ xqspi->genfifoentry = genfifoentry;
+ if ((transfer_len) < GQSPI_GENFIFO_IMM_DATA_MASK) {
+ genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK;
+ genfifoentry |= transfer_len;
+ zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry);
+ } else {
+ int tempcount = transfer_len;
+ u32 exponent = 8; /* 2^8 = 256 */
+ u8 imm_data = tempcount & 0xFF;
+
+ tempcount &= ~(tempcount & 0xFF);
+ /* Immediate entry */
+ if (tempcount != 0) {
+ /* Exponent entries */
+ genfifoentry |= GQSPI_GENFIFO_EXP;
+ while (tempcount != 0) {
+ if (tempcount & GQSPI_GENFIFO_EXP_START) {
+ genfifoentry &=
+ ~GQSPI_GENFIFO_IMM_DATA_MASK;
+ genfifoentry |= exponent;
+ zynqmp_gqspi_write(xqspi,
+ GQSPI_GEN_FIFO_OFST,
+ genfifoentry);
+ }
+ tempcount = tempcount >> 1;
+ exponent++;
+ }
+ }
+ if (imm_data != 0) {
+ genfifoentry &= ~GQSPI_GENFIFO_EXP;
+ genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK;
+ genfifoentry |= (u8) (imm_data & 0xFF);
+ zynqmp_gqspi_write(xqspi,
+ GQSPI_GEN_FIFO_OFST, genfifoentry);
+ }
+ }
+
+ if ((xqspi->mode == GQSPI_MODE_IO) &&
+ (xqspi->rxbuf != NULL)) {
+ /* Dummy generic FIFO entry */
+ zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0);
+ }
+
+ /* Since we are using manual mode */
+ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
+ zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) |
+ GQSPI_CFG_START_GEN_FIFO_MASK);
+
+ if (xqspi->txbuf != NULL)
+ /* Enable interrupts for TX */
+ zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST,
+ GQSPI_IER_TXEMPTY_MASK |
+ GQSPI_IER_GENFIFOEMPTY_MASK |
+ GQSPI_IER_TXNOT_FULL_MASK);
+
+ if (xqspi->rxbuf != NULL) {
+ /* Enable interrupts for RX */
+ if (xqspi->mode == GQSPI_MODE_DMA) {
+ /* Enable DMA interrupts */
+ zynqmp_gqspi_write(xqspi,
+ GQSPI_QSPIDMA_DST_I_EN_OFST,
+ GQSPI_QSPIDMA_DST_I_EN_DONE_MASK);
+ } else {
+ zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST,
+ GQSPI_IER_GENFIFOEMPTY_MASK |
+ GQSPI_IER_RXNEMPTY_MASK |
+ GQSPI_IER_RXEMPTY_MASK);
+ }
+ }
+
+ return transfer->len;
+}
+
+/**
+ * zynqmp_qspi_suspend: Suspend method for the QSPI driver
+ * @_dev: Address of the platform_device structure
+ *
+ * This function stops the QSPI driver queue and disables the QSPI controller
+ *
+ * Return: Always 0
+ */
+static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
+{
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device,
+ dev);
+ struct spi_master *master = platform_get_drvdata(pdev);
+
+ spi_master_suspend(master);
+
+ zynqmp_unprepare_transfer_hardware(master);
+
+ return 0;
+}
+
+/**
+ * zynqmp_qspi_resume: Resume method for the QSPI driver
+ * @dev: Address of the platform_device structure
+ *
+ * The function starts the QSPI driver queue and initializes the QSPI
+ * controller
+ *
+ * Return: 0 on success; error value otherwise
+ */
+static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
+{
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device,
+ dev);
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+ int ret = 0;
+
+ ret = clk_enable(xqspi->pclk);
+ if (ret) {
+ dev_err(dev, "Cannot enable APB clock.\n");
+ return ret;
+ }
+
+ ret = clk_enable(xqspi->refclk);
+ if (ret) {
+ dev_err(dev, "Cannot enable device clock.\n");
+ clk_disable(xqspi->pclk);
+ return ret;
+ }
+
+ spi_master_resume(master);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(zynqmp_qspi_dev_pm_ops, zynqmp_qspi_suspend,
+ zynqmp_qspi_resume);
+
+/**
+ * zynqmp_qspi_probe: Probe method for the QSPI driver
+ * @pdev: Pointer to the platform_device structure
+ *
+ * This function initializes the driver data structures and the hardware.
+ *
+ * Return: 0 on success; error value otherwise
+ */
+static int zynqmp_qspi_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct spi_master *master;
+ struct zynqmp_qspi *xqspi;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*xqspi));
+ if (!master)
+ return -ENOMEM;
+
+ xqspi = spi_master_get_devdata(master);
+ master->dev.of_node = pdev->dev.of_node;
+ platform_set_drvdata(pdev, master);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xqspi->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(xqspi->regs)) {
+ ret = PTR_ERR(xqspi->regs);
+ goto remove_master;
+ }
+
+ xqspi->dev = dev;
+ xqspi->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(xqspi->pclk)) {
+ dev_err(dev, "pclk clock not found.\n");
+ ret = PTR_ERR(xqspi->pclk);
+ goto remove_master;
+ }
+
+ ret = clk_prepare_enable(xqspi->pclk);
+ if (ret) {
+ dev_err(dev, "Unable to enable APB clock.\n");
+ goto remove_master;
+ }
+
+ xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk");
+ if (IS_ERR(xqspi->refclk)) {
+ dev_err(dev, "ref_clk clock not found.\n");
+ ret = PTR_ERR(xqspi->refclk);
+ goto clk_dis_pclk;
+ }
+
+ ret = clk_prepare_enable(xqspi->refclk);
+ if (ret) {
+ dev_err(dev, "Unable to enable device clock.\n");
+ goto clk_dis_pclk;
+ }
+
+ /* QSPI controller initializations */
+ zynqmp_qspi_init_hw(xqspi);
+
+ xqspi->irq = platform_get_irq(pdev, 0);
+ if (xqspi->irq <= 0) {
+ ret = -ENXIO;
+ dev_err(dev, "irq resource not found\n");
+ goto clk_dis_all;
+ }
+ ret = devm_request_irq(&pdev->dev, xqspi->irq, zynqmp_qspi_irq,
+ 0, pdev->name, master);
+ if (ret != 0) {
+ ret = -ENXIO;
+ dev_err(dev, "request_irq failed\n");
+ goto clk_dis_all;
+ }
+
+ master->num_chipselect = GQSPI_DEFAULT_NUM_CS;
+
+ master->setup = zynqmp_qspi_setup;
+ master->set_cs = zynqmp_qspi_chipselect;
+ master->transfer_one = zynqmp_qspi_start_transfer;
+ master->prepare_transfer_hardware = zynqmp_prepare_transfer_hardware;
+ master->unprepare_transfer_hardware =
+ zynqmp_unprepare_transfer_hardware;
+ master->max_speed_hz = clk_get_rate(xqspi->refclk) / 2;
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
+ SPI_TX_DUAL | SPI_TX_QUAD;
+
+ if (master->dev.parent == NULL)
+ master->dev.parent = &master->dev;
+
+ ret = spi_register_master(master);
+ if (ret)
+ goto clk_dis_all;
+
+ return 0;
+
+clk_dis_all:
+ clk_disable_unprepare(xqspi->refclk);
+clk_dis_pclk:
+ clk_disable_unprepare(xqspi->pclk);
+remove_master:
+ spi_master_put(master);
+
+ return ret;
+}
+
+/**
+ * zynqmp_qspi_remove: Remove method for the QSPI driver
+ * @pdev: Pointer to the platform_device structure
+ *
+ * This function is called if a device is physically removed from the system or
+ * if the driver module is being unloaded. It frees all resources allocated to
+ * the device.
+ *
+ * Return: 0 Always
+ */
+static int zynqmp_qspi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+
+ zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0);
+ clk_disable_unprepare(xqspi->refclk);
+ clk_disable_unprepare(xqspi->pclk);
+
+ spi_unregister_master(master);
+
+ return 0;
+}
+
+static const struct of_device_id zynqmp_qspi_of_match[] = {
+ { .compatible = "xlnx,zynqmp-qspi-1.0", },
+ { /* End of table */ }
+};
+
+MODULE_DEVICE_TABLE(of, zynqmp_qspi_of_match);
+
+static struct platform_driver zynqmp_qspi_driver = {
+ .probe = zynqmp_qspi_probe,
+ .remove = zynqmp_qspi_remove,
+ .driver = {
+ .name = "zynqmp-qspi",
+ .of_match_table = zynqmp_qspi_of_match,
+ .pm = &zynqmp_qspi_dev_pm_ops,
+ },
+};
+
+module_platform_driver(zynqmp_qspi_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx Zynqmp QSPI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d5d7d2235163..cf8b91b23a76 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -571,7 +571,7 @@ static int __spi_map_msg(struct spi_master *master, struct spi_message *msg)
return 0;
}
-static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
+static int __spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
{
struct spi_transfer *xfer;
struct device *tx_dev, *rx_dev;
@@ -599,13 +599,32 @@ static inline int __spi_map_msg(struct spi_master *master,
return 0;
}
-static inline int spi_unmap_msg(struct spi_master *master,
- struct spi_message *msg)
+static inline int __spi_unmap_msg(struct spi_master *master,
+ struct spi_message *msg)
{
return 0;
}
#endif /* !CONFIG_HAS_DMA */
+static inline int spi_unmap_msg(struct spi_master *master,
+ struct spi_message *msg)
+{
+ struct spi_transfer *xfer;
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ /*
+ * Restore the original value of tx_buf or rx_buf if they are
+ * NULL.
+ */
+ if (xfer->tx_buf == master->dummy_tx)
+ xfer->tx_buf = NULL;
+ if (xfer->rx_buf == master->dummy_rx)
+ xfer->rx_buf = NULL;
+ }
+
+ return __spi_unmap_msg(master, msg);
+}
+
static int spi_map_msg(struct spi_master *master, struct spi_message *msg)
{
struct spi_transfer *xfer;
@@ -979,9 +998,6 @@ void spi_finalize_current_message(struct spi_master *master)
spin_lock_irqsave(&master->queue_lock, flags);
mesg = master->cur_msg;
- master->cur_msg = NULL;
-
- queue_kthread_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags);
spi_unmap_msg(master, mesg);
@@ -994,9 +1010,13 @@ void spi_finalize_current_message(struct spi_master *master)
}
}
- trace_spi_message_done(mesg);
-
+ spin_lock_irqsave(&master->queue_lock, flags);
+ master->cur_msg = NULL;
master->cur_msg_prepared = false;
+ queue_kthread_work(&master->kworker, &master->pump_messages);
+ spin_unlock_irqrestore(&master->queue_lock, flags);
+
+ trace_spi_message_done(mesg);
mesg->state = NULL;
if (mesg->complete)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 92c909eed6b5..dd616ff0ffc5 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -95,37 +95,25 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
/*-------------------------------------------------------------------------*/
-/*
- * We can't use the standard synchronous wrappers for file I/O; we
- * need to protect against async removal of the underlying spi_device.
- */
-static void spidev_complete(void *arg)
-{
- complete(arg);
-}
-
static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
-
- message->complete = spidev_complete;
- message->context = &done;
+ struct spi_device *spi;
spin_lock_irq(&spidev->spi_lock);
- if (spidev->spi == NULL)
+ spi = spidev->spi;
+ spin_unlock_irq(&spidev->spi_lock);
+
+ if (spi == NULL)
status = -ESHUTDOWN;
else
- status = spi_async(spidev->spi, message);
- spin_unlock_irq(&spidev->spi_lock);
+ status = spi_sync(spi, message);
+
+ if (status == 0)
+ status = message->actual_length;
- if (status == 0) {
- wait_for_completion(&done);
- status = message->status;
- if (status == 0)
- status = message->actual_length;
- }
return status;
}
@@ -647,7 +635,6 @@ err_find_dev:
static int spidev_release(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
- int status = 0;
mutex_lock(&device_list_lock);
spidev = filp->private_data;
@@ -676,7 +663,7 @@ static int spidev_release(struct inode *inode, struct file *filp)
}
mutex_unlock(&device_list_lock);
- return status;
+ return 0;
}
static const struct file_operations spidev_fops = {
diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c
index 09428412139e..c5352ea4821e 100644
--- a/drivers/ssb/driver_chipcommon_pmu.c
+++ b/drivers/ssb/driver_chipcommon_pmu.c
@@ -621,8 +621,8 @@ static u32 ssb_pmu_get_alp_clock_clk0(struct ssb_chipcommon *cc)
u32 crystalfreq;
const struct pmu0_plltab_entry *e = NULL;
- crystalfreq = chipco_read32(cc, SSB_CHIPCO_PMU_CTL) &
- SSB_CHIPCO_PMU_CTL_XTALFREQ >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT;
+ crystalfreq = (chipco_read32(cc, SSB_CHIPCO_PMU_CTL) &
+ SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT;
e = pmu0_plltab_find_entry(crystalfreq);
BUG_ON(!e);
return e->freq * 1000;
@@ -634,7 +634,7 @@ u32 ssb_pmu_get_alp_clock(struct ssb_chipcommon *cc)
switch (bus->chip_id) {
case 0x5354:
- ssb_pmu_get_alp_clock_clk0(cc);
+ return ssb_pmu_get_alp_clock_clk0(cc);
default:
ssb_err("ERROR: PMU alp clock unknown for device %04X\n",
bus->chip_id);
diff --git a/drivers/ssb/driver_pcicore.c b/drivers/ssb/driver_pcicore.c
index 15a7ee3859dd..5fe1c22e289b 100644
--- a/drivers/ssb/driver_pcicore.c
+++ b/drivers/ssb/driver_pcicore.c
@@ -359,12 +359,13 @@ static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
/*
* Accessing PCI config without a proper delay after devices reset (not
- * GPIO reset) was causing reboots on WRT300N v1.0.
+ * GPIO reset) was causing reboots on WRT300N v1.0 (BCM4704).
* Tested delay 850 us lowered reboot chance to 50-80%, 1000 us fixed it
* completely. Flushing all writes was also tested but with no luck.
+ * The same problem was reported for WRT350N v1 (BCM4705), so we just
+ * sleep here unconditionally.
*/
- if (pc->dev->bus->chip_id == 0x4704)
- usleep_range(1000, 2000);
+ usleep_range(1000, 2000);
/* Enable PCI bridge BAR0 prefetch and burst */
val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
diff --git a/drivers/staging/gdm724x/gdm_mux.c b/drivers/staging/gdm724x/gdm_mux.c
index 8199b0a697bb..1cf24e4edf25 100644
--- a/drivers/staging/gdm724x/gdm_mux.c
+++ b/drivers/staging/gdm724x/gdm_mux.c
@@ -158,7 +158,7 @@ static int up_to_host(struct mux_rx *r)
unsigned int start_flag;
unsigned int payload_size;
unsigned short packet_type;
- int dummy_cnt;
+ int total_len;
u32 packet_size_sum = r->offset;
int index;
int ret = TO_HOST_INVALID_PACKET;
@@ -176,10 +176,10 @@ static int up_to_host(struct mux_rx *r)
break;
}
- dummy_cnt = ALIGN(MUX_HEADER_SIZE + payload_size, 4);
+ total_len = ALIGN(MUX_HEADER_SIZE + payload_size, 4);
if (len - packet_size_sum <
- MUX_HEADER_SIZE + payload_size + dummy_cnt) {
+ total_len) {
pr_err("invalid payload : %d %d %04x\n",
payload_size, len, packet_type);
break;
@@ -202,7 +202,7 @@ static int up_to_host(struct mux_rx *r)
break;
}
- packet_size_sum += MUX_HEADER_SIZE + payload_size + dummy_cnt;
+ packet_size_sum += total_len;
if (len - packet_size_sum <= MUX_HEADER_SIZE + 2) {
ret = r->callback(NULL,
0,
@@ -361,7 +361,6 @@ static int gdm_mux_send(void *priv_dev, void *data, int len, int tty_index,
struct mux_pkt_header *mux_header;
struct mux_tx *t = NULL;
static u32 seq_num = 1;
- int dummy_cnt;
int total_len;
int ret;
unsigned long flags;
@@ -374,9 +373,7 @@ static int gdm_mux_send(void *priv_dev, void *data, int len, int tty_index,
spin_lock_irqsave(&mux_dev->write_lock, flags);
- dummy_cnt = ALIGN(MUX_HEADER_SIZE + len, 4);
-
- total_len = len + MUX_HEADER_SIZE + dummy_cnt;
+ total_len = ALIGN(MUX_HEADER_SIZE + len, 4);
t = alloc_mux_tx(total_len);
if (!t) {
@@ -392,7 +389,8 @@ static int gdm_mux_send(void *priv_dev, void *data, int len, int tty_index,
mux_header->packet_type = __cpu_to_le16(packet_type[tty_index]);
memcpy(t->buf+MUX_HEADER_SIZE, data, len);
- memset(t->buf+MUX_HEADER_SIZE+len, 0, dummy_cnt);
+ memset(t->buf+MUX_HEADER_SIZE+len, 0, total_len - MUX_HEADER_SIZE -
+ len);
t->len = total_len;
t->callback = cb;
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
index 3bad441de8dc..c41b5575df05 100644
--- a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
@@ -647,6 +647,7 @@ kib_conn_t *kiblnd_create_conn(kib_peer_t *peer, struct rdma_cm_id *cmid,
kib_dev_t *dev;
struct ib_qp_init_attr *init_qp_attr;
struct kib_sched_info *sched;
+ struct ib_cq_init_attr cq_attr = {};
kib_conn_t *conn;
struct ib_cq *cq;
unsigned long flags;
@@ -742,10 +743,11 @@ kib_conn_t *kiblnd_create_conn(kib_peer_t *peer, struct rdma_cm_id *cmid,
kiblnd_map_rx_descs(conn);
+ cq_attr.cqe = IBLND_CQ_ENTRIES(version);
+ cq_attr.comp_vector = kiblnd_get_completion_vector(conn, cpt);
cq = ib_create_cq(cmid->device,
kiblnd_cq_completion, kiblnd_cq_event, conn,
- IBLND_CQ_ENTRIES(version),
- kiblnd_get_completion_vector(conn, cpt));
+ &cq_attr);
if (IS_ERR(cq)) {
CERROR("Can't create CQ: %ld, cqe: %d\n",
PTR_ERR(cq), IBLND_CQ_ENTRIES(version));
diff --git a/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h b/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
index 3925db160650..513c81f43d6e 100644
--- a/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
+++ b/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
@@ -189,22 +189,7 @@ static inline int ll_quota_off(struct super_block *sb, int off, int remount)
#endif
-
-/*
- * After 3.1, kernel's nameidata.intent.open.flags is different
- * with lustre's lookup_intent.it_flags, as lustre's it_flags'
- * lower bits equal to FMODE_xxx while kernel doesn't transliterate
- * lower bits of nameidata.intent.open.flags to FMODE_xxx.
- * */
#include <linux/version.h>
-static inline int ll_namei_to_lookup_intent_flag(int flag)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
- flag = (flag & ~O_ACCMODE) | OPEN_FMODE(flag);
-#endif
- return flag;
-}
-
#include <linux/fs.h>
# define ll_umode_t umode_t
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c
index cc3ab351943e..f9262243f935 100644
--- a/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c
@@ -87,7 +87,7 @@ static void cfs_cpu_core_siblings(int cpu, cpumask_t *mask)
/* return cpumask of HTs in the same core */
static void cfs_cpu_ht_siblings(int cpu, cpumask_t *mask)
{
- cpumask_copy(mask, topology_thread_cpumask(cpu));
+ cpumask_copy(mask, topology_sibling_cpumask(cpu));
}
static void cfs_node_to_cpumask(int node, cpumask_t *mask)
diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h
index 5f918e3c4683..528af9011653 100644
--- a/drivers/staging/lustre/lustre/llite/llite_internal.h
+++ b/drivers/staging/lustre/lustre/llite/llite_internal.h
@@ -57,12 +57,6 @@
#define VM_FAULT_RETRY 0
#endif
-/* Kernel 3.1 kills LOOKUP_CONTINUE, LOOKUP_PARENT is equivalent to it.
- * seem kernel commit 49084c3bb2055c401f3493c13edae14d49128ca0 */
-#ifndef LOOKUP_CONTINUE
-#define LOOKUP_CONTINUE LOOKUP_PARENT
-#endif
-
/** Only used on client-side for indicating the tail of dir hash/offset. */
#define LL_DIR_END_OFF 0x7fffffffffffffffULL
#define LL_DIR_END_OFF_32BIT 0x7fffffffUL
diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c
index 3711e671a4df..69b203651905 100644
--- a/drivers/staging/lustre/lustre/llite/symlink.c
+++ b/drivers/staging/lustre/lustre/llite/symlink.c
@@ -118,7 +118,7 @@ failed:
return rc;
}
-static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *ll_follow_link(struct dentry *dentry, void **cookie)
{
struct inode *inode = d_inode(dentry);
struct ptlrpc_request *request = NULL;
@@ -126,32 +126,22 @@ static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd)
char *symname = NULL;
CDEBUG(D_VFSTRACE, "VFS Op\n");
- /* Limit the recursive symlink depth to 5 instead of default
- * 8 links when kernel has 4k stack to prevent stack overflow.
- * For 8k stacks we need to limit it to 7 for local servers. */
- if (THREAD_SIZE < 8192 && current->link_count >= 6) {
- rc = -ELOOP;
- } else if (THREAD_SIZE == 8192 && current->link_count >= 8) {
- rc = -ELOOP;
- } else {
- ll_inode_size_lock(inode);
- rc = ll_readlink_internal(inode, &request, &symname);
- ll_inode_size_unlock(inode);
- }
+ ll_inode_size_lock(inode);
+ rc = ll_readlink_internal(inode, &request, &symname);
+ ll_inode_size_unlock(inode);
if (rc) {
ptlrpc_req_finished(request);
- request = NULL;
- symname = ERR_PTR(rc);
+ return ERR_PTR(rc);
}
- nd_set_link(nd, symname);
/* symname may contain a pointer to the request message buffer,
* we delay request releasing until ll_put_link then.
*/
- return request;
+ *cookie = request;
+ return symname;
}
-static void ll_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
+static void ll_put_link(struct inode *unused, void *cookie)
{
ptlrpc_req_finished(cookie);
}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/service.c b/drivers/staging/lustre/lustre/ptlrpc/service.c
index 8e61421515cb..344189ac5698 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/service.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/service.c
@@ -557,7 +557,7 @@ ptlrpc_server_nthreads_check(struct ptlrpc_service *svc,
* there are.
*/
/* weight is # of HTs */
- if (cpumask_weight(topology_thread_cpumask(0)) > 1) {
+ if (cpumask_weight(topology_sibling_cpumask(0)) > 1) {
/* depress thread factor for hyper-thread */
factor = factor - (factor >> 1) + (factor >> 3);
}
@@ -2768,7 +2768,7 @@ int ptlrpc_hr_init(void)
init_waitqueue_head(&ptlrpc_hr.hr_waitq);
- weight = cpumask_weight(topology_thread_cpumask(0));
+ weight = cpumask_weight(topology_sibling_cpumask(0));
cfs_percpt_for_each(hrp, i, ptlrpc_hr.hr_partitions) {
hrp->hrp_cpt = i;
diff --git a/drivers/staging/media/omap4iss/Kconfig b/drivers/staging/media/omap4iss/Kconfig
index b78643f907e7..072dac04a750 100644
--- a/drivers/staging/media/omap4iss/Kconfig
+++ b/drivers/staging/media/omap4iss/Kconfig
@@ -2,6 +2,7 @@ config VIDEO_OMAP4
bool "OMAP 4 Camera support"
depends on VIDEO_V4L2=y && VIDEO_V4L2_SUBDEV_API && I2C=y && ARCH_OMAP4
depends on HAS_DMA
+ select MFD_SYSCON
select VIDEOBUF2_DMA_CONTIG
---help---
Driver for an OMAP 4 ISS controller.
diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c
index e0ad5e520e2d..7ced940bd807 100644
--- a/drivers/staging/media/omap4iss/iss.c
+++ b/drivers/staging/media/omap4iss/iss.c
@@ -17,6 +17,7 @@
#include <linux/dma-mapping.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -1386,6 +1387,16 @@ static int iss_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, iss);
+ /*
+ * TODO: When implementing DT support switch to syscon regmap lookup by
+ * phandle.
+ */
+ iss->syscon = syscon_regmap_lookup_by_compatible("syscon");
+ if (IS_ERR(iss->syscon)) {
+ ret = PTR_ERR(iss->syscon);
+ goto error;
+ }
+
/* Clocks */
ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP);
if (ret < 0)
diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h
index 734cfeeb0314..35df8b4709e6 100644
--- a/drivers/staging/media/omap4iss/iss.h
+++ b/drivers/staging/media/omap4iss/iss.h
@@ -29,6 +29,8 @@
#include "iss_ipipe.h"
#include "iss_resizer.h"
+struct regmap;
+
#define to_iss_device(ptr_module) \
container_of(ptr_module, struct iss_device, ptr_module)
#define to_device(ptr_module) \
@@ -79,6 +81,7 @@ struct iss_reg {
/*
* struct iss_device - ISS device structure.
+ * @syscon: Regmap for the syscon register space
* @crashed: Bitmask of crashed entities (indexed by entity ID)
*/
struct iss_device {
@@ -93,6 +96,7 @@ struct iss_device {
struct resource *res[OMAP4_ISS_MEM_LAST];
void __iomem *regs[OMAP4_ISS_MEM_LAST];
+ struct regmap *syscon;
u64 raw_dmamask;
diff --git a/drivers/staging/media/omap4iss/iss_csiphy.c b/drivers/staging/media/omap4iss/iss_csiphy.c
index 7c3d55d811ef..748607f8918f 100644
--- a/drivers/staging/media/omap4iss/iss_csiphy.c
+++ b/drivers/staging/media/omap4iss/iss_csiphy.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/regmap.h>
#include "../../../../arch/arm/mach-omap2/control.h"
@@ -140,9 +141,11 @@ int omap4iss_csiphy_config(struct iss_device *iss,
* - bit [18] : CSIPHY1 CTRLCLK enable
* - bit [17:16] : CSIPHY1 config: 00 d-phy, 01/10 ccp2
*/
- cam_rx_ctrl = omap4_ctrl_pad_readl(
- OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
-
+ /*
+ * TODO: When implementing DT support specify the CONTROL_CAMERA_RX
+ * register offset in the syscon property instead of hardcoding it.
+ */
+ regmap_read(iss->syscon, 0x68, &cam_rx_ctrl);
if (subdevs->interface == ISS_INTERFACE_CSI2A_PHY1) {
cam_rx_ctrl &= ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
@@ -166,8 +169,7 @@ int omap4iss_csiphy_config(struct iss_device *iss,
cam_rx_ctrl |= OMAP4_CAMERARX_CSI22_CTRLCLKEN_MASK;
}
- omap4_ctrl_pad_writel(cam_rx_ctrl,
- OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX);
+ regmap_write(iss->syscon, 0x68, cam_rx_ctrl);
/* Reset used lane count */
csi2->phy->used_data_lanes = 0;
diff --git a/drivers/staging/ozwpan/ozhcd.c b/drivers/staging/ozwpan/ozhcd.c
index 5ff4716b72c3..784b5ecfa849 100644
--- a/drivers/staging/ozwpan/ozhcd.c
+++ b/drivers/staging/ozwpan/ozhcd.c
@@ -746,8 +746,8 @@ void oz_hcd_pd_reset(void *hpd, void *hport)
/*
* Context: softirq
*/
-void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status, const u8 *desc,
- int length, int offset, int total_size)
+void oz_hcd_get_desc_cnf(void *hport, u8 req_id, u8 status, const u8 *desc,
+ u8 length, u16 offset, u16 total_size)
{
struct oz_port *port = hport;
struct urb *urb;
@@ -759,8 +759,8 @@ void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status, const u8 *desc,
if (!urb)
return;
if (status == 0) {
- int copy_len;
- int required_size = urb->transfer_buffer_length;
+ unsigned int copy_len;
+ unsigned int required_size = urb->transfer_buffer_length;
if (required_size > total_size)
required_size = total_size;
diff --git a/drivers/staging/ozwpan/ozusbif.h b/drivers/staging/ozwpan/ozusbif.h
index 4249fa374012..d2a6085345be 100644
--- a/drivers/staging/ozwpan/ozusbif.h
+++ b/drivers/staging/ozwpan/ozusbif.h
@@ -29,8 +29,8 @@ void oz_usb_request_heartbeat(void *hpd);
/* Confirmation functions.
*/
-void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status,
- const u8 *desc, int length, int offset, int total_size);
+void oz_hcd_get_desc_cnf(void *hport, u8 req_id, u8 status,
+ const u8 *desc, u8 length, u16 offset, u16 total_size);
void oz_hcd_control_cnf(void *hport, u8 req_id, u8 rcode,
const u8 *data, int data_len);
diff --git a/drivers/staging/ozwpan/ozusbsvc1.c b/drivers/staging/ozwpan/ozusbsvc1.c
index d434d8c6fff6..f660bb198c65 100644
--- a/drivers/staging/ozwpan/ozusbsvc1.c
+++ b/drivers/staging/ozwpan/ozusbsvc1.c
@@ -326,7 +326,11 @@ static void oz_usb_handle_ep_data(struct oz_usb_ctx *usb_ctx,
struct oz_multiple_fixed *body =
(struct oz_multiple_fixed *)data_hdr;
u8 *data = body->data;
- int n = (len - sizeof(struct oz_multiple_fixed)+1)
+ unsigned int n;
+ if (!body->unit_size ||
+ len < sizeof(struct oz_multiple_fixed) - 1)
+ break;
+ n = (len - (sizeof(struct oz_multiple_fixed) - 1))
/ body->unit_size;
while (n--) {
oz_hcd_data_ind(usb_ctx->hport, body->endpoint,
@@ -390,10 +394,15 @@ void oz_usb_rx(struct oz_pd *pd, struct oz_elt *elt)
case OZ_GET_DESC_RSP: {
struct oz_get_desc_rsp *body =
(struct oz_get_desc_rsp *)usb_hdr;
- int data_len = elt->length -
- sizeof(struct oz_get_desc_rsp) + 1;
- u16 offs = le16_to_cpu(get_unaligned(&body->offset));
- u16 total_size =
+ u16 offs, total_size;
+ u8 data_len;
+
+ if (elt->length < sizeof(struct oz_get_desc_rsp) - 1)
+ break;
+ data_len = elt->length -
+ (sizeof(struct oz_get_desc_rsp) - 1);
+ offs = le16_to_cpu(get_unaligned(&body->offset));
+ total_size =
le16_to_cpu(get_unaligned(&body->total_size));
oz_dbg(ON, "USB_REQ_GET_DESCRIPTOR - cnf\n");
oz_hcd_get_desc_cnf(usb_ctx->hport, body->req_id,
diff --git a/drivers/staging/rtl8712/rtl8712_led.c b/drivers/staging/rtl8712/rtl8712_led.c
index f1d47a0676c3..ada8d5dafd49 100644
--- a/drivers/staging/rtl8712/rtl8712_led.c
+++ b/drivers/staging/rtl8712/rtl8712_led.c
@@ -898,11 +898,11 @@ static void SwLedControlMode1(struct _adapter *padapter,
IS_LED_WPS_BLINKING(pLed))
return;
if (pLed->bLedLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedLinkBlinkInProgress = false;
}
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
pLed->bLedNoLinkBlinkInProgress = true;
@@ -921,11 +921,11 @@ static void SwLedControlMode1(struct _adapter *padapter,
IS_LED_WPS_BLINKING(pLed))
return;
if (pLed->bLedNoLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
pLed->bLedLinkBlinkInProgress = true;
@@ -946,15 +946,15 @@ static void SwLedControlMode1(struct _adapter *padapter,
if (IS_LED_WPS_BLINKING(pLed))
return;
if (pLed->bLedNoLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
if (pLed->bLedLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedLinkBlinkInProgress = false;
}
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
pLed->bLedScanBlinkInProgress = true;
@@ -975,11 +975,11 @@ static void SwLedControlMode1(struct _adapter *padapter,
IS_LED_WPS_BLINKING(pLed))
return;
if (pLed->bLedNoLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
if (pLed->bLedLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedLinkBlinkInProgress = false;
}
pLed->bLedBlinkInProgress = true;
@@ -998,19 +998,19 @@ static void SwLedControlMode1(struct _adapter *padapter,
case LED_CTL_START_WPS_BOTTON:
if (pLed->bLedWPSBlinkInProgress == false) {
if (pLed->bLedNoLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
if (pLed->bLedLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedLinkBlinkInProgress = false;
}
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
pLed->bLedWPSBlinkInProgress = true;
@@ -1025,23 +1025,23 @@ static void SwLedControlMode1(struct _adapter *padapter,
break;
case LED_CTL_STOP_WPS:
if (pLed->bLedNoLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
if (pLed->bLedLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedLinkBlinkInProgress = false;
}
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
if (pLed->bLedWPSBlinkInProgress)
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
else
pLed->bLedWPSBlinkInProgress = true;
pLed->CurrLedState = LED_BLINK_WPS_STOP;
@@ -1057,7 +1057,7 @@ static void SwLedControlMode1(struct _adapter *padapter,
break;
case LED_CTL_STOP_WPS_FAIL:
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
pLed->bLedNoLinkBlinkInProgress = true;
@@ -1073,23 +1073,23 @@ static void SwLedControlMode1(struct _adapter *padapter,
pLed->CurrLedState = LED_OFF;
pLed->BlinkingLedState = LED_OFF;
if (pLed->bLedNoLinkBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
if (pLed->bLedLinkBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedLinkBlinkInProgress = false;
}
if (pLed->bLedBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
mod_timer(&pLed->BlinkTimer,
@@ -1116,7 +1116,7 @@ static void SwLedControlMode2(struct _adapter *padapter,
return;
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
pLed->bLedScanBlinkInProgress = true;
@@ -1154,11 +1154,11 @@ static void SwLedControlMode2(struct _adapter *padapter,
pLed->CurrLedState = LED_ON;
pLed->BlinkingLedState = LED_ON;
if (pLed->bLedBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
@@ -1170,11 +1170,11 @@ static void SwLedControlMode2(struct _adapter *padapter,
case LED_CTL_START_WPS_BOTTON:
if (pLed->bLedWPSBlinkInProgress == false) {
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
pLed->bLedWPSBlinkInProgress = true;
@@ -1214,15 +1214,15 @@ static void SwLedControlMode2(struct _adapter *padapter,
pLed->CurrLedState = LED_OFF;
pLed->BlinkingLedState = LED_OFF;
if (pLed->bLedBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
mod_timer(&pLed->BlinkTimer,
@@ -1248,7 +1248,7 @@ static void SwLedControlMode3(struct _adapter *padapter,
if (IS_LED_WPS_BLINKING(pLed))
return;
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
pLed->bLedScanBlinkInProgress = true;
@@ -1286,11 +1286,11 @@ static void SwLedControlMode3(struct _adapter *padapter,
pLed->CurrLedState = LED_ON;
pLed->BlinkingLedState = LED_ON;
if (pLed->bLedBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
mod_timer(&pLed->BlinkTimer,
@@ -1300,11 +1300,11 @@ static void SwLedControlMode3(struct _adapter *padapter,
case LED_CTL_START_WPS_BOTTON:
if (pLed->bLedWPSBlinkInProgress == false) {
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
pLed->bLedWPSBlinkInProgress = true;
@@ -1319,7 +1319,7 @@ static void SwLedControlMode3(struct _adapter *padapter,
break;
case LED_CTL_STOP_WPS:
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&(pLed->BlinkTimer));
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
} else
pLed->bLedWPSBlinkInProgress = true;
@@ -1336,7 +1336,7 @@ static void SwLedControlMode3(struct _adapter *padapter,
break;
case LED_CTL_STOP_WPS_FAIL:
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
pLed->CurrLedState = LED_OFF;
@@ -1357,15 +1357,15 @@ static void SwLedControlMode3(struct _adapter *padapter,
pLed->CurrLedState = LED_OFF;
pLed->BlinkingLedState = LED_OFF;
if (pLed->bLedBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
mod_timer(&pLed->BlinkTimer,
@@ -1388,7 +1388,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
case LED_CTL_START_TO_LINK:
if (pLed1->bLedWPSBlinkInProgress) {
pLed1->bLedWPSBlinkInProgress = false;
- del_timer_sync(&pLed1->BlinkTimer);
+ del_timer(&pLed1->BlinkTimer);
pLed1->BlinkingLedState = LED_OFF;
pLed1->CurrLedState = LED_OFF;
if (pLed1->bLedOn)
@@ -1400,11 +1400,11 @@ static void SwLedControlMode4(struct _adapter *padapter,
IS_LED_WPS_BLINKING(pLed))
return;
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedNoLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
pLed->bLedStartToLinkBlinkInProgress = true;
@@ -1426,7 +1426,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
if (LedAction == LED_CTL_LINK) {
if (pLed1->bLedWPSBlinkInProgress) {
pLed1->bLedWPSBlinkInProgress = false;
- del_timer_sync(&pLed1->BlinkTimer);
+ del_timer(&pLed1->BlinkTimer);
pLed1->BlinkingLedState = LED_OFF;
pLed1->CurrLedState = LED_OFF;
if (pLed1->bLedOn)
@@ -1439,7 +1439,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
IS_LED_WPS_BLINKING(pLed))
return;
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
pLed->bLedNoLinkBlinkInProgress = true;
@@ -1460,11 +1460,11 @@ static void SwLedControlMode4(struct _adapter *padapter,
if (IS_LED_WPS_BLINKING(pLed))
return;
if (pLed->bLedNoLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
pLed->bLedScanBlinkInProgress = true;
@@ -1485,7 +1485,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
IS_LED_WPS_BLINKING(pLed))
return;
if (pLed->bLedNoLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
pLed->bLedBlinkInProgress = true;
@@ -1503,7 +1503,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
case LED_CTL_START_WPS_BOTTON:
if (pLed1->bLedWPSBlinkInProgress) {
pLed1->bLedWPSBlinkInProgress = false;
- del_timer_sync(&(pLed1->BlinkTimer));
+ del_timer(&pLed1->BlinkTimer);
pLed1->BlinkingLedState = LED_OFF;
pLed1->CurrLedState = LED_OFF;
if (pLed1->bLedOn)
@@ -1512,15 +1512,15 @@ static void SwLedControlMode4(struct _adapter *padapter,
}
if (pLed->bLedWPSBlinkInProgress == false) {
if (pLed->bLedNoLinkBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
pLed->bLedWPSBlinkInProgress = true;
@@ -1538,7 +1538,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
break;
case LED_CTL_STOP_WPS: /*WPS connect success*/
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
pLed->bLedNoLinkBlinkInProgress = true;
@@ -1552,7 +1552,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
break;
case LED_CTL_STOP_WPS_FAIL: /*WPS authentication fail*/
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
pLed->bLedNoLinkBlinkInProgress = true;
@@ -1565,7 +1565,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
/*LED1 settings*/
if (pLed1->bLedWPSBlinkInProgress)
- del_timer_sync(&pLed1->BlinkTimer);
+ del_timer(&pLed1->BlinkTimer);
else
pLed1->bLedWPSBlinkInProgress = true;
pLed1->CurrLedState = LED_BLINK_WPS_STOP;
@@ -1578,7 +1578,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
break;
case LED_CTL_STOP_WPS_FAIL_OVERLAP: /*WPS session overlap*/
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
pLed->bLedNoLinkBlinkInProgress = true;
@@ -1591,7 +1591,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
/*LED1 settings*/
if (pLed1->bLedWPSBlinkInProgress)
- del_timer_sync(&pLed1->BlinkTimer);
+ del_timer(&pLed1->BlinkTimer);
else
pLed1->bLedWPSBlinkInProgress = true;
pLed1->CurrLedState = LED_BLINK_WPS_STOP_OVERLAP;
@@ -1607,31 +1607,31 @@ static void SwLedControlMode4(struct _adapter *padapter,
pLed->CurrLedState = LED_OFF;
pLed->BlinkingLedState = LED_OFF;
if (pLed->bLedNoLinkBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedNoLinkBlinkInProgress = false;
}
if (pLed->bLedLinkBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedLinkBlinkInProgress = false;
}
if (pLed->bLedBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
if (pLed->bLedScanBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedScanBlinkInProgress = false;
}
if (pLed->bLedStartToLinkBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedStartToLinkBlinkInProgress = false;
}
if (pLed1->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed1->BlinkTimer);
+ del_timer(&pLed1->BlinkTimer);
pLed1->bLedWPSBlinkInProgress = false;
}
pLed1->BlinkingLedState = LED_UNKNOWN;
@@ -1671,7 +1671,7 @@ static void SwLedControlMode5(struct _adapter *padapter,
; /* dummy branch */
else if (pLed->bLedScanBlinkInProgress == false) {
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
pLed->bLedScanBlinkInProgress = true;
@@ -1705,7 +1705,7 @@ static void SwLedControlMode5(struct _adapter *padapter,
pLed->CurrLedState = LED_OFF;
pLed->BlinkingLedState = LED_OFF;
if (pLed->bLedBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
SwLedOff(padapter, pLed);
@@ -1756,7 +1756,7 @@ static void SwLedControlMode6(struct _adapter *padapter,
case LED_CTL_START_WPS_BOTTON:
if (pLed->bLedWPSBlinkInProgress == false) {
if (pLed->bLedBlinkInProgress == true) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
pLed->bLedWPSBlinkInProgress = true;
@@ -1772,7 +1772,7 @@ static void SwLedControlMode6(struct _adapter *padapter,
case LED_CTL_STOP_WPS_FAIL:
case LED_CTL_STOP_WPS:
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
pLed->CurrLedState = LED_ON;
@@ -1784,11 +1784,11 @@ static void SwLedControlMode6(struct _adapter *padapter,
pLed->CurrLedState = LED_OFF;
pLed->BlinkingLedState = LED_OFF;
if (pLed->bLedBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedBlinkInProgress = false;
}
if (pLed->bLedWPSBlinkInProgress) {
- del_timer_sync(&pLed->BlinkTimer);
+ del_timer(&pLed->BlinkTimer);
pLed->bLedWPSBlinkInProgress = false;
}
SwLedOff(padapter, pLed);
diff --git a/drivers/staging/rtl8712/rtl871x_cmd.c b/drivers/staging/rtl8712/rtl871x_cmd.c
index 1a1c38f885d6..e35854d28f90 100644
--- a/drivers/staging/rtl8712/rtl871x_cmd.c
+++ b/drivers/staging/rtl8712/rtl871x_cmd.c
@@ -910,7 +910,7 @@ void r8712_createbss_cmd_callback(struct _adapter *padapter,
if (pcmd->res != H2C_SUCCESS)
mod_timer(&pmlmepriv->assoc_timer,
jiffies + msecs_to_jiffies(1));
- del_timer_sync(&pmlmepriv->assoc_timer);
+ del_timer(&pmlmepriv->assoc_timer);
#ifdef __BIG_ENDIAN
/* endian_convert */
pnetwork->Length = le32_to_cpu(pnetwork->Length);
diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c
index 42fba3f5b593..cb0b6387789f 100644
--- a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c
+++ b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c
@@ -1900,23 +1900,20 @@ static int r871x_mp_ioctl_hdl(struct net_device *dev,
struct mp_ioctl_handler *phandler;
struct mp_ioctl_param *poidparam;
unsigned long BytesRead, BytesWritten, BytesNeeded;
- u8 *pparmbuf = NULL, bset;
+ u8 *pparmbuf, bset;
u16 len;
uint status;
int ret = 0;
- if ((!p->length) || (!p->pointer)) {
- ret = -EINVAL;
- goto _r871x_mp_ioctl_hdl_exit;
- }
+ if ((!p->length) || (!p->pointer))
+ return -EINVAL;
+
bset = (u8)(p->flags & 0xFFFF);
len = p->length;
- pparmbuf = NULL;
pparmbuf = memdup_user(p->pointer, len);
- if (IS_ERR(pparmbuf)) {
- ret = PTR_ERR(pparmbuf);
- goto _r871x_mp_ioctl_hdl_exit;
- }
+ if (IS_ERR(pparmbuf))
+ return PTR_ERR(pparmbuf);
+
poidparam = (struct mp_ioctl_param *)pparmbuf;
if (poidparam->subcode >= MAX_MP_IOCTL_SUBCODE) {
ret = -EINVAL;
diff --git a/drivers/staging/rtl8712/rtl871x_mlme.c b/drivers/staging/rtl8712/rtl871x_mlme.c
index fb2b195b90af..c044b0e55ba9 100644
--- a/drivers/staging/rtl8712/rtl871x_mlme.c
+++ b/drivers/staging/rtl8712/rtl871x_mlme.c
@@ -582,7 +582,7 @@ void r8712_surveydone_event_callback(struct _adapter *adapter, u8 *pbuf)
spin_lock_irqsave(&pmlmepriv->lock, irqL);
if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) {
- del_timer_sync(&pmlmepriv->scan_to_timer);
+ del_timer(&pmlmepriv->scan_to_timer);
_clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
}
@@ -696,7 +696,7 @@ void r8712_ind_disconnect(struct _adapter *padapter)
}
if (padapter->pwrctrlpriv.pwr_mode !=
padapter->registrypriv.power_mgnt) {
- del_timer_sync(&pmlmepriv->dhcp_timer);
+ del_timer(&pmlmepriv->dhcp_timer);
r8712_set_ps_mode(padapter, padapter->registrypriv.power_mgnt,
padapter->registrypriv.smart_ps);
}
@@ -910,7 +910,7 @@ void r8712_joinbss_event_callback(struct _adapter *adapter, u8 *pbuf)
if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)
== true)
r8712_indicate_connect(adapter);
- del_timer_sync(&pmlmepriv->assoc_timer);
+ del_timer(&pmlmepriv->assoc_timer);
} else
goto ignore_joinbss_callback;
} else {
diff --git a/drivers/staging/rtl8712/rtl871x_pwrctrl.c b/drivers/staging/rtl8712/rtl871x_pwrctrl.c
index aaa584435c87..9bc04f474d18 100644
--- a/drivers/staging/rtl8712/rtl871x_pwrctrl.c
+++ b/drivers/staging/rtl8712/rtl871x_pwrctrl.c
@@ -103,7 +103,7 @@ void r8712_cpwm_int_hdl(struct _adapter *padapter,
if (pwrpriv->cpwm_tog == ((preportpwrstate->state) & 0x80))
return;
- del_timer_sync(&padapter->pwrctrlpriv.rpwm_check_timer);
+ del_timer(&padapter->pwrctrlpriv.rpwm_check_timer);
_enter_pwrlock(&pwrpriv->lock);
pwrpriv->cpwm = (preportpwrstate->state) & 0xf;
if (pwrpriv->cpwm >= PS_STATE_S2) {
diff --git a/drivers/staging/rtl8712/rtl871x_sta_mgt.c b/drivers/staging/rtl8712/rtl871x_sta_mgt.c
index 7bb96c47f188..a9b93d0f6f56 100644
--- a/drivers/staging/rtl8712/rtl871x_sta_mgt.c
+++ b/drivers/staging/rtl8712/rtl871x_sta_mgt.c
@@ -198,7 +198,7 @@ void r8712_free_stainfo(struct _adapter *padapter, struct sta_info *psta)
* cancel reordering_ctrl_timer */
for (i = 0; i < 16; i++) {
preorder_ctrl = &psta->recvreorder_ctrl[i];
- del_timer_sync(&preorder_ctrl->reordering_ctrl_timer);
+ del_timer(&preorder_ctrl->reordering_ctrl_timer);
}
spin_lock(&(pfree_sta_queue->lock));
/* insert into free_sta_queue; 20061114 */
diff --git a/drivers/staging/rts5208/rtsx.c b/drivers/staging/rts5208/rtsx.c
index d64b6ed9c0c9..aed49bf762b4 100644
--- a/drivers/staging/rts5208/rtsx.c
+++ b/drivers/staging/rts5208/rtsx.c
@@ -230,7 +230,6 @@ static struct scsi_host_template rtsx_host_template = {
/* queue commands only, only one command per LUN */
.can_queue = 1,
- .cmd_per_lun = 1,
/* unknown initiator id */
.this_id = -1,
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 3c7ea95dd9f9..dbbb2f879a29 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -1250,7 +1250,7 @@ err_enable:
return -ENODEV;
}
-static void __exit lynxfb_pci_remove(struct pci_dev *pdev)
+static void lynxfb_pci_remove(struct pci_dev *pdev)
{
struct fb_info *info;
struct lynx_share *share;
diff --git a/drivers/staging/vt6655/card.c b/drivers/staging/vt6655/card.c
index 1cdcf49b2445..e00c0605d154 100644
--- a/drivers/staging/vt6655/card.c
+++ b/drivers/staging/vt6655/card.c
@@ -362,12 +362,16 @@ bool CARDbSetPhyParameter(struct vnt_private *pDevice, u8 bb_type)
* Return Value: none
*/
bool CARDbUpdateTSF(struct vnt_private *pDevice, unsigned char byRxRate,
- u64 qwBSSTimestamp, u64 qwLocalTSF)
+ u64 qwBSSTimestamp)
{
+ u64 local_tsf;
u64 qwTSFOffset = 0;
- if (qwBSSTimestamp != qwLocalTSF) {
- qwTSFOffset = CARDqGetTSFOffset(byRxRate, qwBSSTimestamp, qwLocalTSF);
+ CARDbGetCurrentTSF(pDevice, &local_tsf);
+
+ if (qwBSSTimestamp != local_tsf) {
+ qwTSFOffset = CARDqGetTSFOffset(byRxRate, qwBSSTimestamp,
+ local_tsf);
/* adjust TSF, HW's TSF add TSF Offset reg */
VNSvOutPortD(pDevice->PortOffset + MAC_REG_TSFOFST, (u32)qwTSFOffset);
VNSvOutPortD(pDevice->PortOffset + MAC_REG_TSFOFST + 4, (u32)(qwTSFOffset >> 32));
diff --git a/drivers/staging/vt6655/card.h b/drivers/staging/vt6655/card.h
index 2dfc41952271..16cca49e680a 100644
--- a/drivers/staging/vt6655/card.h
+++ b/drivers/staging/vt6655/card.h
@@ -83,7 +83,7 @@ bool CARDbRadioPowerOff(struct vnt_private *);
bool CARDbRadioPowerOn(struct vnt_private *);
bool CARDbSetPhyParameter(struct vnt_private *, u8);
bool CARDbUpdateTSF(struct vnt_private *, unsigned char byRxRate,
- u64 qwBSSTimestamp, u64 qwLocalTSF);
+ u64 qwBSSTimestamp);
bool CARDbSetBeaconPeriod(struct vnt_private *, unsigned short wBeaconInterval);
#endif /* __CARD_H__ */
diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c
index 4bb4f8ee4132..0343ae386f03 100644
--- a/drivers/staging/vt6655/device_main.c
+++ b/drivers/staging/vt6655/device_main.c
@@ -912,7 +912,11 @@ static int vnt_int_report_rate(struct vnt_private *priv,
if (!(tsr1 & TSR1_TERR)) {
info->status.rates[0].idx = idx;
- info->flags |= IEEE80211_TX_STAT_ACK;
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
+ else
+ info->flags |= IEEE80211_TX_STAT_ACK;
}
return 0;
@@ -937,9 +941,6 @@ static int device_tx_srv(struct vnt_private *pDevice, unsigned int uIdx)
/* Only the status of first TD in the chain is correct */
if (pTD->m_td1TD1.byTCR & TCR_STP) {
if ((pTD->pTDInfo->byFlags & TD_FLAGS_NETIF_SKB) != 0) {
-
- vnt_int_report_rate(pDevice, pTD->pTDInfo, byTsr0, byTsr1);
-
if (!(byTsr1 & TSR1_TERR)) {
if (byTsr0 != 0) {
pr_debug(" Tx[%d] OK but has error. tsr1[%02X] tsr0[%02X]\n",
@@ -958,6 +959,9 @@ static int device_tx_srv(struct vnt_private *pDevice, unsigned int uIdx)
(int)uIdx, byTsr1, byTsr0);
}
}
+
+ vnt_int_report_rate(pDevice, pTD->pTDInfo, byTsr0, byTsr1);
+
device_free_tx_buf(pDevice, pTD);
pDevice->iTDUsed[uIdx]--;
}
@@ -989,10 +993,8 @@ static void device_free_tx_buf(struct vnt_private *pDevice, PSTxDesc pDesc)
skb->len, DMA_TO_DEVICE);
}
- if (pTDInfo->byFlags & TD_FLAGS_NETIF_SKB)
+ if (skb)
ieee80211_tx_status_irqsafe(pDevice->hw, skb);
- else
- dev_kfree_skb_irq(skb);
pTDInfo->skb_dma = 0;
pTDInfo->skb = NULL;
@@ -1204,14 +1206,6 @@ static int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb)
if (dma_idx == TYPE_AC0DMA)
head_td->pTDInfo->byFlags = TD_FLAGS_NETIF_SKB;
- priv->iTDUsed[dma_idx]++;
-
- /* Take ownership */
- wmb();
- head_td->m_td0TD0.f1Owner = OWNED_BY_NIC;
-
- /* get Next */
- wmb();
priv->apCurrTD[dma_idx] = head_td->next;
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1232,11 +1226,18 @@ static int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb)
head_td->buff_addr = cpu_to_le32(head_td->pTDInfo->skb_dma);
+ /* Poll Transmit the adapter */
+ wmb();
+ head_td->m_td0TD0.f1Owner = OWNED_BY_NIC;
+ wmb(); /* second memory barrier */
+
if (head_td->pTDInfo->byFlags & TD_FLAGS_NETIF_SKB)
MACvTransmitAC0(priv->PortOffset);
else
MACvTransmit0(priv->PortOffset);
+ priv->iTDUsed[dma_idx]++;
+
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
@@ -1416,9 +1417,16 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw,
priv->current_aid = conf->aid;
- if (changed & BSS_CHANGED_BSSID)
+ if (changed & BSS_CHANGED_BSSID) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
MACvWriteBSSIDAddress(priv->PortOffset, (u8 *)conf->bssid);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+
if (changed & BSS_CHANGED_BASIC_RATES) {
priv->basic_rates = conf->basic_rates;
@@ -1477,7 +1485,7 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ASSOC && priv->op_mode != NL80211_IFTYPE_AP) {
if (conf->assoc) {
CARDbUpdateTSF(priv, conf->beacon_rate->hw_value,
- conf->sync_device_ts, conf->sync_tsf);
+ conf->sync_tsf);
CARDbSetBeaconPeriod(priv, conf->beacon_int);
diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c
index f6c2cf8590c4..5c589962a1e8 100644
--- a/drivers/staging/vt6656/rxtx.c
+++ b/drivers/staging/vt6656/rxtx.c
@@ -805,10 +805,18 @@ int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb)
vnt_schedule_command(priv, WLAN_CMD_SETPOWER);
}
- if (current_rate > RATE_11M)
- pkt_type = priv->packet_type;
- else
+ if (current_rate > RATE_11M) {
+ if (info->band == IEEE80211_BAND_5GHZ) {
+ pkt_type = PK_TYPE_11A;
+ } else {
+ if (tx_rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ pkt_type = PK_TYPE_11GB;
+ else
+ pkt_type = PK_TYPE_11GA;
+ }
+ } else {
pkt_type = PK_TYPE_11B;
+ }
spin_lock_irqsave(&priv->lock, flags);
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 34871a628b11..4672bb1a24d0 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -23,7 +23,7 @@
#include <linux/module.h>
#include <linux/idr.h>
#include <asm/unaligned.h>
-#include <scsi/scsi_device.h>
+#include <scsi/scsi_proto.h>
#include <scsi/iscsi_proto.h>
#include <scsi/scsi_tcq.h>
#include <target/target_core_base.h>
@@ -230,7 +230,7 @@ int iscsit_access_np(struct iscsi_np *np, struct iscsi_portal_group *tpg)
* Here we serialize access across the TIQN+TPG Tuple.
*/
ret = down_interruptible(&tpg->np_login_sem);
- if ((ret != 0) || signal_pending(current))
+ if (ret != 0)
return -1;
spin_lock_bh(&tpg->tpg_state_lock);
diff --git a/drivers/target/iscsi/iscsi_target_device.c b/drivers/target/iscsi/iscsi_target_device.c
index 34c3cd1b05ce..5fabcd3d623f 100644
--- a/drivers/target/iscsi/iscsi_target_device.c
+++ b/drivers/target/iscsi/iscsi_target_device.c
@@ -17,7 +17,6 @@
* GNU General Public License for more details.
******************************************************************************/
-#include <scsi/scsi_device.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index 8ce94ff744e6..70d799dfab03 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -346,6 +346,7 @@ static int iscsi_login_zero_tsih_s1(
if (IS_ERR(sess->se_sess)) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ kfree(sess->sess_ops);
kfree(sess);
return -ENOMEM;
}
diff --git a/drivers/target/iscsi/iscsi_target_tmr.c b/drivers/target/iscsi/iscsi_target_tmr.c
index b0224a77e26d..fe9a582ca6af 100644
--- a/drivers/target/iscsi/iscsi_target_tmr.c
+++ b/drivers/target/iscsi/iscsi_target_tmr.c
@@ -17,7 +17,7 @@
******************************************************************************/
#include <asm/unaligned.h>
-#include <scsi/scsi_device.h>
+#include <scsi/scsi_proto.h>
#include <scsi/iscsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
index e8a240818353..5e3295fe404d 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.c
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -161,10 +161,7 @@ struct iscsi_portal_group *iscsit_get_tpg_from_np(
int iscsit_get_tpg(
struct iscsi_portal_group *tpg)
{
- int ret;
-
- ret = mutex_lock_interruptible(&tpg->tpg_access_lock);
- return ((ret != 0) || signal_pending(current)) ? -1 : 0;
+ return mutex_lock_interruptible(&tpg->tpg_access_lock);
}
void iscsit_put_tpg(struct iscsi_portal_group *tpg)
diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c
index 18b0f9703ff2..ce81f17ad1ba 100644
--- a/drivers/target/sbp/sbp_target.c
+++ b/drivers/target/sbp/sbp_target.c
@@ -30,7 +30,7 @@
#include <linux/ctype.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
-#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
#include <scsi/scsi_tcq.h>
#include <target/target_core_base.h>
#include <target/target_core_backend.h>
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index 75cbde1f7c5b..8ca373774276 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -28,8 +28,7 @@
#include <linux/configfs.h>
#include <linux/export.h>
#include <linux/file.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_proto.h>
#include <asm/unaligned.h>
#include <target/target_core_base.h>
@@ -704,7 +703,7 @@ target_alua_state_check(struct se_cmd *cmd)
if (dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)
return 0;
- if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return 0;
if (!port)
@@ -2377,7 +2376,7 @@ ssize_t core_alua_store_secondary_write_metadata(
int core_setup_alua(struct se_device *dev)
{
- if (dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV &&
+ if (!(dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH) &&
!(dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)) {
struct t10_alua_lu_gp_member *lu_gp_mem;
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c
index ddaf76a4ac2a..e7b0430a0575 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -212,10 +212,6 @@ static struct config_group *target_core_register_fabric(
pr_debug("Target_Core_ConfigFS: REGISTER -> Allocated Fabric:"
" %s\n", tf->tf_group.cg_item.ci_name);
- /*
- * Setup tf_ops.tf_subsys pointer for usage with configfs_depend_item()
- */
- tf->tf_ops.tf_subsys = tf->tf_subsys;
tf->tf_fabric = &tf->tf_group.cg_item;
pr_debug("Target_Core_ConfigFS: REGISTER -> Set tf->tf_fabric"
" for %s\n", name);
@@ -291,10 +287,17 @@ static struct configfs_subsystem target_core_fabrics = {
},
};
-struct configfs_subsystem *target_core_subsystem[] = {
- &target_core_fabrics,
- NULL,
-};
+int target_depend_item(struct config_item *item)
+{
+ return configfs_depend_item(&target_core_fabrics, item);
+}
+EXPORT_SYMBOL(target_depend_item);
+
+void target_undepend_item(struct config_item *item)
+{
+ return configfs_undepend_item(&target_core_fabrics, item);
+}
+EXPORT_SYMBOL(target_undepend_item);
/*##############################################################################
// Start functions called by external Target Fabrics Modules
@@ -467,7 +470,6 @@ int target_register_template(const struct target_core_fabric_ops *fo)
* struct target_fabric_configfs->tf_cit_tmpl
*/
tf->tf_module = fo->module;
- tf->tf_subsys = target_core_subsystem[0];
snprintf(tf->tf_name, TARGET_FABRIC_NAME_SIZE, "%s", fo->name);
tf->tf_ops = *fo;
@@ -809,7 +811,7 @@ static ssize_t target_core_dev_pr_show_attr_res_holder(struct se_device *dev,
{
int ret;
- if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return sprintf(page, "Passthrough\n");
spin_lock(&dev->dev_reservation_lock);
@@ -960,7 +962,7 @@ SE_DEV_PR_ATTR_RO(res_pr_type);
static ssize_t target_core_dev_pr_show_attr_res_type(
struct se_device *dev, char *page)
{
- if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return sprintf(page, "SPC_PASSTHROUGH\n");
else if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
return sprintf(page, "SPC2_RESERVATIONS\n");
@@ -973,7 +975,7 @@ SE_DEV_PR_ATTR_RO(res_type);
static ssize_t target_core_dev_pr_show_attr_res_aptpl_active(
struct se_device *dev, char *page)
{
- if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return 0;
return sprintf(page, "APTPL Bit Status: %s\n",
@@ -988,7 +990,7 @@ SE_DEV_PR_ATTR_RO(res_aptpl_active);
static ssize_t target_core_dev_pr_show_attr_res_aptpl_metadata(
struct se_device *dev, char *page)
{
- if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return 0;
return sprintf(page, "Ready to process PR APTPL metadata..\n");
@@ -1035,7 +1037,7 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
u16 port_rpti = 0, tpgt = 0;
u8 type = 0, scope;
- if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return 0;
if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
return 0;
@@ -2870,7 +2872,7 @@ static int __init target_core_init_configfs(void)
{
struct config_group *target_cg, *hba_cg = NULL, *alua_cg = NULL;
struct config_group *lu_gp_cg = NULL;
- struct configfs_subsystem *subsys;
+ struct configfs_subsystem *subsys = &target_core_fabrics;
struct t10_alua_lu_gp *lu_gp;
int ret;
@@ -2878,7 +2880,6 @@ static int __init target_core_init_configfs(void)
" Engine: %s on %s/%s on "UTS_RELEASE"\n",
TARGET_CORE_VERSION, utsname()->sysname, utsname()->machine);
- subsys = target_core_subsystem[0];
config_group_init(&subsys->su_group);
mutex_init(&subsys->su_mutex);
@@ -3008,13 +3009,10 @@ out_global:
static void __exit target_core_exit_configfs(void)
{
- struct configfs_subsystem *subsys;
struct config_group *hba_cg, *alua_cg, *lu_gp_cg;
struct config_item *item;
int i;
- subsys = target_core_subsystem[0];
-
lu_gp_cg = &alua_lu_gps_group;
for (i = 0; lu_gp_cg->default_groups[i]; i++) {
item = &lu_gp_cg->default_groups[i]->cg_item;
@@ -3045,8 +3043,8 @@ static void __exit target_core_exit_configfs(void)
* We expect subsys->su_group.default_groups to be released
* by configfs subsystem provider logic..
*/
- configfs_unregister_subsystem(subsys);
- kfree(subsys->su_group.default_groups);
+ configfs_unregister_subsystem(&target_core_fabrics);
+ kfree(target_core_fabrics.su_group.default_groups);
core_alua_free_lu_gp(default_lu_gp);
default_lu_gp = NULL;
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index 7faa6aef9a4d..417f88b498c7 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -33,10 +33,11 @@
#include <linux/kthread.h>
#include <linux/in.h>
#include <linux/export.h>
+#include <asm/unaligned.h>
#include <net/sock.h>
#include <net/tcp.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_device.h>
+#include <scsi/scsi_common.h>
+#include <scsi/scsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_backend.h>
@@ -527,7 +528,7 @@ static void core_export_port(
list_add_tail(&port->sep_list, &dev->dev_sep_list);
spin_unlock(&dev->se_port_lock);
- if (dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV &&
+ if (!(dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH) &&
!(dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)) {
tg_pt_gp_mem = core_alua_allocate_tg_pt_gp_mem(port);
if (IS_ERR(tg_pt_gp_mem) || !tg_pt_gp_mem) {
@@ -1603,7 +1604,7 @@ int target_configure_device(struct se_device *dev)
* anything virtual (IBLOCK, FILEIO, RAMDISK), but not for TCM/pSCSI
* passthrough because this is being provided by the backend LLD.
*/
- if (dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV) {
+ if (!(dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)) {
strncpy(&dev->t10_wwn.vendor[0], "LIO-ORG", 8);
strncpy(&dev->t10_wwn.model[0],
dev->transport->inquiry_prod, 16);
@@ -1707,3 +1708,76 @@ void core_dev_release_virtual_lun0(void)
target_free_device(g_lun0_dev);
core_delete_hba(hba);
}
+
+/*
+ * Common CDB parsing for kernel and user passthrough.
+ */
+sense_reason_t
+passthrough_parse_cdb(struct se_cmd *cmd,
+ sense_reason_t (*exec_cmd)(struct se_cmd *cmd))
+{
+ unsigned char *cdb = cmd->t_task_cdb;
+
+ /*
+ * Clear a lun set in the cdb if the initiator talking to use spoke
+ * and old standards version, as we can't assume the underlying device
+ * won't choke up on it.
+ */
+ switch (cdb[0]) {
+ case READ_10: /* SBC - RDProtect */
+ case READ_12: /* SBC - RDProtect */
+ case READ_16: /* SBC - RDProtect */
+ case SEND_DIAGNOSTIC: /* SPC - SELF-TEST Code */
+ case VERIFY: /* SBC - VRProtect */
+ case VERIFY_16: /* SBC - VRProtect */
+ case WRITE_VERIFY: /* SBC - VRProtect */
+ case WRITE_VERIFY_12: /* SBC - VRProtect */
+ case MAINTENANCE_IN: /* SPC - Parameter Data Format for SA RTPG */
+ break;
+ default:
+ cdb[1] &= 0x1f; /* clear logical unit number */
+ break;
+ }
+
+ /*
+ * For REPORT LUNS we always need to emulate the response, for everything
+ * else, pass it up.
+ */
+ if (cdb[0] == REPORT_LUNS) {
+ cmd->execute_cmd = spc_emulate_report_luns;
+ return TCM_NO_SENSE;
+ }
+
+ /* Set DATA_CDB flag for ops that should have it */
+ switch (cdb[0]) {
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ case WRITE_VERIFY:
+ case WRITE_VERIFY_12:
+ case 0x8e: /* WRITE_VERIFY_16 */
+ case COMPARE_AND_WRITE:
+ case XDWRITEREAD_10:
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ break;
+ case VARIABLE_LENGTH_CMD:
+ switch (get_unaligned_be16(&cdb[8])) {
+ case READ_32:
+ case WRITE_32:
+ case 0x0c: /* WRITE_VERIFY_32 */
+ case XDWRITEREAD_32:
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ break;
+ }
+ }
+
+ cmd->execute_cmd = exec_cmd;
+
+ return TCM_NO_SENSE;
+}
+EXPORT_SYMBOL(passthrough_parse_cdb);
diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c
index 35bfe77160d8..41f4f270f919 100644
--- a/drivers/target/target_core_fabric_lib.c
+++ b/drivers/target/target_core_fabric_lib.c
@@ -29,8 +29,8 @@
#include <linux/ctype.h>
#include <linux/spinlock.h>
#include <linux/export.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+
+#include <scsi/scsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index f7e6e51aed36..d48379a258c7 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -31,8 +31,7 @@
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/falloc.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
+#include <scsi/scsi_proto.h>
#include <asm/unaligned.h>
#include <target/target_core_base.h>
@@ -958,7 +957,6 @@ static struct se_subsystem_api fileio_template = {
.inquiry_prod = "FILEIO",
.inquiry_rev = FD_VERSION,
.owner = THIS_MODULE,
- .transport_type = TRANSPORT_PLUGIN_VHBA_PDEV,
.attach_hba = fd_attach_hba,
.detach_hba = fd_detach_hba,
.alloc_device = fd_alloc_device,
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
index 1b7947c2510f..972ed1781ae2 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -35,8 +35,7 @@
#include <linux/genhd.h>
#include <linux/file.h>
#include <linux/module.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
+#include <scsi/scsi_proto.h>
#include <asm/unaligned.h>
#include <target/target_core_base.h>
@@ -904,7 +903,6 @@ static struct se_subsystem_api iblock_template = {
.inquiry_prod = "IBLOCK",
.inquiry_rev = IBLOCK_VERSION,
.owner = THIS_MODULE,
- .transport_type = TRANSPORT_PLUGIN_VHBA_PDEV,
.attach_hba = iblock_attach_hba,
.detach_hba = iblock_detach_hba,
.alloc_device = iblock_alloc_device,
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h
index 874a9bc988d8..68bd7f5d9f73 100644
--- a/drivers/target/target_core_internal.h
+++ b/drivers/target/target_core_internal.h
@@ -4,9 +4,6 @@
/* target_core_alua.c */
extern struct t10_alua_lu_gp *default_lu_gp;
-/* target_core_configfs.c */
-extern struct configfs_subsystem *target_core_subsystem[];
-
/* target_core_device.c */
extern struct mutex g_device_mutex;
extern struct list_head g_device_list;
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index c1aa9655e96e..7ca642361f9c 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -28,8 +28,7 @@
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/file.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_proto.h>
#include <asm/unaligned.h>
#include <target/target_core_base.h>
@@ -1367,41 +1366,26 @@ void core_scsi3_free_all_registrations(
static int core_scsi3_tpg_depend_item(struct se_portal_group *tpg)
{
- return configfs_depend_item(tpg->se_tpg_tfo->tf_subsys,
- &tpg->tpg_group.cg_item);
+ return target_depend_item(&tpg->tpg_group.cg_item);
}
static void core_scsi3_tpg_undepend_item(struct se_portal_group *tpg)
{
- configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys,
- &tpg->tpg_group.cg_item);
-
+ target_undepend_item(&tpg->tpg_group.cg_item);
atomic_dec_mb(&tpg->tpg_pr_ref_count);
}
static int core_scsi3_nodeacl_depend_item(struct se_node_acl *nacl)
{
- struct se_portal_group *tpg = nacl->se_tpg;
-
if (nacl->dynamic_node_acl)
return 0;
-
- return configfs_depend_item(tpg->se_tpg_tfo->tf_subsys,
- &nacl->acl_group.cg_item);
+ return target_depend_item(&nacl->acl_group.cg_item);
}
static void core_scsi3_nodeacl_undepend_item(struct se_node_acl *nacl)
{
- struct se_portal_group *tpg = nacl->se_tpg;
-
- if (nacl->dynamic_node_acl) {
- atomic_dec_mb(&nacl->acl_pr_ref_count);
- return;
- }
-
- configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys,
- &nacl->acl_group.cg_item);
-
+ if (!nacl->dynamic_node_acl)
+ target_undepend_item(&nacl->acl_group.cg_item);
atomic_dec_mb(&nacl->acl_pr_ref_count);
}
@@ -1419,8 +1403,7 @@ static int core_scsi3_lunacl_depend_item(struct se_dev_entry *se_deve)
nacl = lun_acl->se_lun_nacl;
tpg = nacl->se_tpg;
- return configfs_depend_item(tpg->se_tpg_tfo->tf_subsys,
- &lun_acl->se_lun_group.cg_item);
+ return target_depend_item(&lun_acl->se_lun_group.cg_item);
}
static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve)
@@ -1438,9 +1421,7 @@ static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve)
nacl = lun_acl->se_lun_nacl;
tpg = nacl->se_tpg;
- configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys,
- &lun_acl->se_lun_group.cg_item);
-
+ target_undepend_item(&lun_acl->se_lun_group.cg_item);
atomic_dec_mb(&se_deve->pr_ref_count);
}
@@ -4111,7 +4092,7 @@ target_check_reservation(struct se_cmd *cmd)
return 0;
if (dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)
return 0;
- if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return 0;
spin_lock(&dev->dev_reservation_lock);
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index f6c954c4635f..26581e215141 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -36,9 +36,7 @@
#include <linux/module.h>
#include <asm/unaligned.h>
-#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
-#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
@@ -521,6 +519,7 @@ static int pscsi_configure_device(struct se_device *dev)
" pdv_host_id: %d\n", pdv->pdv_host_id);
return -EINVAL;
}
+ pdv->pdv_lld_host = sh;
}
} else {
if (phv->phv_mode == PHV_VIRTUAL_HOST_ID) {
@@ -603,6 +602,8 @@ static void pscsi_free_device(struct se_device *dev)
if ((phv->phv_mode == PHV_LLD_SCSI_HOST_NO) &&
(phv->phv_lld_host != NULL))
scsi_host_put(phv->phv_lld_host);
+ else if (pdv->pdv_lld_host)
+ scsi_host_put(pdv->pdv_lld_host);
if ((sd->type == TYPE_DISK) || (sd->type == TYPE_ROM))
scsi_device_put(sd);
@@ -970,64 +971,13 @@ fail:
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
-/*
- * Clear a lun set in the cdb if the initiator talking to use spoke
- * and old standards version, as we can't assume the underlying device
- * won't choke up on it.
- */
-static inline void pscsi_clear_cdb_lun(unsigned char *cdb)
-{
- switch (cdb[0]) {
- case READ_10: /* SBC - RDProtect */
- case READ_12: /* SBC - RDProtect */
- case READ_16: /* SBC - RDProtect */
- case SEND_DIAGNOSTIC: /* SPC - SELF-TEST Code */
- case VERIFY: /* SBC - VRProtect */
- case VERIFY_16: /* SBC - VRProtect */
- case WRITE_VERIFY: /* SBC - VRProtect */
- case WRITE_VERIFY_12: /* SBC - VRProtect */
- case MAINTENANCE_IN: /* SPC - Parameter Data Format for SA RTPG */
- break;
- default:
- cdb[1] &= 0x1f; /* clear logical unit number */
- break;
- }
-}
-
static sense_reason_t
pscsi_parse_cdb(struct se_cmd *cmd)
{
- unsigned char *cdb = cmd->t_task_cdb;
-
if (cmd->se_cmd_flags & SCF_BIDI)
return TCM_UNSUPPORTED_SCSI_OPCODE;
- pscsi_clear_cdb_lun(cdb);
-
- /*
- * For REPORT LUNS we always need to emulate the response, for everything
- * else the default for pSCSI is to pass the command to the underlying
- * LLD / physical hardware.
- */
- switch (cdb[0]) {
- case REPORT_LUNS:
- cmd->execute_cmd = spc_emulate_report_luns;
- return 0;
- case READ_6:
- case READ_10:
- case READ_12:
- case READ_16:
- case WRITE_6:
- case WRITE_10:
- case WRITE_12:
- case WRITE_16:
- case WRITE_VERIFY:
- cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
- /* FALLTHROUGH*/
- default:
- cmd->execute_cmd = pscsi_execute_cmd;
- return 0;
- }
+ return passthrough_parse_cdb(cmd, pscsi_execute_cmd);
}
static sense_reason_t
@@ -1189,7 +1139,7 @@ static struct configfs_attribute *pscsi_backend_dev_attrs[] = {
static struct se_subsystem_api pscsi_template = {
.name = "pscsi",
.owner = THIS_MODULE,
- .transport_type = TRANSPORT_PLUGIN_PHBA_PDEV,
+ .transport_flags = TRANSPORT_FLAG_PASSTHROUGH,
.attach_hba = pscsi_attach_hba,
.detach_hba = pscsi_detach_hba,
.pmode_enable_hba = pscsi_pmode_enable_hba,
diff --git a/drivers/target/target_core_pscsi.h b/drivers/target/target_core_pscsi.h
index 1bd757dff8ee..6d2007e35df6 100644
--- a/drivers/target/target_core_pscsi.h
+++ b/drivers/target/target_core_pscsi.h
@@ -16,13 +16,13 @@
#define PS_TIMEOUT_OTHER (500*HZ)
#include <linux/device.h>
-#include <scsi/scsi_driver.h>
-#include <scsi/scsi_device.h>
#include <linux/kref.h>
#include <linux/kobject.h>
+struct scsi_device;
+
struct pscsi_plugin_task {
- unsigned char pscsi_sense[SCSI_SENSE_BUFFERSIZE];
+ unsigned char pscsi_sense[TRANSPORT_SENSE_BUFFER];
int pscsi_direction;
int pscsi_result;
u32 pscsi_resid;
@@ -45,6 +45,7 @@ struct pscsi_dev_virt {
int pdv_lun_id;
struct block_device *pdv_bd;
struct scsi_device *pdv_sd;
+ struct Scsi_Host *pdv_lld_host;
} ____cacheline_aligned;
typedef enum phv_modes {
diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c
index a263bf5fab8d..b2d8f6f91633 100644
--- a/drivers/target/target_core_rd.c
+++ b/drivers/target/target_core_rd.c
@@ -29,8 +29,7 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
+#include <scsi/scsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_backend.h>
@@ -733,7 +732,6 @@ static struct se_subsystem_api rd_mcp_template = {
.name = "rd_mcp",
.inquiry_prod = "RAMDISK-MCP",
.inquiry_rev = RD_MCP_VERSION,
- .transport_type = TRANSPORT_PLUGIN_VHBA_VDEV,
.attach_hba = rd_attach_hba,
.detach_hba = rd_detach_hba,
.alloc_device = rd_alloc_device,
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 8855781ac653..43719b393ca9 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -25,7 +25,7 @@
#include <linux/ratelimit.h>
#include <linux/crc-t10dif.h>
#include <asm/unaligned.h>
-#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
#include <scsi/scsi_tcq.h>
#include <target/target_core_base.h>
@@ -568,7 +568,7 @@ sbc_compare_and_write(struct se_cmd *cmd)
* comparision using SGLs at cmd->t_bidi_data_sg..
*/
rc = down_interruptible(&dev->caw_sem);
- if ((rc != 0) || signal_pending(current)) {
+ if (rc != 0) {
cmd->transport_complete_callback = NULL;
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c
index 7912aa124385..52ea640274f4 100644
--- a/drivers/target/target_core_spc.c
+++ b/drivers/target/target_core_spc.c
@@ -24,7 +24,8 @@
#include <linux/module.h>
#include <asm/unaligned.h>
-#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
+#include <scsi/scsi_common.h>
#include <scsi/scsi_tcq.h>
#include <target/target_core_base.h>
diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c
index 03538994d2f7..40f6c1378041 100644
--- a/drivers/target/target_core_stat.c
+++ b/drivers/target/target_core_stat.c
@@ -33,9 +33,6 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/configfs.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
#include <target/target_core_base.h>
#include <target/target_core_backend.h>
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index 315ec3458eeb..a5bb0c46e57e 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -27,8 +27,6 @@
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/export.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
#include <target/target_core_base.h>
#include <target/target_core_backend.h>
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c
index 47f064415bf6..84de757bd458 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -32,8 +32,7 @@
#include <linux/export.h>
#include <net/sock.h>
#include <net/tcp.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_backend.h>
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 3fe5cb240b6f..fdf867230e18 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -37,9 +37,7 @@
#include <asm/unaligned.h>
#include <net/sock.h>
#include <net/tcp.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_backend.h>
@@ -1196,7 +1194,7 @@ transport_check_alloc_task_attr(struct se_cmd *cmd)
* Check if SAM Task Attribute emulation is enabled for this
* struct se_device storage object
*/
- if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return 0;
if (cmd->sam_task_attr == TCM_ACA_TAG) {
@@ -1770,7 +1768,7 @@ static int target_write_prot_action(struct se_cmd *cmd)
sectors, 0, NULL, 0);
if (unlikely(cmd->pi_err)) {
spin_lock_irq(&cmd->t_state_lock);
- cmd->transport_state &= ~CMD_T_BUSY|CMD_T_SENT;
+ cmd->transport_state &= ~(CMD_T_BUSY|CMD_T_SENT);
spin_unlock_irq(&cmd->t_state_lock);
transport_generic_request_failure(cmd, cmd->pi_err);
return -1;
@@ -1787,7 +1785,7 @@ static bool target_handle_task_attr(struct se_cmd *cmd)
{
struct se_device *dev = cmd->se_dev;
- if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return false;
/*
@@ -1868,7 +1866,7 @@ void target_execute_cmd(struct se_cmd *cmd)
if (target_handle_task_attr(cmd)) {
spin_lock_irq(&cmd->t_state_lock);
- cmd->transport_state &= ~CMD_T_BUSY|CMD_T_SENT;
+ cmd->transport_state &= ~(CMD_T_BUSY | CMD_T_SENT);
spin_unlock_irq(&cmd->t_state_lock);
return;
}
@@ -1912,7 +1910,7 @@ static void transport_complete_task_attr(struct se_cmd *cmd)
{
struct se_device *dev = cmd->se_dev;
- if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return;
if (cmd->sam_task_attr == TCM_SIMPLE_TAG) {
@@ -1957,8 +1955,7 @@ static void transport_complete_qf(struct se_cmd *cmd)
case DMA_TO_DEVICE:
if (cmd->se_cmd_flags & SCF_BIDI) {
ret = cmd->se_tfo->queue_data_in(cmd);
- if (ret < 0)
- break;
+ break;
}
/* Fall through for DMA_TO_DEVICE */
case DMA_NONE:
diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c
index 1738b1646988..e44cc94b12cb 100644
--- a/drivers/target/target_core_ua.c
+++ b/drivers/target/target_core_ua.c
@@ -25,8 +25,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index dbc872a6c981..5efef9a2a3d3 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -19,12 +19,13 @@
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/idr.h>
+#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/parser.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
#include <linux/uio_driver.h>
#include <net/genetlink.h>
+#include <scsi/scsi_common.h>
+#include <scsi/scsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
#include <target/target_core_backend.h>
@@ -71,13 +72,6 @@ struct tcmu_hba {
u32 host_id;
};
-/* User wants all cmds or just some */
-enum passthru_level {
- TCMU_PASS_ALL = 0,
- TCMU_PASS_IO,
- TCMU_PASS_INVALID,
-};
-
#define TCMU_CONFIG_LEN 256
struct tcmu_dev {
@@ -89,7 +83,6 @@ struct tcmu_dev {
#define TCMU_DEV_BIT_OPEN 0
#define TCMU_DEV_BIT_BROKEN 1
unsigned long flags;
- enum passthru_level pass_level;
struct uio_info uio_info;
@@ -683,8 +676,6 @@ static struct se_device *tcmu_alloc_device(struct se_hba *hba, const char *name)
setup_timer(&udev->timeout, tcmu_device_timedout,
(unsigned long)udev);
- udev->pass_level = TCMU_PASS_ALL;
-
return &udev->se_dev;
}
@@ -948,13 +939,13 @@ static void tcmu_free_device(struct se_device *dev)
}
enum {
- Opt_dev_config, Opt_dev_size, Opt_err, Opt_pass_level,
+ Opt_dev_config, Opt_dev_size, Opt_hw_block_size, Opt_err,
};
static match_table_t tokens = {
{Opt_dev_config, "dev_config=%s"},
{Opt_dev_size, "dev_size=%u"},
- {Opt_pass_level, "pass_level=%u"},
+ {Opt_hw_block_size, "hw_block_size=%u"},
{Opt_err, NULL}
};
@@ -965,7 +956,7 @@ static ssize_t tcmu_set_configfs_dev_params(struct se_device *dev,
char *orig, *ptr, *opts, *arg_p;
substring_t args[MAX_OPT_ARGS];
int ret = 0, token;
- int arg;
+ unsigned long tmp_ul;
opts = kstrdup(page, GFP_KERNEL);
if (!opts)
@@ -998,15 +989,23 @@ static ssize_t tcmu_set_configfs_dev_params(struct se_device *dev,
if (ret < 0)
pr_err("kstrtoul() failed for dev_size=\n");
break;
- case Opt_pass_level:
- match_int(args, &arg);
- if (arg >= TCMU_PASS_INVALID) {
- pr_warn("TCMU: Invalid pass_level: %d\n", arg);
+ case Opt_hw_block_size:
+ arg_p = match_strdup(&args[0]);
+ if (!arg_p) {
+ ret = -ENOMEM;
break;
}
-
- pr_debug("TCMU: Setting pass_level to %d\n", arg);
- udev->pass_level = arg;
+ ret = kstrtoul(arg_p, 0, &tmp_ul);
+ kfree(arg_p);
+ if (ret < 0) {
+ pr_err("kstrtoul() failed for hw_block_size=\n");
+ break;
+ }
+ if (!tmp_ul) {
+ pr_err("hw_block_size must be nonzero\n");
+ break;
+ }
+ dev->dev_attrib.hw_block_size = tmp_ul;
break;
default:
break;
@@ -1024,8 +1023,7 @@ static ssize_t tcmu_show_configfs_dev_params(struct se_device *dev, char *b)
bl = sprintf(b + bl, "Config: %s ",
udev->dev_config[0] ? udev->dev_config : "NULL");
- bl += sprintf(b + bl, "Size: %zu PassLevel: %u\n",
- udev->dev_size, udev->pass_level);
+ bl += sprintf(b + bl, "Size: %zu\n", udev->dev_size);
return bl;
}
@@ -1039,20 +1037,6 @@ static sector_t tcmu_get_blocks(struct se_device *dev)
}
static sense_reason_t
-tcmu_execute_rw(struct se_cmd *se_cmd, struct scatterlist *sgl, u32 sgl_nents,
- enum dma_data_direction data_direction)
-{
- int ret;
-
- ret = tcmu_queue_cmd(se_cmd);
-
- if (ret != 0)
- return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
- else
- return TCM_NO_SENSE;
-}
-
-static sense_reason_t
tcmu_pass_op(struct se_cmd *se_cmd)
{
int ret = tcmu_queue_cmd(se_cmd);
@@ -1063,91 +1047,29 @@ tcmu_pass_op(struct se_cmd *se_cmd)
return TCM_NO_SENSE;
}
-static struct sbc_ops tcmu_sbc_ops = {
- .execute_rw = tcmu_execute_rw,
- .execute_sync_cache = tcmu_pass_op,
- .execute_write_same = tcmu_pass_op,
- .execute_write_same_unmap = tcmu_pass_op,
- .execute_unmap = tcmu_pass_op,
-};
-
static sense_reason_t
tcmu_parse_cdb(struct se_cmd *cmd)
{
- unsigned char *cdb = cmd->t_task_cdb;
- struct tcmu_dev *udev = TCMU_DEV(cmd->se_dev);
- sense_reason_t ret;
-
- switch (udev->pass_level) {
- case TCMU_PASS_ALL:
- /* We're just like pscsi, then */
- /*
- * For REPORT LUNS we always need to emulate the response, for everything
- * else, pass it up.
- */
- switch (cdb[0]) {
- case REPORT_LUNS:
- cmd->execute_cmd = spc_emulate_report_luns;
- break;
- case READ_6:
- case READ_10:
- case READ_12:
- case READ_16:
- case WRITE_6:
- case WRITE_10:
- case WRITE_12:
- case WRITE_16:
- case WRITE_VERIFY:
- cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
- /* FALLTHROUGH */
- default:
- cmd->execute_cmd = tcmu_pass_op;
- }
- ret = TCM_NO_SENSE;
- break;
- case TCMU_PASS_IO:
- ret = sbc_parse_cdb(cmd, &tcmu_sbc_ops);
- break;
- default:
- pr_err("Unknown tcm-user pass level %d\n", udev->pass_level);
- ret = TCM_CHECK_CONDITION_ABORT_CMD;
- }
-
- return ret;
+ return passthrough_parse_cdb(cmd, tcmu_pass_op);
}
-DEF_TB_DEFAULT_ATTRIBS(tcmu);
+DEF_TB_DEV_ATTRIB_RO(tcmu, hw_pi_prot_type);
+TB_DEV_ATTR_RO(tcmu, hw_pi_prot_type);
+
+DEF_TB_DEV_ATTRIB_RO(tcmu, hw_block_size);
+TB_DEV_ATTR_RO(tcmu, hw_block_size);
+
+DEF_TB_DEV_ATTRIB_RO(tcmu, hw_max_sectors);
+TB_DEV_ATTR_RO(tcmu, hw_max_sectors);
+
+DEF_TB_DEV_ATTRIB_RO(tcmu, hw_queue_depth);
+TB_DEV_ATTR_RO(tcmu, hw_queue_depth);
static struct configfs_attribute *tcmu_backend_dev_attrs[] = {
- &tcmu_dev_attrib_emulate_model_alias.attr,
- &tcmu_dev_attrib_emulate_dpo.attr,
- &tcmu_dev_attrib_emulate_fua_write.attr,
- &tcmu_dev_attrib_emulate_fua_read.attr,
- &tcmu_dev_attrib_emulate_write_cache.attr,
- &tcmu_dev_attrib_emulate_ua_intlck_ctrl.attr,
- &tcmu_dev_attrib_emulate_tas.attr,
- &tcmu_dev_attrib_emulate_tpu.attr,
- &tcmu_dev_attrib_emulate_tpws.attr,
- &tcmu_dev_attrib_emulate_caw.attr,
- &tcmu_dev_attrib_emulate_3pc.attr,
- &tcmu_dev_attrib_pi_prot_type.attr,
&tcmu_dev_attrib_hw_pi_prot_type.attr,
- &tcmu_dev_attrib_pi_prot_format.attr,
- &tcmu_dev_attrib_enforce_pr_isids.attr,
- &tcmu_dev_attrib_is_nonrot.attr,
- &tcmu_dev_attrib_emulate_rest_reord.attr,
- &tcmu_dev_attrib_force_pr_aptpl.attr,
&tcmu_dev_attrib_hw_block_size.attr,
- &tcmu_dev_attrib_block_size.attr,
&tcmu_dev_attrib_hw_max_sectors.attr,
- &tcmu_dev_attrib_optimal_sectors.attr,
&tcmu_dev_attrib_hw_queue_depth.attr,
- &tcmu_dev_attrib_queue_depth.attr,
- &tcmu_dev_attrib_max_unmap_lba_count.attr,
- &tcmu_dev_attrib_max_unmap_block_desc_count.attr,
- &tcmu_dev_attrib_unmap_granularity.attr,
- &tcmu_dev_attrib_unmap_granularity_alignment.attr,
- &tcmu_dev_attrib_max_write_same_len.attr,
NULL,
};
@@ -1156,7 +1078,7 @@ static struct se_subsystem_api tcmu_template = {
.inquiry_prod = "USER",
.inquiry_rev = TCMU_VERSION,
.owner = THIS_MODULE,
- .transport_type = TRANSPORT_PLUGIN_VHBA_PDEV,
+ .transport_flags = TRANSPORT_FLAG_PASSTHROUGH,
.attach_hba = tcmu_attach_hba,
.detach_hba = tcmu_detach_hba,
.alloc_device = tcmu_alloc_device,
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c
index a600ff15dcfd..5ec0d00edaa3 100644
--- a/drivers/target/target_core_xcopy.c
+++ b/drivers/target/target_core_xcopy.c
@@ -25,8 +25,7 @@
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/configfs.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_proto.h>
#include <asm/unaligned.h>
#include <target/target_core_base.h>
@@ -58,7 +57,6 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
bool src)
{
struct se_device *se_dev;
- struct configfs_subsystem *subsys = target_core_subsystem[0];
unsigned char tmp_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN], *dev_wwn;
int rc;
@@ -90,8 +88,7 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
" se_dev\n", xop->src_dev);
}
- rc = configfs_depend_item(subsys,
- &se_dev->dev_group.cg_item);
+ rc = target_depend_item(&se_dev->dev_group.cg_item);
if (rc != 0) {
pr_err("configfs_depend_item attempt failed:"
" %d for se_dev: %p\n", rc, se_dev);
@@ -99,8 +96,8 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
return rc;
}
- pr_debug("Called configfs_depend_item for subsys: %p se_dev: %p"
- " se_dev->se_dev_group: %p\n", subsys, se_dev,
+ pr_debug("Called configfs_depend_item for se_dev: %p"
+ " se_dev->se_dev_group: %p\n", se_dev,
&se_dev->dev_group);
mutex_unlock(&g_device_mutex);
@@ -373,7 +370,6 @@ static int xcopy_pt_get_cmd_state(struct se_cmd *se_cmd)
static void xcopy_pt_undepend_remotedev(struct xcopy_op *xop)
{
- struct configfs_subsystem *subsys = target_core_subsystem[0];
struct se_device *remote_dev;
if (xop->op_origin == XCOL_SOURCE_RECV_OP)
@@ -381,11 +377,11 @@ static void xcopy_pt_undepend_remotedev(struct xcopy_op *xop)
else
remote_dev = xop->src_dev;
- pr_debug("Calling configfs_undepend_item for subsys: %p"
+ pr_debug("Calling configfs_undepend_item for"
" remote_dev: %p remote_dev->dev_group: %p\n",
- subsys, remote_dev, &remote_dev->dev_group.cg_item);
+ remote_dev, &remote_dev->dev_group.cg_item);
- configfs_undepend_item(subsys, &remote_dev->dev_group.cg_item);
+ target_undepend_item(&remote_dev->dev_group.cg_item);
}
static void xcopy_pt_release_cmd(struct se_cmd *se_cmd)
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
index edcafa4490c0..1bf78e7c994c 100644
--- a/drivers/target/tcm_fc/tfc_cmd.c
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -30,10 +30,6 @@
#include <linux/hash.h>
#include <linux/percpu_ida.h>
#include <asm/unaligned.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_tcq.h>
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c
index 65dce1345966..86b699b94c7b 100644
--- a/drivers/target/tcm_fc/tfc_conf.c
+++ b/drivers/target/tcm_fc/tfc_conf.c
@@ -34,10 +34,6 @@
#include <linux/kernel.h>
#include <linux/ctype.h>
#include <asm/unaligned.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_cmnd.h>
#include <scsi/libfc.h>
#include <target/target_core_base.h>
diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c
index 583e755d8091..fe585d1cce23 100644
--- a/drivers/target/tcm_fc/tfc_io.c
+++ b/drivers/target/tcm_fc/tfc_io.c
@@ -39,10 +39,6 @@
#include <linux/hash.h>
#include <linux/ratelimit.h>
#include <asm/unaligned.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_cmnd.h>
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c
index ccee7e332a4d..f2a616d4f2c4 100644
--- a/drivers/target/tcm_fc/tfc_sess.c
+++ b/drivers/target/tcm_fc/tfc_sess.c
@@ -32,10 +32,6 @@
#include <linux/rculist.h>
#include <linux/kref.h>
#include <asm/unaligned.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_cmnd.h>
#include <scsi/libfc.h>
#include <target/target_core_base.h>
diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c
index c2556cf5186b..01255fd65135 100644
--- a/drivers/thermal/armada_thermal.c
+++ b/drivers/thermal/armada_thermal.c
@@ -224,9 +224,9 @@ static const struct armada_thermal_data armada380_data = {
.is_valid_shift = 10,
.temp_shift = 0,
.temp_mask = 0x3ff,
- .coef_b = 1169498786UL,
- .coef_m = 2000000UL,
- .coef_div = 4289,
+ .coef_b = 2931108200UL,
+ .coef_m = 5000000UL,
+ .coef_div = 10502,
.inverted = true,
};
diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c
index 12623bc02f46..725718e97a0b 100644
--- a/drivers/thermal/intel_powerclamp.c
+++ b/drivers/thermal/intel_powerclamp.c
@@ -206,51 +206,57 @@ static void find_target_mwait(void)
}
+struct pkg_cstate_info {
+ bool skip;
+ int msr_index;
+ int cstate_id;
+};
+
+#define PKG_CSTATE_INIT(id) { \
+ .msr_index = MSR_PKG_C##id##_RESIDENCY, \
+ .cstate_id = id \
+ }
+
+static struct pkg_cstate_info pkg_cstates[] = {
+ PKG_CSTATE_INIT(2),
+ PKG_CSTATE_INIT(3),
+ PKG_CSTATE_INIT(6),
+ PKG_CSTATE_INIT(7),
+ PKG_CSTATE_INIT(8),
+ PKG_CSTATE_INIT(9),
+ PKG_CSTATE_INIT(10),
+ {NULL},
+};
+
static bool has_pkg_state_counter(void)
{
- u64 tmp;
- return !rdmsrl_safe(MSR_PKG_C2_RESIDENCY, &tmp) ||
- !rdmsrl_safe(MSR_PKG_C3_RESIDENCY, &tmp) ||
- !rdmsrl_safe(MSR_PKG_C6_RESIDENCY, &tmp) ||
- !rdmsrl_safe(MSR_PKG_C7_RESIDENCY, &tmp);
+ u64 val;
+ struct pkg_cstate_info *info = pkg_cstates;
+
+ /* check if any one of the counter msrs exists */
+ while (info->msr_index) {
+ if (!rdmsrl_safe(info->msr_index, &val))
+ return true;
+ info++;
+ }
+
+ return false;
}
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;
+ struct pkg_cstate_info *info = pkg_cstates;
+
+ while (info->msr_index) {
+ if (!info->skip) {
+ if (!rdmsrl_safe(info->msr_index, &val))
+ count += val;
+ else
+ info->skip = true;
+ }
+ info++;
}
return count;
@@ -667,7 +673,7 @@ static struct thermal_cooling_device_ops powerclamp_cooling_ops = {
};
/* runs on Nehalem and later */
-static const struct x86_cpu_id intel_powerclamp_ids[] = {
+static const struct x86_cpu_id intel_powerclamp_ids[] __initconst = {
{ X86_VENDOR_INTEL, 6, 0x1a},
{ X86_VENDOR_INTEL, 6, 0x1c},
{ X86_VENDOR_INTEL, 6, 0x1e},
@@ -689,12 +695,13 @@ static const struct x86_cpu_id intel_powerclamp_ids[] = {
{ X86_VENDOR_INTEL, 6, 0x46},
{ X86_VENDOR_INTEL, 6, 0x4c},
{ X86_VENDOR_INTEL, 6, 0x4d},
+ { X86_VENDOR_INTEL, 6, 0x4f},
{ X86_VENDOR_INTEL, 6, 0x56},
{}
};
MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
-static int powerclamp_probe(void)
+static int __init powerclamp_probe(void)
{
if (!x86_match_cpu(intel_powerclamp_ids)) {
pr_err("Intel powerclamp does not run on family %d model %d\n",
@@ -760,7 +767,7 @@ file_error:
debugfs_remove_recursive(debug_dir);
}
-static int powerclamp_init(void)
+static int __init powerclamp_init(void)
{
int retval;
int bitmap_size;
@@ -809,7 +816,7 @@ exit_free:
}
module_init(powerclamp_init);
-static void powerclamp_exit(void)
+static void __exit powerclamp_exit(void)
{
unregister_hotcpu_notifier(&powerclamp_cpu_notifier);
end_power_clamp();
diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
index 3aa46ac7cdbc..cd8f5f93b42c 100644
--- a/drivers/thermal/rockchip_thermal.c
+++ b/drivers/thermal/rockchip_thermal.c
@@ -529,7 +529,7 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
thermal->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
if (IS_ERR(thermal->pclk)) {
- error = PTR_ERR(thermal->clk);
+ error = PTR_ERR(thermal->pclk);
dev_err(&pdev->dev, "failed to get apb_pclk clock: %d\n",
error);
return error;
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index 0531c752fbbb..8e391812e503 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -103,7 +103,7 @@ static inline int of_thermal_get_ntrips(struct thermal_zone_device *tz)
static inline bool of_thermal_is_trip_valid(struct thermal_zone_device *tz,
int trip)
{
- return 0;
+ return false;
}
static inline const struct thermal_trip *
of_thermal_get_trip_points(struct thermal_zone_device *tz)
diff --git a/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c b/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c
index a4929272074f..58b5c6694cd4 100644
--- a/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c
+++ b/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c
@@ -420,7 +420,8 @@ const struct ti_bandgap_data dra752_data = {
TI_BANDGAP_FEATURE_FREEZE_BIT |
TI_BANDGAP_FEATURE_TALERT |
TI_BANDGAP_FEATURE_COUNTER_DELAY |
- TI_BANDGAP_FEATURE_HISTORY_BUFFER,
+ TI_BANDGAP_FEATURE_HISTORY_BUFFER |
+ TI_BANDGAP_FEATURE_ERRATA_814,
.fclock_name = "l3instr_ts_gclk_div",
.div_ck_name = "l3instr_ts_gclk_div",
.conv_table = dra752_adc_to_temp,
diff --git a/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c b/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c
index eff0c80fd4af..79ff70c446ba 100644
--- a/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c
+++ b/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c
@@ -319,7 +319,8 @@ const struct ti_bandgap_data omap5430_data = {
TI_BANDGAP_FEATURE_FREEZE_BIT |
TI_BANDGAP_FEATURE_TALERT |
TI_BANDGAP_FEATURE_COUNTER_DELAY |
- TI_BANDGAP_FEATURE_HISTORY_BUFFER,
+ TI_BANDGAP_FEATURE_HISTORY_BUFFER |
+ TI_BANDGAP_FEATURE_ERRATA_813,
.fclock_name = "l3instr_ts_gclk_div",
.div_ck_name = "l3instr_ts_gclk_div",
.conv_table = omap5430_adc_to_temp,
diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
index 62a5d449c388..bc14dc874594 100644
--- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c
+++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
@@ -119,6 +119,37 @@ exit:
}
/**
+ * ti_errata814_bandgap_read_temp() - helper function to read dra7 sensor temperature
+ * @bgp: pointer to ti_bandgap structure
+ * @reg: desired register (offset) to be read
+ *
+ * Function to read dra7 bandgap sensor temperature. This is done separately
+ * so as to workaround the errata "Bandgap Temperature read Dtemp can be
+ * corrupted" - Errata ID: i814".
+ * Read accesses to registers listed below can be corrupted due to incorrect
+ * resynchronization between clock domains.
+ * Read access to registers below can be corrupted :
+ * CTRL_CORE_DTEMP_MPU/GPU/CORE/DSPEVE/IVA_n (n = 0 to 4)
+ * CTRL_CORE_TEMP_SENSOR_MPU/GPU/CORE/DSPEVE/IVA_n
+ *
+ * Return: the register value.
+ */
+static u32 ti_errata814_bandgap_read_temp(struct ti_bandgap *bgp, u32 reg)
+{
+ u32 val1, val2;
+
+ val1 = ti_bandgap_readl(bgp, reg);
+ val2 = ti_bandgap_readl(bgp, reg);
+
+ /* If both times we read the same value then that is right */
+ if (val1 == val2)
+ return val1;
+
+ /* if val1 and val2 are different read it third time */
+ return ti_bandgap_readl(bgp, reg);
+}
+
+/**
* ti_bandgap_read_temp() - helper function to read sensor temperature
* @bgp: pointer to ti_bandgap structure
* @id: bandgap sensor id
@@ -148,7 +179,11 @@ static u32 ti_bandgap_read_temp(struct ti_bandgap *bgp, int id)
}
/* read temperature */
- temp = ti_bandgap_readl(bgp, reg);
+ if (TI_BANDGAP_HAS(bgp, ERRATA_814))
+ temp = ti_errata814_bandgap_read_temp(bgp, reg);
+ else
+ temp = ti_bandgap_readl(bgp, reg);
+
temp &= tsr->bgap_dtemp_mask;
if (TI_BANDGAP_HAS(bgp, FREEZE_BIT))
@@ -410,7 +445,7 @@ static int ti_bandgap_update_alert_threshold(struct ti_bandgap *bgp, int id,
{
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;
+ u32 thresh_val, reg_val, t_hot, t_cold, ctrl;
int err = 0;
tsr = bgp->conf->sensors[id].registers;
@@ -442,8 +477,47 @@ static int ti_bandgap_update_alert_threshold(struct ti_bandgap *bgp, int id,
~(tsr->threshold_thot_mask | tsr->threshold_tcold_mask);
reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask)) |
(t_cold << __ffs(tsr->threshold_tcold_mask));
+
+ /**
+ * Errata i813:
+ * Spurious Thermal Alert: Talert can happen randomly while the device
+ * remains under the temperature limit defined for this event to trig.
+ * This spurious event is caused by a incorrect re-synchronization
+ * between clock domains. The comparison between configured threshold
+ * and current temperature value can happen while the value is
+ * transitioning (metastable), thus causing inappropriate event
+ * generation. No spurious event occurs as long as the threshold value
+ * stays unchanged. Spurious event can be generated while a thermal
+ * alert threshold is modified in
+ * CONTROL_BANDGAP_THRESHOLD_MPU/GPU/CORE/DSPEVE/IVA_n.
+ */
+
+ if (TI_BANDGAP_HAS(bgp, ERRATA_813)) {
+ /* Mask t_hot and t_cold events at the IP Level */
+ ctrl = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl);
+
+ if (hot)
+ ctrl &= ~tsr->mask_hot_mask;
+ else
+ ctrl &= ~tsr->mask_cold_mask;
+
+ ti_bandgap_writel(bgp, ctrl, tsr->bgap_mask_ctrl);
+ }
+
+ /* Write the threshold value */
ti_bandgap_writel(bgp, reg_val, tsr->bgap_threshold);
+ if (TI_BANDGAP_HAS(bgp, ERRATA_813)) {
+ /* Unmask t_hot and t_cold events at the IP Level */
+ ctrl = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl);
+ if (hot)
+ ctrl |= tsr->mask_hot_mask;
+ else
+ ctrl |= tsr->mask_cold_mask;
+
+ ti_bandgap_writel(bgp, ctrl, tsr->bgap_mask_ctrl);
+ }
+
if (err) {
dev_err(bgp->dev, "failed to reprogram thot threshold\n");
err = -EIO;
diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.h b/drivers/thermal/ti-soc-thermal/ti-bandgap.h
index b3adf72f252d..0c52f7afba00 100644
--- a/drivers/thermal/ti-soc-thermal/ti-bandgap.h
+++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.h
@@ -318,6 +318,10 @@ struct ti_temp_sensor {
* TI_BANDGAP_FEATURE_HISTORY_BUFFER - used when the bandgap device features
* a history buffer of temperatures.
*
+ * TI_BANDGAP_FEATURE_ERRATA_814 - used to workaorund when the bandgap device
+ * has Errata 814
+ * TI_BANDGAP_FEATURE_ERRATA_813 - used to workaorund when the bandgap device
+ * has Errata 813
* 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.
*/
@@ -331,6 +335,8 @@ struct ti_temp_sensor {
#define TI_BANDGAP_FEATURE_FREEZE_BIT BIT(7)
#define TI_BANDGAP_FEATURE_COUNTER_DELAY BIT(8)
#define TI_BANDGAP_FEATURE_HISTORY_BUFFER BIT(9)
+#define TI_BANDGAP_FEATURE_ERRATA_814 BIT(10)
+#define TI_BANDGAP_FEATURE_ERRATA_813 BIT(11)
#define TI_BANDGAP_HAS(b, f) \
((b)->conf->features & TI_BANDGAP_FEATURE_ ## f)
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c
index f1e57425e39f..7a3d146a5f0e 100644
--- a/drivers/tty/hvc/hvc_xen.c
+++ b/drivers/tty/hvc/hvc_xen.c
@@ -289,7 +289,7 @@ static int xen_initial_domain_console_init(void)
return -ENOMEM;
}
- info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0);
+ info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false);
info->vtermno = HVC_COOKIE;
spin_lock(&xencons_lock);
@@ -299,11 +299,27 @@ static int xen_initial_domain_console_init(void)
return 0;
}
+static void xen_console_update_evtchn(struct xencons_info *info)
+{
+ if (xen_hvm_domain()) {
+ uint64_t v;
+ int err;
+
+ err = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v);
+ if (!err && v)
+ info->evtchn = v;
+ } else
+ info->evtchn = xen_start_info->console.domU.evtchn;
+}
+
void xen_console_resume(void)
{
struct xencons_info *info = vtermno_to_xencons(HVC_COOKIE);
- if (info != NULL && info->irq)
+ if (info != NULL && info->irq) {
+ if (!xen_initial_domain())
+ xen_console_update_evtchn(info);
rebind_evtchn_irq(info->evtchn, info->irq);
+ }
}
static void xencons_disconnect_backend(struct xencons_info *info)
diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c
index 04d9e23d1ee1..358323c83b4f 100644
--- a/drivers/tty/mips_ejtag_fdc.c
+++ b/drivers/tty/mips_ejtag_fdc.c
@@ -174,13 +174,13 @@ struct mips_ejtag_fdc_tty {
static inline void mips_ejtag_fdc_write(struct mips_ejtag_fdc_tty *priv,
unsigned int offs, unsigned int data)
{
- iowrite32(data, priv->reg + offs);
+ __raw_writel(data, priv->reg + offs);
}
static inline unsigned int mips_ejtag_fdc_read(struct mips_ejtag_fdc_tty *priv,
unsigned int offs)
{
- return ioread32(priv->reg + offs);
+ return __raw_readl(priv->reg + offs);
}
/* Encoding of byte stream in FDC words */
@@ -347,9 +347,9 @@ static void mips_ejtag_fdc_console_write(struct console *c, const char *s,
s += inc[word.bytes - 1];
/* Busy wait until there's space in fifo */
- while (ioread32(regs + REG_FDSTAT) & REG_FDSTAT_TXF)
+ while (__raw_readl(regs + REG_FDSTAT) & REG_FDSTAT_TXF)
;
- iowrite32(word.word, regs + REG_FDTX(c->index));
+ __raw_writel(word.word, regs + REG_FDTX(c->index));
}
out:
local_irq_restore(flags);
@@ -1227,7 +1227,7 @@ static int kgdbfdc_read_char(void)
/* Read next word from KGDB channel */
do {
- stat = ioread32(regs + REG_FDSTAT);
+ stat = __raw_readl(regs + REG_FDSTAT);
/* No data waiting? */
if (stat & REG_FDSTAT_RXE)
@@ -1236,7 +1236,7 @@ static int kgdbfdc_read_char(void)
/* Read next word */
channel = (stat & REG_FDSTAT_RXCHAN) >>
REG_FDSTAT_RXCHAN_SHIFT;
- data = ioread32(regs + REG_FDRX);
+ data = __raw_readl(regs + REG_FDRX);
} while (channel != CONFIG_MIPS_EJTAG_FDC_KGDB_CHAN);
/* Decode into rbuf */
@@ -1266,9 +1266,10 @@ static void kgdbfdc_push_one(void)
return;
/* Busy wait until there's space in fifo */
- while (ioread32(regs + REG_FDSTAT) & REG_FDSTAT_TXF)
+ while (__raw_readl(regs + REG_FDSTAT) & REG_FDSTAT_TXF)
;
- iowrite32(word.word, regs + REG_FDTX(CONFIG_MIPS_EJTAG_FDC_KGDB_CHAN));
+ __raw_writel(word.word,
+ regs + REG_FDTX(CONFIG_MIPS_EJTAG_FDC_KGDB_CHAN));
}
/* flush the whole write buffer to the TX FIFO */
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 91abc00aa833..2c34c3249972 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -3170,7 +3170,7 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state)
return gsmtty_modem_update(dlci, encode);
}
-static void gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty)
+static void gsmtty_cleanup(struct tty_struct *tty)
{
struct gsm_dlci *dlci = tty->driver_data;
struct gsm_mux *gsm = dlci->gsm;
@@ -3178,7 +3178,6 @@ static void gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty)
dlci_put(dlci);
dlci_put(gsm->dlci[0]);
mux_put(gsm);
- driver->ttys[tty->index] = NULL;
}
/* Virtual ttys for the demux */
@@ -3199,7 +3198,7 @@ static const struct tty_operations gsmtty_ops = {
.tiocmget = gsmtty_tiocmget,
.tiocmset = gsmtty_tiocmset,
.break_ctl = gsmtty_break_ctl,
- .remove = gsmtty_remove,
+ .cleanup = gsmtty_cleanup,
};
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c
index 644ddb841d9f..bbc4ce66c2c1 100644
--- a/drivers/tty/n_hdlc.c
+++ b/drivers/tty/n_hdlc.c
@@ -600,7 +600,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
add_wait_queue(&tty->read_wait, &wait);
for (;;) {
- if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
+ if (test_bit(TTY_OTHER_DONE, &tty->flags)) {
ret = -EIO;
break;
}
@@ -828,7 +828,7 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
/* set bits for operations that won't block */
if (n_hdlc->rx_buf_list.head)
mask |= POLLIN | POLLRDNORM; /* readable */
- if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+ if (test_bit(TTY_OTHER_DONE, &tty->flags))
mask |= POLLHUP;
if (tty_hung_up_p(filp))
mask |= POLLHUP;
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index cf6e0f2e1331..396344cb011f 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -162,6 +162,17 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
return put_user(x, ptr);
}
+static inline int tty_copy_to_user(struct tty_struct *tty,
+ void __user *to,
+ const void *from,
+ unsigned long n)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ tty_audit_add_data(tty, to, n, ldata->icanon);
+ return copy_to_user(to, from, n);
+}
+
/**
* n_tty_kick_worker - start input worker (if required)
* @tty: terminal
@@ -1949,6 +1960,18 @@ static inline int input_available_p(struct tty_struct *tty, int poll)
return ldata->commit_head - ldata->read_tail >= amt;
}
+static inline int check_other_done(struct tty_struct *tty)
+{
+ int done = test_bit(TTY_OTHER_DONE, &tty->flags);
+ if (done) {
+ /* paired with cmpxchg() in check_other_closed(); ensures
+ * read buffer head index is not stale
+ */
+ smp_mb__after_atomic();
+ }
+ return done;
+}
+
/**
* copy_from_read_buf - copy read data directly
* @tty: terminal device
@@ -2058,8 +2081,8 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
size = N_TTY_BUF_SIZE - tail;
n = eol - tail;
- if (n > 4096)
- n += 4096;
+ if (n > N_TTY_BUF_SIZE)
+ n += N_TTY_BUF_SIZE;
n += found;
c = n;
@@ -2072,12 +2095,12 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
__func__, eol, found, n, c, size, more);
if (n > size) {
- ret = copy_to_user(*b, read_buf_addr(ldata, tail), size);
+ ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), size);
if (ret)
return -EFAULT;
- ret = copy_to_user(*b + size, ldata->read_buf, n - size);
+ ret = tty_copy_to_user(tty, *b + size, ldata->read_buf, n - size);
} else
- ret = copy_to_user(*b, read_buf_addr(ldata, tail), n);
+ ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), n);
if (ret)
return -EFAULT;
@@ -2167,7 +2190,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
struct n_tty_data *ldata = tty->disc_data;
unsigned char __user *b = buf;
DEFINE_WAIT_FUNC(wait, woken_wake_function);
- int c;
+ int c, done;
int minimum, time;
ssize_t retval = 0;
long timeout;
@@ -2235,8 +2258,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
((minimum - (b - buf)) >= 1))
ldata->minimum_to_wake = (minimum - (b - buf));
+ done = check_other_done(tty);
+
if (!input_available_p(tty, 0)) {
- if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
+ if (done) {
retval = -EIO;
break;
}
@@ -2443,12 +2468,12 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
poll_wait(file, &tty->read_wait, wait);
poll_wait(file, &tty->write_wait, wait);
+ if (check_other_done(tty))
+ mask |= POLLHUP;
if (input_available_p(tty, 1))
mask |= POLLIN | POLLRDNORM;
if (tty->packet && tty->link->ctrl_status)
mask |= POLLPRI | POLLIN | POLLRDNORM;
- if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
- mask |= POLLHUP;
if (tty_hung_up_p(file))
mask |= POLLHUP;
if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) {
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index e72ee629cead..4d5e8409769c 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -53,9 +53,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
/* Review - krefs on tty_link ?? */
if (!tty->link)
return;
- tty_flush_to_ldisc(tty->link);
set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
- wake_up_interruptible(&tty->link->read_wait);
+ tty_flip_buffer_push(tty->link->port);
wake_up_interruptible(&tty->link->write_wait);
if (tty->driver->subtype == PTY_TYPE_MASTER) {
set_bit(TTY_OTHER_CLOSED, &tty->flags);
@@ -243,7 +242,9 @@ static int pty_open(struct tty_struct *tty, struct file *filp)
goto out;
clear_bit(TTY_IO_ERROR, &tty->flags);
+ /* TTY_OTHER_CLOSED must be cleared before TTY_OTHER_DONE */
clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
+ clear_bit(TTY_OTHER_DONE, &tty->link->flags);
set_bit(TTY_THROTTLED, &tty->flags);
return 0;
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 9289999cb7c6..dce1a23706e8 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -562,12 +562,36 @@ static irqreturn_t omap_wake_irq(int irq, void *dev_id)
return IRQ_NONE;
}
+#ifdef CONFIG_SERIAL_8250_DMA
+static int omap_8250_dma_handle_irq(struct uart_port *port);
+#endif
+
+static irqreturn_t omap8250_irq(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+ struct uart_8250_port *up = up_to_u8250p(port);
+ unsigned int iir;
+ int ret;
+
+#ifdef CONFIG_SERIAL_8250_DMA
+ if (up->dma) {
+ ret = omap_8250_dma_handle_irq(port);
+ return IRQ_RETVAL(ret);
+ }
+#endif
+
+ serial8250_rpm_get(up);
+ iir = serial_port_in(port, UART_IIR);
+ ret = serial8250_handle_irq(port, iir);
+ serial8250_rpm_put(up);
+
+ return IRQ_RETVAL(ret);
+}
+
static int omap_8250_startup(struct uart_port *port)
{
- struct uart_8250_port *up =
- container_of(port, struct uart_8250_port, port);
+ struct uart_8250_port *up = up_to_u8250p(port);
struct omap8250_priv *priv = port->private_data;
-
int ret;
if (priv->wakeirq) {
@@ -580,10 +604,31 @@ static int omap_8250_startup(struct uart_port *port)
pm_runtime_get_sync(port->dev);
- ret = serial8250_do_startup(port);
- if (ret)
+ up->mcr = 0;
+ serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+
+ serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+ up->lsr_saved_flags = 0;
+ up->msr_saved_flags = 0;
+
+ if (up->dma) {
+ ret = serial8250_request_dma(up);
+ if (ret) {
+ dev_warn_ratelimited(port->dev,
+ "failed to request DMA\n");
+ up->dma = NULL;
+ }
+ }
+
+ ret = request_irq(port->irq, omap8250_irq, IRQF_SHARED,
+ dev_name(port->dev), port);
+ if (ret < 0)
goto err;
+ up->ier = UART_IER_RLSI | UART_IER_RDI;
+ serial_out(up, UART_IER, up->ier);
+
#ifdef CONFIG_PM
up->capabilities |= UART_CAP_RPM;
#endif
@@ -610,8 +655,7 @@ err:
static void omap_8250_shutdown(struct uart_port *port)
{
- struct uart_8250_port *up =
- container_of(port, struct uart_8250_port, port);
+ struct uart_8250_port *up = up_to_u8250p(port);
struct omap8250_priv *priv = port->private_data;
flush_work(&priv->qos_work);
@@ -621,11 +665,24 @@ static void omap_8250_shutdown(struct uart_port *port)
pm_runtime_get_sync(port->dev);
serial_out(up, UART_OMAP_WER, 0);
- serial8250_do_shutdown(port);
+
+ up->ier = 0;
+ serial_out(up, UART_IER, 0);
+
+ if (up->dma)
+ serial8250_release_dma(up);
+
+ /*
+ * Disable break condition and FIFOs
+ */
+ if (up->lcr & UART_LCR_SBC)
+ serial_out(up, UART_LCR, up->lcr & ~UART_LCR_SBC);
+ serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
pm_runtime_mark_last_busy(port->dev);
pm_runtime_put_autosuspend(port->dev);
+ free_irq(port->irq, port);
if (priv->wakeirq)
free_irq(priv->wakeirq, port);
}
@@ -974,6 +1031,13 @@ static inline int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
}
#endif
+static int omap8250_no_handle_irq(struct uart_port *port)
+{
+ /* IRQ has not been requested but handling irq? */
+ WARN_ONCE(1, "Unexpected irq handling before port startup\n");
+ return 0;
+}
+
static int omap8250_probe(struct platform_device *pdev)
{
struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1075,6 +1139,7 @@ static int omap8250_probe(struct platform_device *pdev)
pm_runtime_get_sync(&pdev->dev);
omap_serial_fill_features_erratas(&up, priv);
+ up.port.handle_irq = omap8250_no_handle_irq;
#ifdef CONFIG_SERIAL_8250_DMA
if (pdev->dev.of_node) {
/*
@@ -1088,7 +1153,6 @@ static int omap8250_probe(struct platform_device *pdev)
ret = of_property_count_strings(pdev->dev.of_node, "dma-names");
if (ret == 2) {
up.dma = &priv->omap8250_dma;
- up.port.handle_irq = omap_8250_dma_handle_irq;
priv->omap8250_dma.fn = the_no_dma_filter_fn;
priv->omap8250_dma.tx_dma = omap_8250_tx_dma;
priv->omap8250_dma.rx_dma = omap_8250_rx_dma;
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 08da4d3e2162..46bcebba54b2 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1998,6 +1998,8 @@ pci_wch_ch38x_setup(struct serial_private *priv,
#define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250
#define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470
+#define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358
+
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588
@@ -2520,6 +2522,13 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.setup = pci_xr17v35x_setup,
},
+ {
+ .vendor = PCI_VENDOR_ID_EXAR,
+ .device = PCI_DEVICE_ID_EXAR_XR17V8358,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_xr17v35x_setup,
+ },
/*
* Xircom cards
*/
@@ -2999,6 +3008,7 @@ enum pci_board_num_t {
pbn_exar_XR17V352,
pbn_exar_XR17V354,
pbn_exar_XR17V358,
+ pbn_exar_XR17V8358,
pbn_exar_ibm_saturn,
pbn_pasemi_1682M,
pbn_ni8430_2,
@@ -3685,6 +3695,14 @@ static struct pciserial_board pci_boards[] = {
.reg_shift = 0,
.first_offset = 0,
},
+ [pbn_exar_XR17V8358] = {
+ .flags = FL_BASE0,
+ .num_ports = 16,
+ .base_baud = 7812500,
+ .uart_offset = 0x400,
+ .reg_shift = 0,
+ .first_offset = 0,
+ },
[pbn_exar_ibm_saturn] = {
.flags = FL_BASE0,
.num_ports = 1,
@@ -5080,7 +5098,7 @@ static struct pci_device_id serial_pci_tbl[] = {
0,
0, pbn_exar_XR17C158 },
/*
- * Exar Corp. XR17V35[248] Dual/Quad/Octal PCIe UARTs
+ * Exar Corp. XR17V[48]35[248] Dual/Quad/Octal/Hexa PCIe UARTs
*/
{ PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V352,
PCI_ANY_ID, PCI_ANY_ID,
@@ -5094,7 +5112,10 @@ static struct pci_device_id serial_pci_tbl[] = {
PCI_ANY_ID, PCI_ANY_ID,
0,
0, pbn_exar_XR17V358 },
-
+ { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V8358,
+ PCI_ANY_ID, PCI_ANY_ID,
+ 0,
+ 0, pbn_exar_XR17V8358 },
/*
* Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke)
*/
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 5a4e9d579585..763eb20fe321 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1249,20 +1249,19 @@ __acquires(&uap->port.lock)
/*
* Transmit a character
- * There must be at least one free entry in the TX FIFO to accept the char.
*
- * Returns true if the FIFO might have space in it afterwards;
- * returns false if the FIFO definitely became full.
+ * Returns true if the character was successfully queued to the FIFO.
+ * Returns false otherwise.
*/
static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c)
{
+ if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
+ return false; /* unable to transmit character */
+
writew(c, uap->port.membase + UART01x_DR);
uap->port.icount.tx++;
- if (likely(uap->tx_irq_seen > 1))
- return true;
-
- return !(readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF);
+ return true;
}
static bool pl011_tx_chars(struct uart_amba_port *uap)
@@ -1296,7 +1295,8 @@ static bool pl011_tx_chars(struct uart_amba_port *uap)
return false;
if (uap->port.x_char) {
- pl011_tx_char(uap, uap->port.x_char);
+ if (!pl011_tx_char(uap, uap->port.x_char))
+ goto done;
uap->port.x_char = 0;
--count;
}
@@ -1639,6 +1639,9 @@ static int pl011_startup(struct uart_port *port)
writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS);
+ /* Assume that TX IRQ doesn't work until we see one: */
+ uap->tx_irq_seen = 0;
+
spin_lock_irq(&uap->port.lock);
/* restore RTS and DTR */
@@ -1702,7 +1705,7 @@ static void pl011_shutdown(struct uart_port *port)
spin_lock_irq(&uap->port.lock);
uap->im = 0;
writew(uap->im, uap->port.membase + UART011_IMSC);
- writew(0xffff & ~UART011_TXIS, uap->port.membase + UART011_ICR);
+ writew(0xffff, uap->port.membase + UART011_ICR);
spin_unlock_irq(&uap->port.lock);
pl011_dma_shutdown(uap);
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index d58fe4763d9e..27dade29646b 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -880,6 +880,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
config.direction = DMA_MEM_TO_DEV;
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
config.dst_addr = port->mapbase + ATMEL_US_THR;
+ config.dst_maxburst = 1;
ret = dmaengine_slave_config(atmel_port->chan_tx,
&config);
@@ -1059,6 +1060,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
config.direction = DMA_DEV_TO_MEM;
config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
config.src_addr = port->mapbase + ATMEL_US_RHR;
+ config.src_maxburst = 1;
ret = dmaengine_slave_config(atmel_port->chan_rx,
&config);
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index 5fdc9f3ecd64..6dc471e30e79 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -187,13 +187,8 @@ static int __init param_setup_earlycon(char *buf)
return 0;
err = setup_earlycon(buf);
- if (err == -ENOENT) {
- pr_warn("no match for %s\n", buf);
- err = 0;
- } else if (err == -EALREADY) {
- pr_warn("already registered\n");
- err = 0;
- }
+ if (err == -ENOENT || err == -EALREADY)
+ return 0;
return err;
}
early_param("earlycon", param_setup_earlycon);
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index c8cfa0637128..88250395b0ce 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -911,6 +911,14 @@ static void dma_rx_callback(void *data)
status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state);
count = RX_BUF_SIZE - state.residue;
+
+ if (readl(sport->port.membase + USR2) & USR2_IDLE) {
+ /* In condition [3] the SDMA counted up too early */
+ count--;
+
+ writel(USR2_IDLE, sport->port.membase + USR2);
+ }
+
dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
if (count) {
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index 5b73afb9f9f3..137381e649e5 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -346,7 +346,6 @@ static const struct of_device_id of_platform_serial_table[] = {
{ .compatible = "ibm,qpace-nwp-serial",
.data = (void *)PORT_NWPSERIAL, },
#endif
- { .type = "serial", .data = (void *)PORT_UNKNOWN, },
{ /* end of list */ },
};
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 211479aa34bb..7f49172ccd86 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -1735,6 +1735,8 @@ static int serial_omap_probe(struct platform_device *pdev)
err_add_port:
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ pm_qos_remove_request(&up->pm_qos_request);
+ device_init_wakeup(up->dev, false);
err_rs485:
err_port_line:
return ret;
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index cf08876922f1..a0ae942d9562 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1068,8 +1068,9 @@ static int s3c64xx_serial_startup(struct uart_port *port)
spin_lock_irqsave(&port->lock, flags);
ufcon = rd_regl(port, S3C2410_UFCON);
- ufcon |= S3C2410_UFCON_RESETRX | S3C2410_UFCON_RESETTX |
- S5PV210_UFCON_RXTRIG8;
+ ufcon |= S3C2410_UFCON_RESETRX | S5PV210_UFCON_RXTRIG8;
+ if (!uart_console(port))
+ ufcon |= S3C2410_UFCON_RESETTX;
wr_regl(port, S3C2410_UFCON, ufcon);
enable_rx_pio(ourport);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index eb5b03be9dfd..0b7bb12dfc68 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1770,7 +1770,7 @@ static const struct file_operations uart_proc_fops = {
* @port: the port to write the message
* @s: array of characters
* @count: number of characters in string to write
- * @write: function to write character to port
+ * @putchar: function to write character to port
*/
void uart_console_write(struct uart_port *port, const char *s,
unsigned int count,
diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 0ec756c62bcf..d7b846d41630 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -55,7 +55,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
count++;
}
- gpiod_set_array(count, desc_array, value_array);
+ gpiod_set_array_value(count, desc_array, value_array);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 708eead850b0..b1c6bd3d483f 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -632,7 +632,8 @@ MODULE_DEVICE_TABLE(of, ulite_of_match);
static int ulite_probe(struct platform_device *pdev)
{
- struct resource *res, *res2;
+ struct resource *res;
+ int irq;
int id = pdev->id;
#ifdef CONFIG_OF
const __be32 *prop;
@@ -646,11 +647,11 @@ static int ulite_probe(struct platform_device *pdev)
if (!res)
return -ENODEV;
- res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res2)
- return -ENODEV;
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return -ENXIO;
- return ulite_assign(&pdev->dev, id, res->start, res2->start);
+ return ulite_assign(&pdev->dev, id, res->start, irq);
}
static int ulite_remove(struct platform_device *pdev)
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index f218ec658f5d..3ddbac767db3 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1331,9 +1331,9 @@ static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend,
*/
static int cdns_uart_probe(struct platform_device *pdev)
{
- int rc, id;
+ int rc, id, irq;
struct uart_port *port;
- struct resource *res, *res2;
+ struct resource *res;
struct cdns_uart *cdns_uart_data;
cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data),
@@ -1380,9 +1380,9 @@ static int cdns_uart_probe(struct platform_device *pdev)
goto err_out_clk_disable;
}
- res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res2) {
- rc = -ENODEV;
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ rc = -ENXIO;
goto err_out_clk_disable;
}
@@ -1411,7 +1411,7 @@ static int cdns_uart_probe(struct platform_device *pdev)
* and triggers invocation of the config_port() entry point.
*/
port->mapbase = res->start;
- port->irq = res2->start;
+ port->irq = irq;
port->dev = &pdev->dev;
port->uartclk = clk_get_rate(cdns_uart_data->uartclk);
port->private_data = cdns_uart_data;
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index 75661641f5fe..2f78b77f0f81 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -37,6 +37,28 @@
#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF)
+/*
+ * If all tty flip buffers have been processed by flush_to_ldisc() or
+ * dropped by tty_buffer_flush(), check if the linked pty has been closed.
+ * If so, wake the reader/poll to process
+ */
+static inline void check_other_closed(struct tty_struct *tty)
+{
+ unsigned long flags, old;
+
+ /* transition from TTY_OTHER_CLOSED => TTY_OTHER_DONE must be atomic */
+ for (flags = ACCESS_ONCE(tty->flags);
+ test_bit(TTY_OTHER_CLOSED, &flags);
+ ) {
+ old = flags;
+ __set_bit(TTY_OTHER_DONE, &flags);
+ flags = cmpxchg(&tty->flags, old, flags);
+ if (old == flags) {
+ wake_up_interruptible(&tty->read_wait);
+ break;
+ }
+ }
+}
/**
* tty_buffer_lock_exclusive - gain exclusive access to buffer
@@ -229,6 +251,8 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
if (ld && ld->ops->flush_buffer)
ld->ops->flush_buffer(tty);
+ check_other_closed(tty);
+
atomic_dec(&buf->priority);
mutex_unlock(&buf->lock);
}
@@ -471,8 +495,10 @@ static void flush_to_ldisc(struct work_struct *work)
smp_rmb();
count = head->commit - head->read;
if (!count) {
- if (next == NULL)
+ if (next == NULL) {
+ check_other_closed(tty);
break;
+ }
buf->head = next;
tty_buffer_free(port, head);
continue;
@@ -489,19 +515,6 @@ static void flush_to_ldisc(struct work_struct *work)
}
/**
- * tty_flush_to_ldisc
- * @tty: tty to push
- *
- * Push the terminal flip buffers to the line discipline.
- *
- * Must not be called from IRQ context.
- */
-void tty_flush_to_ldisc(struct tty_struct *tty)
-{
- flush_work(&tty->port->buf.work);
-}
-
-/**
* tty_flip_buffer_push - terminal
* @port: tty port to push
*
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index 632fc8152061..8e53fe469664 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -536,7 +536,7 @@ EXPORT_SYMBOL(tty_termios_hw_change);
* Locking: termios_rwsem
*/
-static int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
+int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
{
struct ktermios old_termios;
struct tty_ldisc *ld;
@@ -569,6 +569,7 @@ static int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
up_write(&tty->termios_rwsem);
return 0;
}
+EXPORT_SYMBOL_GPL(tty_set_termios);
/**
* set_termios - set termios values for a tty
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index dfb05edcdb96..5b7061a33103 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -88,9 +88,13 @@ static ssize_t ci_port_test_write(struct file *file, const char __user *ubuf,
char buf[32];
int ret;
- if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ count = min_t(size_t, sizeof(buf) - 1, count);
+ if (copy_from_user(buf, ubuf, count))
return -EFAULT;
+ /* sscanf requires a zero terminated string */
+ buf[count] = '\0';
+
if (sscanf(buf, "%u", &mode) != 1)
return -EINVAL;
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index 083acf45ad5a..19d655a743b5 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -520,7 +520,6 @@ static int ci_otg_start_host(struct otg_fsm *fsm, int on)
{
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
- mutex_unlock(&fsm->lock);
if (on) {
ci_role_stop(ci);
ci_role_start(ci, CI_ROLE_HOST);
@@ -529,7 +528,6 @@ static int ci_otg_start_host(struct otg_fsm *fsm, int on)
hw_device_reset(ci);
ci_role_start(ci, CI_ROLE_GADGET);
}
- mutex_lock(&fsm->lock);
return 0;
}
@@ -537,12 +535,10 @@ static int ci_otg_start_gadget(struct otg_fsm *fsm, int on)
{
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
- mutex_unlock(&fsm->lock);
if (on)
usb_gadget_vbus_connect(&ci->gadget);
else
usb_gadget_vbus_disconnect(&ci->gadget);
- mutex_lock(&fsm->lock);
return 0;
}
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 3e15add665e2..5c8f58114677 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1142,11 +1142,16 @@ static int acm_probe(struct usb_interface *intf,
}
while (buflen > 0) {
+ elength = buffer[0];
+ if (!elength) {
+ dev_err(&intf->dev, "skipping garbage byte\n");
+ elength = 1;
+ goto next_desc;
+ }
if (buffer[1] != USB_DT_CS_INTERFACE) {
dev_err(&intf->dev, "skipping garbage\n");
goto next_desc;
}
- elength = buffer[0];
switch (buffer[2]) {
case USB_CDC_UNION_TYPE: /* we've found it */
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 41e510ae8c83..d85abfed84cc 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -106,6 +106,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x04f3, 0x010c), .driver_info =
USB_QUIRK_DEVICE_QUALIFIER },
+ { USB_DEVICE(0x04f3, 0x0125), .driver_info =
+ USB_QUIRK_DEVICE_QUALIFIER },
+
{ USB_DEVICE(0x04f3, 0x016f), .driver_info =
USB_QUIRK_DEVICE_QUALIFIER },
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index fdab715a0631..c0eafa6fd403 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -339,7 +339,7 @@
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
-#define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1)
+#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
#define DWC3_DGCMD_CMDACT (1 << 10)
#define DWC3_DGCMD_CMDIOC (1 << 8)
@@ -355,7 +355,7 @@
#define DWC3_DEPCMD_PARAM_SHIFT 16
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
-#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1)
+#define DWC3_DEPCMD_STATUS(x) (((x) >> 12) & 0x0F)
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
#define DWC3_DEPCMD_CMDACT (1 << 10)
#define DWC3_DEPCMD_CMDIOC (1 << 8)
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index edba5348be18..6b486a36863c 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -65,8 +65,8 @@
#define USBOTGSS_IRQENABLE_SET_MISC 0x003c
#define USBOTGSS_IRQENABLE_CLR_MISC 0x0040
#define USBOTGSS_IRQMISC_OFFSET 0x03fc
-#define USBOTGSS_UTMI_OTG_CTRL 0x0080
-#define USBOTGSS_UTMI_OTG_STATUS 0x0084
+#define USBOTGSS_UTMI_OTG_STATUS 0x0080
+#define USBOTGSS_UTMI_OTG_CTRL 0x0084
#define USBOTGSS_UTMI_OTG_OFFSET 0x0480
#define USBOTGSS_TXFIFO_DEPTH 0x0508
#define USBOTGSS_RXFIFO_DEPTH 0x050c
@@ -98,20 +98,20 @@
#define USBOTGSS_IRQMISC_DISCHRGVBUS_FALL (1 << 3)
#define USBOTGSS_IRQMISC_IDPULLUP_FALL (1 << 0)
-/* UTMI_OTG_CTRL REGISTER */
-#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS (1 << 5)
-#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS (1 << 4)
-#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS (1 << 3)
-#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP (1 << 0)
-
/* UTMI_OTG_STATUS REGISTER */
-#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE (1 << 31)
-#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT (1 << 9)
-#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8)
-#define USBOTGSS_UTMI_OTG_STATUS_IDDIG (1 << 4)
-#define USBOTGSS_UTMI_OTG_STATUS_SESSEND (1 << 3)
-#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID (1 << 2)
-#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1)
+#define USBOTGSS_UTMI_OTG_STATUS_DRVVBUS (1 << 5)
+#define USBOTGSS_UTMI_OTG_STATUS_CHRGVBUS (1 << 4)
+#define USBOTGSS_UTMI_OTG_STATUS_DISCHRGVBUS (1 << 3)
+#define USBOTGSS_UTMI_OTG_STATUS_IDPULLUP (1 << 0)
+
+/* UTMI_OTG_CTRL REGISTER */
+#define USBOTGSS_UTMI_OTG_CTRL_SW_MODE (1 << 31)
+#define USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT (1 << 9)
+#define USBOTGSS_UTMI_OTG_CTRL_TXBITSTUFFENABLE (1 << 8)
+#define USBOTGSS_UTMI_OTG_CTRL_IDDIG (1 << 4)
+#define USBOTGSS_UTMI_OTG_CTRL_SESSEND (1 << 3)
+#define USBOTGSS_UTMI_OTG_CTRL_SESSVALID (1 << 2)
+#define USBOTGSS_UTMI_OTG_CTRL_VBUSVALID (1 << 1)
struct dwc3_omap {
struct device *dev;
@@ -119,7 +119,7 @@ struct dwc3_omap {
int irq;
void __iomem *base;
- u32 utmi_otg_status;
+ u32 utmi_otg_ctrl;
u32 utmi_otg_offset;
u32 irqmisc_offset;
u32 irq_eoi_offset;
@@ -153,15 +153,15 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
writel(value, base + offset);
}
-static u32 dwc3_omap_read_utmi_status(struct dwc3_omap *omap)
+static u32 dwc3_omap_read_utmi_ctrl(struct dwc3_omap *omap)
{
- return dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS +
+ return dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_CTRL +
omap->utmi_otg_offset);
}
-static void dwc3_omap_write_utmi_status(struct dwc3_omap *omap, u32 value)
+static void dwc3_omap_write_utmi_ctrl(struct dwc3_omap *omap, u32 value)
{
- dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS +
+ dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_CTRL +
omap->utmi_otg_offset, value);
}
@@ -235,25 +235,25 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
}
}
- val = dwc3_omap_read_utmi_status(omap);
- 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_write_utmi_status(omap, val);
+ val = dwc3_omap_read_utmi_ctrl(omap);
+ val &= ~(USBOTGSS_UTMI_OTG_CTRL_IDDIG
+ | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
+ | USBOTGSS_UTMI_OTG_CTRL_SESSEND);
+ val |= USBOTGSS_UTMI_OTG_CTRL_SESSVALID
+ | USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT;
+ dwc3_omap_write_utmi_ctrl(omap, val);
break;
case OMAP_DWC3_VBUS_VALID:
dev_dbg(omap->dev, "VBUS Connect\n");
- val = dwc3_omap_read_utmi_status(omap);
- 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_write_utmi_status(omap, val);
+ val = dwc3_omap_read_utmi_ctrl(omap);
+ val &= ~USBOTGSS_UTMI_OTG_CTRL_SESSEND;
+ val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG
+ | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
+ | USBOTGSS_UTMI_OTG_CTRL_SESSVALID
+ | USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT;
+ dwc3_omap_write_utmi_ctrl(omap, val);
break;
case OMAP_DWC3_ID_FLOAT:
@@ -263,13 +263,13 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
case OMAP_DWC3_VBUS_OFF:
dev_dbg(omap->dev, "VBUS Disconnect\n");
- val = dwc3_omap_read_utmi_status(omap);
- 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_write_utmi_status(omap, val);
+ val = dwc3_omap_read_utmi_ctrl(omap);
+ val &= ~(USBOTGSS_UTMI_OTG_CTRL_SESSVALID
+ | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
+ | USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT);
+ val |= USBOTGSS_UTMI_OTG_CTRL_SESSEND
+ | USBOTGSS_UTMI_OTG_CTRL_IDDIG;
+ dwc3_omap_write_utmi_ctrl(omap, val);
break;
default:
@@ -422,22 +422,22 @@ static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap)
struct device_node *node = omap->dev->of_node;
int utmi_mode = 0;
- reg = dwc3_omap_read_utmi_status(omap);
+ reg = dwc3_omap_read_utmi_ctrl(omap);
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;
+ reg |= USBOTGSS_UTMI_OTG_CTRL_SW_MODE;
break;
case DWC3_OMAP_UTMI_MODE_HW:
- reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ reg &= ~USBOTGSS_UTMI_OTG_CTRL_SW_MODE;
break;
default:
dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode);
}
- dwc3_omap_write_utmi_status(omap, reg);
+ dwc3_omap_write_utmi_ctrl(omap, reg);
}
static int dwc3_omap_extcon_register(struct dwc3_omap *omap)
@@ -614,7 +614,7 @@ static int dwc3_omap_suspend(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
- omap->utmi_otg_status = dwc3_omap_read_utmi_status(omap);
+ omap->utmi_otg_ctrl = dwc3_omap_read_utmi_ctrl(omap);
dwc3_omap_disable_irqs(omap);
return 0;
@@ -624,7 +624,7 @@ static int dwc3_omap_resume(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
- dwc3_omap_write_utmi_status(omap, omap->utmi_otg_status);
+ dwc3_omap_write_utmi_ctrl(omap, omap->utmi_otg_ctrl);
dwc3_omap_enable_irqs(omap);
pm_runtime_disable(dev);
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index c42765b3a060..0495c94a23d7 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1295,6 +1295,7 @@ static void purge_configs_funcs(struct gadget_info *gi)
}
}
c->next_interface_id = 0;
+ memset(c->interface, 0, sizeof(c->interface));
c->superspeed = 0;
c->highspeed = 0;
c->fullspeed = 0;
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 6bdb57069044..3507f880eb74 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -315,7 +315,6 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
return ret;
}
- set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
return len;
}
break;
@@ -847,7 +846,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
ret = ep->status;
if (io_data->read && ret > 0) {
ret = copy_to_iter(data, ret, &io_data->data);
- if (unlikely(iov_iter_count(&io_data->data)))
+ if (!ret)
ret = -EFAULT;
}
}
@@ -1463,8 +1462,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
{
ENTER();
- if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
- ffs_closed(ffs);
+ ffs_closed(ffs);
BUG_ON(ffs->gadget);
@@ -3422,9 +3420,13 @@ static int ffs_ready(struct ffs_data *ffs)
ffs_obj->desc_ready = true;
ffs_obj->ffs_data = ffs;
- if (ffs_obj->ffs_ready_callback)
+ if (ffs_obj->ffs_ready_callback) {
ret = ffs_obj->ffs_ready_callback(ffs);
+ if (ret)
+ goto done;
+ }
+ set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
done:
ffs_dev_unlock();
return ret;
@@ -3443,7 +3445,8 @@ static void ffs_closed(struct ffs_data *ffs)
ffs_obj->desc_ready = false;
- if (ffs_obj->ffs_closed_callback)
+ if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags) &&
+ ffs_obj->ffs_closed_callback)
ffs_obj->ffs_closed_callback(ffs);
if (!ffs_obj->opts || ffs_obj->opts->no_configfs
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 13dfc9915b1d..f7f35a36c09a 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -437,12 +437,20 @@ static int hidg_setup(struct usb_function *f,
| USB_REQ_GET_DESCRIPTOR):
switch (value >> 8) {
case HID_DT_HID:
+ {
+ struct hid_descriptor hidg_desc_copy = hidg_desc;
+
VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
+ hidg_desc_copy.desc[0].bDescriptorType = HID_DT_REPORT;
+ hidg_desc_copy.desc[0].wDescriptorLength =
+ cpu_to_le16(hidg->report_desc_length);
+
length = min_t(unsigned short, length,
- hidg_desc.bLength);
- memcpy(req->buf, &hidg_desc, length);
+ hidg_desc_copy.bLength);
+ memcpy(req->buf, &hidg_desc_copy, length);
goto respond;
break;
+ }
case HID_DT_REPORT:
VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
length = min_t(unsigned short, length,
@@ -632,6 +640,10 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+ /*
+ * We can use hidg_desc struct here but we should not relay
+ * that its content won't change after returning from this function.
+ */
hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
hidg_desc.desc[0].wDescriptorLength =
cpu_to_le16(hidg->report_desc_length);
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 259b656c0b3e..6316aa5b1c49 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -973,7 +973,13 @@ static ssize_t f_midi_opts_id_show(struct f_midi_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = strlcpy(page, opts->id, PAGE_SIZE);
+ if (opts->id) {
+ result = strlcpy(page, opts->id, PAGE_SIZE);
+ } else {
+ page[0] = 0;
+ result = 0;
+ }
+
mutex_unlock(&opts->lock);
return result;
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 9719abfb6145..7856b3394494 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -588,7 +588,10 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (intf == 1) {
if (alt == 1) {
- config_ep_by_speed(cdev->gadget, f, out_ep);
+ err = config_ep_by_speed(cdev->gadget, f, out_ep);
+ if (err)
+ return err;
+
usb_ep_enable(out_ep);
out_ep->driver_data = audio;
audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index 89179ab20c10..7ee057930ae7 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -113,6 +113,7 @@ struct gs_port {
int write_allocated;
struct gs_buf port_write_buf;
wait_queue_head_t drain_wait; /* wait while writes drain */
+ bool write_busy;
/* REVISIT this state ... */
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
@@ -363,7 +364,7 @@ __acquires(&port->port_lock)
int status = 0;
bool do_tty_wake = false;
- while (!list_empty(pool)) {
+ while (!port->write_busy && !list_empty(pool)) {
struct usb_request *req;
int len;
@@ -393,9 +394,11 @@ __acquires(&port->port_lock)
* NOTE that we may keep sending data for a while after
* the TTY closed (dev->ioport->port_tty is NULL).
*/
+ port->write_busy = true;
spin_unlock(&port->port_lock);
status = usb_ep_queue(in, req, GFP_ATOMIC);
spin_lock(&port->port_lock);
+ port->write_busy = false;
if (status) {
pr_debug("%s: %s %s err %d\n",
diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c
index c30b7b572465..1194b09ae746 100644
--- a/drivers/usb/gadget/legacy/acm_ms.c
+++ b/drivers/usb/gadget/legacy/acm_ms.c
@@ -121,7 +121,7 @@ static struct usb_function *f_msg;
/*
* We _always_ have both ACM and mass storage functions.
*/
-static int __init acm_ms_do_config(struct usb_configuration *c)
+static int acm_ms_do_config(struct usb_configuration *c)
{
struct fsg_opts *opts;
int status;
@@ -174,7 +174,7 @@ static struct usb_configuration acm_ms_config_driver = {
/*-------------------------------------------------------------------------*/
-static int __init acm_ms_bind(struct usb_composite_dev *cdev)
+static int acm_ms_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct fsg_opts *opts;
@@ -249,7 +249,7 @@ fail_get_msg:
return status;
}
-static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
+static int acm_ms_unbind(struct usb_composite_dev *cdev)
{
usb_put_function(f_msg);
usb_put_function_instance(fi_msg);
@@ -258,13 +258,13 @@ static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static __refdata struct usb_composite_driver acm_ms_driver = {
+static struct usb_composite_driver acm_ms_driver = {
.name = "g_acm_ms",
.dev = &device_desc,
.max_speed = USB_SPEED_SUPER,
.strings = dev_strings,
.bind = acm_ms_bind,
- .unbind = __exit_p(acm_ms_unbind),
+ .unbind = acm_ms_unbind,
};
module_usb_composite_driver(acm_ms_driver);
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index f46a3956e43d..f289caf18a45 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -167,7 +167,7 @@ static const struct usb_descriptor_header *otg_desc[] = {
/*-------------------------------------------------------------------------*/
-static int __init audio_do_config(struct usb_configuration *c)
+static int audio_do_config(struct usb_configuration *c)
{
int status;
@@ -216,7 +216,7 @@ static struct usb_configuration audio_config_driver = {
/*-------------------------------------------------------------------------*/
-static int __init audio_bind(struct usb_composite_dev *cdev)
+static int audio_bind(struct usb_composite_dev *cdev)
{
#ifndef CONFIG_GADGET_UAC1
struct f_uac2_opts *uac2_opts;
@@ -276,7 +276,7 @@ fail:
return status;
}
-static int __exit audio_unbind(struct usb_composite_dev *cdev)
+static int audio_unbind(struct usb_composite_dev *cdev)
{
#ifdef CONFIG_GADGET_UAC1
if (!IS_ERR_OR_NULL(f_uac1))
@@ -292,13 +292,13 @@ static int __exit audio_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static __refdata struct usb_composite_driver audio_driver = {
+static struct usb_composite_driver audio_driver = {
.name = "g_audio",
.dev = &device_desc,
.strings = audio_strings,
.max_speed = USB_SPEED_HIGH,
.bind = audio_bind,
- .unbind = __exit_p(audio_unbind),
+ .unbind = audio_unbind,
};
module_usb_composite_driver(audio_driver);
diff --git a/drivers/usb/gadget/legacy/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c
index 2e85d9473478..afd3e37921a7 100644
--- a/drivers/usb/gadget/legacy/cdc2.c
+++ b/drivers/usb/gadget/legacy/cdc2.c
@@ -104,7 +104,7 @@ static struct usb_function_instance *fi_ecm;
/*
* We _always_ have both CDC ECM and CDC ACM functions.
*/
-static int __init cdc_do_config(struct usb_configuration *c)
+static int cdc_do_config(struct usb_configuration *c)
{
int status;
@@ -153,7 +153,7 @@ static struct usb_configuration cdc_config_driver = {
/*-------------------------------------------------------------------------*/
-static int __init cdc_bind(struct usb_composite_dev *cdev)
+static int cdc_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct f_ecm_opts *ecm_opts;
@@ -211,7 +211,7 @@ fail:
return status;
}
-static int __exit cdc_unbind(struct usb_composite_dev *cdev)
+static int cdc_unbind(struct usb_composite_dev *cdev)
{
usb_put_function(f_acm);
usb_put_function_instance(fi_serial);
@@ -222,13 +222,13 @@ static int __exit cdc_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static __refdata struct usb_composite_driver cdc_driver = {
+static struct usb_composite_driver cdc_driver = {
.name = "g_cdc",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = cdc_bind,
- .unbind = __exit_p(cdc_unbind),
+ .unbind = cdc_unbind,
};
module_usb_composite_driver(cdc_driver);
diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
index 633683a72a11..204b10b1a7e7 100644
--- a/drivers/usb/gadget/legacy/dbgp.c
+++ b/drivers/usb/gadget/legacy/dbgp.c
@@ -284,7 +284,7 @@ fail_1:
return -ENODEV;
}
-static int __init dbgp_bind(struct usb_gadget *gadget,
+static int dbgp_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
int err, stp;
@@ -406,7 +406,7 @@ fail:
return err;
}
-static __refdata struct usb_gadget_driver dbgp_driver = {
+static struct usb_gadget_driver dbgp_driver = {
.function = "dbgp",
.max_speed = USB_SPEED_HIGH,
.bind = dbgp_bind,
diff --git a/drivers/usb/gadget/legacy/ether.c b/drivers/usb/gadget/legacy/ether.c
index c5fdc61cdc4a..a3323dca218f 100644
--- a/drivers/usb/gadget/legacy/ether.c
+++ b/drivers/usb/gadget/legacy/ether.c
@@ -222,7 +222,7 @@ static struct usb_function *f_rndis;
* the first one present. That's to make Microsoft's drivers happy,
* and to follow DOCSIS 1.0 (cable modem standard).
*/
-static int __init rndis_do_config(struct usb_configuration *c)
+static int rndis_do_config(struct usb_configuration *c)
{
int status;
@@ -264,7 +264,7 @@ MODULE_PARM_DESC(use_eem, "use CDC EEM mode");
/*
* We _always_ have an ECM, CDC Subset, or EEM configuration.
*/
-static int __init eth_do_config(struct usb_configuration *c)
+static int eth_do_config(struct usb_configuration *c)
{
int status = 0;
@@ -318,7 +318,7 @@ static struct usb_configuration eth_config_driver = {
/*-------------------------------------------------------------------------*/
-static int __init eth_bind(struct usb_composite_dev *cdev)
+static int eth_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct f_eem_opts *eem_opts = NULL;
@@ -447,7 +447,7 @@ fail:
return status;
}
-static int __exit eth_unbind(struct usb_composite_dev *cdev)
+static int eth_unbind(struct usb_composite_dev *cdev)
{
if (has_rndis()) {
usb_put_function(f_rndis);
@@ -466,13 +466,13 @@ static int __exit eth_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static __refdata struct usb_composite_driver eth_driver = {
+static struct usb_composite_driver eth_driver = {
.name = "g_ether",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
.bind = eth_bind,
- .unbind = __exit_p(eth_unbind),
+ .unbind = eth_unbind,
};
module_usb_composite_driver(eth_driver);
diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c
index b01b88e1b716..e821931c965c 100644
--- a/drivers/usb/gadget/legacy/g_ffs.c
+++ b/drivers/usb/gadget/legacy/g_ffs.c
@@ -163,7 +163,7 @@ static int gfs_unbind(struct usb_composite_dev *cdev);
static int gfs_do_config(struct usb_configuration *c);
-static __refdata struct usb_composite_driver gfs_driver = {
+static struct usb_composite_driver gfs_driver = {
.name = DRIVER_NAME,
.dev = &gfs_dev_desc,
.strings = gfs_dev_strings,
@@ -304,8 +304,10 @@ static int functionfs_ready_callback(struct ffs_data *ffs)
gfs_registered = true;
ret = usb_composite_probe(&gfs_driver);
- if (unlikely(ret < 0))
+ if (unlikely(ret < 0)) {
+ ++missing_funcs;
gfs_registered = false;
+ }
return ret;
}
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index e02a095294ac..da19c486b61e 100644
--- a/drivers/usb/gadget/legacy/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -118,7 +118,7 @@ static struct usb_gadget_strings *dev_strings[] = {
static struct usb_function_instance *fi_midi;
static struct usb_function *f_midi;
-static int __exit midi_unbind(struct usb_composite_dev *dev)
+static int midi_unbind(struct usb_composite_dev *dev)
{
usb_put_function(f_midi);
usb_put_function_instance(fi_midi);
@@ -133,7 +133,7 @@ static struct usb_configuration midi_config = {
.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
};
-static int __init midi_bind_config(struct usb_configuration *c)
+static int midi_bind_config(struct usb_configuration *c)
{
int status;
@@ -150,7 +150,7 @@ static int __init midi_bind_config(struct usb_configuration *c)
return 0;
}
-static int __init midi_bind(struct usb_composite_dev *cdev)
+static int midi_bind(struct usb_composite_dev *cdev)
{
struct f_midi_opts *midi_opts;
int status;
@@ -185,13 +185,13 @@ put:
return status;
}
-static __refdata struct usb_composite_driver midi_driver = {
+static struct usb_composite_driver midi_driver = {
.name = (char *) longname,
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = midi_bind,
- .unbind = __exit_p(midi_unbind),
+ .unbind = midi_unbind,
};
module_usb_composite_driver(midi_driver);
diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c
index 614b06d80b41..2baa572686c6 100644
--- a/drivers/usb/gadget/legacy/hid.c
+++ b/drivers/usb/gadget/legacy/hid.c
@@ -106,7 +106,7 @@ static struct usb_gadget_strings *dev_strings[] = {
/****************************** Configurations ******************************/
-static int __init do_config(struct usb_configuration *c)
+static int do_config(struct usb_configuration *c)
{
struct hidg_func_node *e, *n;
int status = 0;
@@ -147,7 +147,7 @@ static struct usb_configuration config_driver = {
/****************************** Gadget Bind ******************************/
-static int __init hid_bind(struct usb_composite_dev *cdev)
+static int hid_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct list_head *tmp;
@@ -205,7 +205,7 @@ put:
return status;
}
-static int __exit hid_unbind(struct usb_composite_dev *cdev)
+static int hid_unbind(struct usb_composite_dev *cdev)
{
struct hidg_func_node *n;
@@ -216,7 +216,7 @@ static int __exit hid_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static int __init hidg_plat_driver_probe(struct platform_device *pdev)
+static int hidg_plat_driver_probe(struct platform_device *pdev)
{
struct hidg_func_descriptor *func = dev_get_platdata(&pdev->dev);
struct hidg_func_node *entry;
@@ -252,13 +252,13 @@ static int hidg_plat_driver_remove(struct platform_device *pdev)
/****************************** Some noise ******************************/
-static __refdata struct usb_composite_driver hidg_driver = {
+static struct usb_composite_driver hidg_driver = {
.name = "g_hid",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = hid_bind,
- .unbind = __exit_p(hid_unbind),
+ .unbind = hid_unbind,
};
static struct platform_driver hidg_plat_driver = {
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index 8e27a8c96444..e7bfb081f111 100644
--- a/drivers/usb/gadget/legacy/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -130,7 +130,7 @@ static int msg_thread_exits(struct fsg_common *common)
return 0;
}
-static int __init msg_do_config(struct usb_configuration *c)
+static int msg_do_config(struct usb_configuration *c)
{
struct fsg_opts *opts;
int ret;
@@ -170,7 +170,7 @@ static struct usb_configuration msg_config_driver = {
/****************************** Gadget Bind ******************************/
-static int __init msg_bind(struct usb_composite_dev *cdev)
+static int msg_bind(struct usb_composite_dev *cdev)
{
static const struct fsg_operations ops = {
.thread_exits = msg_thread_exits,
@@ -248,7 +248,7 @@ static int msg_unbind(struct usb_composite_dev *cdev)
/****************************** Some noise ******************************/
-static __refdata struct usb_composite_driver msg_driver = {
+static struct usb_composite_driver msg_driver = {
.name = "g_mass_storage",
.dev = &msg_device_desc,
.max_speed = USB_SPEED_SUPER,
diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c
index 39d27bb343b4..b21b51f0c9fa 100644
--- a/drivers/usb/gadget/legacy/multi.c
+++ b/drivers/usb/gadget/legacy/multi.c
@@ -149,7 +149,7 @@ static struct usb_function *f_acm_rndis;
static struct usb_function *f_rndis;
static struct usb_function *f_msg_rndis;
-static __init int rndis_do_config(struct usb_configuration *c)
+static int rndis_do_config(struct usb_configuration *c)
{
struct fsg_opts *fsg_opts;
int ret;
@@ -237,7 +237,7 @@ static struct usb_function *f_acm_multi;
static struct usb_function *f_ecm;
static struct usb_function *f_msg_multi;
-static __init int cdc_do_config(struct usb_configuration *c)
+static int cdc_do_config(struct usb_configuration *c)
{
struct fsg_opts *fsg_opts;
int ret;
@@ -466,7 +466,7 @@ fail:
return status;
}
-static int __exit multi_unbind(struct usb_composite_dev *cdev)
+static int multi_unbind(struct usb_composite_dev *cdev)
{
#ifdef CONFIG_USB_G_MULTI_CDC
usb_put_function(f_msg_multi);
@@ -497,13 +497,13 @@ static int __exit multi_unbind(struct usb_composite_dev *cdev)
/****************************** Some noise ******************************/
-static __refdata struct usb_composite_driver multi_driver = {
+static struct usb_composite_driver multi_driver = {
.name = "g_multi",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = multi_bind,
- .unbind = __exit_p(multi_unbind),
+ .unbind = multi_unbind,
.needs_serial = 1,
};
diff --git a/drivers/usb/gadget/legacy/ncm.c b/drivers/usb/gadget/legacy/ncm.c
index e90e23db2acb..6ce7421412e9 100644
--- a/drivers/usb/gadget/legacy/ncm.c
+++ b/drivers/usb/gadget/legacy/ncm.c
@@ -107,7 +107,7 @@ static struct usb_function *f_ncm;
/*-------------------------------------------------------------------------*/
-static int __init ncm_do_config(struct usb_configuration *c)
+static int ncm_do_config(struct usb_configuration *c)
{
int status;
@@ -143,7 +143,7 @@ static struct usb_configuration ncm_config_driver = {
/*-------------------------------------------------------------------------*/
-static int __init gncm_bind(struct usb_composite_dev *cdev)
+static int gncm_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct f_ncm_opts *ncm_opts;
@@ -186,7 +186,7 @@ fail:
return status;
}
-static int __exit gncm_unbind(struct usb_composite_dev *cdev)
+static int gncm_unbind(struct usb_composite_dev *cdev)
{
if (!IS_ERR_OR_NULL(f_ncm))
usb_put_function(f_ncm);
@@ -195,13 +195,13 @@ static int __exit gncm_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static __refdata struct usb_composite_driver ncm_driver = {
+static struct usb_composite_driver ncm_driver = {
.name = "g_ncm",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = gncm_bind,
- .unbind = __exit_p(gncm_unbind),
+ .unbind = gncm_unbind,
};
module_usb_composite_driver(ncm_driver);
diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c
index 9b8fd701648c..4bb498a38a1c 100644
--- a/drivers/usb/gadget/legacy/nokia.c
+++ b/drivers/usb/gadget/legacy/nokia.c
@@ -118,7 +118,7 @@ static struct usb_function_instance *fi_obex1;
static struct usb_function_instance *fi_obex2;
static struct usb_function_instance *fi_phonet;
-static int __init nokia_bind_config(struct usb_configuration *c)
+static int nokia_bind_config(struct usb_configuration *c)
{
struct usb_function *f_acm;
struct usb_function *f_phonet = NULL;
@@ -224,7 +224,7 @@ err_get_acm:
return status;
}
-static int __init nokia_bind(struct usb_composite_dev *cdev)
+static int nokia_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int status;
@@ -307,7 +307,7 @@ err_usb:
return status;
}
-static int __exit nokia_unbind(struct usb_composite_dev *cdev)
+static int nokia_unbind(struct usb_composite_dev *cdev)
{
if (!IS_ERR_OR_NULL(f_obex1_cfg2))
usb_put_function(f_obex1_cfg2);
@@ -338,13 +338,13 @@ static int __exit nokia_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static __refdata struct usb_composite_driver nokia_driver = {
+static struct usb_composite_driver nokia_driver = {
.name = "g_nokia",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = nokia_bind,
- .unbind = __exit_p(nokia_unbind),
+ .unbind = nokia_unbind,
};
module_usb_composite_driver(nokia_driver);
diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c
index d5b6ee725a2a..1ce7df1060a5 100644
--- a/drivers/usb/gadget/legacy/printer.c
+++ b/drivers/usb/gadget/legacy/printer.c
@@ -126,7 +126,7 @@ static struct usb_configuration printer_cfg_driver = {
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
};
-static int __init printer_do_config(struct usb_configuration *c)
+static int printer_do_config(struct usb_configuration *c)
{
struct usb_gadget *gadget = c->cdev->gadget;
int status = 0;
@@ -152,7 +152,7 @@ static int __init printer_do_config(struct usb_configuration *c)
return status;
}
-static int __init printer_bind(struct usb_composite_dev *cdev)
+static int printer_bind(struct usb_composite_dev *cdev)
{
struct f_printer_opts *opts;
int ret, len;
@@ -191,7 +191,7 @@ static int __init printer_bind(struct usb_composite_dev *cdev)
return ret;
}
-static int __exit printer_unbind(struct usb_composite_dev *cdev)
+static int printer_unbind(struct usb_composite_dev *cdev)
{
usb_put_function(f_printer);
usb_put_function_instance(fi_printer);
@@ -199,7 +199,7 @@ static int __exit printer_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static __refdata struct usb_composite_driver printer_driver = {
+static struct usb_composite_driver printer_driver = {
.name = shortname,
.dev = &device_desc,
.strings = dev_strings,
diff --git a/drivers/usb/gadget/legacy/serial.c b/drivers/usb/gadget/legacy/serial.c
index 1f5f978d35d5..8b7528f9b78e 100644
--- a/drivers/usb/gadget/legacy/serial.c
+++ b/drivers/usb/gadget/legacy/serial.c
@@ -174,7 +174,7 @@ out:
return ret;
}
-static int __init gs_bind(struct usb_composite_dev *cdev)
+static int gs_bind(struct usb_composite_dev *cdev)
{
int status;
@@ -230,7 +230,7 @@ static int gs_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static __refdata struct usb_composite_driver gserial_driver = {
+static struct usb_composite_driver gserial_driver = {
.name = "g_serial",
.dev = &device_desc,
.strings = dev_strings,
diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
index 8b80addc4ce6..6ce932f90ef8 100644
--- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
@@ -16,7 +16,6 @@
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/usb/storage.h>
-#include <scsi/scsi.h>
#include <scsi/scsi_tcq.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
@@ -2397,7 +2396,7 @@ static int usb_target_bind(struct usb_composite_dev *cdev)
return 0;
}
-static __refdata struct usb_composite_driver usbg_driver = {
+static struct usb_composite_driver usbg_driver = {
.name = "g_target",
.dev = &usbg_device_desc,
.strings = usbg_strings,
diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.h b/drivers/usb/gadget/legacy/tcm_usb_gadget.h
index 8289219925b8..9fb3544cc80f 100644
--- a/drivers/usb/gadget/legacy/tcm_usb_gadget.h
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.h
@@ -6,7 +6,6 @@
#include <linux/usb/composite.h>
#include <linux/usb/uas.h>
#include <linux/usb/storage.h>
-#include <scsi/scsi.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c
index 04a3da20f742..72c976bf3530 100644
--- a/drivers/usb/gadget/legacy/webcam.c
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -334,7 +334,7 @@ static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
* USB configuration
*/
-static int __init
+static int
webcam_config_bind(struct usb_configuration *c)
{
int status = 0;
@@ -358,7 +358,7 @@ static struct usb_configuration webcam_config_driver = {
.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
};
-static int /* __init_or_exit */
+static int
webcam_unbind(struct usb_composite_dev *cdev)
{
if (!IS_ERR_OR_NULL(f_uvc))
@@ -368,7 +368,7 @@ webcam_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static int __init
+static int
webcam_bind(struct usb_composite_dev *cdev)
{
struct f_uvc_opts *uvc_opts;
@@ -422,7 +422,7 @@ error:
* Driver
*/
-static __refdata struct usb_composite_driver webcam_driver = {
+static struct usb_composite_driver webcam_driver = {
.name = "g_webcam",
.dev = &webcam_device_descriptor,
.strings = webcam_device_strings,
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index 5ee95152493c..c986e8addb90 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -272,7 +272,7 @@ static struct usb_function_instance *func_inst_lb;
module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(qlen, "depth of loopback queue");
-static int __init zero_bind(struct usb_composite_dev *cdev)
+static int zero_bind(struct usb_composite_dev *cdev)
{
struct f_ss_opts *ss_opts;
struct f_lb_opts *lb_opts;
@@ -400,7 +400,7 @@ static int zero_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static __refdata struct usb_composite_driver zero_driver = {
+static struct usb_composite_driver zero_driver = {
.name = "zero",
.dev = &device_desc,
.strings = dev_strings,
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index 2fbedca3c2b4..fc4226462f8f 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -1942,7 +1942,7 @@ err_unprepare_fclk:
return retval;
}
-static int __exit at91udc_remove(struct platform_device *pdev)
+static int at91udc_remove(struct platform_device *pdev)
{
struct at91_udc *udc = platform_get_drvdata(pdev);
unsigned long flags;
@@ -2018,7 +2018,7 @@ static int at91udc_resume(struct platform_device *pdev)
#endif
static struct platform_driver at91_udc_driver = {
- .remove = __exit_p(at91udc_remove),
+ .remove = at91udc_remove,
.shutdown = at91udc_shutdown,
.suspend = at91udc_suspend,
.resume = at91udc_resume,
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 4c01953a0869..351d48550c33 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -2186,7 +2186,7 @@ static int usba_udc_probe(struct platform_device *pdev)
return 0;
}
-static int __exit usba_udc_remove(struct platform_device *pdev)
+static int usba_udc_remove(struct platform_device *pdev)
{
struct usba_udc *udc;
int i;
@@ -2258,7 +2258,7 @@ static int usba_udc_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume);
static struct platform_driver udc_driver = {
- .remove = __exit_p(usba_udc_remove),
+ .remove = usba_udc_remove,
.driver = {
.name = "atmel_usba_udc",
.pm = &usba_udc_pm_ops,
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 55fcb930f92e..c60022b46a48 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -2525,7 +2525,7 @@ err_kfree:
/* Driver removal function
* Free resources and finish pending transactions
*/
-static int __exit fsl_udc_remove(struct platform_device *pdev)
+static int fsl_udc_remove(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
@@ -2663,7 +2663,7 @@ static const struct platform_device_id fsl_udc_devtype[] = {
};
MODULE_DEVICE_TABLE(platform, fsl_udc_devtype);
static struct platform_driver udc_driver = {
- .remove = __exit_p(fsl_udc_remove),
+ .remove = fsl_udc_remove,
/* Just for FSL i.mx SoC currently */
.id_table = fsl_udc_devtype,
/* these suspend and resume are not usb suspend and resume */
diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
index fb4df159d32d..3970f453de49 100644
--- a/drivers/usb/gadget/udc/fusb300_udc.c
+++ b/drivers/usb/gadget/udc/fusb300_udc.c
@@ -1342,7 +1342,7 @@ static const struct usb_gadget_ops fusb300_gadget_ops = {
.udc_stop = fusb300_udc_stop,
};
-static int __exit fusb300_remove(struct platform_device *pdev)
+static int fusb300_remove(struct platform_device *pdev)
{
struct fusb300 *fusb300 = platform_get_drvdata(pdev);
@@ -1492,7 +1492,7 @@ clean_up:
}
static struct platform_driver fusb300_driver = {
- .remove = __exit_p(fusb300_remove),
+ .remove = fusb300_remove,
.driver = {
.name = (char *) udc_name,
},
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index 8c7c83c93713..309706fe4bf0 100644
--- a/drivers/usb/gadget/udc/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -1528,7 +1528,7 @@ static const struct usb_gadget_ops m66592_gadget_ops = {
.pullup = m66592_pullup,
};
-static int __exit m66592_remove(struct platform_device *pdev)
+static int m66592_remove(struct platform_device *pdev)
{
struct m66592 *m66592 = platform_get_drvdata(pdev);
@@ -1695,7 +1695,7 @@ clean_up:
/*-------------------------------------------------------------------------*/
static struct platform_driver m66592_driver = {
- .remove = __exit_p(m66592_remove),
+ .remove = m66592_remove,
.driver = {
.name = (char *) udc_name,
},
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index 2495fe9c95c5..0293f7169dee 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1820,7 +1820,7 @@ static const struct usb_gadget_ops r8a66597_gadget_ops = {
.set_selfpowered = r8a66597_set_selfpowered,
};
-static int __exit r8a66597_remove(struct platform_device *pdev)
+static int r8a66597_remove(struct platform_device *pdev)
{
struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
@@ -1974,7 +1974,7 @@ clean_up2:
/*-------------------------------------------------------------------------*/
static struct platform_driver r8a66597_driver = {
- .remove = __exit_p(r8a66597_remove),
+ .remove = r8a66597_remove,
.driver = {
.name = (char *) udc_name,
},
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index b808951491cc..99fd9a5667df 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -1487,7 +1487,7 @@ static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on)
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
- s3c2410_udc_set_pullup(udc, is_on ? 0 : 1);
+ s3c2410_udc_set_pullup(udc, is_on);
return 0;
}
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index dd3e9fd31b80..1f24274477ab 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -2071,8 +2071,8 @@ static int xudc_probe(struct platform_device *pdev)
/* Map the registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
udc->addr = devm_ioremap_resource(&pdev->dev, res);
- if (!udc->addr)
- return -ENOMEM;
+ if (IS_ERR(udc->addr))
+ return PTR_ERR(udc->addr);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 9db74ca7e5b9..275c92e53a59 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -88,13 +88,20 @@ static int ehci_msm_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(hcd->regs)) {
- ret = PTR_ERR(hcd->regs);
+ if (!res) {
+ dev_err(&pdev->dev, "Unable to get memory resource\n");
+ ret = -ENODEV;
goto put_hcd;
}
+
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
+ hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto put_hcd;
+ }
/*
* OTG driver takes care of PHY initialization, clock management,
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index f5397a517c54..7d34cbfaf373 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2026,8 +2026,13 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
break;
case COMP_DEV_ERR:
case COMP_STALL:
+ frame->status = -EPROTO;
+ skip_td = true;
+ break;
case COMP_TX_ERR:
frame->status = -EPROTO;
+ if (event_trb != td->last_trb)
+ return 0;
skip_td = true;
break;
case COMP_STOP:
@@ -2640,7 +2645,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
xhci_halt(xhci);
hw_died:
spin_unlock(&xhci->lock);
- return -ESHUTDOWN;
+ return IRQ_HANDLED;
}
/*
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index ec8ac1674854..36bf089b708f 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -3682,18 +3682,21 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
unsigned long flags;
- int ret;
+ int ret, slot_id;
struct xhci_command *command;
command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
if (!command)
return 0;
+ /* xhci->slot_id and xhci->addr_dev are not thread-safe */
+ mutex_lock(&xhci->mutex);
spin_lock_irqsave(&xhci->lock, flags);
command->completion = &xhci->addr_dev;
ret = xhci_queue_slot_control(xhci, command, TRB_ENABLE_SLOT, 0);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
+ mutex_unlock(&xhci->mutex);
xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
kfree(command);
return 0;
@@ -3702,8 +3705,10 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
spin_unlock_irqrestore(&xhci->lock, flags);
wait_for_completion(command->completion);
+ slot_id = xhci->slot_id;
+ mutex_unlock(&xhci->mutex);
- if (!xhci->slot_id || command->status != COMP_SUCCESS) {
+ if (!slot_id || command->status != COMP_SUCCESS) {
xhci_err(xhci, "Error while assigning device slot ID\n");
xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
HCS_MAX_SLOTS(
@@ -3728,11 +3733,11 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
* xhci_discover_or_reset_device(), which may be called as part of
* mass storage driver error handling.
*/
- if (!xhci_alloc_virt_device(xhci, xhci->slot_id, udev, GFP_NOIO)) {
+ if (!xhci_alloc_virt_device(xhci, slot_id, udev, GFP_NOIO)) {
xhci_warn(xhci, "Could not allocate xHCI USB device data structures\n");
goto disable_slot;
}
- udev->slot_id = xhci->slot_id;
+ udev->slot_id = slot_id;
#ifndef CONFIG_USB_DEFAULT_PERSIST
/*
@@ -3778,12 +3783,15 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
struct xhci_slot_ctx *slot_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
u64 temp_64;
- struct xhci_command *command;
+ struct xhci_command *command = NULL;
+
+ mutex_lock(&xhci->mutex);
if (!udev->slot_id) {
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Bad Slot ID %d", udev->slot_id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
virt_dev = xhci->devs[udev->slot_id];
@@ -3796,7 +3804,8 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
*/
xhci_warn(xhci, "Virt dev invalid for slot_id 0x%x!\n",
udev->slot_id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (setup == SETUP_CONTEXT_ONLY) {
@@ -3804,13 +3813,15 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
if (GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state)) ==
SLOT_STATE_DEFAULT) {
xhci_dbg(xhci, "Slot already in default state\n");
- return 0;
+ goto out;
}
}
command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
- if (!command)
- return -ENOMEM;
+ if (!command) {
+ ret = -ENOMEM;
+ goto out;
+ }
command->in_ctx = virt_dev->in_ctx;
command->completion = &xhci->addr_dev;
@@ -3820,8 +3831,8 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
- kfree(command);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
/*
* If this is the first Set Address since device plug-in or
@@ -3848,8 +3859,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"FIXME: allocate a command ring segment");
- kfree(command);
- return ret;
+ goto out;
}
xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -3896,10 +3906,8 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
ret = -EINVAL;
break;
}
- if (ret) {
- kfree(command);
- return ret;
- }
+ if (ret)
+ goto out;
temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Op regs DCBAA ptr = %#016llx", temp_64);
@@ -3932,8 +3940,10 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Internal device address = %d",
le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK);
+out:
+ mutex_unlock(&xhci->mutex);
kfree(command);
- return 0;
+ return ret;
}
int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
@@ -4855,6 +4865,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
return 0;
}
+ mutex_init(&xhci->mutex);
xhci->cap_regs = hcd->regs;
xhci->op_regs = hcd->regs +
HC_LENGTH(readl(&xhci->cap_regs->hc_capbase));
@@ -5011,4 +5022,12 @@ static int __init xhci_hcd_init(void)
BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
return 0;
}
+
+/*
+ * If an init function is provided, an exit function must also be provided
+ * to allow module unload.
+ */
+static void __exit xhci_hcd_fini(void) { }
+
module_init(xhci_hcd_init);
+module_exit(xhci_hcd_fini);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 8e421b89632d..6977f8491fa7 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1267,7 +1267,7 @@ union xhci_trb {
* since the command ring is 64-byte aligned.
* It must also be greater than 16.
*/
-#define TRBS_PER_SEGMENT 64
+#define TRBS_PER_SEGMENT 256
/* Allow two commands + a link TRB, along with any reserved command TRBs */
#define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3)
#define TRB_SEGMENT_SIZE (TRBS_PER_SEGMENT*16)
@@ -1497,6 +1497,8 @@ struct xhci_hcd {
struct list_head lpm_failed_devs;
/* slot enabling and address device helpers */
+ /* these are not thread safe so use mutex */
+ struct mutex mutex;
struct completion addr_dev;
int slot_id;
/* For USB 3.0 LPM enable/disable. */
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index 6431d08c8d9d..a4dbb0cd80da 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -635,7 +635,6 @@ static struct scsi_host_template mts_scsi_host_template = {
.sg_tablesize = SG_ALL,
.can_queue = 1,
.this_id = -1,
- .cmd_per_lun = 1,
.use_clustering = 1,
.emulated = 1,
.slave_alloc = mts_slave_alloc,
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 82503a7ff6c8..cce22ff1c2eb 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -69,12 +69,6 @@
#define USB_DEVICE_ID_LD_HYBRID 0x2090 /* USB Product ID of Automotive Hybrid */
#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0 /* USB Product ID of Heat control */
-#define USB_VENDOR_ID_VERNIER 0x08f7
-#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
-#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
-#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
-#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006
-
#ifdef CONFIG_USB_DYNAMIC_MINORS
#define USB_LD_MINOR_BASE 0
#else
@@ -115,10 +109,6 @@ static const struct usb_device_id ld_usb_table[] = {
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MCT) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) },
{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) },
- { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
- { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
- { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
- { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ld_usb_table);
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 3789b08ef67b..6dca3d794ced 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -2021,13 +2021,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
if (musb->ops->quirks)
musb->io.quirks = musb->ops->quirks;
- /* At least tusb6010 has it's own offsets.. */
- if (musb->ops->ep_offset)
- musb->io.ep_offset = musb->ops->ep_offset;
- if (musb->ops->ep_select)
- musb->io.ep_select = musb->ops->ep_select;
-
- /* ..and some devices use indexed offset or flat offset */
+ /* Most devices use indexed offset or flat offset */
if (musb->io.quirks & MUSB_INDEXED_EP) {
musb->io.ep_offset = musb_indexed_ep_offset;
musb->io.ep_select = musb_indexed_ep_select;
@@ -2036,6 +2030,12 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb->io.ep_select = musb_flat_ep_select;
}
+ /* At least tusb6010 has its own offsets */
+ if (musb->ops->ep_offset)
+ musb->io.ep_offset = musb->ops->ep_offset;
+ if (musb->ops->ep_select)
+ musb->io.ep_select = musb->ops->ep_select;
+
if (musb->ops->fifo_mode)
fifo_mode = musb->ops->fifo_mode;
else
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index 7225d526df04..03ab0c699f74 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -1179,7 +1179,7 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev,
}
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
ab8500_usb_link_status_irq,
- IRQF_NO_SUSPEND | IRQF_SHARED,
+ IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
"usb-link-status", ab);
if (err < 0) {
dev_err(ab->dev, "request_irq failed for link status irq\n");
@@ -1195,7 +1195,7 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev,
}
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
ab8500_usb_disconnect_irq,
- IRQF_NO_SUSPEND | IRQF_SHARED,
+ IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
"usb-id-fall", ab);
if (err < 0) {
dev_err(ab->dev, "request_irq failed for ID fall irq\n");
@@ -1211,7 +1211,7 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev,
}
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
ab8500_usb_disconnect_irq,
- IRQF_NO_SUSPEND | IRQF_SHARED,
+ IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
"usb-vbus-fall", ab);
if (err < 0) {
dev_err(ab->dev, "request_irq failed for Vbus fall irq\n");
diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c
index 1e0e10dd6ba5..3af263cc0caa 100644
--- a/drivers/usb/phy/phy-isp1301-omap.c
+++ b/drivers/usb/phy/phy-isp1301-omap.c
@@ -94,7 +94,7 @@ struct isp1301 {
#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3)
-#if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE)
+#if defined(CONFIG_TPS65010) || (defined(CONFIG_TPS65010_MODULE) && defined(MODULE))
#include <linux/i2c/tps65010.h>
diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c
index 845f658276b1..2b28443d07b9 100644
--- a/drivers/usb/phy/phy-tahvo.c
+++ b/drivers/usb/phy/phy-tahvo.c
@@ -401,7 +401,8 @@ static int tahvo_usb_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, tu);
tu->irq = platform_get_irq(pdev, 0);
- ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, 0,
+ ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt,
+ IRQF_ONESHOT,
"tahvo-vbus", tu);
if (ret) {
dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n",
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 8597cf9cfceb..c0f5c652d272 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -611,6 +611,8 @@ struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = {
static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
{
struct usbhs_pipe *pipe = pkt->pipe;
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv);
if (usbhs_pipe_is_busy(pipe))
return 0;
@@ -624,6 +626,9 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
usbhs_pipe_data_sequence(pipe, pkt->sequence);
pkt->sequence = -1; /* -1 sequence will be ignored */
+ if (usbhs_pipe_is_dcp(pipe))
+ usbhsf_fifo_clear(pipe, fifo);
+
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
usbhs_pipe_enable(pipe);
usbhs_pipe_running(pipe, 1);
@@ -673,7 +678,14 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
*is_done = 1;
usbhsf_rx_irq_ctrl(pipe, 0);
usbhs_pipe_running(pipe, 0);
- usbhs_pipe_disable(pipe); /* disable pipe first */
+ /*
+ * If function mode, since this controller is possible to enter
+ * Control Write status stage at this timing, this driver
+ * should not disable the pipe. If such a case happens, this
+ * controller is not able to complete the status stage.
+ */
+ if (!usbhs_mod_is_host(priv) && !usbhs_pipe_is_dcp(pipe))
+ usbhs_pipe_disable(pipe); /* disable pipe first */
}
/*
@@ -1227,15 +1239,21 @@ static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo,
{
char name[16];
- snprintf(name, sizeof(name), "tx%d", channel);
- fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
- if (IS_ERR(fifo->tx_chan))
- fifo->tx_chan = NULL;
-
- snprintf(name, sizeof(name), "rx%d", channel);
- fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
- if (IS_ERR(fifo->rx_chan))
- fifo->rx_chan = NULL;
+ /*
+ * To avoid complex handing for DnFIFOs, the driver uses each
+ * DnFIFO as TX or RX direction (not bi-direction).
+ * So, the driver uses odd channels for TX, even channels for RX.
+ */
+ snprintf(name, sizeof(name), "ch%d", channel);
+ if (channel & 1) {
+ fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
+ if (IS_ERR(fifo->tx_chan))
+ fifo->tx_chan = NULL;
+ } else {
+ fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
+ if (IS_ERR(fifo->rx_chan))
+ fifo->rx_chan = NULL;
+ }
}
static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo,
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index b7cf1982d1d9..56ecb8b5115d 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -171,7 +171,7 @@ config USB_SERIAL_FTDI_SIO
---help---
Say Y here if you want to use a FTDI SIO single port USB to serial
converter device. The implementation I have is called the USC-1000.
- This driver has also be tested with the 245 and 232 devices.
+ This driver has also been tested with the 245 and 232 devices.
See <http://ftdi-usb-sio.sourceforge.net/> for more
information on this driver and the device.
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 84ce2d74894c..ffd739e31bfc 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -127,6 +127,8 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */
{ USB_DEVICE(0x10C4, 0x8977) }, /* CEL MeshWorks DevKit Device */
+ { USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */
+ { USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */
{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8eb68a31cab6..4c8b3b82103d 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -699,6 +699,7 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(XSENS_VID, XSENS_AWINDA_DONGLE_PID) },
{ USB_DEVICE(XSENS_VID, XSENS_AWINDA_STATION_PID) },
{ USB_DEVICE(XSENS_VID, XSENS_CONVERTER_PID) },
+ { USB_DEVICE(XSENS_VID, XSENS_MTDEVBOARD_PID) },
{ USB_DEVICE(XSENS_VID, XSENS_MTW_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_OMNI1509) },
{ USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 4e4f46f3c89c..792e054126de 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -155,6 +155,7 @@
#define XSENS_AWINDA_STATION_PID 0x0101
#define XSENS_AWINDA_DONGLE_PID 0x0102
#define XSENS_MTW_PID 0x0200 /* Xsens MTw */
+#define XSENS_MTDEVBOARD_PID 0x0300 /* Motion Tracker Development Board */
#define XSENS_CONVERTER_PID 0xD00D /* Xsens USB-serial converter */
/* Xsens devices using FTDI VID */
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 829604d11f3f..f5257af33ecf 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -61,7 +61,6 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
{ USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) },
- { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) },
{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1),
.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },
{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65),
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 71fd9da1d6e7..e3b7af8adfb7 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -62,10 +62,6 @@
#define ALCATEL_VENDOR_ID 0x11f7
#define ALCATEL_PRODUCT_ID 0x02df
-/* Samsung I330 phone cradle */
-#define SAMSUNG_VENDOR_ID 0x04e8
-#define SAMSUNG_PRODUCT_ID 0x8001
-
#define SIEMENS_VENDOR_ID 0x11f5
#define SIEMENS_PRODUCT_ID_SX1 0x0001
#define SIEMENS_PRODUCT_ID_X65 0x0003
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index bf2bd40e5f2a..60afb39eb73c 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -95,7 +95,7 @@ static const struct usb_device_id id_table[] = {
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(ACER_VENDOR_ID, ACER_S10_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
- { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID),
+ { USB_DEVICE_INTERFACE_CLASS(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID, 0xff),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 0e400f382f3a..996ef1e882d3 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -558,7 +558,6 @@ struct scsi_host_template usb_stor_host_template = {
/* queue commands only, only one command per LUN */
.can_queue = 1,
- .cmd_per_lun = 1,
/* unknown initiator id */
.this_id = -1,
diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h
index 9893d696fc97..f58caa9e6a27 100644
--- a/drivers/usb/storage/uas-detect.h
+++ b/drivers/usb/storage/uas-detect.h
@@ -51,7 +51,8 @@ static int uas_find_endpoints(struct usb_host_interface *alt,
}
static int uas_use_uas_driver(struct usb_interface *intf,
- const struct usb_device_id *id)
+ const struct usb_device_id *id,
+ unsigned long *flags_ret)
{
struct usb_host_endpoint *eps[4] = { };
struct usb_device *udev = interface_to_usbdev(intf);
@@ -73,7 +74,7 @@ static int uas_use_uas_driver(struct usb_interface *intf,
* this writing the following versions exist:
* ASM1051 - no uas support version
* ASM1051 - with broken (*) uas support
- * ASM1053 - with working uas support
+ * ASM1053 - with working uas support, but problems with large xfers
* ASM1153 - with working uas support
*
* Devices with these chips re-use a number of device-ids over the
@@ -103,6 +104,9 @@ static int uas_use_uas_driver(struct usb_interface *intf,
} else if (usb_ss_max_streams(&eps[1]->ss_ep_comp) == 32) {
/* Possibly an ASM1051, disable uas */
flags |= US_FL_IGNORE_UAS;
+ } else {
+ /* ASM1053, these have issues with large transfers */
+ flags |= US_FL_MAX_SECTORS_240;
}
}
@@ -132,5 +136,8 @@ static int uas_use_uas_driver(struct usb_interface *intf,
return 0;
}
+ if (flags_ret)
+ *flags_ret = flags;
+
return 1;
}
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 6cdabdc119a7..f68921909552 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -759,7 +759,10 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)
static int uas_slave_alloc(struct scsi_device *sdev)
{
- sdev->hostdata = (void *)sdev->host->hostdata;
+ struct uas_dev_info *devinfo =
+ (struct uas_dev_info *)sdev->host->hostdata;
+
+ sdev->hostdata = devinfo;
/* USB has unusual DMA-alignment requirements: Although the
* starting address of each scatter-gather element doesn't matter,
@@ -778,6 +781,11 @@ static int uas_slave_alloc(struct scsi_device *sdev)
*/
blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1));
+ if (devinfo->flags & US_FL_MAX_SECTORS_64)
+ blk_queue_max_hw_sectors(sdev->request_queue, 64);
+ else if (devinfo->flags & US_FL_MAX_SECTORS_240)
+ blk_queue_max_hw_sectors(sdev->request_queue, 240);
+
return 0;
}
@@ -803,7 +811,6 @@ static struct scsi_host_template uas_host_template = {
.can_queue = 65536, /* Is there a limit on the _host_ ? */
.this_id = -1,
.sg_tablesize = SG_NONE,
- .cmd_per_lun = 1, /* until we override it */
.skip_settle_delay = 1,
.use_blk_tags = 1,
};
@@ -887,8 +894,9 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
struct Scsi_Host *shost = NULL;
struct uas_dev_info *devinfo;
struct usb_device *udev = interface_to_usbdev(intf);
+ unsigned long dev_flags;
- if (!uas_use_uas_driver(intf, id))
+ if (!uas_use_uas_driver(intf, id, &dev_flags))
return -ENODEV;
if (uas_switch_interface(udev, intf))
@@ -910,8 +918,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
devinfo->udev = udev;
devinfo->resetting = 0;
devinfo->shutdown = 0;
- devinfo->flags = id->driver_info;
- usb_stor_adjust_quirks(udev, &devinfo->flags);
+ devinfo->flags = dev_flags;
init_usb_anchor(&devinfo->cmd_urbs);
init_usb_anchor(&devinfo->sense_urbs);
init_usb_anchor(&devinfo->data_urbs);
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index d684b4b8108f..caf188800c67 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -766,6 +766,13 @@ UNUSUAL_DEV( 0x059f, 0x0643, 0x0000, 0x0000,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_GO_SLOW ),
+/* Reported by Christian Schaller <cschalle@redhat.com> */
+UNUSUAL_DEV( 0x059f, 0x0651, 0x0000, 0x0000,
+ "LaCie",
+ "External HDD",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_WP_DETECT ),
+
/* Submitted by Joel Bourquard <numlock@freesurf.ch>
* Some versions of this device need the SubClass and Protocol overrides
* while others don't.
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 5600c33fcadb..6c10c888f35f 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -479,7 +479,8 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags)
US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT |
US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 |
US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE |
- US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES);
+ US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES |
+ US_FL_MAX_SECTORS_240);
p = quirks;
while (*p) {
@@ -520,6 +521,9 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags)
case 'f':
f |= US_FL_NO_REPORT_OPCODES;
break;
+ case 'g':
+ f |= US_FL_MAX_SECTORS_240;
+ break;
case 'h':
f |= US_FL_CAPACITY_HEURISTICS;
break;
@@ -1080,7 +1084,7 @@ static int storage_probe(struct usb_interface *intf,
/* If uas is enabled and this device can do uas then ignore it. */
#if IS_ENABLED(CONFIG_USB_UAS)
- if (uas_use_uas_driver(intf, id))
+ if (uas_use_uas_driver(intf, id, NULL))
return -ENXIO;
#endif
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
index 7d092ddc8119..454017928ed0 100644
--- a/drivers/vfio/Kconfig
+++ b/drivers/vfio/Kconfig
@@ -21,7 +21,7 @@ config VFIO_VIRQFD
menuconfig VFIO
tristate "VFIO Non-Privileged userspace driver framework"
depends on IOMMU_API
- select VFIO_IOMMU_TYPE1 if (X86 || S390 || ARM_SMMU)
+ select VFIO_IOMMU_TYPE1 if (X86 || S390 || ARM_SMMU || ARM_SMMU_V3)
select VFIO_IOMMU_SPAPR_TCE if (PPC_POWERNV || PPC_PSERIES)
select VFIO_SPAPR_EEH if (PPC_POWERNV || PPC_PSERIES)
select ANON_INODES
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 69fab0fd15ae..e9851add6f4e 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -907,8 +907,14 @@ static void vfio_pci_request(void *device_data, unsigned int count)
mutex_lock(&vdev->igate);
if (vdev->req_trigger) {
- dev_dbg(&vdev->pdev->dev, "Requesting device from user\n");
+ if (!(count % 10))
+ dev_notice_ratelimited(&vdev->pdev->dev,
+ "Relaying device request to user (#%u)\n",
+ count);
eventfd_signal(vdev->req_trigger, 1);
+ } else if (count == 0) {
+ dev_warn(&vdev->pdev->dev,
+ "No device request channel registered, blocked until released by user\n");
}
mutex_unlock(&vdev->igate);
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 0d336625ac71..e1278fe04b1e 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -710,6 +710,8 @@ void *vfio_del_group_dev(struct device *dev)
void *device_data = device->device_data;
struct vfio_unbound_dev *unbound;
unsigned int i = 0;
+ long ret;
+ bool interrupted = false;
/*
* The group exists so long as we have a device reference. Get
@@ -755,9 +757,22 @@ void *vfio_del_group_dev(struct device *dev)
vfio_device_put(device);
- } while (wait_event_interruptible_timeout(vfio.release_q,
- !vfio_dev_present(group, dev),
- HZ * 10) <= 0);
+ if (interrupted) {
+ ret = wait_event_timeout(vfio.release_q,
+ !vfio_dev_present(group, dev), HZ * 10);
+ } else {
+ ret = wait_event_interruptible_timeout(vfio.release_q,
+ !vfio_dev_present(group, dev), HZ * 10);
+ if (ret == -ERESTARTSYS) {
+ interrupted = true;
+ dev_warn(dev,
+ "Device is currently in use, task"
+ " \"%s\" (%d) "
+ "blocked until device is released",
+ current->comm, task_pid_nr(current));
+ }
+ }
+ } while (ret <= 0);
vfio_group_put(group);
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 5e19bb53b3a9..5b30d27dab50 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -37,7 +37,8 @@
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/unaligned.h>
-#include <scsi/scsi.h>
+#include <scsi/scsi_common.h>
+#include <scsi/scsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
#include <target/target_core_fabric_configfs.h>
@@ -1409,8 +1410,7 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs,
* dependency now.
*/
se_tpg = &tpg->se_tpg;
- ret = configfs_depend_item(se_tpg->se_tpg_tfo->tf_subsys,
- &se_tpg->tpg_group.cg_item);
+ ret = target_depend_item(&se_tpg->tpg_group.cg_item);
if (ret) {
pr_warn("configfs_depend_item() failed: %d\n", ret);
kfree(vs_tpg);
@@ -1513,8 +1513,7 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs,
* to allow vhost-scsi WWPN se_tpg->tpg_group shutdown to occur.
*/
se_tpg = &tpg->se_tpg;
- configfs_undepend_item(se_tpg->se_tpg_tfo->tf_subsys,
- &se_tpg->tpg_group.cg_item);
+ target_undepend_item(&se_tpg->tpg_group.cg_item);
}
if (match) {
for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) {
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 3a145a643e0d..6897f1c1bc73 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -274,6 +274,10 @@ static int pwm_backlight_probe(struct platform_device *pdev)
pb->pwm = devm_pwm_get(&pdev->dev, NULL);
if (IS_ERR(pb->pwm)) {
+ ret = PTR_ERR(pb->pwm);
+ if (ret == -EPROBE_DEFER)
+ goto err_alloc;
+
dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
pb->legacy = true;
pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index a6ab9299813c..bb4e96255974 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -687,7 +687,7 @@ static int newport_scroll(struct vc_data *vc, int t, int b, int dir,
static void newport_bmove(struct vc_data *vc, int sy, int sx, int dy,
int dx, int h, int w)
{
- short xs, ys, xe, ye, xoffs, yoffs, tmp;
+ short xs, ys, xe, ye, xoffs, yoffs;
xs = sx << 3;
xe = ((sx + w) << 3) - 1;
@@ -701,9 +701,7 @@ static void newport_bmove(struct vc_data *vc, int sy, int sx, int dy,
yoffs = (dy - sy) << 4;
if (xoffs > 0) {
/* move to the right, exchange starting points */
- tmp = xe;
- xe = xs;
- xs = tmp;
+ swap(xe, xs);
}
newport_wait(npregs);
npregs->set.drawmode0 = (NPORT_DMODE0_S2S | NPORT_DMODE0_BLOCK |
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 109462303087..2d98de535e0f 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2326,13 +2326,6 @@ config FB_PRE_INIT_FB
Select this option if display contents should be inherited as set by
the bootloader.
-config FB_MSM
- tristate "MSM Framebuffer support"
- depends on FB && ARCH_MSM
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
-
config FB_MX3
tristate "MX3 Framebuffer support"
depends on FB && MX3_IPU
@@ -2478,6 +2471,7 @@ config FB_SSD1307
select FB_SYS_IMAGEBLIT
select FB_DEFERRED_IO
select PWM
+ select FB_BACKLIGHT
help
This driver implements support for the Solomon SSD1307
OLED controller over I2C.
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index 1979afffccfe..cecea5063a80 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -126,7 +126,6 @@ obj-y += omap2/
obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o
obj-$(CONFIG_FB_CARMINE) += carminefb.o
obj-$(CONFIG_FB_MB862XX) += mb862xx/
-obj-$(CONFIG_FB_MSM) += msm/
obj-$(CONFIG_FB_NUC900) += nuc900fb.o
obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o
obj-$(CONFIG_FB_PUV3_UNIGFX) += fb-puv3.o
diff --git a/drivers/video/fbdev/amifb.c b/drivers/video/fbdev/amifb.c
index 35f7900a0573..1d702e13aaff 100644
--- a/drivers/video/fbdev/amifb.c
+++ b/drivers/video/fbdev/amifb.c
@@ -2052,7 +2052,7 @@ static void ami_set_sprite(const struct amifb_par *par)
{
copins *copl, *cops;
u_short hs, vs, ve;
- u_long pl, ps, pt;
+ u_long pl, ps;
short mx, my;
cops = copdisplay.list[currentcop][0];
@@ -2078,7 +2078,7 @@ static void ami_set_sprite(const struct amifb_par *par)
if (mod2(vs)) {
lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve + 1);
- pt = pl; pl = ps; ps = pt;
+ swap(pl, ps);
} else {
lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve + 1);
shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve);
@@ -3705,8 +3705,8 @@ default_chipset:
* access the videomem with writethrough cache
*/
info->fix.smem_start = (u_long)ZTWO_PADDR(videomemory);
- videomemory = (u_long)ioremap_writethrough(info->fix.smem_start,
- info->fix.smem_len);
+ videomemory = (u_long)ioremap_wt(info->fix.smem_start,
+ info->fix.smem_len);
if (!videomemory) {
dev_warn(&pdev->dev,
"Unable to map videomem cached writethrough\n");
diff --git a/drivers/video/fbdev/atafb.c b/drivers/video/fbdev/atafb.c
index cb9ee2556850..d6ce613e12ad 100644
--- a/drivers/video/fbdev/atafb.c
+++ b/drivers/video/fbdev/atafb.c
@@ -3185,8 +3185,7 @@ int __init atafb_init(void)
/* Map the video memory (physical address given) to somewhere
* in the kernel address space.
*/
- external_screen_base = ioremap_writethrough(external_addr,
- external_len);
+ external_screen_base = ioremap_wt(external_addr, external_len);
if (external_vgaiobase)
external_vgaiobase =
(unsigned long)ioremap(external_vgaiobase, 0x10000);
diff --git a/drivers/video/fbdev/atmel_lcdfb.c b/drivers/video/fbdev/atmel_lcdfb.c
index 94a8d04e60f9..abadc490fa1f 100644
--- a/drivers/video/fbdev/atmel_lcdfb.c
+++ b/drivers/video/fbdev/atmel_lcdfb.c
@@ -1266,7 +1266,8 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
goto stop_clk;
}
- info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
+ info->screen_base = ioremap_wc(info->fix.smem_start,
+ info->fix.smem_len);
if (!info->screen_base) {
ret = -ENOMEM;
goto release_intmem;
diff --git a/drivers/video/fbdev/aty/aty128fb.c b/drivers/video/fbdev/aty/aty128fb.c
index 0156954bf340..c42ce2fdfd44 100644
--- a/drivers/video/fbdev/aty/aty128fb.c
+++ b/drivers/video/fbdev/aty/aty128fb.c
@@ -80,10 +80,6 @@
#include <asm/btext.h>
#endif /* CONFIG_BOOTX_TEXT */
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
#include <video/aty128.h>
/* Debug flag */
@@ -399,10 +395,7 @@ static int default_cmode = CMODE_8;
static int default_crt_on = 0;
static int default_lcd_on = 1;
-
-#ifdef CONFIG_MTRR
static bool mtrr = true;
-#endif
#ifdef CONFIG_FB_ATY128_BACKLIGHT
#ifdef CONFIG_PMAC_BACKLIGHT
@@ -456,9 +449,7 @@ struct aty128fb_par {
u32 vram_size; /* onboard video ram */
int chip_gen;
const struct aty128_meminfo *mem; /* onboard mem info */
-#ifdef CONFIG_MTRR
- struct { int vram; int vram_valid; } mtrr;
-#endif
+ int wc_cookie;
int blitter_may_be_busy;
int fifo_slots; /* free slots in FIFO (64 max) */
@@ -1725,12 +1716,10 @@ static int aty128fb_setup(char *options)
#endif
continue;
}
-#ifdef CONFIG_MTRR
if(!strncmp(this_opt, "nomtrr", 6)) {
mtrr = 0;
continue;
}
-#endif
#ifdef CONFIG_PPC_PMAC
/* vmode and cmode deprecated */
if (!strncmp(this_opt, "vmode:", 6)) {
@@ -2133,7 +2122,7 @@ static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
par->vram_size = aty_ld_le32(CNFG_MEMSIZE) & 0x03FFFFFF;
/* Virtualize the framebuffer */
- info->screen_base = ioremap(fb_addr, par->vram_size);
+ info->screen_base = ioremap_wc(fb_addr, par->vram_size);
if (!info->screen_base)
goto err_unmap_out;
@@ -2170,15 +2159,9 @@ static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!aty128_init(pdev, ent))
goto err_out;
-#ifdef CONFIG_MTRR
- if (mtrr) {
- par->mtrr.vram = mtrr_add(info->fix.smem_start,
- par->vram_size, MTRR_TYPE_WRCOMB, 1);
- par->mtrr.vram_valid = 1;
- /* let there be speed */
- printk(KERN_INFO "aty128fb: Rage128 MTRR set to ON\n");
- }
-#endif /* CONFIG_MTRR */
+ if (mtrr)
+ par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
+ par->vram_size);
return 0;
err_out:
@@ -2212,11 +2195,7 @@ static void aty128_remove(struct pci_dev *pdev)
aty128_bl_exit(info->bl_dev);
#endif
-#ifdef CONFIG_MTRR
- if (par->mtrr.vram_valid)
- mtrr_del(par->mtrr.vram, info->fix.smem_start,
- par->vram_size);
-#endif /* CONFIG_MTRR */
+ arch_phys_wc_del(par->wc_cookie);
iounmap(par->regbase);
iounmap(info->screen_base);
@@ -2625,8 +2604,5 @@ MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
MODULE_LICENSE("GPL");
module_param(mode_option, charp, 0);
MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
-#ifdef CONFIG_MTRR
module_param_named(nomtrr, mtrr, invbool, 0);
MODULE_PARM_DESC(nomtrr, "bool: Disable MTRR support (0 or 1=disabled) (default=0)");
-#endif
-
diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c
index 01237c8fcdc6..2bdb070707e4 100644
--- a/drivers/video/fbdev/aty/radeon_base.c
+++ b/drivers/video/fbdev/aty/radeon_base.c
@@ -85,10 +85,6 @@
#endif /* CONFIG_PPC */
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
#include <video/radeon.h>
#include <linux/radeonfb.h>
@@ -271,9 +267,7 @@ static bool mirror = 0;
static int panel_yres = 0;
static bool force_dfp = 0;
static bool force_measure_pll = 0;
-#ifdef CONFIG_MTRR
static bool nomtrr = 0;
-#endif
static bool force_sleep;
static bool ignore_devlist;
#ifdef CONFIG_PMAC_BACKLIGHT
@@ -2260,8 +2254,8 @@ static int radeonfb_pci_register(struct pci_dev *pdev,
rinfo->mapped_vram = min_t(unsigned long, MAX_MAPPED_VRAM, rinfo->video_ram);
do {
- rinfo->fb_base = ioremap (rinfo->fb_base_phys,
- rinfo->mapped_vram);
+ rinfo->fb_base = ioremap_wc(rinfo->fb_base_phys,
+ rinfo->mapped_vram);
} while (rinfo->fb_base == NULL &&
((rinfo->mapped_vram /= 2) >= MIN_MAPPED_VRAM));
@@ -2359,11 +2353,9 @@ static int radeonfb_pci_register(struct pci_dev *pdev,
goto err_unmap_fb;
}
-#ifdef CONFIG_MTRR
- rinfo->mtrr_hdl = nomtrr ? -1 : mtrr_add(rinfo->fb_base_phys,
- rinfo->video_ram,
- MTRR_TYPE_WRCOMB, 1);
-#endif
+ if (!nomtrr)
+ rinfo->wc_cookie = arch_phys_wc_add(rinfo->fb_base_phys,
+ rinfo->video_ram);
if (backlight)
radeonfb_bl_init(rinfo);
@@ -2428,12 +2420,7 @@ static void radeonfb_pci_unregister(struct pci_dev *pdev)
#endif
del_timer_sync(&rinfo->lvds_timer);
-
-#ifdef CONFIG_MTRR
- if (rinfo->mtrr_hdl >= 0)
- mtrr_del(rinfo->mtrr_hdl, 0, 0);
-#endif
-
+ arch_phys_wc_del(rinfo->wc_cookie);
unregister_framebuffer(info);
radeonfb_bl_exit(rinfo);
@@ -2489,10 +2476,8 @@ static int __init radeonfb_setup (char *options)
panel_yres = simple_strtoul((this_opt+11), NULL, 0);
} else if (!strncmp(this_opt, "backlight:", 10)) {
backlight = simple_strtoul(this_opt+10, NULL, 0);
-#ifdef CONFIG_MTRR
} else if (!strncmp(this_opt, "nomtrr", 6)) {
nomtrr = 1;
-#endif
} else if (!strncmp(this_opt, "nomodeset", 9)) {
nomodeset = 1;
} else if (!strncmp(this_opt, "force_measure_pll", 17)) {
@@ -2552,10 +2537,8 @@ module_param(monitor_layout, charp, 0);
MODULE_PARM_DESC(monitor_layout, "Specify monitor mapping (like XFree86)");
module_param(force_measure_pll, bool, 0);
MODULE_PARM_DESC(force_measure_pll, "Force measurement of PLL (debug)");
-#ifdef CONFIG_MTRR
module_param(nomtrr, bool, 0);
MODULE_PARM_DESC(nomtrr, "bool: disable use of MTRR registers");
-#endif
module_param(panel_yres, int, 0);
MODULE_PARM_DESC(panel_yres, "int: set panel yres");
module_param(mode_option, charp, 0);
diff --git a/drivers/video/fbdev/aty/radeonfb.h b/drivers/video/fbdev/aty/radeonfb.h
index 039def41c920..5bc1944ea1a9 100644
--- a/drivers/video/fbdev/aty/radeonfb.h
+++ b/drivers/video/fbdev/aty/radeonfb.h
@@ -340,7 +340,7 @@ struct radeonfb_info {
struct pll_info pll;
- int mtrr_hdl;
+ int wc_cookie;
u32 save_regs[100];
int asleep;
diff --git a/drivers/video/fbdev/core/Makefile b/drivers/video/fbdev/core/Makefile
index 67f28e20a892..23d86a8b7d7b 100644
--- a/drivers/video/fbdev/core/Makefile
+++ b/drivers/video/fbdev/core/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_FB_CMDLINE) += fb_cmdline.o
obj-$(CONFIG_FB) += fb.o
fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
modedb.o fbcvt.o
+fb-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o
fb-objs := $(fb-y)
obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o
@@ -14,4 +15,3 @@ obj-$(CONFIG_FB_SYS_IMAGEBLIT) += sysimgblt.o
obj-$(CONFIG_FB_SYS_FOPS) += fb_sys_fops.o
obj-$(CONFIG_FB_SVGALIB) += svgalib.o
obj-$(CONFIG_FB_DDC) += fb_ddc.o
-obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index d6cab1fd9a47..3fc63c208d08 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -242,5 +242,3 @@ void fb_deferred_io_cleanup(struct fb_info *info)
mutex_destroy(&fbdefio->lock);
}
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c
index 01ef1b953390..d787533d9c8b 100644
--- a/drivers/video/fbdev/core/fbmon.c
+++ b/drivers/video/fbdev/core/fbmon.c
@@ -1475,7 +1475,9 @@ int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
if (ret)
return ret;
- fb_videomode_from_videomode(&vm, fb);
+ ret = fb_videomode_from_videomode(&vm, fb);
+ if (ret)
+ return ret;
pr_debug("%s: got %dx%d display mode from %s\n",
of_node_full_name(np), vm.hactive, vm.vactive, np->name);
diff --git a/drivers/video/fbdev/gbefb.c b/drivers/video/fbdev/gbefb.c
index 6d9ef39810c8..b63d55f481fa 100644
--- a/drivers/video/fbdev/gbefb.c
+++ b/drivers/video/fbdev/gbefb.c
@@ -22,9 +22,6 @@
#include <linux/module.h>
#include <linux/io.h>
-#ifdef CONFIG_X86
-#include <asm/mtrr.h>
-#endif
#ifdef CONFIG_MIPS
#include <asm/addrspace.h>
#endif
@@ -38,6 +35,7 @@ static struct sgi_gbe *gbe;
struct gbefb_par {
struct fb_var_screeninfo var;
struct gbe_timing_info timing;
+ int wc_cookie;
int valid;
};
@@ -1175,8 +1173,8 @@ static int gbefb_probe(struct platform_device *p_dev)
if (gbe_mem_phys) {
/* memory was allocated at boot time */
- gbe_mem = devm_ioremap_nocache(&p_dev->dev, gbe_mem_phys,
- gbe_mem_size);
+ gbe_mem = devm_ioremap_wc(&p_dev->dev, gbe_mem_phys,
+ gbe_mem_size);
if (!gbe_mem) {
printk(KERN_ERR "gbefb: couldn't map framebuffer\n");
ret = -ENOMEM;
@@ -1187,8 +1185,8 @@ static int gbefb_probe(struct platform_device *p_dev)
} else {
/* try to allocate memory with the classical allocator
* this has high chance to fail on low memory machines */
- gbe_mem = dma_alloc_coherent(NULL, gbe_mem_size, &gbe_dma_addr,
- GFP_KERNEL);
+ gbe_mem = dma_alloc_writecombine(NULL, gbe_mem_size,
+ &gbe_dma_addr, GFP_KERNEL);
if (!gbe_mem) {
printk(KERN_ERR "gbefb: couldn't allocate framebuffer memory\n");
ret = -ENOMEM;
@@ -1198,9 +1196,8 @@ static int gbefb_probe(struct platform_device *p_dev)
gbe_mem_phys = (unsigned long) gbe_dma_addr;
}
-#ifdef CONFIG_X86
- mtrr_add(gbe_mem_phys, gbe_mem_size, MTRR_TYPE_WRCOMB, 1);
-#endif
+ par = info->par;
+ par->wc_cookie = arch_phys_wc_add(gbe_mem_phys, gbe_mem_size);
/* map framebuffer memory into tiles table */
for (i = 0; i < (gbe_mem_size >> TILE_SHIFT); i++)
@@ -1215,7 +1212,6 @@ static int gbefb_probe(struct platform_device *p_dev)
/* reset GBE */
gbe_reset();
- par = info->par;
/* turn on default video mode */
if (fb_find_mode(&par->var, info, mode_option, NULL, 0,
default_mode, 8) == 0)
@@ -1240,8 +1236,9 @@ static int gbefb_probe(struct platform_device *p_dev)
return 0;
out_gbe_unmap:
+ arch_phys_wc_del(par->wc_cookie);
if (gbe_dma_addr)
- dma_free_coherent(NULL, gbe_mem_size, gbe_mem, gbe_mem_phys);
+ dma_free_writecombine(NULL, gbe_mem_size, gbe_mem, gbe_mem_phys);
out_tiles_free:
dma_free_coherent(NULL, GBE_TLB_SIZE * sizeof(uint16_t),
(void *)gbe_tiles.cpu, gbe_tiles.dma);
@@ -1256,11 +1253,13 @@ out_release_framebuffer:
static int gbefb_remove(struct platform_device* p_dev)
{
struct fb_info *info = platform_get_drvdata(p_dev);
+ struct gbefb_par *par = info->par;
unregister_framebuffer(info);
gbe_turn_off();
+ arch_phys_wc_del(par->wc_cookie);
if (gbe_dma_addr)
- dma_free_coherent(NULL, gbe_mem_size, gbe_mem, gbe_mem_phys);
+ dma_free_writecombine(NULL, gbe_mem_size, gbe_mem, gbe_mem_phys);
dma_free_coherent(NULL, GBE_TLB_SIZE * sizeof(uint16_t),
(void *)gbe_tiles.cpu, gbe_tiles.dma);
release_mem_region(GBE_BASE, sizeof(struct sgi_gbe));
diff --git a/drivers/video/fbdev/geode/gxfb_core.c b/drivers/video/fbdev/geode/gxfb_core.c
index 124d7c7e2d14..ec9fc9ac23de 100644
--- a/drivers/video/fbdev/geode/gxfb_core.c
+++ b/drivers/video/fbdev/geode/gxfb_core.c
@@ -263,7 +263,8 @@ static int gxfb_map_video_memory(struct fb_info *info, struct pci_dev *dev)
info->fix.smem_start = pci_resource_start(dev, 0);
info->fix.smem_len = vram ? vram : gx_frame_buffer_size();
- info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
+ info->screen_base = ioremap_wc(info->fix.smem_start,
+ info->fix.smem_len);
if (!info->screen_base)
return -ENOMEM;
diff --git a/drivers/video/fbdev/hpfb.c b/drivers/video/fbdev/hpfb.c
index a1b7e5fa9b09..9476d196f510 100644
--- a/drivers/video/fbdev/hpfb.c
+++ b/drivers/video/fbdev/hpfb.c
@@ -241,8 +241,8 @@ static int hpfb_init_one(unsigned long phys_base, unsigned long virt_base)
fb_info.fix.line_length = fb_width;
fb_height = (in_8(fb_regs + HPFB_FBHMSB) << 8) | in_8(fb_regs + HPFB_FBHLSB);
fb_info.fix.smem_len = fb_width * fb_height;
- fb_start = (unsigned long)ioremap_writethrough(fb_info.fix.smem_start,
- fb_info.fix.smem_len);
+ fb_start = (unsigned long)ioremap_wt(fb_info.fix.smem_start,
+ fb_info.fix.smem_len);
hpfb_defined.xres = (in_8(fb_regs + HPFB_DWMSB) << 8) | in_8(fb_regs + HPFB_DWLSB);
hpfb_defined.yres = (in_8(fb_regs + HPFB_DHMSB) << 8) | in_8(fb_regs + HPFB_DHLSB);
hpfb_defined.xres_virtual = hpfb_defined.xres;
diff --git a/drivers/video/fbdev/i810/i810.h b/drivers/video/fbdev/i810/i810.h
index 1414b73ac55b..7b1c002bfb08 100644
--- a/drivers/video/fbdev/i810/i810.h
+++ b/drivers/video/fbdev/i810/i810.h
@@ -199,7 +199,6 @@
#define HAS_FONTCACHE 8
/* driver flags */
-#define HAS_MTRR 1
#define HAS_ACCELERATION 2
#define ALWAYS_SYNC 4
#define LOCKUP 8
@@ -281,7 +280,7 @@ struct i810fb_par {
u32 ovract;
u32 cur_state;
u32 ddc_num;
- int mtrr_reg;
+ int wc_cookie;
u16 bltcntl;
u8 interlace;
};
diff --git a/drivers/video/fbdev/i810/i810_main.c b/drivers/video/fbdev/i810/i810_main.c
index bb674e431741..025b882a4826 100644
--- a/drivers/video/fbdev/i810/i810_main.c
+++ b/drivers/video/fbdev/i810/i810_main.c
@@ -41,6 +41,7 @@
#include <linux/resource.h>
#include <linux/unistd.h>
#include <linux/console.h>
+#include <linux/io.h>
#include <asm/io.h>
#include <asm/div64.h>
@@ -1816,7 +1817,9 @@ static void i810_init_device(struct i810fb_par *par)
u8 reg;
u8 __iomem *mmio = par->mmio_start_virtual;
- if (mtrr) set_mtrr(par);
+ if (mtrr)
+ par->wc_cookie= arch_phys_wc_add((u32) par->aperture.physical,
+ par->aperture.size);
i810_init_cursor(par);
@@ -1865,8 +1868,8 @@ static int i810_allocate_pci_resource(struct i810fb_par *par,
}
par->res_flags |= FRAMEBUFFER_REQ;
- par->aperture.virtual = ioremap_nocache(par->aperture.physical,
- par->aperture.size);
+ par->aperture.virtual = ioremap_wc(par->aperture.physical,
+ par->aperture.size);
if (!par->aperture.virtual) {
printk("i810fb_init: cannot remap framebuffer region\n");
return -ENODEV;
@@ -2096,7 +2099,7 @@ static void i810fb_release_resource(struct fb_info *info,
struct i810fb_par *par)
{
struct gtt_data *gtt = &par->i810_gtt;
- unset_mtrr(par);
+ arch_phys_wc_del(par->wc_cookie);
i810_delete_i2c_busses(par);
diff --git a/drivers/video/fbdev/i810/i810_main.h b/drivers/video/fbdev/i810/i810_main.h
index a25afaa534ba..7bfaaad1d0fa 100644
--- a/drivers/video/fbdev/i810/i810_main.h
+++ b/drivers/video/fbdev/i810/i810_main.h
@@ -60,32 +60,6 @@ static inline void flush_cache(void)
#define flush_cache() do { } while(0)
#endif
-#ifdef CONFIG_MTRR
-
-#include <asm/mtrr.h>
-
-static inline void set_mtrr(struct i810fb_par *par)
-{
- par->mtrr_reg = mtrr_add((u32) par->aperture.physical,
- par->aperture.size, MTRR_TYPE_WRCOMB, 1);
- if (par->mtrr_reg < 0) {
- printk(KERN_ERR "set_mtrr: unable to set MTRR\n");
- return;
- }
- par->dev_flags |= HAS_MTRR;
-}
-static inline void unset_mtrr(struct i810fb_par *par)
-{
- if (par->dev_flags & HAS_MTRR)
- mtrr_del(par->mtrr_reg, (u32) par->aperture.physical,
- par->aperture.size);
-}
-#else
-#define set_mtrr(x) printk("set_mtrr: MTRR is disabled in the kernel\n")
-
-#define unset_mtrr(x) do { } while (0)
-#endif /* CONFIG_MTRR */
-
#ifdef CONFIG_FB_I810_GTF
#define IS_DVT (0)
#else
diff --git a/drivers/video/fbdev/imxfb.c b/drivers/video/fbdev/imxfb.c
index 84d1d29e532c..cee88603efc9 100644
--- a/drivers/video/fbdev/imxfb.c
+++ b/drivers/video/fbdev/imxfb.c
@@ -170,7 +170,7 @@ struct imxfb_info {
struct regulator *lcd_pwr;
};
-static struct platform_device_id imxfb_devtype[] = {
+static const struct platform_device_id imxfb_devtype[] = {
{
.name = "imx1-fb",
.driver_data = IMX1_FB,
diff --git a/drivers/video/fbdev/intelfb/intelfb.h b/drivers/video/fbdev/intelfb/intelfb.h
index 6b51175629c7..37f8339ea88c 100644
--- a/drivers/video/fbdev/intelfb/intelfb.h
+++ b/drivers/video/fbdev/intelfb/intelfb.h
@@ -285,9 +285,7 @@ struct intelfb_info {
/* use a gart reserved fb mem */
u8 fbmem_gart;
- /* mtrr support */
- int mtrr_reg;
- u32 has_mtrr;
+ int wc_cookie;
/* heap data */
struct intelfb_heap_data aperture;
diff --git a/drivers/video/fbdev/intelfb/intelfbdrv.c b/drivers/video/fbdev/intelfb/intelfbdrv.c
index b847d530471a..bbec737eef30 100644
--- a/drivers/video/fbdev/intelfb/intelfbdrv.c
+++ b/drivers/video/fbdev/intelfb/intelfbdrv.c
@@ -124,10 +124,6 @@
#include <asm/io.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
#include "intelfb.h"
#include "intelfbhw.h"
#include "../edid.h"
@@ -411,33 +407,6 @@ module_init(intelfb_init);
module_exit(intelfb_exit);
/***************************************************************
- * mtrr support functions *
- ***************************************************************/
-
-#ifdef CONFIG_MTRR
-static inline void set_mtrr(struct intelfb_info *dinfo)
-{
- dinfo->mtrr_reg = mtrr_add(dinfo->aperture.physical,
- dinfo->aperture.size, MTRR_TYPE_WRCOMB, 1);
- if (dinfo->mtrr_reg < 0) {
- ERR_MSG("unable to set MTRR\n");
- return;
- }
- dinfo->has_mtrr = 1;
-}
-static inline void unset_mtrr(struct intelfb_info *dinfo)
-{
- if (dinfo->has_mtrr)
- mtrr_del(dinfo->mtrr_reg, dinfo->aperture.physical,
- dinfo->aperture.size);
-}
-#else
-#define set_mtrr(x) WRN_MSG("MTRR is disabled in the kernel\n")
-
-#define unset_mtrr(x) do { } while (0)
-#endif /* CONFIG_MTRR */
-
-/***************************************************************
* driver init / cleanup *
***************************************************************/
@@ -456,7 +425,7 @@ static void cleanup(struct intelfb_info *dinfo)
if (dinfo->registered)
unregister_framebuffer(dinfo->info);
- unset_mtrr(dinfo);
+ arch_phys_wc_del(dinfo->wc_cookie);
if (dinfo->fbmem_gart && dinfo->gtt_fb_mem) {
agp_unbind_memory(dinfo->gtt_fb_mem);
@@ -675,7 +644,7 @@ static int intelfb_pci_register(struct pci_dev *pdev,
/* Allocate memories (which aren't stolen) */
/* Map the fb and MMIO regions */
/* ioremap only up to the end of used aperture */
- dinfo->aperture.virtual = (u8 __iomem *)ioremap_nocache
+ dinfo->aperture.virtual = (u8 __iomem *)ioremap_wc
(dinfo->aperture.physical, ((offset + dinfo->fb.offset) << 12)
+ dinfo->fb.size);
if (!dinfo->aperture.virtual) {
@@ -772,7 +741,8 @@ static int intelfb_pci_register(struct pci_dev *pdev,
agp_backend_release(bridge);
if (mtrr)
- set_mtrr(dinfo);
+ dinfo->wc_cookie = arch_phys_wc_add(dinfo->aperture.physical,
+ dinfo->aperture.size);
DBG_MSG("fb: 0x%x(+ 0x%x)/0x%x (0x%p)\n",
dinfo->fb.physical, dinfo->fb.offset, dinfo->fb.size,
diff --git a/drivers/video/fbdev/matrox/matroxfb_base.c b/drivers/video/fbdev/matrox/matroxfb_base.c
index 62539ca1cfa9..11eb094396ae 100644
--- a/drivers/video/fbdev/matrox/matroxfb_base.c
+++ b/drivers/video/fbdev/matrox/matroxfb_base.c
@@ -370,12 +370,9 @@ static void matroxfb_remove(struct matrox_fb_info *minfo, int dummy)
matroxfb_unregister_device(minfo);
unregister_framebuffer(&minfo->fbcon);
matroxfb_g450_shutdown(minfo);
-#ifdef CONFIG_MTRR
- if (minfo->mtrr.vram_valid)
- mtrr_del(minfo->mtrr.vram, minfo->video.base, minfo->video.len);
-#endif
- mga_iounmap(minfo->mmio.vbase);
- mga_iounmap(minfo->video.vbase);
+ arch_phys_wc_del(minfo->wc_cookie);
+ iounmap(minfo->mmio.vbase.vaddr);
+ iounmap(minfo->video.vbase.vaddr);
release_mem_region(minfo->video.base, minfo->video.len_maximum);
release_mem_region(minfo->mmio.base, 16384);
kfree(minfo);
@@ -591,12 +588,8 @@ static int matroxfb_decode_var(const struct matrox_fb_info *minfo,
unsigned int max_yres;
while (m1) {
- int t;
-
while (m2 >= m1) m2 -= m1;
- t = m1;
- m1 = m2;
- m2 = t;
+ swap(m1, m2);
}
m2 = linelen * PAGE_SIZE / m2;
*ydstorg = m2 = 0x400000 % m2;
@@ -1256,9 +1249,7 @@ static int nobios; /* "matroxfb:nobios" */
static int noinit = 1; /* "matroxfb:init" */
static int inverse; /* "matroxfb:inverse" */
static int sgram; /* "matroxfb:sgram" */
-#ifdef CONFIG_MTRR
static int mtrr = 1; /* "matroxfb:nomtrr" */
-#endif
static int grayscale; /* "matroxfb:grayscale" */
static int dev = -1; /* "matroxfb:dev:xxxxx" */
static unsigned int vesa = ~0; /* "matroxfb:vesa:xxxxx" */
@@ -1717,14 +1708,17 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b)
if (mem && (mem < memsize))
memsize = mem;
err = -ENOMEM;
- if (mga_ioremap(ctrlptr_phys, 16384, MGA_IOREMAP_MMIO, &minfo->mmio.vbase)) {
+
+ minfo->mmio.vbase.vaddr = ioremap_nocache(ctrlptr_phys, 16384);
+ if (!minfo->mmio.vbase.vaddr) {
printk(KERN_ERR "matroxfb: cannot ioremap(%lX, 16384), matroxfb disabled\n", ctrlptr_phys);
goto failVideoMR;
}
minfo->mmio.base = ctrlptr_phys;
minfo->mmio.len = 16384;
minfo->video.base = video_base_phys;
- if (mga_ioremap(video_base_phys, memsize, MGA_IOREMAP_FB, &minfo->video.vbase)) {
+ minfo->video.vbase.vaddr = ioremap_wc(video_base_phys, memsize);
+ if (!minfo->video.vbase.vaddr) {
printk(KERN_ERR "matroxfb: cannot ioremap(%lX, %d), matroxfb disabled\n",
video_base_phys, memsize);
goto failCtrlIO;
@@ -1772,13 +1766,9 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b)
minfo->video.len_usable = minfo->video.len;
if (minfo->video.len_usable > b->base->maxdisplayable)
minfo->video.len_usable = b->base->maxdisplayable;
-#ifdef CONFIG_MTRR
- if (mtrr) {
- minfo->mtrr.vram = mtrr_add(video_base_phys, minfo->video.len, MTRR_TYPE_WRCOMB, 1);
- minfo->mtrr.vram_valid = 1;
- printk(KERN_INFO "matroxfb: MTRR's turned on\n");
- }
-#endif /* CONFIG_MTRR */
+ if (mtrr)
+ minfo->wc_cookie = arch_phys_wc_add(video_base_phys,
+ minfo->video.len);
if (!minfo->devflags.novga)
request_region(0x3C0, 32, "matrox");
@@ -1947,9 +1937,9 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b)
return 0;
failVideoIO:;
matroxfb_g450_shutdown(minfo);
- mga_iounmap(minfo->video.vbase);
+ iounmap(minfo->video.vbase.vaddr);
failCtrlIO:;
- mga_iounmap(minfo->mmio.vbase);
+ iounmap(minfo->mmio.vbase.vaddr);
failVideoMR:;
release_mem_region(video_base_phys, minfo->video.len_maximum);
failCtrlMR:;
@@ -2443,10 +2433,8 @@ static int __init matroxfb_setup(char *options) {
nobios = !value;
else if (!strcmp(this_opt, "init"))
noinit = !value;
-#ifdef CONFIG_MTRR
else if (!strcmp(this_opt, "mtrr"))
mtrr = value;
-#endif
else if (!strcmp(this_opt, "inv24"))
inv24 = value;
else if (!strcmp(this_opt, "cross4MB"))
@@ -2515,10 +2503,8 @@ module_param(noinit, int, 0);
MODULE_PARM_DESC(noinit, "Disables W/SG/SD-RAM and bus interface initialization (0 or 1=do not initialize) (default=0)");
module_param(memtype, int, 0);
MODULE_PARM_DESC(memtype, "Memory type for G200/G400 (see Documentation/fb/matroxfb.txt for explanation) (default=3 for G200, 0 for G400)");
-#ifdef CONFIG_MTRR
module_param(mtrr, int, 0);
MODULE_PARM_DESC(mtrr, "This speeds up video memory accesses (0=disabled or 1) (default=1)");
-#endif
module_param(sgram, int, 0);
MODULE_PARM_DESC(sgram, "Indicates that G100/G200/G400 has SGRAM memory (0=SDRAM, 1=SGRAM) (default=0)");
module_param(inv24, int, 0);
diff --git a/drivers/video/fbdev/matrox/matroxfb_base.h b/drivers/video/fbdev/matrox/matroxfb_base.h
index 89a8a89a5eb2..09b02cd1eb0e 100644
--- a/drivers/video/fbdev/matrox/matroxfb_base.h
+++ b/drivers/video/fbdev/matrox/matroxfb_base.h
@@ -44,9 +44,6 @@
#include <asm/io.h>
#include <asm/unaligned.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
#if defined(CONFIG_PPC_PMAC)
#include <asm/prom.h>
@@ -187,23 +184,6 @@ static inline void __iomem* vaddr_va(vaddr_t va) {
return va.vaddr;
}
-#define MGA_IOREMAP_NORMAL 0
-#define MGA_IOREMAP_NOCACHE 1
-
-#define MGA_IOREMAP_FB MGA_IOREMAP_NOCACHE
-#define MGA_IOREMAP_MMIO MGA_IOREMAP_NOCACHE
-static inline int mga_ioremap(unsigned long phys, unsigned long size, int flags, vaddr_t* virt) {
- if (flags & MGA_IOREMAP_NOCACHE)
- virt->vaddr = ioremap_nocache(phys, size);
- else
- virt->vaddr = ioremap(phys, size);
- return (virt->vaddr == NULL); /* 0, !0... 0, error_code in future */
-}
-
-static inline void mga_iounmap(vaddr_t va) {
- iounmap(va.vaddr);
-}
-
struct my_timming {
unsigned int pixclock;
int mnp;
@@ -449,12 +429,7 @@ struct matrox_fb_info {
int plnwt;
int srcorg;
} capable;
-#ifdef CONFIG_MTRR
- struct {
- int vram;
- int vram_valid;
- } mtrr;
-#endif
+ int wc_cookie;
struct {
int precise_width;
int mga_24bpp_fix;
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
deleted file mode 100644
index 802d6ae523fb..000000000000
--- a/drivers/video/fbdev/msm/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-
-# core framebuffer
-#
-obj-y := msm_fb.o
-
-# MDP DMA/PPP engine
-#
-obj-y += mdp.o mdp_scale_tables.o mdp_ppp.o
-
-# MDDI interface
-#
-obj-y += mddi.o
-
-# MDDI client/panel drivers
-#
-obj-y += mddi_client_dummy.o
-obj-y += mddi_client_toshiba.o
-obj-y += mddi_client_nt35399.o
-
diff --git a/drivers/video/fbdev/msm/mddi.c b/drivers/video/fbdev/msm/mddi.c
deleted file mode 100644
index e0f8011a3c4b..000000000000
--- a/drivers/video/fbdev/msm/mddi.c
+++ /dev/null
@@ -1,821 +0,0 @@
-/*
- * MSM MDDI Transport
- *
- * Copyright (C) 2007 Google Incorporated
- * Copyright (C) 2007 QUALCOMM Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/gfp.h>
-#include <linux/spinlock.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/sched.h>
-#include <linux/platform_data/video-msm_fb.h>
-#include "mddi_hw.h"
-
-#define FLAG_DISABLE_HIBERNATION 0x0001
-#define FLAG_HAVE_CAPS 0x0002
-#define FLAG_HAS_VSYNC_IRQ 0x0004
-#define FLAG_HAVE_STATUS 0x0008
-
-#define CMD_GET_CLIENT_CAP 0x0601
-#define CMD_GET_CLIENT_STATUS 0x0602
-
-union mddi_rev {
- unsigned char raw[MDDI_REV_BUFFER_SIZE];
- struct mddi_rev_packet hdr;
- struct mddi_client_status status;
- struct mddi_client_caps caps;
- struct mddi_register_access reg;
-};
-
-struct reg_read_info {
- struct completion done;
- uint32_t reg;
- uint32_t status;
- uint32_t result;
-};
-
-struct mddi_info {
- uint16_t flags;
- uint16_t version;
- char __iomem *base;
- int irq;
- struct clk *clk;
- struct msm_mddi_client_data client_data;
-
- /* buffer for rev encap packets */
- void *rev_data;
- dma_addr_t rev_addr;
- struct mddi_llentry *reg_write_data;
- dma_addr_t reg_write_addr;
- struct mddi_llentry *reg_read_data;
- dma_addr_t reg_read_addr;
- size_t rev_data_curr;
-
- spinlock_t int_lock;
- uint32_t int_enable;
- uint32_t got_int;
- wait_queue_head_t int_wait;
-
- struct mutex reg_write_lock;
- struct mutex reg_read_lock;
- struct reg_read_info *reg_read;
-
- struct mddi_client_caps caps;
- struct mddi_client_status status;
-
- void (*power_client)(struct msm_mddi_client_data *, int);
-
- /* client device published to bind us to the
- * appropriate mddi_client driver
- */
- char client_name[20];
-
- struct platform_device client_pdev;
-};
-
-static void mddi_init_rev_encap(struct mddi_info *mddi);
-
-#define mddi_readl(r) readl(mddi->base + (MDDI_##r))
-#define mddi_writel(v, r) writel((v), mddi->base + (MDDI_##r))
-
-void mddi_activate_link(struct msm_mddi_client_data *cdata)
-{
- struct mddi_info *mddi = container_of(cdata, struct mddi_info,
- client_data);
-
- mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
-}
-
-static void mddi_handle_link_list_done(struct mddi_info *mddi)
-{
-}
-
-static void mddi_reset_rev_encap_ptr(struct mddi_info *mddi)
-{
- printk(KERN_INFO "mddi: resetting rev ptr\n");
- mddi->rev_data_curr = 0;
- mddi_writel(mddi->rev_addr, REV_PTR);
- mddi_writel(mddi->rev_addr, REV_PTR);
- mddi_writel(MDDI_CMD_FORCE_NEW_REV_PTR, CMD);
-}
-
-static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev)
-{
- int i;
- struct reg_read_info *ri;
-
- if ((rev->hdr.length <= MDDI_REV_BUFFER_SIZE - 2) &&
- (rev->hdr.length >= sizeof(struct mddi_rev_packet) - 2)) {
-
- switch (rev->hdr.type) {
- case TYPE_CLIENT_CAPS:
- memcpy(&mddi->caps, &rev->caps,
- sizeof(struct mddi_client_caps));
- mddi->flags |= FLAG_HAVE_CAPS;
- wake_up(&mddi->int_wait);
- break;
- case TYPE_CLIENT_STATUS:
- memcpy(&mddi->status, &rev->status,
- sizeof(struct mddi_client_status));
- mddi->flags |= FLAG_HAVE_STATUS;
- wake_up(&mddi->int_wait);
- break;
- case TYPE_REGISTER_ACCESS:
- ri = mddi->reg_read;
- if (ri == 0) {
- printk(KERN_INFO "rev: got reg %x = %x without "
- " pending read\n",
- rev->reg.register_address,
- rev->reg.register_data_list);
- break;
- }
- if (ri->reg != rev->reg.register_address) {
- printk(KERN_INFO "rev: got reg %x = %x for "
- "wrong register, expected "
- "%x\n",
- rev->reg.register_address,
- rev->reg.register_data_list, ri->reg);
- break;
- }
- mddi->reg_read = NULL;
- ri->status = 0;
- ri->result = rev->reg.register_data_list;
- complete(&ri->done);
- break;
- default:
- printk(KERN_INFO "rev: unknown reverse packet: "
- "len=%04x type=%04x CURR_REV_PTR=%x\n",
- rev->hdr.length, rev->hdr.type,
- mddi_readl(CURR_REV_PTR));
- for (i = 0; i < rev->hdr.length + 2; i++) {
- if ((i % 16) == 0)
- printk(KERN_INFO "\n");
- printk(KERN_INFO " %02x", rev->raw[i]);
- }
- printk(KERN_INFO "\n");
- mddi_reset_rev_encap_ptr(mddi);
- }
- } else {
- printk(KERN_INFO "bad rev length, %d, CURR_REV_PTR %x\n",
- rev->hdr.length, mddi_readl(CURR_REV_PTR));
- mddi_reset_rev_encap_ptr(mddi);
- }
-}
-
-static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask);
-
-static void mddi_handle_rev_data_avail(struct mddi_info *mddi)
-{
- uint32_t rev_data_count;
- uint32_t rev_crc_err_count;
- struct reg_read_info *ri;
- size_t prev_offset;
- uint16_t length;
-
- union mddi_rev *crev = mddi->rev_data + mddi->rev_data_curr;
-
- /* clear the interrupt */
- mddi_writel(MDDI_INT_REV_DATA_AVAIL, INT);
- rev_data_count = mddi_readl(REV_PKT_CNT);
- rev_crc_err_count = mddi_readl(REV_CRC_ERR);
- if (rev_data_count > 1)
- printk(KERN_INFO "rev_data_count %d\n", rev_data_count);
-
- if (rev_crc_err_count) {
- printk(KERN_INFO "rev_crc_err_count %d, INT %x\n",
- rev_crc_err_count, mddi_readl(INT));
- ri = mddi->reg_read;
- if (ri == 0) {
- printk(KERN_INFO "rev: got crc error without pending "
- "read\n");
- } else {
- mddi->reg_read = NULL;
- ri->status = -EIO;
- ri->result = -1;
- complete(&ri->done);
- }
- }
-
- if (rev_data_count == 0)
- return;
-
- prev_offset = mddi->rev_data_curr;
-
- length = *((uint8_t *)mddi->rev_data + mddi->rev_data_curr);
- mddi->rev_data_curr++;
- if (mddi->rev_data_curr == MDDI_REV_BUFFER_SIZE)
- mddi->rev_data_curr = 0;
- length += *((uint8_t *)mddi->rev_data + mddi->rev_data_curr) << 8;
- mddi->rev_data_curr += 1 + length;
- if (mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE)
- mddi->rev_data_curr =
- mddi->rev_data_curr % MDDI_REV_BUFFER_SIZE;
-
- if (length > MDDI_REV_BUFFER_SIZE - 2) {
- printk(KERN_INFO "mddi: rev data length greater than buffer"
- "size\n");
- mddi_reset_rev_encap_ptr(mddi);
- return;
- }
-
- if (prev_offset + 2 + length >= MDDI_REV_BUFFER_SIZE) {
- union mddi_rev tmprev;
- size_t rem = MDDI_REV_BUFFER_SIZE - prev_offset;
- memcpy(&tmprev.raw[0], mddi->rev_data + prev_offset, rem);
- memcpy(&tmprev.raw[rem], mddi->rev_data, 2 + length - rem);
- mddi_handle_rev_data(mddi, &tmprev);
- } else {
- mddi_handle_rev_data(mddi, crev);
- }
-
- if (prev_offset < MDDI_REV_BUFFER_SIZE / 2 &&
- mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE / 2) {
- mddi_writel(mddi->rev_addr, REV_PTR);
- }
-}
-
-static irqreturn_t mddi_isr(int irq, void *data)
-{
- struct msm_mddi_client_data *cdata = data;
- struct mddi_info *mddi = container_of(cdata, struct mddi_info,
- client_data);
- uint32_t active, status;
-
- spin_lock(&mddi->int_lock);
-
- active = mddi_readl(INT);
- status = mddi_readl(STAT);
-
- mddi_writel(active, INT);
-
- /* ignore any interrupts we have disabled */
- active &= mddi->int_enable;
-
- mddi->got_int |= active;
- wake_up(&mddi->int_wait);
-
- if (active & MDDI_INT_PRI_LINK_LIST_DONE) {
- mddi->int_enable &= (~MDDI_INT_PRI_LINK_LIST_DONE);
- mddi_handle_link_list_done(mddi);
- }
- if (active & MDDI_INT_REV_DATA_AVAIL)
- mddi_handle_rev_data_avail(mddi);
-
- if (active & ~MDDI_INT_NEED_CLEAR)
- mddi->int_enable &= ~(active & ~MDDI_INT_NEED_CLEAR);
-
- if (active & MDDI_INT_LINK_ACTIVE) {
- mddi->int_enable &= (~MDDI_INT_LINK_ACTIVE);
- mddi->int_enable |= MDDI_INT_IN_HIBERNATION;
- }
-
- if (active & MDDI_INT_IN_HIBERNATION) {
- mddi->int_enable &= (~MDDI_INT_IN_HIBERNATION);
- mddi->int_enable |= MDDI_INT_LINK_ACTIVE;
- }
-
- mddi_writel(mddi->int_enable, INTEN);
- spin_unlock(&mddi->int_lock);
-
- return IRQ_HANDLED;
-}
-
-static long mddi_wait_interrupt_timeout(struct mddi_info *mddi,
- uint32_t intmask, int timeout)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&mddi->int_lock, irq_flags);
- mddi->got_int &= ~intmask;
- mddi->int_enable |= intmask;
- mddi_writel(mddi->int_enable, INTEN);
- spin_unlock_irqrestore(&mddi->int_lock, irq_flags);
- return wait_event_timeout(mddi->int_wait, mddi->got_int & intmask,
- timeout);
-}
-
-static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask)
-{
- if (mddi_wait_interrupt_timeout(mddi, intmask, HZ/10) == 0)
- printk(KERN_INFO "mddi_wait_interrupt %d, timeout "
- "waiting for %x, INT = %x, STAT = %x gotint = %x\n",
- current->pid, intmask, mddi_readl(INT), mddi_readl(STAT),
- mddi->got_int);
-}
-
-static void mddi_init_rev_encap(struct mddi_info *mddi)
-{
- memset(mddi->rev_data, 0xee, MDDI_REV_BUFFER_SIZE);
- mddi_writel(mddi->rev_addr, REV_PTR);
- mddi_writel(MDDI_CMD_FORCE_NEW_REV_PTR, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-}
-
-void mddi_set_auto_hibernate(struct msm_mddi_client_data *cdata, int on)
-{
- struct mddi_info *mddi = container_of(cdata, struct mddi_info,
- client_data);
- mddi_writel(MDDI_CMD_POWERDOWN, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_IN_HIBERNATION);
- mddi_writel(MDDI_CMD_HIBERNATE | !!on, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-}
-
-
-static uint16_t mddi_init_registers(struct mddi_info *mddi)
-{
- mddi_writel(0x0001, VERSION);
- mddi_writel(MDDI_HOST_BYTES_PER_SUBFRAME, BPS);
- mddi_writel(0x0003, SPM); /* subframes per media */
- mddi_writel(0x0005, TA1_LEN);
- mddi_writel(MDDI_HOST_TA2_LEN, TA2_LEN);
- mddi_writel(0x0096, DRIVE_HI);
- /* 0x32 normal, 0x50 for Toshiba display */
- mddi_writel(0x0050, DRIVE_LO);
- mddi_writel(0x003C, DISP_WAKE); /* wakeup counter */
- mddi_writel(MDDI_HOST_REV_RATE_DIV, REV_RATE_DIV);
-
- mddi_writel(MDDI_REV_BUFFER_SIZE, REV_SIZE);
- mddi_writel(MDDI_MAX_REV_PKT_SIZE, REV_ENCAP_SZ);
-
- /* disable periodic rev encap */
- mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
- if (mddi_readl(PAD_CTL) == 0) {
- /* If we are turning on band gap, need to wait 5us before
- * turning on the rest of the PAD */
- mddi_writel(0x08000, PAD_CTL);
- udelay(5);
- }
-
- /* Recommendation from PAD hw team */
- mddi_writel(0xa850f, PAD_CTL);
-
-
- /* Need an even number for counts */
- mddi_writel(0x60006, DRIVER_START_CNT);
-
- mddi_set_auto_hibernate(&mddi->client_data, 0);
-
- mddi_writel(MDDI_CMD_DISP_IGNORE, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
- mddi_init_rev_encap(mddi);
- return mddi_readl(CORE_VER) & 0xffff;
-}
-
-static void mddi_suspend(struct msm_mddi_client_data *cdata)
-{
- struct mddi_info *mddi = container_of(cdata, struct mddi_info,
- client_data);
- /* turn off the client */
- if (mddi->power_client)
- mddi->power_client(&mddi->client_data, 0);
- /* turn off the link */
- mddi_writel(MDDI_CMD_RESET, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- /* turn off the clock */
- clk_disable(mddi->clk);
-}
-
-static void mddi_resume(struct msm_mddi_client_data *cdata)
-{
- struct mddi_info *mddi = container_of(cdata, struct mddi_info,
- client_data);
- mddi_set_auto_hibernate(&mddi->client_data, 0);
- /* turn on the client */
- if (mddi->power_client)
- mddi->power_client(&mddi->client_data, 1);
- /* turn on the clock */
- clk_enable(mddi->clk);
- /* set up the local registers */
- mddi->rev_data_curr = 0;
- mddi_init_registers(mddi);
- mddi_writel(mddi->int_enable, INTEN);
- mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
- mddi_writel(MDDI_CMD_SEND_RTD, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- mddi_set_auto_hibernate(&mddi->client_data, 1);
-}
-
-static int mddi_get_client_caps(struct mddi_info *mddi)
-{
- int i, j;
-
- /* clear any stale interrupts */
- mddi_writel(0xffffffff, INT);
-
- mddi->int_enable = MDDI_INT_LINK_ACTIVE |
- MDDI_INT_IN_HIBERNATION |
- MDDI_INT_PRI_LINK_LIST_DONE |
- MDDI_INT_REV_DATA_AVAIL |
- MDDI_INT_REV_OVERFLOW |
- MDDI_INT_REV_OVERWRITE |
- MDDI_INT_RTD_FAILURE;
- mddi_writel(mddi->int_enable, INTEN);
-
- mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
- for (j = 0; j < 3; j++) {
- /* the toshiba vga panel does not respond to get
- * caps unless you SEND_RTD, but the first SEND_RTD
- * will fail...
- */
- for (i = 0; i < 4; i++) {
- uint32_t stat;
-
- mddi_writel(MDDI_CMD_SEND_RTD, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- stat = mddi_readl(STAT);
- printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, "
- "rtd val %x\n", mddi_readl(INT), stat,
- mddi_readl(RTD_VAL));
- if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0)
- break;
- msleep(1);
- }
-
- mddi_writel(CMD_GET_CLIENT_CAP, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS,
- HZ / 100);
-
- if (mddi->flags & FLAG_HAVE_CAPS)
- break;
- printk(KERN_INFO "mddi_init, timeout waiting for caps\n");
- }
- return mddi->flags & FLAG_HAVE_CAPS;
-}
-
-/* link must be active when this is called */
-int mddi_check_status(struct mddi_info *mddi)
-{
- int ret = -1, retry = 3;
- mutex_lock(&mddi->reg_read_lock);
- mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
- do {
- mddi->flags &= ~FLAG_HAVE_STATUS;
- mddi_writel(CMD_GET_CLIENT_STATUS, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- wait_event_timeout(mddi->int_wait,
- mddi->flags & FLAG_HAVE_STATUS,
- HZ / 100);
-
- if (mddi->flags & FLAG_HAVE_STATUS) {
- if (mddi->status.crc_error_count)
- printk(KERN_INFO "mddi status: crc_error "
- "count: %d\n",
- mddi->status.crc_error_count);
- else
- ret = 0;
- break;
- } else
- printk(KERN_INFO "mddi status: failed to get client "
- "status\n");
- mddi_writel(MDDI_CMD_SEND_RTD, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- } while (--retry);
-
- mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- mutex_unlock(&mddi->reg_read_lock);
- return ret;
-}
-
-
-void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val,
- uint32_t reg)
-{
- struct mddi_info *mddi = container_of(cdata, struct mddi_info,
- client_data);
- struct mddi_llentry *ll;
- struct mddi_register_access *ra;
-
- mutex_lock(&mddi->reg_write_lock);
-
- ll = mddi->reg_write_data;
-
- ra = &(ll->u.r);
- ra->length = 14 + 4;
- ra->type = TYPE_REGISTER_ACCESS;
- ra->client_id = 0;
- ra->read_write_info = MDDI_WRITE | 1;
- ra->crc16 = 0;
-
- ra->register_address = reg;
- ra->register_data_list = val;
-
- ll->flags = 1;
- ll->header_count = 14;
- ll->data_count = 4;
- ll->data = mddi->reg_write_addr + offsetof(struct mddi_llentry,
- u.r.register_data_list);
- ll->next = 0;
- ll->reserved = 0;
-
- mddi_writel(mddi->reg_write_addr, PRI_PTR);
-
- mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE);
- mutex_unlock(&mddi->reg_write_lock);
-}
-
-uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg)
-{
- struct mddi_info *mddi = container_of(cdata, struct mddi_info,
- client_data);
- struct mddi_llentry *ll;
- struct mddi_register_access *ra;
- struct reg_read_info ri;
- unsigned s;
- int retry_count = 2;
- unsigned long irq_flags;
-
- mutex_lock(&mddi->reg_read_lock);
-
- ll = mddi->reg_read_data;
-
- ra = &(ll->u.r);
- ra->length = 14;
- ra->type = TYPE_REGISTER_ACCESS;
- ra->client_id = 0;
- ra->read_write_info = MDDI_READ | 1;
- ra->crc16 = 0;
-
- ra->register_address = reg;
-
- ll->flags = 0x11;
- ll->header_count = 14;
- ll->data_count = 0;
- ll->data = 0;
- ll->next = 0;
- ll->reserved = 0;
-
- s = mddi_readl(STAT);
-
- ri.reg = reg;
- ri.status = -1;
-
- do {
- init_completion(&ri.done);
- mddi->reg_read = &ri;
- mddi_writel(mddi->reg_read_addr, PRI_PTR);
-
- mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE);
-
- /* Enable Periodic Reverse Encapsulation. */
- mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- if (wait_for_completion_timeout(&ri.done, HZ/10) == 0 &&
- !ri.done.done) {
- printk(KERN_INFO "mddi_remote_read(%x) timeout "
- "(%d %d %d)\n",
- reg, ri.status, ri.result, ri.done.done);
- spin_lock_irqsave(&mddi->int_lock, irq_flags);
- mddi->reg_read = NULL;
- spin_unlock_irqrestore(&mddi->int_lock, irq_flags);
- ri.status = -1;
- ri.result = -1;
- }
- if (ri.status == 0)
- break;
-
- mddi_writel(MDDI_CMD_SEND_RTD, CMD);
- mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- printk(KERN_INFO "mddi_remote_read: failed, sent "
- "MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x "
- "curr_rev_ptr %x\n", mddi_readl(INT), mddi_readl(STAT),
- mddi_readl(RTD_VAL), mddi_readl(CURR_REV_PTR));
- } while (retry_count-- > 0);
- /* Disable Periodic Reverse Encapsulation. */
- mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- mddi->reg_read = NULL;
- mutex_unlock(&mddi->reg_read_lock);
- return ri.result;
-}
-
-static struct mddi_info mddi_info[2];
-
-static int mddi_clk_setup(struct platform_device *pdev, struct mddi_info *mddi,
- unsigned long clk_rate)
-{
- int ret;
-
- /* set up the clocks */
- mddi->clk = clk_get(&pdev->dev, "mddi_clk");
- if (IS_ERR(mddi->clk)) {
- printk(KERN_INFO "mddi: failed to get clock\n");
- return PTR_ERR(mddi->clk);
- }
- ret = clk_enable(mddi->clk);
- if (ret)
- goto fail;
- ret = clk_set_rate(mddi->clk, clk_rate);
- if (ret)
- goto fail;
- return 0;
-
-fail:
- clk_put(mddi->clk);
- return ret;
-}
-
-static int __init mddi_rev_data_setup(struct mddi_info *mddi)
-{
- void *dma;
- dma_addr_t dma_addr;
-
- /* set up dma buffer */
- dma = dma_alloc_coherent(NULL, 0x1000, &dma_addr, GFP_KERNEL);
- if (dma == 0)
- return -ENOMEM;
- mddi->rev_data = dma;
- mddi->rev_data_curr = 0;
- mddi->rev_addr = dma_addr;
- mddi->reg_write_data = dma + MDDI_REV_BUFFER_SIZE;
- mddi->reg_write_addr = dma_addr + MDDI_REV_BUFFER_SIZE;
- mddi->reg_read_data = mddi->reg_write_data + 1;
- mddi->reg_read_addr = mddi->reg_write_addr +
- sizeof(*mddi->reg_write_data);
- return 0;
-}
-
-static int mddi_probe(struct platform_device *pdev)
-{
- struct msm_mddi_platform_data *pdata = pdev->dev.platform_data;
- struct mddi_info *mddi = &mddi_info[pdev->id];
- struct resource *resource;
- int ret, i;
-
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!resource) {
- printk(KERN_ERR "mddi: no associated mem resource!\n");
- return -ENOMEM;
- }
- mddi->base = ioremap(resource->start, resource_size(resource));
- if (!mddi->base) {
- printk(KERN_ERR "mddi: failed to remap base!\n");
- ret = -EINVAL;
- goto error_ioremap;
- }
- resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!resource) {
- printk(KERN_ERR "mddi: no associated irq resource!\n");
- ret = -EINVAL;
- goto error_get_irq_resource;
- }
- mddi->irq = resource->start;
- printk(KERN_INFO "mddi: init() base=0x%p irq=%d\n", mddi->base,
- mddi->irq);
- mddi->power_client = pdata->power_client;
-
- mutex_init(&mddi->reg_write_lock);
- mutex_init(&mddi->reg_read_lock);
- spin_lock_init(&mddi->int_lock);
- init_waitqueue_head(&mddi->int_wait);
-
- ret = mddi_clk_setup(pdev, mddi, pdata->clk_rate);
- if (ret) {
- printk(KERN_ERR "mddi: failed to setup clock!\n");
- goto error_clk_setup;
- }
-
- ret = mddi_rev_data_setup(mddi);
- if (ret) {
- printk(KERN_ERR "mddi: failed to setup rev data!\n");
- goto error_rev_data;
- }
-
- mddi->int_enable = 0;
- mddi_writel(mddi->int_enable, INTEN);
- ret = request_irq(mddi->irq, mddi_isr, 0, "mddi",
- &mddi->client_data);
- if (ret) {
- printk(KERN_ERR "mddi: failed to request enable irq!\n");
- goto error_request_irq;
- }
-
- /* turn on the mddi client bridge chip */
- if (mddi->power_client)
- mddi->power_client(&mddi->client_data, 1);
-
- /* initialize the mddi registers */
- mddi_set_auto_hibernate(&mddi->client_data, 0);
- mddi_writel(MDDI_CMD_RESET, CMD);
- mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
- mddi->version = mddi_init_registers(mddi);
- if (mddi->version < 0x20) {
- printk(KERN_ERR "mddi: unsupported version 0x%x\n",
- mddi->version);
- ret = -ENODEV;
- goto error_mddi_version;
- }
-
- /* read the capabilities off the client */
- if (!mddi_get_client_caps(mddi)) {
- printk(KERN_INFO "mddi: no client found\n");
- /* power down the panel */
- mddi_writel(MDDI_CMD_POWERDOWN, CMD);
- printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT));
- msleep(100);
- printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT));
- return 0;
- }
- mddi_set_auto_hibernate(&mddi->client_data, 1);
-
- if (mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0)
- pdata->fixup(&mddi->caps.Mfr_Name, &mddi->caps.Product_Code);
-
- mddi->client_pdev.id = 0;
- for (i = 0; i < pdata->num_clients; i++) {
- if (pdata->client_platform_data[i].product_id ==
- (mddi->caps.Mfr_Name << 16 | mddi->caps.Product_Code)) {
- mddi->client_data.private_client_data =
- pdata->client_platform_data[i].client_data;
- mddi->client_pdev.name =
- pdata->client_platform_data[i].name;
- mddi->client_pdev.id =
- pdata->client_platform_data[i].id;
- /* XXX: possibly set clock */
- break;
- }
- }
-
- if (i >= pdata->num_clients)
- mddi->client_pdev.name = "mddi_c_dummy";
- printk(KERN_INFO "mddi: registering panel %s\n",
- mddi->client_pdev.name);
-
- mddi->client_data.suspend = mddi_suspend;
- mddi->client_data.resume = mddi_resume;
- mddi->client_data.activate_link = mddi_activate_link;
- mddi->client_data.remote_write = mddi_remote_write;
- mddi->client_data.remote_read = mddi_remote_read;
- mddi->client_data.auto_hibernate = mddi_set_auto_hibernate;
- mddi->client_data.fb_resource = pdata->fb_resource;
- if (pdev->id == 0)
- mddi->client_data.interface_type = MSM_MDDI_PMDH_INTERFACE;
- else if (pdev->id == 1)
- mddi->client_data.interface_type = MSM_MDDI_EMDH_INTERFACE;
- else {
- printk(KERN_ERR "mddi: can not determine interface %d!\n",
- pdev->id);
- ret = -EINVAL;
- goto error_mddi_interface;
- }
-
- mddi->client_pdev.dev.platform_data = &mddi->client_data;
- printk(KERN_INFO "mddi: publish: %s\n", mddi->client_name);
- platform_device_register(&mddi->client_pdev);
- return 0;
-
-error_mddi_interface:
-error_mddi_version:
- free_irq(mddi->irq, 0);
-error_request_irq:
- dma_free_coherent(NULL, 0x1000, mddi->rev_data, mddi->rev_addr);
-error_rev_data:
-error_clk_setup:
-error_get_irq_resource:
- iounmap(mddi->base);
-error_ioremap:
-
- printk(KERN_INFO "mddi: mddi_init() failed (%d)\n", ret);
- return ret;
-}
-
-
-static struct platform_driver mddi_driver = {
- .probe = mddi_probe,
- .driver = { .name = "msm_mddi" },
-};
-
-static int __init _mddi_init(void)
-{
- return platform_driver_register(&mddi_driver);
-}
-
-module_init(_mddi_init);
diff --git a/drivers/video/fbdev/msm/mddi_client_dummy.c b/drivers/video/fbdev/msm/mddi_client_dummy.c
deleted file mode 100644
index cdb8f69a5d88..000000000000
--- a/drivers/video/fbdev/msm/mddi_client_dummy.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* drivers/video/msm_fb/mddi_client_dummy.c
- *
- * Support for "dummy" mddi client devices which require no
- * special initialization code.
- *
- * Copyright (C) 2007 Google Incorporated
- *
- * 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/slab.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-
-#include <linux/platform_data/video-msm_fb.h>
-
-struct panel_info {
- struct platform_device pdev;
- struct msm_panel_data panel_data;
-};
-
-static int mddi_dummy_suspend(struct msm_panel_data *panel_data)
-{
- return 0;
-}
-
-static int mddi_dummy_resume(struct msm_panel_data *panel_data)
-{
- return 0;
-}
-
-static int mddi_dummy_blank(struct msm_panel_data *panel_data)
-{
- return 0;
-}
-
-static int mddi_dummy_unblank(struct msm_panel_data *panel_data)
-{
- return 0;
-}
-
-static int mddi_dummy_probe(struct platform_device *pdev)
-{
- struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
- struct panel_info *panel =
- devm_kzalloc(&pdev->dev, sizeof(struct panel_info), GFP_KERNEL);
- if (!panel)
- return -ENOMEM;
- platform_set_drvdata(pdev, panel);
- panel->panel_data.suspend = mddi_dummy_suspend;
- panel->panel_data.resume = mddi_dummy_resume;
- panel->panel_data.blank = mddi_dummy_blank;
- panel->panel_data.unblank = mddi_dummy_unblank;
- panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES;
- panel->pdev.name = "msm_panel";
- panel->pdev.id = pdev->id;
- platform_device_add_resources(&panel->pdev,
- client_data->fb_resource, 1);
- panel->panel_data.fb_data = client_data->private_client_data;
- panel->pdev.dev.platform_data = &panel->panel_data;
- return platform_device_register(&panel->pdev);
-}
-
-static struct platform_driver mddi_client_dummy = {
- .probe = mddi_dummy_probe,
- .driver = { .name = "mddi_c_dummy" },
-};
-
-static int __init mddi_client_dummy_init(void)
-{
- platform_driver_register(&mddi_client_dummy);
- return 0;
-}
-
-module_init(mddi_client_dummy_init);
-
diff --git a/drivers/video/fbdev/msm/mddi_client_nt35399.c b/drivers/video/fbdev/msm/mddi_client_nt35399.c
deleted file mode 100644
index f96df32e5509..000000000000
--- a/drivers/video/fbdev/msm/mddi_client_nt35399.c
+++ /dev/null
@@ -1,252 +0,0 @@
-/* drivers/video/msm_fb/mddi_client_nt35399.c
- *
- * Support for Novatek NT35399 MDDI client of Sapphire
- *
- * Copyright (C) 2008 HTC Incorporated
- * Author: Solomon Chiu (solomon_chiu@htc.com)
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <linux/gpio.h>
-#include <linux/slab.h>
-#include <linux/platform_data/video-msm_fb.h>
-
-static DECLARE_WAIT_QUEUE_HEAD(nt35399_vsync_wait);
-
-struct panel_info {
- struct msm_mddi_client_data *client_data;
- struct platform_device pdev;
- struct msm_panel_data panel_data;
- struct msmfb_callback *fb_callback;
- struct work_struct panel_work;
- struct workqueue_struct *fb_wq;
- int nt35399_got_int;
-};
-
-static void
-nt35399_request_vsync(struct msm_panel_data *panel_data,
- struct msmfb_callback *callback)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- panel->fb_callback = callback;
- if (panel->nt35399_got_int) {
- panel->nt35399_got_int = 0;
- client_data->activate_link(client_data); /* clears interrupt */
- }
-}
-
-static void nt35399_wait_vsync(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- if (panel->nt35399_got_int) {
- panel->nt35399_got_int = 0;
- client_data->activate_link(client_data); /* clears interrupt */
- }
-
- if (wait_event_timeout(nt35399_vsync_wait, panel->nt35399_got_int,
- HZ/2) == 0)
- printk(KERN_ERR "timeout waiting for VSYNC\n");
-
- panel->nt35399_got_int = 0;
- /* interrupt clears when screen dma starts */
-}
-
-static int nt35399_suspend(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
- int ret;
-
- ret = bridge_data->uninit(bridge_data, client_data);
- if (ret) {
- printk(KERN_INFO "mddi nt35399 client: non zero return from "
- "uninit\n");
- return ret;
- }
- client_data->suspend(client_data);
- return 0;
-}
-
-static int nt35399_resume(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
- int ret;
-
- client_data->resume(client_data);
- ret = bridge_data->init(bridge_data, client_data);
- if (ret)
- return ret;
- return 0;
-}
-
-static int nt35399_blank(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
-
- return bridge_data->blank(bridge_data, client_data);
-}
-
-static int nt35399_unblank(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
-
- return bridge_data->unblank(bridge_data, client_data);
-}
-
-irqreturn_t nt35399_vsync_interrupt(int irq, void *data)
-{
- struct panel_info *panel = data;
-
- panel->nt35399_got_int = 1;
-
- if (panel->fb_callback) {
- panel->fb_callback->func(panel->fb_callback);
- panel->fb_callback = NULL;
- }
-
- wake_up(&nt35399_vsync_wait);
-
- return IRQ_HANDLED;
-}
-
-static int setup_vsync(struct panel_info *panel, int init)
-{
- int ret;
- int gpio = 97;
- unsigned int irq;
-
- if (!init) {
- ret = 0;
- goto uninit;
- }
- ret = gpio_request_one(gpio, GPIOF_IN, "vsync");
- if (ret)
- goto err_request_gpio_failed;
-
- ret = irq = gpio_to_irq(gpio);
- if (ret < 0)
- goto err_get_irq_num_failed;
-
- ret = request_irq(irq, nt35399_vsync_interrupt, IRQF_TRIGGER_RISING,
- "vsync", panel);
- if (ret)
- goto err_request_irq_failed;
-
- printk(KERN_INFO "vsync on gpio %d now %d\n",
- gpio, gpio_get_value(gpio));
- return 0;
-
-uninit:
- free_irq(gpio_to_irq(gpio), panel->client_data);
-err_request_irq_failed:
-err_get_irq_num_failed:
- gpio_free(gpio);
-err_request_gpio_failed:
- return ret;
-}
-
-static int mddi_nt35399_probe(struct platform_device *pdev)
-{
- struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
-
- int ret;
-
- struct panel_info *panel = devm_kzalloc(&pdev->dev,
- sizeof(struct panel_info),
- GFP_KERNEL);
-
- printk(KERN_DEBUG "%s: enter.\n", __func__);
-
- if (!panel)
- return -ENOMEM;
- platform_set_drvdata(pdev, panel);
-
- ret = setup_vsync(panel, 1);
- if (ret) {
- dev_err(&pdev->dev, "mddi_nt35399_setup_vsync failed\n");
- return ret;
- }
-
- panel->client_data = client_data;
- panel->panel_data.suspend = nt35399_suspend;
- panel->panel_data.resume = nt35399_resume;
- panel->panel_data.wait_vsync = nt35399_wait_vsync;
- panel->panel_data.request_vsync = nt35399_request_vsync;
- panel->panel_data.blank = nt35399_blank;
- panel->panel_data.unblank = nt35399_unblank;
- panel->panel_data.fb_data = &bridge_data->fb_data;
- panel->panel_data.caps = 0;
-
- panel->pdev.name = "msm_panel";
- panel->pdev.id = pdev->id;
- panel->pdev.resource = client_data->fb_resource;
- panel->pdev.num_resources = 1;
- panel->pdev.dev.platform_data = &panel->panel_data;
-
- if (bridge_data->init)
- bridge_data->init(bridge_data, client_data);
-
- platform_device_register(&panel->pdev);
-
- return 0;
-}
-
-static int mddi_nt35399_remove(struct platform_device *pdev)
-{
- struct panel_info *panel = platform_get_drvdata(pdev);
-
- setup_vsync(panel, 0);
- return 0;
-}
-
-static struct platform_driver mddi_client_0bda_8a47 = {
- .probe = mddi_nt35399_probe,
- .remove = mddi_nt35399_remove,
- .driver = { .name = "mddi_c_0bda_8a47" },
-};
-
-static int __init mddi_client_nt35399_init(void)
-{
- return platform_driver_register(&mddi_client_0bda_8a47);
-}
-
-module_init(mddi_client_nt35399_init);
-
diff --git a/drivers/video/fbdev/msm/mddi_client_toshiba.c b/drivers/video/fbdev/msm/mddi_client_toshiba.c
deleted file mode 100644
index 061d7dfebbf3..000000000000
--- a/drivers/video/fbdev/msm/mddi_client_toshiba.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/* drivers/video/msm_fb/mddi_client_toshiba.c
- *
- * Support for Toshiba TC358720XBG mddi client devices which require no
- * special initialization code.
- *
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/gpio.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/platform_data/video-msm_fb.h>
-
-
-#define LCD_CONTROL_BLOCK_BASE 0x110000
-#define CMN (LCD_CONTROL_BLOCK_BASE|0x10)
-#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18)
-#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34)
-#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C)
-#define VPOS (LCD_CONTROL_BLOCK_BASE|0xC0)
-#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20)
-#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54)
-#define WSYN_DLY (LCD_CONTROL_BLOCK_BASE|0x58)
-#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C)
-
-#define BASE5 0x150000
-#define BASE6 0x160000
-#define BASE7 0x170000
-
-#define GPIOIEV (BASE5 + 0x10)
-#define GPIOIE (BASE5 + 0x14)
-#define GPIORIS (BASE5 + 0x18)
-#define GPIOMIS (BASE5 + 0x1C)
-#define GPIOIC (BASE5 + 0x20)
-
-#define INTMASK (BASE6 + 0x0C)
-#define INTMASK_VWAKEOUT (1U << 0)
-#define INTMASK_VWAKEOUT_ACTIVE_LOW (1U << 8)
-#define GPIOSEL (BASE7 + 0x00)
-#define GPIOSEL_VWAKEINT (1U << 0)
-
-static DECLARE_WAIT_QUEUE_HEAD(toshiba_vsync_wait);
-
-struct panel_info {
- struct msm_mddi_client_data *client_data;
- struct platform_device pdev;
- struct msm_panel_data panel_data;
- struct msmfb_callback *toshiba_callback;
- int toshiba_got_int;
-};
-
-
-static void toshiba_request_vsync(struct msm_panel_data *panel_data,
- struct msmfb_callback *callback)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- panel->toshiba_callback = callback;
- if (panel->toshiba_got_int) {
- panel->toshiba_got_int = 0;
- client_data->activate_link(client_data);
- }
-}
-
-static void toshiba_clear_vsync(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- client_data->activate_link(client_data);
-}
-
-static void toshiba_wait_vsync(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- if (panel->toshiba_got_int) {
- panel->toshiba_got_int = 0;
- client_data->activate_link(client_data); /* clears interrupt */
- }
- if (wait_event_timeout(toshiba_vsync_wait, panel->toshiba_got_int,
- HZ/2) == 0)
- printk(KERN_ERR "timeout waiting for VSYNC\n");
- panel->toshiba_got_int = 0;
- /* interrupt clears when screen dma starts */
-}
-
-static int toshiba_suspend(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
- int ret;
-
- ret = bridge_data->uninit(bridge_data, client_data);
- if (ret) {
- printk(KERN_INFO "mddi toshiba client: non zero return from "
- "uninit\n");
- return ret;
- }
- client_data->suspend(client_data);
- return 0;
-}
-
-static int toshiba_resume(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
-
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
- int ret;
-
- client_data->resume(client_data);
- ret = bridge_data->init(bridge_data, client_data);
- if (ret)
- return ret;
- return 0;
-}
-
-static int toshiba_blank(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
-
- return bridge_data->blank(bridge_data, client_data);
-}
-
-static int toshiba_unblank(struct msm_panel_data *panel_data)
-{
- struct panel_info *panel = container_of(panel_data, struct panel_info,
- panel_data);
- struct msm_mddi_client_data *client_data = panel->client_data;
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
-
- return bridge_data->unblank(bridge_data, client_data);
-}
-
-irqreturn_t toshiba_vsync_interrupt(int irq, void *data)
-{
- struct panel_info *panel = data;
-
- panel->toshiba_got_int = 1;
- if (panel->toshiba_callback) {
- panel->toshiba_callback->func(panel->toshiba_callback);
- panel->toshiba_callback = 0;
- }
- wake_up(&toshiba_vsync_wait);
- return IRQ_HANDLED;
-}
-
-static int setup_vsync(struct panel_info *panel,
- int init)
-{
- int ret;
- int gpio = 97;
- unsigned int irq;
-
- if (!init) {
- ret = 0;
- goto uninit;
- }
- ret = gpio_request_one(gpio, GPIOF_IN, "vsync");
- if (ret)
- goto err_request_gpio_failed;
-
- ret = irq = gpio_to_irq(gpio);
- if (ret < 0)
- goto err_get_irq_num_failed;
-
- ret = request_irq(irq, toshiba_vsync_interrupt, IRQF_TRIGGER_RISING,
- "vsync", panel);
- if (ret)
- goto err_request_irq_failed;
- printk(KERN_INFO "vsync on gpio %d now %d\n",
- gpio, gpio_get_value(gpio));
- return 0;
-
-uninit:
- free_irq(gpio_to_irq(gpio), panel);
-err_request_irq_failed:
-err_get_irq_num_failed:
- gpio_free(gpio);
-err_request_gpio_failed:
- return ret;
-}
-
-static int mddi_toshiba_probe(struct platform_device *pdev)
-{
- int ret;
- struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
- struct msm_mddi_bridge_platform_data *bridge_data =
- client_data->private_client_data;
- struct panel_info *panel =
- kzalloc(sizeof(struct panel_info), GFP_KERNEL);
- if (!panel)
- return -ENOMEM;
- platform_set_drvdata(pdev, panel);
-
- /* mddi_remote_write(mddi, 0, WAKEUP); */
- client_data->remote_write(client_data, GPIOSEL_VWAKEINT, GPIOSEL);
- client_data->remote_write(client_data, INTMASK_VWAKEOUT, INTMASK);
-
- ret = setup_vsync(panel, 1);
- if (ret) {
- dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n");
- return ret;
- }
-
- panel->client_data = client_data;
- panel->panel_data.suspend = toshiba_suspend;
- panel->panel_data.resume = toshiba_resume;
- panel->panel_data.wait_vsync = toshiba_wait_vsync;
- panel->panel_data.request_vsync = toshiba_request_vsync;
- panel->panel_data.clear_vsync = toshiba_clear_vsync;
- panel->panel_data.blank = toshiba_blank;
- panel->panel_data.unblank = toshiba_unblank;
- panel->panel_data.fb_data = &bridge_data->fb_data;
- panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES;
-
- panel->pdev.name = "msm_panel";
- panel->pdev.id = pdev->id;
- panel->pdev.resource = client_data->fb_resource;
- panel->pdev.num_resources = 1;
- panel->pdev.dev.platform_data = &panel->panel_data;
- bridge_data->init(bridge_data, client_data);
- platform_device_register(&panel->pdev);
-
- return 0;
-}
-
-static int mddi_toshiba_remove(struct platform_device *pdev)
-{
- struct panel_info *panel = platform_get_drvdata(pdev);
-
- setup_vsync(panel, 0);
- kfree(panel);
- return 0;
-}
-
-static struct platform_driver mddi_client_d263_0000 = {
- .probe = mddi_toshiba_probe,
- .remove = mddi_toshiba_remove,
- .driver = { .name = "mddi_c_d263_0000" },
-};
-
-static int __init mddi_client_toshiba_init(void)
-{
- platform_driver_register(&mddi_client_d263_0000);
- return 0;
-}
-
-module_init(mddi_client_toshiba_init);
-
diff --git a/drivers/video/fbdev/msm/mddi_hw.h b/drivers/video/fbdev/msm/mddi_hw.h
deleted file mode 100644
index 45cc01fc1e7f..000000000000
--- a/drivers/video/fbdev/msm/mddi_hw.h
+++ /dev/null
@@ -1,305 +0,0 @@
-/* drivers/video/msm_fb/mddi_hw.h
- *
- * MSM MDDI Hardware Registers and Structures
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef _MDDI_HW_H_
-#define _MDDI_HW_H_
-
-#include <linux/types.h>
-
-#define MDDI_CMD 0x0000
-#define MDDI_VERSION 0x0004
-#define MDDI_PRI_PTR 0x0008
-#define MDDI_SEC_PTR 0x000c
-#define MDDI_BPS 0x0010
-#define MDDI_SPM 0x0014
-#define MDDI_INT 0x0018
-#define MDDI_INTEN 0x001c
-#define MDDI_REV_PTR 0x0020
-#define MDDI_REV_SIZE 0x0024
-#define MDDI_STAT 0x0028
-#define MDDI_REV_RATE_DIV 0x002c
-#define MDDI_REV_CRC_ERR 0x0030
-#define MDDI_TA1_LEN 0x0034
-#define MDDI_TA2_LEN 0x0038
-#define MDDI_TEST_BUS 0x003c
-#define MDDI_TEST 0x0040
-#define MDDI_REV_PKT_CNT 0x0044
-#define MDDI_DRIVE_HI 0x0048
-#define MDDI_DRIVE_LO 0x004c
-#define MDDI_DISP_WAKE 0x0050
-#define MDDI_REV_ENCAP_SZ 0x0054
-#define MDDI_RTD_VAL 0x0058
-#define MDDI_PAD_CTL 0x0068
-#define MDDI_DRIVER_START_CNT 0x006c
-#define MDDI_NEXT_PRI_PTR 0x0070
-#define MDDI_NEXT_SEC_PTR 0x0074
-#define MDDI_MISR_CTL 0x0078
-#define MDDI_MISR_DATA 0x007c
-#define MDDI_SF_CNT 0x0080
-#define MDDI_MF_CNT 0x0084
-#define MDDI_CURR_REV_PTR 0x0088
-#define MDDI_CORE_VER 0x008c
-
-#define MDDI_INT_PRI_PTR_READ 0x0001
-#define MDDI_INT_SEC_PTR_READ 0x0002
-#define MDDI_INT_REV_DATA_AVAIL 0x0004
-#define MDDI_INT_DISP_REQ 0x0008
-#define MDDI_INT_PRI_UNDERFLOW 0x0010
-#define MDDI_INT_SEC_UNDERFLOW 0x0020
-#define MDDI_INT_REV_OVERFLOW 0x0040
-#define MDDI_INT_CRC_ERROR 0x0080
-#define MDDI_INT_MDDI_IN 0x0100
-#define MDDI_INT_PRI_OVERWRITE 0x0200
-#define MDDI_INT_SEC_OVERWRITE 0x0400
-#define MDDI_INT_REV_OVERWRITE 0x0800
-#define MDDI_INT_DMA_FAILURE 0x1000
-#define MDDI_INT_LINK_ACTIVE 0x2000
-#define MDDI_INT_IN_HIBERNATION 0x4000
-#define MDDI_INT_PRI_LINK_LIST_DONE 0x8000
-#define MDDI_INT_SEC_LINK_LIST_DONE 0x10000
-#define MDDI_INT_NO_CMD_PKTS_PEND 0x20000
-#define MDDI_INT_RTD_FAILURE 0x40000
-#define MDDI_INT_REV_PKT_RECEIVED 0x80000
-#define MDDI_INT_REV_PKTS_AVAIL 0x100000
-
-#define MDDI_INT_NEED_CLEAR ( \
- MDDI_INT_REV_DATA_AVAIL | \
- MDDI_INT_PRI_UNDERFLOW | \
- MDDI_INT_SEC_UNDERFLOW | \
- MDDI_INT_REV_OVERFLOW | \
- MDDI_INT_CRC_ERROR | \
- MDDI_INT_REV_PKT_RECEIVED)
-
-
-#define MDDI_STAT_LINK_ACTIVE 0x0001
-#define MDDI_STAT_NEW_REV_PTR 0x0002
-#define MDDI_STAT_NEW_PRI_PTR 0x0004
-#define MDDI_STAT_NEW_SEC_PTR 0x0008
-#define MDDI_STAT_IN_HIBERNATION 0x0010
-#define MDDI_STAT_PRI_LINK_LIST_DONE 0x0020
-#define MDDI_STAT_SEC_LINK_LIST_DONE 0x0040
-#define MDDI_STAT_PENDING_TIMING_PKT 0x0080
-#define MDDI_STAT_PENDING_REV_ENCAP 0x0100
-#define MDDI_STAT_PENDING_POWERDOWN 0x0200
-#define MDDI_STAT_RTD_MEAS_FAIL 0x0800
-#define MDDI_STAT_CLIENT_WAKEUP_REQ 0x1000
-
-
-#define MDDI_CMD_POWERDOWN 0x0100
-#define MDDI_CMD_POWERUP 0x0200
-#define MDDI_CMD_HIBERNATE 0x0300
-#define MDDI_CMD_RESET 0x0400
-#define MDDI_CMD_DISP_IGNORE 0x0501
-#define MDDI_CMD_DISP_LISTEN 0x0500
-#define MDDI_CMD_SEND_REV_ENCAP 0x0600
-#define MDDI_CMD_GET_CLIENT_CAP 0x0601
-#define MDDI_CMD_GET_CLIENT_STATUS 0x0602
-#define MDDI_CMD_SEND_RTD 0x0700
-#define MDDI_CMD_LINK_ACTIVE 0x0900
-#define MDDI_CMD_PERIODIC_REV_ENCAP 0x0A00
-#define MDDI_CMD_FORCE_NEW_REV_PTR 0x0C00
-
-
-
-#define MDDI_VIDEO_REV_PKT_SIZE 0x40
-#define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE 0x60
-#define MDDI_MAX_REV_PKT_SIZE 0x60
-
-/* #define MDDI_REV_BUFFER_SIZE 128 */
-#define MDDI_REV_BUFFER_SIZE (MDDI_MAX_REV_PKT_SIZE * 4)
-
-/* MDP sends 256 pixel packets, so lower value hibernates more without
- * significantly increasing latency of waiting for next subframe */
-#define MDDI_HOST_BYTES_PER_SUBFRAME 0x3C00
-#define MDDI_HOST_TA2_LEN 0x000c
-#define MDDI_HOST_REV_RATE_DIV 0x0002
-
-
-struct __attribute__((packed)) mddi_rev_packet {
- uint16_t length;
- uint16_t type;
- uint16_t client_id;
-};
-
-struct __attribute__((packed)) mddi_client_status {
- uint16_t length;
- uint16_t type;
- uint16_t client_id;
- uint16_t reverse_link_request; /* bytes needed in rev encap message */
- uint8_t crc_error_count;
- uint8_t capability_change;
- uint16_t graphics_busy_flags;
- uint16_t crc16;
-};
-
-struct __attribute__((packed)) mddi_client_caps {
- uint16_t length; /* length, exclusive of this field */
- uint16_t type; /* 66 */
- uint16_t client_id;
-
- uint16_t Protocol_Version;
- uint16_t Minimum_Protocol_Version;
- uint16_t Data_Rate_Capability;
- uint8_t Interface_Type_Capability;
- uint8_t Number_of_Alt_Displays;
- uint16_t PostCal_Data_Rate;
- uint16_t Bitmap_Width;
- uint16_t Bitmap_Height;
- uint16_t Display_Window_Width;
- uint16_t Display_Window_Height;
- uint32_t Color_Map_Size;
- uint16_t Color_Map_RGB_Width;
- uint16_t RGB_Capability;
- uint8_t Monochrome_Capability;
- uint8_t Reserved_1;
- uint16_t Y_Cb_Cr_Capability;
- uint16_t Bayer_Capability;
- uint16_t Alpha_Cursor_Image_Planes;
- uint32_t Client_Feature_Capability_Indicators;
- uint8_t Maximum_Video_Frame_Rate_Capability;
- uint8_t Minimum_Video_Frame_Rate_Capability;
- uint16_t Minimum_Sub_frame_Rate;
- uint16_t Audio_Buffer_Depth;
- uint16_t Audio_Channel_Capability;
- uint16_t Audio_Sample_Rate_Capability;
- uint8_t Audio_Sample_Resolution;
- uint8_t Mic_Audio_Sample_Resolution;
- uint16_t Mic_Sample_Rate_Capability;
- uint8_t Keyboard_Data_Format;
- uint8_t pointing_device_data_format;
- uint16_t content_protection_type;
- uint16_t Mfr_Name;
- uint16_t Product_Code;
- uint16_t Reserved_3;
- uint32_t Serial_Number;
- uint8_t Week_of_Manufacture;
- uint8_t Year_of_Manufacture;
-
- uint16_t crc16;
-} mddi_client_capability_type;
-
-
-struct __attribute__((packed)) mddi_video_stream {
- uint16_t length;
- uint16_t type; /* 16 */
- uint16_t client_id; /* 0 */
-
- uint16_t video_data_format_descriptor;
-/* format of each pixel in the Pixel Data in the present stream in the
- * present packet.
- * If bits [15:13] = 000 monochrome
- * If bits [15:13] = 001 color pixels (palette).
- * If bits [15:13] = 010 color pixels in raw RGB
- * If bits [15:13] = 011 data in 4:2:2 Y Cb Cr format
- * If bits [15:13] = 100 Bayer pixels
- */
-
- uint16_t pixel_data_attributes;
-/* interpreted as follows:
- * Bits [1:0] = 11 pixel data is displayed to both eyes
- * Bits [1:0] = 10 pixel data is routed to the left eye only.
- * Bits [1:0] = 01 pixel data is routed to the right eye only.
- * Bits [1:0] = 00 pixel data is routed to the alternate display.
- * Bit 2 is 0 Pixel Data is in the standard progressive format.
- * Bit 2 is 1 Pixel Data is in interlace format.
- * Bit 3 is 0 Pixel Data is in the standard progressive format.
- * Bit 3 is 1 Pixel Data is in alternate pixel format.
- * Bit 4 is 0 Pixel Data is to or from the display frame buffer.
- * Bit 4 is 1 Pixel Data is to or from the camera.
- * Bit 5 is 0 pixel data contains the next consecutive row of pixels.
- * Bit 5 is 1 X Left Edge, Y Top Edge, X Right Edge, Y Bottom Edge,
- * X Start, and Y Start parameters are not defined and
- * shall be ignored by the client.
- * Bits [7:6] = 01 Pixel data is written to the offline image buffer.
- * Bits [7:6] = 00 Pixel data is written to the buffer to refresh display.
- * Bits [7:6] = 11 Pixel data is written to all image buffers.
- * Bits [7:6] = 10 Invalid. Reserved for future use.
- * Bits 8 through 11 alternate display number.
- * Bits 12 through 14 are reserved for future use and shall be set to zero.
- * Bit 15 is 1 the row of pixels is the last row of pixels in a frame.
- */
-
- uint16_t x_left_edge;
- uint16_t y_top_edge;
- /* X,Y coordinate of the top left edge of the screen window */
-
- uint16_t x_right_edge;
- uint16_t y_bottom_edge;
- /* X,Y coordinate of the bottom right edge of the window being
- * updated. */
-
- uint16_t x_start;
- uint16_t y_start;
- /* (X Start, Y Start) is the first pixel in the Pixel Data field
- * below. */
-
- uint16_t pixel_count;
- /* number of pixels in the Pixel Data field below. */
-
- uint16_t parameter_CRC;
- /* 16-bit CRC of all bytes from the Packet Length to the Pixel Count. */
-
- uint16_t reserved;
- /* 16-bit variable to make structure align on 4 byte boundary */
-};
-
-#define TYPE_VIDEO_STREAM 16
-#define TYPE_CLIENT_CAPS 66
-#define TYPE_REGISTER_ACCESS 146
-#define TYPE_CLIENT_STATUS 70
-
-struct __attribute__((packed)) mddi_register_access {
- uint16_t length;
- uint16_t type; /* 146 */
- uint16_t client_id;
-
- uint16_t read_write_info;
- /* Bits 13:0 a 14-bit unsigned integer that specifies the number of
- * 32-bit Register Data List items to be transferred in the
- * Register Data List field.
- * Bits[15:14] = 00 Write to register(s);
- * Bits[15:14] = 10 Read from register(s);
- * Bits[15:14] = 11 Response to a Read.
- * Bits[15:14] = 01 this value is reserved for future use. */
-#define MDDI_WRITE (0 << 14)
-#define MDDI_READ (2 << 14)
-#define MDDI_READ_RESP (3 << 14)
-
- uint32_t register_address;
- /* the register address that is to be written to or read from. */
-
- uint16_t crc16;
-
- uint32_t register_data_list;
- /* list of 4-byte register data values for/from client registers */
-};
-
-struct __attribute__((packed)) mddi_llentry {
- uint16_t flags;
- uint16_t header_count;
- uint16_t data_count;
- dma_addr_t data; /* 32 bit */
- struct mddi_llentry *next;
- uint16_t reserved;
- union {
- struct mddi_video_stream v;
- struct mddi_register_access r;
- uint32_t _[12];
- } u;
-};
-
-#endif
diff --git a/drivers/video/fbdev/msm/mdp.c b/drivers/video/fbdev/msm/mdp.c
deleted file mode 100644
index 113c7876c855..000000000000
--- a/drivers/video/fbdev/msm/mdp.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/* drivers/video/msm_fb/mdp.c
- *
- * MSM MDP Interface (used by framebuffer core)
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/fb.h>
-#include <linux/msm_mdp.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/clk.h>
-#include <linux/file.h>
-#include <linux/major.h>
-#include <linux/slab.h>
-
-#include <linux/platform_data/video-msm_fb.h>
-#include <linux/platform_device.h>
-#include <linux/export.h>
-
-#include "mdp_hw.h"
-
-struct class *mdp_class;
-
-#define MDP_CMD_DEBUG_ACCESS_BASE (0x10000)
-
-static uint16_t mdp_default_ccs[] = {
- 0x254, 0x000, 0x331, 0x254, 0xF38, 0xE61, 0x254, 0x409, 0x000,
- 0x010, 0x080, 0x080
-};
-
-static DECLARE_WAIT_QUEUE_HEAD(mdp_dma2_waitqueue);
-static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue);
-static struct msmfb_callback *dma_callback;
-static struct clk *clk;
-static unsigned int mdp_irq_mask;
-static DEFINE_SPINLOCK(mdp_lock);
-DEFINE_MUTEX(mdp_mutex);
-
-static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
-{
- unsigned long irq_flags;
- int ret = 0;
-
- BUG_ON(!mask);
-
- spin_lock_irqsave(&mdp_lock, irq_flags);
- /* if the mask bits are already set return an error, this interrupt
- * is already enabled */
- if (mdp_irq_mask & mask) {
- printk(KERN_ERR "mdp irq already on already on %x %x\n",
- mdp_irq_mask, mask);
- ret = -1;
- }
- /* if the mdp irq is not already enabled enable it */
- if (!mdp_irq_mask) {
- if (clk)
- clk_enable(clk);
- enable_irq(mdp->irq);
- }
-
- /* update the irq mask to reflect the fact that the interrupt is
- * enabled */
- mdp_irq_mask |= mask;
- spin_unlock_irqrestore(&mdp_lock, irq_flags);
- return ret;
-}
-
-static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
-{
- /* this interrupt is already disabled! */
- if (!(mdp_irq_mask & mask)) {
- printk(KERN_ERR "mdp irq already off %x %x\n",
- mdp_irq_mask, mask);
- return -1;
- }
- /* update the irq mask to reflect the fact that the interrupt is
- * disabled */
- mdp_irq_mask &= ~(mask);
- /* if no one is waiting on the interrupt, disable it */
- if (!mdp_irq_mask) {
- disable_irq_nosync(mdp->irq);
- if (clk)
- clk_disable(clk);
- }
- return 0;
-}
-
-static int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
-{
- unsigned long irq_flags;
- int ret;
-
- spin_lock_irqsave(&mdp_lock, irq_flags);
- ret = locked_disable_mdp_irq(mdp, mask);
- spin_unlock_irqrestore(&mdp_lock, irq_flags);
- return ret;
-}
-
-static irqreturn_t mdp_isr(int irq, void *data)
-{
- uint32_t status;
- unsigned long irq_flags;
- struct mdp_info *mdp = data;
-
- spin_lock_irqsave(&mdp_lock, irq_flags);
-
- status = mdp_readl(mdp, MDP_INTR_STATUS);
- mdp_writel(mdp, status, MDP_INTR_CLEAR);
-
- status &= mdp_irq_mask;
- if (status & DL0_DMA2_TERM_DONE) {
- if (dma_callback) {
- dma_callback->func(dma_callback);
- dma_callback = NULL;
- }
- wake_up(&mdp_dma2_waitqueue);
- }
-
- if (status & DL0_ROI_DONE)
- wake_up(&mdp_ppp_waitqueue);
-
- if (status)
- locked_disable_mdp_irq(mdp, status);
-
- spin_unlock_irqrestore(&mdp_lock, irq_flags);
- return IRQ_HANDLED;
-}
-
-static uint32_t mdp_check_mask(uint32_t mask)
-{
- uint32_t ret;
- unsigned long irq_flags;
-
- spin_lock_irqsave(&mdp_lock, irq_flags);
- ret = mdp_irq_mask & mask;
- spin_unlock_irqrestore(&mdp_lock, irq_flags);
- return ret;
-}
-
-static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq)
-{
- int ret = 0;
- unsigned long irq_flags;
-
- wait_event_timeout(*wq, !mdp_check_mask(mask), HZ);
-
- spin_lock_irqsave(&mdp_lock, irq_flags);
- if (mdp_irq_mask & mask) {
- locked_disable_mdp_irq(mdp, mask);
- printk(KERN_WARNING "timeout waiting for mdp to complete %x\n",
- mask);
- ret = -ETIMEDOUT;
- }
- spin_unlock_irqrestore(&mdp_lock, irq_flags);
-
- return ret;
-}
-
-void mdp_dma_wait(struct mdp_device *mdp_dev)
-{
-#define MDP_MAX_TIMEOUTS 20
- static int timeout_count;
- struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
-
- if (mdp_wait(mdp, DL0_DMA2_TERM_DONE, &mdp_dma2_waitqueue) == -ETIMEDOUT)
- timeout_count++;
- else
- timeout_count = 0;
-
- if (timeout_count > MDP_MAX_TIMEOUTS) {
- printk(KERN_ERR "mdp: dma failed %d times, somethings wrong!\n",
- MDP_MAX_TIMEOUTS);
- BUG();
- }
-}
-
-static int mdp_ppp_wait(struct mdp_info *mdp)
-{
- return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue);
-}
-
-void mdp_dma_to_mddi(struct mdp_info *mdp, uint32_t addr, uint32_t stride,
- uint32_t width, uint32_t height, uint32_t x, uint32_t y,
- struct msmfb_callback *callback)
-{
- uint32_t dma2_cfg;
- uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
-
- if (enable_mdp_irq(mdp, DL0_DMA2_TERM_DONE)) {
- printk(KERN_ERR "mdp_dma_to_mddi: busy\n");
- return;
- }
-
- dma_callback = callback;
-
- dma2_cfg = DMA_PACK_TIGHT |
- DMA_PACK_ALIGN_LSB |
- DMA_PACK_PATTERN_RGB |
- DMA_OUT_SEL_AHB |
- DMA_IBUF_NONCONTIGUOUS;
-
- dma2_cfg |= DMA_IBUF_FORMAT_RGB565;
-
- dma2_cfg |= DMA_OUT_SEL_MDDI;
-
- dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
-
- dma2_cfg |= DMA_DITHER_EN;
-
- /* setup size, address, and stride */
- mdp_writel(mdp, (height << 16) | (width),
- MDP_CMD_DEBUG_ACCESS_BASE + 0x0184);
- mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188);
- mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C);
-
- /* 666 18BPP */
- dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
-
- /* set y & x offset and MDDI transaction parameters */
- mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194);
- mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0);
- mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
- MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4);
-
- mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180);
-
- /* start DMA2 */
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044);
-}
-
-void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride,
- uint32_t width, uint32_t height, uint32_t x, uint32_t y,
- struct msmfb_callback *callback, int interface)
-{
- struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
-
- if (interface == MSM_MDDI_PMDH_INTERFACE) {
- mdp_dma_to_mddi(mdp, addr, stride, width, height, x, y,
- callback);
- }
-}
-
-int get_img(struct mdp_img *img, struct fb_info *info,
- unsigned long *start, unsigned long *len,
- struct file **filep)
-{
- int ret = 0;
- struct fd f = fdget(img->memory_id);
- if (f.file == NULL)
- return -1;
-
- if (MAJOR(file_inode(f.file)->i_rdev) == FB_MAJOR) {
- *start = info->fix.smem_start;
- *len = info->fix.smem_len;
- } else
- ret = -1;
- fdput(f);
-
- return ret;
-}
-
-void put_img(struct file *src_file, struct file *dst_file)
-{
-}
-
-int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb,
- struct mdp_blit_req *req)
-{
- int ret;
- unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0;
- struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
- struct file *src_file = 0, *dst_file = 0;
-
- /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */
- if (unlikely(req->src_rect.h == 0 ||
- req->src_rect.w == 0)) {
- printk(KERN_ERR "mpd_ppp: src img of zero size!\n");
- return -EINVAL;
- }
- if (unlikely(req->dst_rect.h == 0 ||
- req->dst_rect.w == 0))
- return -EINVAL;
-
- /* do this first so that if this fails, the caller can always
- * safely call put_img */
- if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) {
- printk(KERN_ERR "mpd_ppp: could not retrieve src image from "
- "memory\n");
- return -EINVAL;
- }
-
- if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) {
- printk(KERN_ERR "mpd_ppp: could not retrieve dst image from "
- "memory\n");
- return -EINVAL;
- }
- mutex_lock(&mdp_mutex);
-
- /* transp_masking unimplemented */
- req->transp_mask = MDP_TRANSP_NOP;
- if (unlikely((req->transp_mask != MDP_TRANSP_NOP ||
- req->alpha != MDP_ALPHA_NOP ||
- HAS_ALPHA(req->src.format)) &&
- (req->flags & MDP_ROT_90 &&
- req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) {
- int i;
- unsigned int tiles = req->dst_rect.h / 16;
- unsigned int remainder = req->dst_rect.h % 16;
- req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h;
- req->dst_rect.h = 16;
- for (i = 0; i < tiles; i++) {
- enable_mdp_irq(mdp, DL0_ROI_DONE);
- ret = mdp_ppp_blit(mdp, req, src_file, src_start,
- src_len, dst_file, dst_start,
- dst_len);
- if (ret)
- goto err_bad_blit;
- ret = mdp_ppp_wait(mdp);
- if (ret)
- goto err_wait_failed;
- req->dst_rect.y += 16;
- req->src_rect.x += req->src_rect.w;
- }
- if (!remainder)
- goto end;
- req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h;
- req->dst_rect.h = remainder;
- }
- enable_mdp_irq(mdp, DL0_ROI_DONE);
- ret = mdp_ppp_blit(mdp, req, src_file, src_start, src_len, dst_file,
- dst_start,
- dst_len);
- if (ret)
- goto err_bad_blit;
- ret = mdp_ppp_wait(mdp);
- if (ret)
- goto err_wait_failed;
-end:
- put_img(src_file, dst_file);
- mutex_unlock(&mdp_mutex);
- return 0;
-err_bad_blit:
- disable_mdp_irq(mdp, DL0_ROI_DONE);
-err_wait_failed:
- put_img(src_file, dst_file);
- mutex_unlock(&mdp_mutex);
- return ret;
-}
-
-void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id)
-{
- struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
-
- disp_id &= 0xf;
- mdp_writel(mdp, disp_id, MDP_FULL_BYPASS_WORD43);
-}
-
-int register_mdp_client(struct class_interface *cint)
-{
- if (!mdp_class) {
- pr_err("mdp: no mdp_class when registering mdp client\n");
- return -ENODEV;
- }
- cint->class = mdp_class;
- return class_interface_register(cint);
-}
-
-#include "mdp_csc_table.h"
-#include "mdp_scale_tables.h"
-
-int mdp_probe(struct platform_device *pdev)
-{
- struct resource *resource;
- int ret;
- int n;
- struct mdp_info *mdp;
-
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!resource) {
- pr_err("mdp: can not get mdp mem resource!\n");
- return -ENOMEM;
- }
-
- mdp = kzalloc(sizeof(struct mdp_info), GFP_KERNEL);
- if (!mdp)
- return -ENOMEM;
-
- mdp->irq = platform_get_irq(pdev, 0);
- if (mdp->irq < 0) {
- pr_err("mdp: can not get mdp irq\n");
- ret = mdp->irq;
- goto error_get_irq;
- }
-
- mdp->base = ioremap(resource->start, resource_size(resource));
- if (mdp->base == 0) {
- printk(KERN_ERR "msmfb: cannot allocate mdp regs!\n");
- ret = -ENOMEM;
- goto error_ioremap;
- }
-
- mdp->mdp_dev.dma = mdp_dma;
- mdp->mdp_dev.dma_wait = mdp_dma_wait;
- mdp->mdp_dev.blit = mdp_blit;
- mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp;
-
- clk = clk_get(&pdev->dev, "mdp_clk");
- if (IS_ERR(clk)) {
- printk(KERN_INFO "mdp: failed to get mdp clk");
- ret = PTR_ERR(clk);
- goto error_get_clk;
- }
-
- ret = request_irq(mdp->irq, mdp_isr, 0, "msm_mdp", mdp);
- if (ret)
- goto error_request_irq;
- disable_irq(mdp->irq);
- mdp_irq_mask = 0;
-
- /* debug interface write access */
- mdp_writel(mdp, 1, 0x60);
-
- mdp_writel(mdp, MDP_ANY_INTR_MASK, MDP_INTR_ENABLE);
- mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE);
-
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc);
-
- for (n = 0; n < ARRAY_SIZE(csc_table); n++)
- mdp_writel(mdp, csc_table[n].val, csc_table[n].reg);
-
- /* clear up unused fg/main registers */
- /* comp.plane 2&3 ystride */
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120);
-
- /* unpacked pattern */
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c);
-
- /* comp.plane 2 & 3 */
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118);
-
- /* clear unused bg registers */
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0);
- mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4);
-
- for (n = 0; n < ARRAY_SIZE(mdp_upscale_table); n++)
- mdp_writel(mdp, mdp_upscale_table[n].val,
- mdp_upscale_table[n].reg);
-
- for (n = 0; n < 9; n++)
- mdp_writel(mdp, mdp_default_ccs[n], 0x40440 + 4 * n);
- mdp_writel(mdp, mdp_default_ccs[9], 0x40500 + 4 * 0);
- mdp_writel(mdp, mdp_default_ccs[10], 0x40500 + 4 * 0);
- mdp_writel(mdp, mdp_default_ccs[11], 0x40500 + 4 * 0);
-
- /* register mdp device */
- mdp->mdp_dev.dev.parent = &pdev->dev;
- mdp->mdp_dev.dev.class = mdp_class;
- dev_set_name(&mdp->mdp_dev.dev, "mdp%d", pdev->id);
-
- /* if you can remove the platform device you'd have to implement
- * this:
- mdp_dev.release = mdp_class; */
-
- ret = device_register(&mdp->mdp_dev.dev);
- if (ret)
- goto error_device_register;
- return 0;
-
-error_device_register:
- free_irq(mdp->irq, mdp);
-error_request_irq:
-error_get_clk:
- iounmap(mdp->base);
-error_get_irq:
-error_ioremap:
- kfree(mdp);
- return ret;
-}
-
-static struct platform_driver msm_mdp_driver = {
- .probe = mdp_probe,
- .driver = {.name = "msm_mdp"},
-};
-
-static int __init mdp_init(void)
-{
- mdp_class = class_create(THIS_MODULE, "msm_mdp");
- if (IS_ERR(mdp_class)) {
- printk(KERN_ERR "Error creating mdp class\n");
- return PTR_ERR(mdp_class);
- }
- return platform_driver_register(&msm_mdp_driver);
-}
-
-subsys_initcall(mdp_init);
diff --git a/drivers/video/fbdev/msm/mdp_csc_table.h b/drivers/video/fbdev/msm/mdp_csc_table.h
deleted file mode 100644
index d1cde30ead52..000000000000
--- a/drivers/video/fbdev/msm/mdp_csc_table.h
+++ /dev/null
@@ -1,582 +0,0 @@
-/* drivers/video/msm_fb/mdp_csc_table.h
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * 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.
- */
-
-static struct {
- uint32_t reg;
- uint32_t val;
-} csc_table[] = {
- { 0x40400, 0x83 },
- { 0x40404, 0x102 },
- { 0x40408, 0x32 },
- { 0x4040c, 0xffffffb5 },
- { 0x40410, 0xffffff6c },
- { 0x40414, 0xe1 },
- { 0x40418, 0xe1 },
- { 0x4041c, 0xffffff45 },
- { 0x40420, 0xffffffdc },
- { 0x40440, 0x254 },
- { 0x40444, 0x0 },
- { 0x40448, 0x331 },
- { 0x4044c, 0x254 },
- { 0x40450, 0xffffff38 },
- { 0x40454, 0xfffffe61 },
- { 0x40458, 0x254 },
- { 0x4045c, 0x409 },
- { 0x40460, 0x0 },
- { 0x40480, 0x5d },
- { 0x40484, 0x13a },
- { 0x40488, 0x20 },
- { 0x4048c, 0xffffffcd },
- { 0x40490, 0xffffff54 },
- { 0x40494, 0xe1 },
- { 0x40498, 0xe1 },
- { 0x4049c, 0xffffff35 },
- { 0x404a0, 0xffffffec },
- { 0x404c0, 0x254 },
- { 0x404c4, 0x0 },
- { 0x404c8, 0x396 },
- { 0x404cc, 0x254 },
- { 0x404d0, 0xffffff94 },
- { 0x404d4, 0xfffffef0 },
- { 0x404d8, 0x254 },
- { 0x404dc, 0x43a },
- { 0x404e0, 0x0 },
- { 0x40500, 0x10 },
- { 0x40504, 0x80 },
- { 0x40508, 0x80 },
- { 0x40540, 0x10 },
- { 0x40544, 0x80 },
- { 0x40548, 0x80 },
- { 0x40580, 0x10 },
- { 0x40584, 0xeb },
- { 0x40588, 0x10 },
- { 0x4058c, 0xf0 },
- { 0x405c0, 0x10 },
- { 0x405c4, 0xeb },
- { 0x405c8, 0x10 },
- { 0x405cc, 0xf0 },
- { 0x40800, 0x0 },
- { 0x40804, 0x151515 },
- { 0x40808, 0x1d1d1d },
- { 0x4080c, 0x232323 },
- { 0x40810, 0x272727 },
- { 0x40814, 0x2b2b2b },
- { 0x40818, 0x2f2f2f },
- { 0x4081c, 0x333333 },
- { 0x40820, 0x363636 },
- { 0x40824, 0x393939 },
- { 0x40828, 0x3b3b3b },
- { 0x4082c, 0x3e3e3e },
- { 0x40830, 0x404040 },
- { 0x40834, 0x434343 },
- { 0x40838, 0x454545 },
- { 0x4083c, 0x474747 },
- { 0x40840, 0x494949 },
- { 0x40844, 0x4b4b4b },
- { 0x40848, 0x4d4d4d },
- { 0x4084c, 0x4f4f4f },
- { 0x40850, 0x515151 },
- { 0x40854, 0x535353 },
- { 0x40858, 0x555555 },
- { 0x4085c, 0x565656 },
- { 0x40860, 0x585858 },
- { 0x40864, 0x5a5a5a },
- { 0x40868, 0x5b5b5b },
- { 0x4086c, 0x5d5d5d },
- { 0x40870, 0x5e5e5e },
- { 0x40874, 0x606060 },
- { 0x40878, 0x616161 },
- { 0x4087c, 0x636363 },
- { 0x40880, 0x646464 },
- { 0x40884, 0x666666 },
- { 0x40888, 0x676767 },
- { 0x4088c, 0x686868 },
- { 0x40890, 0x6a6a6a },
- { 0x40894, 0x6b6b6b },
- { 0x40898, 0x6c6c6c },
- { 0x4089c, 0x6e6e6e },
- { 0x408a0, 0x6f6f6f },
- { 0x408a4, 0x707070 },
- { 0x408a8, 0x717171 },
- { 0x408ac, 0x727272 },
- { 0x408b0, 0x747474 },
- { 0x408b4, 0x757575 },
- { 0x408b8, 0x767676 },
- { 0x408bc, 0x777777 },
- { 0x408c0, 0x787878 },
- { 0x408c4, 0x797979 },
- { 0x408c8, 0x7a7a7a },
- { 0x408cc, 0x7c7c7c },
- { 0x408d0, 0x7d7d7d },
- { 0x408d4, 0x7e7e7e },
- { 0x408d8, 0x7f7f7f },
- { 0x408dc, 0x808080 },
- { 0x408e0, 0x818181 },
- { 0x408e4, 0x828282 },
- { 0x408e8, 0x838383 },
- { 0x408ec, 0x848484 },
- { 0x408f0, 0x858585 },
- { 0x408f4, 0x868686 },
- { 0x408f8, 0x878787 },
- { 0x408fc, 0x888888 },
- { 0x40900, 0x898989 },
- { 0x40904, 0x8a8a8a },
- { 0x40908, 0x8b8b8b },
- { 0x4090c, 0x8c8c8c },
- { 0x40910, 0x8d8d8d },
- { 0x40914, 0x8e8e8e },
- { 0x40918, 0x8f8f8f },
- { 0x4091c, 0x8f8f8f },
- { 0x40920, 0x909090 },
- { 0x40924, 0x919191 },
- { 0x40928, 0x929292 },
- { 0x4092c, 0x939393 },
- { 0x40930, 0x949494 },
- { 0x40934, 0x959595 },
- { 0x40938, 0x969696 },
- { 0x4093c, 0x969696 },
- { 0x40940, 0x979797 },
- { 0x40944, 0x989898 },
- { 0x40948, 0x999999 },
- { 0x4094c, 0x9a9a9a },
- { 0x40950, 0x9b9b9b },
- { 0x40954, 0x9c9c9c },
- { 0x40958, 0x9c9c9c },
- { 0x4095c, 0x9d9d9d },
- { 0x40960, 0x9e9e9e },
- { 0x40964, 0x9f9f9f },
- { 0x40968, 0xa0a0a0 },
- { 0x4096c, 0xa0a0a0 },
- { 0x40970, 0xa1a1a1 },
- { 0x40974, 0xa2a2a2 },
- { 0x40978, 0xa3a3a3 },
- { 0x4097c, 0xa4a4a4 },
- { 0x40980, 0xa4a4a4 },
- { 0x40984, 0xa5a5a5 },
- { 0x40988, 0xa6a6a6 },
- { 0x4098c, 0xa7a7a7 },
- { 0x40990, 0xa7a7a7 },
- { 0x40994, 0xa8a8a8 },
- { 0x40998, 0xa9a9a9 },
- { 0x4099c, 0xaaaaaa },
- { 0x409a0, 0xaaaaaa },
- { 0x409a4, 0xababab },
- { 0x409a8, 0xacacac },
- { 0x409ac, 0xadadad },
- { 0x409b0, 0xadadad },
- { 0x409b4, 0xaeaeae },
- { 0x409b8, 0xafafaf },
- { 0x409bc, 0xafafaf },
- { 0x409c0, 0xb0b0b0 },
- { 0x409c4, 0xb1b1b1 },
- { 0x409c8, 0xb2b2b2 },
- { 0x409cc, 0xb2b2b2 },
- { 0x409d0, 0xb3b3b3 },
- { 0x409d4, 0xb4b4b4 },
- { 0x409d8, 0xb4b4b4 },
- { 0x409dc, 0xb5b5b5 },
- { 0x409e0, 0xb6b6b6 },
- { 0x409e4, 0xb6b6b6 },
- { 0x409e8, 0xb7b7b7 },
- { 0x409ec, 0xb8b8b8 },
- { 0x409f0, 0xb8b8b8 },
- { 0x409f4, 0xb9b9b9 },
- { 0x409f8, 0xbababa },
- { 0x409fc, 0xbababa },
- { 0x40a00, 0xbbbbbb },
- { 0x40a04, 0xbcbcbc },
- { 0x40a08, 0xbcbcbc },
- { 0x40a0c, 0xbdbdbd },
- { 0x40a10, 0xbebebe },
- { 0x40a14, 0xbebebe },
- { 0x40a18, 0xbfbfbf },
- { 0x40a1c, 0xc0c0c0 },
- { 0x40a20, 0xc0c0c0 },
- { 0x40a24, 0xc1c1c1 },
- { 0x40a28, 0xc1c1c1 },
- { 0x40a2c, 0xc2c2c2 },
- { 0x40a30, 0xc3c3c3 },
- { 0x40a34, 0xc3c3c3 },
- { 0x40a38, 0xc4c4c4 },
- { 0x40a3c, 0xc5c5c5 },
- { 0x40a40, 0xc5c5c5 },
- { 0x40a44, 0xc6c6c6 },
- { 0x40a48, 0xc6c6c6 },
- { 0x40a4c, 0xc7c7c7 },
- { 0x40a50, 0xc8c8c8 },
- { 0x40a54, 0xc8c8c8 },
- { 0x40a58, 0xc9c9c9 },
- { 0x40a5c, 0xc9c9c9 },
- { 0x40a60, 0xcacaca },
- { 0x40a64, 0xcbcbcb },
- { 0x40a68, 0xcbcbcb },
- { 0x40a6c, 0xcccccc },
- { 0x40a70, 0xcccccc },
- { 0x40a74, 0xcdcdcd },
- { 0x40a78, 0xcecece },
- { 0x40a7c, 0xcecece },
- { 0x40a80, 0xcfcfcf },
- { 0x40a84, 0xcfcfcf },
- { 0x40a88, 0xd0d0d0 },
- { 0x40a8c, 0xd0d0d0 },
- { 0x40a90, 0xd1d1d1 },
- { 0x40a94, 0xd2d2d2 },
- { 0x40a98, 0xd2d2d2 },
- { 0x40a9c, 0xd3d3d3 },
- { 0x40aa0, 0xd3d3d3 },
- { 0x40aa4, 0xd4d4d4 },
- { 0x40aa8, 0xd4d4d4 },
- { 0x40aac, 0xd5d5d5 },
- { 0x40ab0, 0xd6d6d6 },
- { 0x40ab4, 0xd6d6d6 },
- { 0x40ab8, 0xd7d7d7 },
- { 0x40abc, 0xd7d7d7 },
- { 0x40ac0, 0xd8d8d8 },
- { 0x40ac4, 0xd8d8d8 },
- { 0x40ac8, 0xd9d9d9 },
- { 0x40acc, 0xd9d9d9 },
- { 0x40ad0, 0xdadada },
- { 0x40ad4, 0xdbdbdb },
- { 0x40ad8, 0xdbdbdb },
- { 0x40adc, 0xdcdcdc },
- { 0x40ae0, 0xdcdcdc },
- { 0x40ae4, 0xdddddd },
- { 0x40ae8, 0xdddddd },
- { 0x40aec, 0xdedede },
- { 0x40af0, 0xdedede },
- { 0x40af4, 0xdfdfdf },
- { 0x40af8, 0xdfdfdf },
- { 0x40afc, 0xe0e0e0 },
- { 0x40b00, 0xe0e0e0 },
- { 0x40b04, 0xe1e1e1 },
- { 0x40b08, 0xe1e1e1 },
- { 0x40b0c, 0xe2e2e2 },
- { 0x40b10, 0xe3e3e3 },
- { 0x40b14, 0xe3e3e3 },
- { 0x40b18, 0xe4e4e4 },
- { 0x40b1c, 0xe4e4e4 },
- { 0x40b20, 0xe5e5e5 },
- { 0x40b24, 0xe5e5e5 },
- { 0x40b28, 0xe6e6e6 },
- { 0x40b2c, 0xe6e6e6 },
- { 0x40b30, 0xe7e7e7 },
- { 0x40b34, 0xe7e7e7 },
- { 0x40b38, 0xe8e8e8 },
- { 0x40b3c, 0xe8e8e8 },
- { 0x40b40, 0xe9e9e9 },
- { 0x40b44, 0xe9e9e9 },
- { 0x40b48, 0xeaeaea },
- { 0x40b4c, 0xeaeaea },
- { 0x40b50, 0xebebeb },
- { 0x40b54, 0xebebeb },
- { 0x40b58, 0xececec },
- { 0x40b5c, 0xececec },
- { 0x40b60, 0xededed },
- { 0x40b64, 0xededed },
- { 0x40b68, 0xeeeeee },
- { 0x40b6c, 0xeeeeee },
- { 0x40b70, 0xefefef },
- { 0x40b74, 0xefefef },
- { 0x40b78, 0xf0f0f0 },
- { 0x40b7c, 0xf0f0f0 },
- { 0x40b80, 0xf1f1f1 },
- { 0x40b84, 0xf1f1f1 },
- { 0x40b88, 0xf2f2f2 },
- { 0x40b8c, 0xf2f2f2 },
- { 0x40b90, 0xf2f2f2 },
- { 0x40b94, 0xf3f3f3 },
- { 0x40b98, 0xf3f3f3 },
- { 0x40b9c, 0xf4f4f4 },
- { 0x40ba0, 0xf4f4f4 },
- { 0x40ba4, 0xf5f5f5 },
- { 0x40ba8, 0xf5f5f5 },
- { 0x40bac, 0xf6f6f6 },
- { 0x40bb0, 0xf6f6f6 },
- { 0x40bb4, 0xf7f7f7 },
- { 0x40bb8, 0xf7f7f7 },
- { 0x40bbc, 0xf8f8f8 },
- { 0x40bc0, 0xf8f8f8 },
- { 0x40bc4, 0xf9f9f9 },
- { 0x40bc8, 0xf9f9f9 },
- { 0x40bcc, 0xfafafa },
- { 0x40bd0, 0xfafafa },
- { 0x40bd4, 0xfafafa },
- { 0x40bd8, 0xfbfbfb },
- { 0x40bdc, 0xfbfbfb },
- { 0x40be0, 0xfcfcfc },
- { 0x40be4, 0xfcfcfc },
- { 0x40be8, 0xfdfdfd },
- { 0x40bec, 0xfdfdfd },
- { 0x40bf0, 0xfefefe },
- { 0x40bf4, 0xfefefe },
- { 0x40bf8, 0xffffff },
- { 0x40bfc, 0xffffff },
- { 0x40c00, 0x0 },
- { 0x40c04, 0x0 },
- { 0x40c08, 0x0 },
- { 0x40c0c, 0x0 },
- { 0x40c10, 0x0 },
- { 0x40c14, 0x0 },
- { 0x40c18, 0x0 },
- { 0x40c1c, 0x0 },
- { 0x40c20, 0x0 },
- { 0x40c24, 0x0 },
- { 0x40c28, 0x0 },
- { 0x40c2c, 0x0 },
- { 0x40c30, 0x0 },
- { 0x40c34, 0x0 },
- { 0x40c38, 0x0 },
- { 0x40c3c, 0x0 },
- { 0x40c40, 0x10101 },
- { 0x40c44, 0x10101 },
- { 0x40c48, 0x10101 },
- { 0x40c4c, 0x10101 },
- { 0x40c50, 0x10101 },
- { 0x40c54, 0x10101 },
- { 0x40c58, 0x10101 },
- { 0x40c5c, 0x10101 },
- { 0x40c60, 0x10101 },
- { 0x40c64, 0x10101 },
- { 0x40c68, 0x20202 },
- { 0x40c6c, 0x20202 },
- { 0x40c70, 0x20202 },
- { 0x40c74, 0x20202 },
- { 0x40c78, 0x20202 },
- { 0x40c7c, 0x20202 },
- { 0x40c80, 0x30303 },
- { 0x40c84, 0x30303 },
- { 0x40c88, 0x30303 },
- { 0x40c8c, 0x30303 },
- { 0x40c90, 0x30303 },
- { 0x40c94, 0x40404 },
- { 0x40c98, 0x40404 },
- { 0x40c9c, 0x40404 },
- { 0x40ca0, 0x40404 },
- { 0x40ca4, 0x40404 },
- { 0x40ca8, 0x50505 },
- { 0x40cac, 0x50505 },
- { 0x40cb0, 0x50505 },
- { 0x40cb4, 0x50505 },
- { 0x40cb8, 0x60606 },
- { 0x40cbc, 0x60606 },
- { 0x40cc0, 0x60606 },
- { 0x40cc4, 0x70707 },
- { 0x40cc8, 0x70707 },
- { 0x40ccc, 0x70707 },
- { 0x40cd0, 0x70707 },
- { 0x40cd4, 0x80808 },
- { 0x40cd8, 0x80808 },
- { 0x40cdc, 0x80808 },
- { 0x40ce0, 0x90909 },
- { 0x40ce4, 0x90909 },
- { 0x40ce8, 0xa0a0a },
- { 0x40cec, 0xa0a0a },
- { 0x40cf0, 0xa0a0a },
- { 0x40cf4, 0xb0b0b },
- { 0x40cf8, 0xb0b0b },
- { 0x40cfc, 0xb0b0b },
- { 0x40d00, 0xc0c0c },
- { 0x40d04, 0xc0c0c },
- { 0x40d08, 0xd0d0d },
- { 0x40d0c, 0xd0d0d },
- { 0x40d10, 0xe0e0e },
- { 0x40d14, 0xe0e0e },
- { 0x40d18, 0xe0e0e },
- { 0x40d1c, 0xf0f0f },
- { 0x40d20, 0xf0f0f },
- { 0x40d24, 0x101010 },
- { 0x40d28, 0x101010 },
- { 0x40d2c, 0x111111 },
- { 0x40d30, 0x111111 },
- { 0x40d34, 0x121212 },
- { 0x40d38, 0x121212 },
- { 0x40d3c, 0x131313 },
- { 0x40d40, 0x131313 },
- { 0x40d44, 0x141414 },
- { 0x40d48, 0x151515 },
- { 0x40d4c, 0x151515 },
- { 0x40d50, 0x161616 },
- { 0x40d54, 0x161616 },
- { 0x40d58, 0x171717 },
- { 0x40d5c, 0x171717 },
- { 0x40d60, 0x181818 },
- { 0x40d64, 0x191919 },
- { 0x40d68, 0x191919 },
- { 0x40d6c, 0x1a1a1a },
- { 0x40d70, 0x1b1b1b },
- { 0x40d74, 0x1b1b1b },
- { 0x40d78, 0x1c1c1c },
- { 0x40d7c, 0x1c1c1c },
- { 0x40d80, 0x1d1d1d },
- { 0x40d84, 0x1e1e1e },
- { 0x40d88, 0x1f1f1f },
- { 0x40d8c, 0x1f1f1f },
- { 0x40d90, 0x202020 },
- { 0x40d94, 0x212121 },
- { 0x40d98, 0x212121 },
- { 0x40d9c, 0x222222 },
- { 0x40da0, 0x232323 },
- { 0x40da4, 0x242424 },
- { 0x40da8, 0x242424 },
- { 0x40dac, 0x252525 },
- { 0x40db0, 0x262626 },
- { 0x40db4, 0x272727 },
- { 0x40db8, 0x272727 },
- { 0x40dbc, 0x282828 },
- { 0x40dc0, 0x292929 },
- { 0x40dc4, 0x2a2a2a },
- { 0x40dc8, 0x2b2b2b },
- { 0x40dcc, 0x2c2c2c },
- { 0x40dd0, 0x2c2c2c },
- { 0x40dd4, 0x2d2d2d },
- { 0x40dd8, 0x2e2e2e },
- { 0x40ddc, 0x2f2f2f },
- { 0x40de0, 0x303030 },
- { 0x40de4, 0x313131 },
- { 0x40de8, 0x323232 },
- { 0x40dec, 0x333333 },
- { 0x40df0, 0x333333 },
- { 0x40df4, 0x343434 },
- { 0x40df8, 0x353535 },
- { 0x40dfc, 0x363636 },
- { 0x40e00, 0x373737 },
- { 0x40e04, 0x383838 },
- { 0x40e08, 0x393939 },
- { 0x40e0c, 0x3a3a3a },
- { 0x40e10, 0x3b3b3b },
- { 0x40e14, 0x3c3c3c },
- { 0x40e18, 0x3d3d3d },
- { 0x40e1c, 0x3e3e3e },
- { 0x40e20, 0x3f3f3f },
- { 0x40e24, 0x404040 },
- { 0x40e28, 0x414141 },
- { 0x40e2c, 0x424242 },
- { 0x40e30, 0x434343 },
- { 0x40e34, 0x444444 },
- { 0x40e38, 0x464646 },
- { 0x40e3c, 0x474747 },
- { 0x40e40, 0x484848 },
- { 0x40e44, 0x494949 },
- { 0x40e48, 0x4a4a4a },
- { 0x40e4c, 0x4b4b4b },
- { 0x40e50, 0x4c4c4c },
- { 0x40e54, 0x4d4d4d },
- { 0x40e58, 0x4f4f4f },
- { 0x40e5c, 0x505050 },
- { 0x40e60, 0x515151 },
- { 0x40e64, 0x525252 },
- { 0x40e68, 0x535353 },
- { 0x40e6c, 0x545454 },
- { 0x40e70, 0x565656 },
- { 0x40e74, 0x575757 },
- { 0x40e78, 0x585858 },
- { 0x40e7c, 0x595959 },
- { 0x40e80, 0x5b5b5b },
- { 0x40e84, 0x5c5c5c },
- { 0x40e88, 0x5d5d5d },
- { 0x40e8c, 0x5e5e5e },
- { 0x40e90, 0x606060 },
- { 0x40e94, 0x616161 },
- { 0x40e98, 0x626262 },
- { 0x40e9c, 0x646464 },
- { 0x40ea0, 0x656565 },
- { 0x40ea4, 0x666666 },
- { 0x40ea8, 0x686868 },
- { 0x40eac, 0x696969 },
- { 0x40eb0, 0x6a6a6a },
- { 0x40eb4, 0x6c6c6c },
- { 0x40eb8, 0x6d6d6d },
- { 0x40ebc, 0x6f6f6f },
- { 0x40ec0, 0x707070 },
- { 0x40ec4, 0x717171 },
- { 0x40ec8, 0x737373 },
- { 0x40ecc, 0x747474 },
- { 0x40ed0, 0x767676 },
- { 0x40ed4, 0x777777 },
- { 0x40ed8, 0x797979 },
- { 0x40edc, 0x7a7a7a },
- { 0x40ee0, 0x7c7c7c },
- { 0x40ee4, 0x7d7d7d },
- { 0x40ee8, 0x7f7f7f },
- { 0x40eec, 0x808080 },
- { 0x40ef0, 0x828282 },
- { 0x40ef4, 0x838383 },
- { 0x40ef8, 0x858585 },
- { 0x40efc, 0x868686 },
- { 0x40f00, 0x888888 },
- { 0x40f04, 0x898989 },
- { 0x40f08, 0x8b8b8b },
- { 0x40f0c, 0x8d8d8d },
- { 0x40f10, 0x8e8e8e },
- { 0x40f14, 0x909090 },
- { 0x40f18, 0x919191 },
- { 0x40f1c, 0x939393 },
- { 0x40f20, 0x959595 },
- { 0x40f24, 0x969696 },
- { 0x40f28, 0x989898 },
- { 0x40f2c, 0x9a9a9a },
- { 0x40f30, 0x9b9b9b },
- { 0x40f34, 0x9d9d9d },
- { 0x40f38, 0x9f9f9f },
- { 0x40f3c, 0xa1a1a1 },
- { 0x40f40, 0xa2a2a2 },
- { 0x40f44, 0xa4a4a4 },
- { 0x40f48, 0xa6a6a6 },
- { 0x40f4c, 0xa7a7a7 },
- { 0x40f50, 0xa9a9a9 },
- { 0x40f54, 0xababab },
- { 0x40f58, 0xadadad },
- { 0x40f5c, 0xafafaf },
- { 0x40f60, 0xb0b0b0 },
- { 0x40f64, 0xb2b2b2 },
- { 0x40f68, 0xb4b4b4 },
- { 0x40f6c, 0xb6b6b6 },
- { 0x40f70, 0xb8b8b8 },
- { 0x40f74, 0xbababa },
- { 0x40f78, 0xbbbbbb },
- { 0x40f7c, 0xbdbdbd },
- { 0x40f80, 0xbfbfbf },
- { 0x40f84, 0xc1c1c1 },
- { 0x40f88, 0xc3c3c3 },
- { 0x40f8c, 0xc5c5c5 },
- { 0x40f90, 0xc7c7c7 },
- { 0x40f94, 0xc9c9c9 },
- { 0x40f98, 0xcbcbcb },
- { 0x40f9c, 0xcdcdcd },
- { 0x40fa0, 0xcfcfcf },
- { 0x40fa4, 0xd1d1d1 },
- { 0x40fa8, 0xd3d3d3 },
- { 0x40fac, 0xd5d5d5 },
- { 0x40fb0, 0xd7d7d7 },
- { 0x40fb4, 0xd9d9d9 },
- { 0x40fb8, 0xdbdbdb },
- { 0x40fbc, 0xdddddd },
- { 0x40fc0, 0xdfdfdf },
- { 0x40fc4, 0xe1e1e1 },
- { 0x40fc8, 0xe3e3e3 },
- { 0x40fcc, 0xe5e5e5 },
- { 0x40fd0, 0xe7e7e7 },
- { 0x40fd4, 0xe9e9e9 },
- { 0x40fd8, 0xebebeb },
- { 0x40fdc, 0xeeeeee },
- { 0x40fe0, 0xf0f0f0 },
- { 0x40fe4, 0xf2f2f2 },
- { 0x40fe8, 0xf4f4f4 },
- { 0x40fec, 0xf6f6f6 },
- { 0x40ff0, 0xf8f8f8 },
- { 0x40ff4, 0xfbfbfb },
- { 0x40ff8, 0xfdfdfd },
- { 0x40ffc, 0xffffff },
-};
diff --git a/drivers/video/fbdev/msm/mdp_hw.h b/drivers/video/fbdev/msm/mdp_hw.h
deleted file mode 100644
index 35848d741001..000000000000
--- a/drivers/video/fbdev/msm/mdp_hw.h
+++ /dev/null
@@ -1,627 +0,0 @@
-/* drivers/video/msm_fb/mdp_hw.h
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#ifndef _MDP_HW_H_
-#define _MDP_HW_H_
-
-#include <linux/platform_data/video-msm_fb.h>
-
-struct mdp_info {
- struct mdp_device mdp_dev;
- char * __iomem base;
- int irq;
-};
-struct mdp_blit_req;
-struct mdp_device;
-int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
- struct file *src_file, unsigned long src_start,
- unsigned long src_len, struct file *dst_file,
- unsigned long dst_start, unsigned long dst_len);
-#define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset)
-#define mdp_readl(mdp, offset) readl(mdp->base + offset)
-
-#define MDP_SYNC_CONFIG_0 (0x00000)
-#define MDP_SYNC_CONFIG_1 (0x00004)
-#define MDP_SYNC_CONFIG_2 (0x00008)
-#define MDP_SYNC_STATUS_0 (0x0000c)
-#define MDP_SYNC_STATUS_1 (0x00010)
-#define MDP_SYNC_STATUS_2 (0x00014)
-#define MDP_SYNC_THRESH_0 (0x00018)
-#define MDP_SYNC_THRESH_1 (0x0001c)
-#define MDP_INTR_ENABLE (0x00020)
-#define MDP_INTR_STATUS (0x00024)
-#define MDP_INTR_CLEAR (0x00028)
-#define MDP_DISPLAY0_START (0x00030)
-#define MDP_DISPLAY1_START (0x00034)
-#define MDP_DISPLAY_STATUS (0x00038)
-#define MDP_EBI2_LCD0 (0x0003c)
-#define MDP_EBI2_LCD1 (0x00040)
-#define MDP_DISPLAY0_ADDR (0x00054)
-#define MDP_DISPLAY1_ADDR (0x00058)
-#define MDP_EBI2_PORTMAP_MODE (0x0005c)
-#define MDP_MODE (0x00060)
-#define MDP_TV_OUT_STATUS (0x00064)
-#define MDP_HW_VERSION (0x00070)
-#define MDP_SW_RESET (0x00074)
-#define MDP_AXI_ERROR_MASTER_STOP (0x00078)
-#define MDP_SEL_CLK_OR_HCLK_TEST_BUS (0x0007c)
-#define MDP_PRIMARY_VSYNC_OUT_CTRL (0x00080)
-#define MDP_SECONDARY_VSYNC_OUT_CTRL (0x00084)
-#define MDP_EXTERNAL_VSYNC_OUT_CTRL (0x00088)
-#define MDP_VSYNC_CTRL (0x0008c)
-#define MDP_CGC_EN (0x00100)
-#define MDP_CMD_STATUS (0x10008)
-#define MDP_PROFILE_EN (0x10010)
-#define MDP_PROFILE_COUNT (0x10014)
-#define MDP_DMA_START (0x10044)
-#define MDP_FULL_BYPASS_WORD0 (0x10100)
-#define MDP_FULL_BYPASS_WORD1 (0x10104)
-#define MDP_COMMAND_CONFIG (0x10104)
-#define MDP_FULL_BYPASS_WORD2 (0x10108)
-#define MDP_FULL_BYPASS_WORD3 (0x1010c)
-#define MDP_FULL_BYPASS_WORD4 (0x10110)
-#define MDP_FULL_BYPASS_WORD6 (0x10118)
-#define MDP_FULL_BYPASS_WORD7 (0x1011c)
-#define MDP_FULL_BYPASS_WORD8 (0x10120)
-#define MDP_FULL_BYPASS_WORD9 (0x10124)
-#define MDP_PPP_SOURCE_CONFIG (0x10124)
-#define MDP_FULL_BYPASS_WORD10 (0x10128)
-#define MDP_FULL_BYPASS_WORD11 (0x1012c)
-#define MDP_FULL_BYPASS_WORD12 (0x10130)
-#define MDP_FULL_BYPASS_WORD13 (0x10134)
-#define MDP_FULL_BYPASS_WORD14 (0x10138)
-#define MDP_PPP_OPERATION_CONFIG (0x10138)
-#define MDP_FULL_BYPASS_WORD15 (0x1013c)
-#define MDP_FULL_BYPASS_WORD16 (0x10140)
-#define MDP_FULL_BYPASS_WORD17 (0x10144)
-#define MDP_FULL_BYPASS_WORD18 (0x10148)
-#define MDP_FULL_BYPASS_WORD19 (0x1014c)
-#define MDP_FULL_BYPASS_WORD20 (0x10150)
-#define MDP_PPP_DESTINATION_CONFIG (0x10150)
-#define MDP_FULL_BYPASS_WORD21 (0x10154)
-#define MDP_FULL_BYPASS_WORD22 (0x10158)
-#define MDP_FULL_BYPASS_WORD23 (0x1015c)
-#define MDP_FULL_BYPASS_WORD24 (0x10160)
-#define MDP_FULL_BYPASS_WORD25 (0x10164)
-#define MDP_FULL_BYPASS_WORD26 (0x10168)
-#define MDP_FULL_BYPASS_WORD27 (0x1016c)
-#define MDP_FULL_BYPASS_WORD29 (0x10174)
-#define MDP_FULL_BYPASS_WORD30 (0x10178)
-#define MDP_FULL_BYPASS_WORD31 (0x1017c)
-#define MDP_FULL_BYPASS_WORD32 (0x10180)
-#define MDP_DMA_CONFIG (0x10180)
-#define MDP_FULL_BYPASS_WORD33 (0x10184)
-#define MDP_FULL_BYPASS_WORD34 (0x10188)
-#define MDP_FULL_BYPASS_WORD35 (0x1018c)
-#define MDP_FULL_BYPASS_WORD37 (0x10194)
-#define MDP_FULL_BYPASS_WORD39 (0x1019c)
-#define MDP_FULL_BYPASS_WORD40 (0x101a0)
-#define MDP_FULL_BYPASS_WORD41 (0x101a4)
-#define MDP_FULL_BYPASS_WORD43 (0x101ac)
-#define MDP_FULL_BYPASS_WORD46 (0x101b8)
-#define MDP_FULL_BYPASS_WORD47 (0x101bc)
-#define MDP_FULL_BYPASS_WORD48 (0x101c0)
-#define MDP_FULL_BYPASS_WORD49 (0x101c4)
-#define MDP_FULL_BYPASS_WORD50 (0x101c8)
-#define MDP_FULL_BYPASS_WORD51 (0x101cc)
-#define MDP_FULL_BYPASS_WORD52 (0x101d0)
-#define MDP_FULL_BYPASS_WORD53 (0x101d4)
-#define MDP_FULL_BYPASS_WORD54 (0x101d8)
-#define MDP_FULL_BYPASS_WORD55 (0x101dc)
-#define MDP_FULL_BYPASS_WORD56 (0x101e0)
-#define MDP_FULL_BYPASS_WORD57 (0x101e4)
-#define MDP_FULL_BYPASS_WORD58 (0x101e8)
-#define MDP_FULL_BYPASS_WORD59 (0x101ec)
-#define MDP_FULL_BYPASS_WORD60 (0x101f0)
-#define MDP_VSYNC_THRESHOLD (0x101f0)
-#define MDP_FULL_BYPASS_WORD61 (0x101f4)
-#define MDP_FULL_BYPASS_WORD62 (0x101f8)
-#define MDP_FULL_BYPASS_WORD63 (0x101fc)
-#define MDP_TFETCH_TEST_MODE (0x20004)
-#define MDP_TFETCH_STATUS (0x20008)
-#define MDP_TFETCH_TILE_COUNT (0x20010)
-#define MDP_TFETCH_FETCH_COUNT (0x20014)
-#define MDP_TFETCH_CONSTANT_COLOR (0x20040)
-#define MDP_CSC_BYPASS (0x40004)
-#define MDP_SCALE_COEFF_LSB (0x5fffc)
-#define MDP_TV_OUT_CTL (0xc0000)
-#define MDP_TV_OUT_FIR_COEFF (0xc0004)
-#define MDP_TV_OUT_BUF_ADDR (0xc0008)
-#define MDP_TV_OUT_CC_DATA (0xc000c)
-#define MDP_TV_OUT_SOBEL (0xc0010)
-#define MDP_TV_OUT_Y_CLAMP (0xc0018)
-#define MDP_TV_OUT_CB_CLAMP (0xc001c)
-#define MDP_TV_OUT_CR_CLAMP (0xc0020)
-#define MDP_TEST_MODE_CLK (0xd0000)
-#define MDP_TEST_MISR_RESET_CLK (0xd0004)
-#define MDP_TEST_EXPORT_MISR_CLK (0xd0008)
-#define MDP_TEST_MISR_CURR_VAL_CLK (0xd000c)
-#define MDP_TEST_MODE_HCLK (0xd0100)
-#define MDP_TEST_MISR_RESET_HCLK (0xd0104)
-#define MDP_TEST_EXPORT_MISR_HCLK (0xd0108)
-#define MDP_TEST_MISR_CURR_VAL_HCLK (0xd010c)
-#define MDP_TEST_MODE_DCLK (0xd0200)
-#define MDP_TEST_MISR_RESET_DCLK (0xd0204)
-#define MDP_TEST_EXPORT_MISR_DCLK (0xd0208)
-#define MDP_TEST_MISR_CURR_VAL_DCLK (0xd020c)
-#define MDP_TEST_CAPTURED_DCLK (0xd0210)
-#define MDP_TEST_MISR_CAPT_VAL_DCLK (0xd0214)
-#define MDP_LCDC_CTL (0xe0000)
-#define MDP_LCDC_HSYNC_CTL (0xe0004)
-#define MDP_LCDC_VSYNC_CTL (0xe0008)
-#define MDP_LCDC_ACTIVE_HCTL (0xe000c)
-#define MDP_LCDC_ACTIVE_VCTL (0xe0010)
-#define MDP_LCDC_BORDER_CLR (0xe0014)
-#define MDP_LCDC_H_BLANK (0xe0018)
-#define MDP_LCDC_V_BLANK (0xe001c)
-#define MDP_LCDC_UNDERFLOW_CLR (0xe0020)
-#define MDP_LCDC_HSYNC_SKEW (0xe0024)
-#define MDP_LCDC_TEST_CTL (0xe0028)
-#define MDP_LCDC_LINE_IRQ (0xe002c)
-#define MDP_LCDC_CTL_POLARITY (0xe0030)
-#define MDP_LCDC_DMA_CONFIG (0xe1000)
-#define MDP_LCDC_DMA_SIZE (0xe1004)
-#define MDP_LCDC_DMA_IBUF_ADDR (0xe1008)
-#define MDP_LCDC_DMA_IBUF_Y_STRIDE (0xe100c)
-
-
-#define MDP_DMA2_TERM 0x1
-#define MDP_DMA3_TERM 0x2
-#define MDP_PPP_TERM 0x3
-
-/* MDP_INTR_ENABLE */
-#define DL0_ROI_DONE (1<<0)
-#define DL1_ROI_DONE (1<<1)
-#define DL0_DMA2_TERM_DONE (1<<2)
-#define DL1_DMA2_TERM_DONE (1<<3)
-#define DL0_PPP_TERM_DONE (1<<4)
-#define DL1_PPP_TERM_DONE (1<<5)
-#define TV_OUT_DMA3_DONE (1<<6)
-#define TV_ENC_UNDERRUN (1<<7)
-#define DL0_FETCH_DONE (1<<11)
-#define DL1_FETCH_DONE (1<<12)
-
-#define MDP_PPP_BUSY_STATUS (DL0_ROI_DONE| \
- DL1_ROI_DONE| \
- DL0_PPP_TERM_DONE| \
- DL1_PPP_TERM_DONE)
-
-#define MDP_ANY_INTR_MASK (DL0_ROI_DONE| \
- DL1_ROI_DONE| \
- DL0_DMA2_TERM_DONE| \
- DL1_DMA2_TERM_DONE| \
- DL0_PPP_TERM_DONE| \
- DL1_PPP_TERM_DONE| \
- DL0_FETCH_DONE| \
- DL1_FETCH_DONE| \
- TV_ENC_UNDERRUN)
-
-#define MDP_TOP_LUMA 16
-#define MDP_TOP_CHROMA 0
-#define MDP_BOTTOM_LUMA 19
-#define MDP_BOTTOM_CHROMA 3
-#define MDP_LEFT_LUMA 22
-#define MDP_LEFT_CHROMA 6
-#define MDP_RIGHT_LUMA 25
-#define MDP_RIGHT_CHROMA 9
-
-#define CLR_G 0x0
-#define CLR_B 0x1
-#define CLR_R 0x2
-#define CLR_ALPHA 0x3
-
-#define CLR_Y CLR_G
-#define CLR_CB CLR_B
-#define CLR_CR CLR_R
-
-/* from lsb to msb */
-#define MDP_GET_PACK_PATTERN(a, x, y, z, bit) \
- (((a)<<(bit*3))|((x)<<(bit*2))|((y)<<bit)|(z))
-
-/* MDP_SYNC_CONFIG_0/1/2 */
-#define MDP_SYNCFG_HGT_LOC 22
-#define MDP_SYNCFG_VSYNC_EXT_EN (1<<21)
-#define MDP_SYNCFG_VSYNC_INT_EN (1<<20)
-
-/* MDP_SYNC_THRESH_0 */
-#define MDP_PRIM_BELOW_LOC 0
-#define MDP_PRIM_ABOVE_LOC 8
-
-/* MDP_{PRIMARY,SECONDARY,EXTERNAL}_VSYNC_OUT_CRL */
-#define VSYNC_PULSE_EN (1<<31)
-#define VSYNC_PULSE_INV (1<<30)
-
-/* MDP_VSYNC_CTRL */
-#define DISP0_VSYNC_MAP_VSYNC0 0
-#define DISP0_VSYNC_MAP_VSYNC1 (1<<0)
-#define DISP0_VSYNC_MAP_VSYNC2 ((1<<0)|(1<<1))
-
-#define DISP1_VSYNC_MAP_VSYNC0 0
-#define DISP1_VSYNC_MAP_VSYNC1 (1<<2)
-#define DISP1_VSYNC_MAP_VSYNC2 ((1<<2)|(1<<3))
-
-#define PRIMARY_LCD_SYNC_EN (1<<4)
-#define PRIMARY_LCD_SYNC_DISABLE 0
-
-#define SECONDARY_LCD_SYNC_EN (1<<5)
-#define SECONDARY_LCD_SYNC_DISABLE 0
-
-#define EXTERNAL_LCD_SYNC_EN (1<<6)
-#define EXTERNAL_LCD_SYNC_DISABLE 0
-
-/* MDP_VSYNC_THRESHOLD / MDP_FULL_BYPASS_WORD60 */
-#define VSYNC_THRESHOLD_ABOVE_LOC 0
-#define VSYNC_THRESHOLD_BELOW_LOC 16
-#define VSYNC_ANTI_TEAR_EN (1<<31)
-
-/* MDP_COMMAND_CONFIG / MDP_FULL_BYPASS_WORD1 */
-#define MDP_CMD_DBGBUS_EN (1<<0)
-
-/* MDP_PPP_SOURCE_CONFIG / MDP_FULL_BYPASS_WORD9&53 */
-#define PPP_SRC_C0G_8BIT ((1<<1)|(1<<0))
-#define PPP_SRC_C1B_8BIT ((1<<3)|(1<<2))
-#define PPP_SRC_C2R_8BIT ((1<<5)|(1<<4))
-#define PPP_SRC_C3A_8BIT ((1<<7)|(1<<6))
-
-#define PPP_SRC_C0G_6BIT (1<<1)
-#define PPP_SRC_C1B_6BIT (1<<3)
-#define PPP_SRC_C2R_6BIT (1<<5)
-
-#define PPP_SRC_C0G_5BIT (1<<0)
-#define PPP_SRC_C1B_5BIT (1<<2)
-#define PPP_SRC_C2R_5BIT (1<<4)
-
-#define PPP_SRC_C3ALPHA_EN (1<<8)
-
-#define PPP_SRC_BPP_1BYTES 0
-#define PPP_SRC_BPP_2BYTES (1<<9)
-#define PPP_SRC_BPP_3BYTES (1<<10)
-#define PPP_SRC_BPP_4BYTES ((1<<10)|(1<<9))
-
-#define PPP_SRC_BPP_ROI_ODD_X (1<<11)
-#define PPP_SRC_BPP_ROI_ODD_Y (1<<12)
-#define PPP_SRC_INTERLVD_2COMPONENTS (1<<13)
-#define PPP_SRC_INTERLVD_3COMPONENTS (1<<14)
-#define PPP_SRC_INTERLVD_4COMPONENTS ((1<<14)|(1<<13))
-
-
-/* RGB666 unpack format
-** TIGHT means R6+G6+B6 together
-** LOOSE means R6+2 +G6+2+ B6+2 (with MSB)
-** or 2+R6 +2+G6 +2+B6 (with LSB)
-*/
-#define PPP_SRC_PACK_TIGHT (1<<17)
-#define PPP_SRC_PACK_LOOSE 0
-#define PPP_SRC_PACK_ALIGN_LSB 0
-#define PPP_SRC_PACK_ALIGN_MSB (1<<18)
-
-#define PPP_SRC_PLANE_INTERLVD 0
-#define PPP_SRC_PLANE_PSEUDOPLNR (1<<20)
-
-#define PPP_SRC_WMV9_MODE (1<<21)
-
-/* MDP_PPP_OPERATION_CONFIG / MDP_FULL_BYPASS_WORD14 */
-#define PPP_OP_SCALE_X_ON (1<<0)
-#define PPP_OP_SCALE_Y_ON (1<<1)
-
-#define PPP_OP_CONVERT_RGB2YCBCR 0
-#define PPP_OP_CONVERT_YCBCR2RGB (1<<2)
-#define PPP_OP_CONVERT_ON (1<<3)
-
-#define PPP_OP_CONVERT_MATRIX_PRIMARY 0
-#define PPP_OP_CONVERT_MATRIX_SECONDARY (1<<4)
-
-#define PPP_OP_LUT_C0_ON (1<<5)
-#define PPP_OP_LUT_C1_ON (1<<6)
-#define PPP_OP_LUT_C2_ON (1<<7)
-
-/* rotate or blend enable */
-#define PPP_OP_ROT_ON (1<<8)
-
-#define PPP_OP_ROT_90 (1<<9)
-#define PPP_OP_FLIP_LR (1<<10)
-#define PPP_OP_FLIP_UD (1<<11)
-
-#define PPP_OP_BLEND_ON (1<<12)
-
-#define PPP_OP_BLEND_SRCPIXEL_ALPHA 0
-#define PPP_OP_BLEND_DSTPIXEL_ALPHA (1<<13)
-#define PPP_OP_BLEND_CONSTANT_ALPHA (1<<14)
-#define PPP_OP_BLEND_SRCPIXEL_TRANSP ((1<<13)|(1<<14))
-
-#define PPP_OP_BLEND_ALPHA_BLEND_NORMAL 0
-#define PPP_OP_BLEND_ALPHA_BLEND_REVERSE (1<<15)
-
-#define PPP_OP_DITHER_EN (1<<16)
-
-#define PPP_OP_COLOR_SPACE_RGB 0
-#define PPP_OP_COLOR_SPACE_YCBCR (1<<17)
-
-#define PPP_OP_SRC_CHROMA_RGB 0
-#define PPP_OP_SRC_CHROMA_H2V1 (1<<18)
-#define PPP_OP_SRC_CHROMA_H1V2 (1<<19)
-#define PPP_OP_SRC_CHROMA_420 ((1<<18)|(1<<19))
-#define PPP_OP_SRC_CHROMA_COSITE 0
-#define PPP_OP_SRC_CHROMA_OFFSITE (1<<20)
-
-#define PPP_OP_DST_CHROMA_RGB 0
-#define PPP_OP_DST_CHROMA_H2V1 (1<<21)
-#define PPP_OP_DST_CHROMA_H1V2 (1<<22)
-#define PPP_OP_DST_CHROMA_420 ((1<<21)|(1<<22))
-#define PPP_OP_DST_CHROMA_COSITE 0
-#define PPP_OP_DST_CHROMA_OFFSITE (1<<23)
-
-#define PPP_BLEND_ALPHA_TRANSP (1<<24)
-
-#define PPP_OP_BG_CHROMA_RGB 0
-#define PPP_OP_BG_CHROMA_H2V1 (1<<25)
-#define PPP_OP_BG_CHROMA_H1V2 (1<<26)
-#define PPP_OP_BG_CHROMA_420 ((1<<25)|(1<<26))
-#define PPP_OP_BG_CHROMA_SITE_COSITE 0
-#define PPP_OP_BG_CHROMA_SITE_OFFSITE (1<<27)
-
-/* MDP_PPP_DESTINATION_CONFIG / MDP_FULL_BYPASS_WORD20 */
-#define PPP_DST_C0G_8BIT ((1<<0)|(1<<1))
-#define PPP_DST_C1B_8BIT ((1<<3)|(1<<2))
-#define PPP_DST_C2R_8BIT ((1<<5)|(1<<4))
-#define PPP_DST_C3A_8BIT ((1<<7)|(1<<6))
-
-#define PPP_DST_C0G_6BIT (1<<1)
-#define PPP_DST_C1B_6BIT (1<<3)
-#define PPP_DST_C2R_6BIT (1<<5)
-
-#define PPP_DST_C0G_5BIT (1<<0)
-#define PPP_DST_C1B_5BIT (1<<2)
-#define PPP_DST_C2R_5BIT (1<<4)
-
-#define PPP_DST_C3A_8BIT ((1<<7)|(1<<6))
-#define PPP_DST_C3ALPHA_EN (1<<8)
-
-#define PPP_DST_INTERLVD_2COMPONENTS (1<<9)
-#define PPP_DST_INTERLVD_3COMPONENTS (1<<10)
-#define PPP_DST_INTERLVD_4COMPONENTS ((1<<10)|(1<<9))
-#define PPP_DST_INTERLVD_6COMPONENTS ((1<<11)|(1<<9))
-
-#define PPP_DST_PACK_LOOSE 0
-#define PPP_DST_PACK_TIGHT (1<<13)
-#define PPP_DST_PACK_ALIGN_LSB 0
-#define PPP_DST_PACK_ALIGN_MSB (1<<14)
-
-#define PPP_DST_OUT_SEL_AXI 0
-#define PPP_DST_OUT_SEL_MDDI (1<<15)
-
-#define PPP_DST_BPP_2BYTES (1<<16)
-#define PPP_DST_BPP_3BYTES (1<<17)
-#define PPP_DST_BPP_4BYTES ((1<<17)|(1<<16))
-
-#define PPP_DST_PLANE_INTERLVD 0
-#define PPP_DST_PLANE_PLANAR (1<<18)
-#define PPP_DST_PLANE_PSEUDOPLNR (1<<19)
-
-#define PPP_DST_TO_TV (1<<20)
-
-#define PPP_DST_MDDI_PRIMARY 0
-#define PPP_DST_MDDI_SECONDARY (1<<21)
-#define PPP_DST_MDDI_EXTERNAL (1<<22)
-
-/* image configurations by image type */
-#define PPP_CFG_MDP_RGB_565(dir) (PPP_##dir##_C2R_5BIT | \
- PPP_##dir##_C0G_6BIT | \
- PPP_##dir##_C1B_5BIT | \
- PPP_##dir##_BPP_2BYTES | \
- PPP_##dir##_INTERLVD_3COMPONENTS | \
- PPP_##dir##_PACK_TIGHT | \
- PPP_##dir##_PACK_ALIGN_LSB | \
- PPP_##dir##_PLANE_INTERLVD)
-
-#define PPP_CFG_MDP_RGB_888(dir) (PPP_##dir##_C2R_8BIT | \
- PPP_##dir##_C0G_8BIT | \
- PPP_##dir##_C1B_8BIT | \
- PPP_##dir##_BPP_3BYTES | \
- PPP_##dir##_INTERLVD_3COMPONENTS | \
- PPP_##dir##_PACK_TIGHT | \
- PPP_##dir##_PACK_ALIGN_LSB | \
- PPP_##dir##_PLANE_INTERLVD)
-
-#define PPP_CFG_MDP_ARGB_8888(dir) (PPP_##dir##_C2R_8BIT | \
- PPP_##dir##_C0G_8BIT | \
- PPP_##dir##_C1B_8BIT | \
- PPP_##dir##_C3A_8BIT | \
- PPP_##dir##_C3ALPHA_EN | \
- PPP_##dir##_BPP_4BYTES | \
- PPP_##dir##_INTERLVD_4COMPONENTS | \
- PPP_##dir##_PACK_TIGHT | \
- PPP_##dir##_PACK_ALIGN_LSB | \
- PPP_##dir##_PLANE_INTERLVD)
-
-#define PPP_CFG_MDP_XRGB_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
-#define PPP_CFG_MDP_RGBA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
-#define PPP_CFG_MDP_BGRA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
-#define PPP_CFG_MDP_RGBX_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
-
-#define PPP_CFG_MDP_Y_CBCR_H2V2(dir) (PPP_##dir##_C2R_8BIT | \
- PPP_##dir##_C0G_8BIT | \
- PPP_##dir##_C1B_8BIT | \
- PPP_##dir##_C3A_8BIT | \
- PPP_##dir##_BPP_2BYTES | \
- PPP_##dir##_INTERLVD_2COMPONENTS | \
- PPP_##dir##_PACK_TIGHT | \
- PPP_##dir##_PACK_ALIGN_LSB | \
- PPP_##dir##_PLANE_PSEUDOPLNR)
-
-#define PPP_CFG_MDP_Y_CRCB_H2V2(dir) PPP_CFG_MDP_Y_CBCR_H2V2(dir)
-
-#define PPP_CFG_MDP_YCRYCB_H2V1(dir) (PPP_##dir##_C2R_8BIT | \
- PPP_##dir##_C0G_8BIT | \
- PPP_##dir##_C1B_8BIT | \
- PPP_##dir##_C3A_8BIT | \
- PPP_##dir##_BPP_2BYTES | \
- PPP_##dir##_INTERLVD_4COMPONENTS | \
- PPP_##dir##_PACK_TIGHT | \
- PPP_##dir##_PACK_ALIGN_LSB |\
- PPP_##dir##_PLANE_INTERLVD)
-
-#define PPP_CFG_MDP_Y_CBCR_H2V1(dir) (PPP_##dir##_C2R_8BIT | \
- PPP_##dir##_C0G_8BIT | \
- PPP_##dir##_C1B_8BIT | \
- PPP_##dir##_C3A_8BIT | \
- PPP_##dir##_BPP_2BYTES | \
- PPP_##dir##_INTERLVD_2COMPONENTS | \
- PPP_##dir##_PACK_TIGHT | \
- PPP_##dir##_PACK_ALIGN_LSB | \
- PPP_##dir##_PLANE_PSEUDOPLNR)
-
-#define PPP_CFG_MDP_Y_CRCB_H2V1(dir) PPP_CFG_MDP_Y_CBCR_H2V1(dir)
-
-#define PPP_PACK_PATTERN_MDP_RGB_565 \
- MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8)
-#define PPP_PACK_PATTERN_MDP_RGB_888 PPP_PACK_PATTERN_MDP_RGB_565
-#define PPP_PACK_PATTERN_MDP_XRGB_8888 \
- MDP_GET_PACK_PATTERN(CLR_B, CLR_G, CLR_R, CLR_ALPHA, 8)
-#define PPP_PACK_PATTERN_MDP_ARGB_8888 PPP_PACK_PATTERN_MDP_XRGB_8888
-#define PPP_PACK_PATTERN_MDP_RGBA_8888 \
- MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8)
-#define PPP_PACK_PATTERN_MDP_BGRA_8888 \
- MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, 8)
-#define PPP_PACK_PATTERN_MDP_RGBX_8888 \
- MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8)
-#define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1 \
- MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8)
-#define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V2 PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1
-#define PPP_PACK_PATTERN_MDP_Y_CRCB_H2V1 \
- MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8)
-#define PPP_PACK_PATTERN_MDP_Y_CRCB_H2V2 PPP_PACK_PATTERN_MDP_Y_CRCB_H2V1
-#define PPP_PACK_PATTERN_MDP_YCRYCB_H2V1 \
- MDP_GET_PACK_PATTERN(CLR_Y, CLR_R, CLR_Y, CLR_B, 8)
-
-#define PPP_CHROMA_SAMP_MDP_RGB_565(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_RGB_888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_XRGB_8888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_ARGB_8888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_RGBA_8888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_BGRA_8888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_RGBX_8888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1
-#define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V2(dir) PPP_OP_##dir##_CHROMA_420
-#define PPP_CHROMA_SAMP_MDP_Y_CRCB_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1
-#define PPP_CHROMA_SAMP_MDP_Y_CRCB_H2V2(dir) PPP_OP_##dir##_CHROMA_420
-#define PPP_CHROMA_SAMP_MDP_YCRYCB_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1
-
-/* Helpful array generation macros */
-#define PPP_ARRAY0(name) \
- [MDP_RGB_565] = PPP_##name##_MDP_RGB_565,\
- [MDP_RGB_888] = PPP_##name##_MDP_RGB_888,\
- [MDP_XRGB_8888] = PPP_##name##_MDP_XRGB_8888,\
- [MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888,\
- [MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888,\
- [MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888,\
- [MDP_RGBX_8888] = PPP_##name##_MDP_RGBX_8888,\
- [MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1,\
- [MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2,\
- [MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1,\
- [MDP_Y_CRCB_H2V2] = PPP_##name##_MDP_Y_CRCB_H2V2,\
- [MDP_YCRYCB_H2V1] = PPP_##name##_MDP_YCRYCB_H2V1
-
-#define PPP_ARRAY1(name, dir) \
- [MDP_RGB_565] = PPP_##name##_MDP_RGB_565(dir),\
- [MDP_RGB_888] = PPP_##name##_MDP_RGB_888(dir),\
- [MDP_XRGB_8888] = PPP_##name##_MDP_XRGB_8888(dir),\
- [MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888(dir),\
- [MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888(dir),\
- [MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888(dir),\
- [MDP_RGBX_8888] = PPP_##name##_MDP_RGBX_8888(dir),\
- [MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1(dir),\
- [MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2(dir),\
- [MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1(dir),\
- [MDP_Y_CRCB_H2V2] = PPP_##name##_MDP_Y_CRCB_H2V2(dir),\
- [MDP_YCRYCB_H2V1] = PPP_##name##_MDP_YCRYCB_H2V1(dir)
-
-#define IS_YCRCB(img) ((img == MDP_Y_CRCB_H2V2) | (img == MDP_Y_CBCR_H2V2) | \
- (img == MDP_Y_CRCB_H2V1) | (img == MDP_Y_CBCR_H2V1) | \
- (img == MDP_YCRYCB_H2V1))
-#define IS_RGB(img) ((img == MDP_RGB_565) | (img == MDP_RGB_888) | \
- (img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \
- (img == MDP_XRGB_8888) | (img == MDP_BGRA_8888) | \
- (img == MDP_RGBX_8888))
-#define HAS_ALPHA(img) ((img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \
- (img == MDP_BGRA_8888))
-
-#define IS_PSEUDOPLNR(img) ((img == MDP_Y_CRCB_H2V2) | \
- (img == MDP_Y_CBCR_H2V2) | \
- (img == MDP_Y_CRCB_H2V1) | \
- (img == MDP_Y_CBCR_H2V1))
-
-/* Mappings from addr to purpose */
-#define PPP_ADDR_SRC_ROI MDP_FULL_BYPASS_WORD2
-#define PPP_ADDR_SRC0 MDP_FULL_BYPASS_WORD3
-#define PPP_ADDR_SRC1 MDP_FULL_BYPASS_WORD4
-#define PPP_ADDR_SRC_YSTRIDE MDP_FULL_BYPASS_WORD7
-#define PPP_ADDR_SRC_CFG MDP_FULL_BYPASS_WORD9
-#define PPP_ADDR_SRC_PACK_PATTERN MDP_FULL_BYPASS_WORD10
-#define PPP_ADDR_OPERATION MDP_FULL_BYPASS_WORD14
-#define PPP_ADDR_PHASEX_INIT MDP_FULL_BYPASS_WORD15
-#define PPP_ADDR_PHASEY_INIT MDP_FULL_BYPASS_WORD16
-#define PPP_ADDR_PHASEX_STEP MDP_FULL_BYPASS_WORD17
-#define PPP_ADDR_PHASEY_STEP MDP_FULL_BYPASS_WORD18
-#define PPP_ADDR_ALPHA_TRANSP MDP_FULL_BYPASS_WORD19
-#define PPP_ADDR_DST_CFG MDP_FULL_BYPASS_WORD20
-#define PPP_ADDR_DST_PACK_PATTERN MDP_FULL_BYPASS_WORD21
-#define PPP_ADDR_DST_ROI MDP_FULL_BYPASS_WORD25
-#define PPP_ADDR_DST0 MDP_FULL_BYPASS_WORD26
-#define PPP_ADDR_DST1 MDP_FULL_BYPASS_WORD27
-#define PPP_ADDR_DST_YSTRIDE MDP_FULL_BYPASS_WORD30
-#define PPP_ADDR_EDGE MDP_FULL_BYPASS_WORD46
-#define PPP_ADDR_BG0 MDP_FULL_BYPASS_WORD48
-#define PPP_ADDR_BG1 MDP_FULL_BYPASS_WORD49
-#define PPP_ADDR_BG_YSTRIDE MDP_FULL_BYPASS_WORD51
-#define PPP_ADDR_BG_CFG MDP_FULL_BYPASS_WORD53
-#define PPP_ADDR_BG_PACK_PATTERN MDP_FULL_BYPASS_WORD54
-
-/* MDP_DMA_CONFIG / MDP_FULL_BYPASS_WORD32 */
-#define DMA_DSTC0G_6BITS (1<<1)
-#define DMA_DSTC1B_6BITS (1<<3)
-#define DMA_DSTC2R_6BITS (1<<5)
-#define DMA_DSTC0G_5BITS (1<<0)
-#define DMA_DSTC1B_5BITS (1<<2)
-#define DMA_DSTC2R_5BITS (1<<4)
-
-#define DMA_PACK_TIGHT (1<<6)
-#define DMA_PACK_LOOSE 0
-#define DMA_PACK_ALIGN_LSB 0
-#define DMA_PACK_ALIGN_MSB (1<<7)
-#define DMA_PACK_PATTERN_RGB \
- (MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 2)<<8)
-
-#define DMA_OUT_SEL_AHB 0
-#define DMA_OUT_SEL_MDDI (1<<14)
-#define DMA_AHBM_LCD_SEL_PRIMARY 0
-#define DMA_AHBM_LCD_SEL_SECONDARY (1<<15)
-#define DMA_IBUF_C3ALPHA_EN (1<<16)
-#define DMA_DITHER_EN (1<<17)
-
-#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY 0
-#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (1<<18)
-#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (1<<19)
-
-#define DMA_IBUF_FORMAT_RGB565 (1<<20)
-#define DMA_IBUF_FORMAT_RGB888_OR_ARGB8888 0
-
-#define DMA_IBUF_NONCONTIGUOUS (1<<21)
-
-/* MDDI REGISTER ? */
-#define MDDI_VDO_PACKET_DESC 0x5666
-#define MDDI_VDO_PACKET_PRIM 0xC3
-#define MDDI_VDO_PACKET_SECD 0xC0
-
-#endif
diff --git a/drivers/video/fbdev/msm/mdp_ppp.c b/drivers/video/fbdev/msm/mdp_ppp.c
deleted file mode 100644
index be6079cdfbb6..000000000000
--- a/drivers/video/fbdev/msm/mdp_ppp.c
+++ /dev/null
@@ -1,731 +0,0 @@
-/* drivers/video/msm/mdp_ppp.c
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * 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/fb.h>
-#include <linux/file.h>
-#include <linux/delay.h>
-#include <linux/msm_mdp.h>
-#include <linux/platform_data/video-msm_fb.h>
-
-#include "mdp_hw.h"
-#include "mdp_scale_tables.h"
-
-#define DLOG(x...) do {} while (0)
-
-#define MDP_DOWNSCALE_BLUR (MDP_DOWNSCALE_MAX + 1)
-static int downscale_y_table = MDP_DOWNSCALE_MAX;
-static int downscale_x_table = MDP_DOWNSCALE_MAX;
-
-struct mdp_regs {
- uint32_t src0;
- uint32_t src1;
- uint32_t dst0;
- uint32_t dst1;
- uint32_t src_cfg;
- uint32_t dst_cfg;
- uint32_t src_pack;
- uint32_t dst_pack;
- uint32_t src_rect;
- uint32_t dst_rect;
- uint32_t src_ystride;
- uint32_t dst_ystride;
- uint32_t op;
- uint32_t src_bpp;
- uint32_t dst_bpp;
- uint32_t edge;
- uint32_t phasex_init;
- uint32_t phasey_init;
- uint32_t phasex_step;
- uint32_t phasey_step;
-};
-
-static uint32_t pack_pattern[] = {
- PPP_ARRAY0(PACK_PATTERN)
-};
-
-static uint32_t src_img_cfg[] = {
- PPP_ARRAY1(CFG, SRC)
-};
-
-static uint32_t dst_img_cfg[] = {
- PPP_ARRAY1(CFG, DST)
-};
-
-static uint32_t bytes_per_pixel[] = {
- [MDP_RGB_565] = 2,
- [MDP_RGB_888] = 3,
- [MDP_XRGB_8888] = 4,
- [MDP_ARGB_8888] = 4,
- [MDP_RGBA_8888] = 4,
- [MDP_BGRA_8888] = 4,
- [MDP_RGBX_8888] = 4,
- [MDP_Y_CBCR_H2V1] = 1,
- [MDP_Y_CBCR_H2V2] = 1,
- [MDP_Y_CRCB_H2V1] = 1,
- [MDP_Y_CRCB_H2V2] = 1,
- [MDP_YCRYCB_H2V1] = 2
-};
-
-static uint32_t dst_op_chroma[] = {
- PPP_ARRAY1(CHROMA_SAMP, DST)
-};
-
-static uint32_t src_op_chroma[] = {
- PPP_ARRAY1(CHROMA_SAMP, SRC)
-};
-
-static uint32_t bg_op_chroma[] = {
- PPP_ARRAY1(CHROMA_SAMP, BG)
-};
-
-static void rotate_dst_addr_x(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
- regs->dst0 += (req->dst_rect.w -
- min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp;
- regs->dst1 += (req->dst_rect.w -
- min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp;
-}
-
-static void rotate_dst_addr_y(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
- regs->dst0 += (req->dst_rect.h -
- min((uint32_t)16, req->dst_rect.h)) *
- regs->dst_ystride;
- regs->dst1 += (req->dst_rect.h -
- min((uint32_t)16, req->dst_rect.h)) *
- regs->dst_ystride;
-}
-
-static void blit_rotate(struct mdp_blit_req *req,
- struct mdp_regs *regs)
-{
- if (req->flags == MDP_ROT_NOP)
- return;
-
- regs->op |= PPP_OP_ROT_ON;
- if ((req->flags & MDP_ROT_90 || req->flags & MDP_FLIP_LR) &&
- !(req->flags & MDP_ROT_90 && req->flags & MDP_FLIP_LR))
- rotate_dst_addr_x(req, regs);
- if (req->flags & MDP_ROT_90)
- regs->op |= PPP_OP_ROT_90;
- if (req->flags & MDP_FLIP_UD) {
- regs->op |= PPP_OP_FLIP_UD;
- rotate_dst_addr_y(req, regs);
- }
- if (req->flags & MDP_FLIP_LR)
- regs->op |= PPP_OP_FLIP_LR;
-}
-
-static void blit_convert(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
- if (req->src.format == req->dst.format)
- return;
- if (IS_RGB(req->src.format) && IS_YCRCB(req->dst.format)) {
- regs->op |= PPP_OP_CONVERT_RGB2YCBCR | PPP_OP_CONVERT_ON;
- } else if (IS_YCRCB(req->src.format) && IS_RGB(req->dst.format)) {
- regs->op |= PPP_OP_CONVERT_YCBCR2RGB | PPP_OP_CONVERT_ON;
- if (req->dst.format == MDP_RGB_565)
- regs->op |= PPP_OP_CONVERT_MATRIX_SECONDARY;
- }
-}
-
-#define GET_BIT_RANGE(value, high, low) \
- (((1 << (high - low + 1)) - 1) & (value >> low))
-static uint32_t transp_convert(struct mdp_blit_req *req)
-{
- uint32_t transp = 0;
- if (req->src.format == MDP_RGB_565) {
- /* pad each value to 8 bits by copying the high bits into the
- * low end, convert RGB to RBG by switching low 2 components */
- transp |= ((GET_BIT_RANGE(req->transp_mask, 15, 11) << 3) |
- (GET_BIT_RANGE(req->transp_mask, 15, 13))) << 16;
-
- transp |= ((GET_BIT_RANGE(req->transp_mask, 4, 0) << 3) |
- (GET_BIT_RANGE(req->transp_mask, 4, 2))) << 8;
-
- transp |= (GET_BIT_RANGE(req->transp_mask, 10, 5) << 2) |
- (GET_BIT_RANGE(req->transp_mask, 10, 9));
- } else {
- /* convert RGB to RBG */
- transp |= (GET_BIT_RANGE(req->transp_mask, 15, 8)) |
- (GET_BIT_RANGE(req->transp_mask, 23, 16) << 16) |
- (GET_BIT_RANGE(req->transp_mask, 7, 0) << 8);
- }
- return transp;
-}
-#undef GET_BIT_RANGE
-
-static void blit_blend(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
- /* TRANSP BLEND */
- if (req->transp_mask != MDP_TRANSP_NOP) {
- req->transp_mask = transp_convert(req);
- if (req->alpha != MDP_ALPHA_NOP) {
- /* use blended transparancy mode
- * pixel = (src == transp) ? dst : blend
- * blend is combo of blend_eq_sel and
- * blend_alpha_sel */
- regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
- PPP_OP_BLEND_ALPHA_BLEND_NORMAL |
- PPP_OP_BLEND_CONSTANT_ALPHA |
- PPP_BLEND_ALPHA_TRANSP;
- } else {
- /* simple transparancy mode
- * pixel = (src == transp) ? dst : src */
- regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
- PPP_OP_BLEND_SRCPIXEL_TRANSP;
- }
- }
-
- req->alpha &= 0xff;
- /* ALPHA BLEND */
- if (HAS_ALPHA(req->src.format)) {
- regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
- PPP_OP_BLEND_SRCPIXEL_ALPHA;
- } else if (req->alpha < MDP_ALPHA_NOP) {
- /* just blend by alpha */
- regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
- PPP_OP_BLEND_ALPHA_BLEND_NORMAL |
- PPP_OP_BLEND_CONSTANT_ALPHA;
- }
-
- regs->op |= bg_op_chroma[req->dst.format];
-}
-
-#define ONE_HALF (1LL << 32)
-#define ONE (1LL << 33)
-#define TWO (2LL << 33)
-#define THREE (3LL << 33)
-#define FRAC_MASK (ONE - 1)
-#define INT_MASK (~FRAC_MASK)
-
-static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin,
- uint32_t *phase_init, uint32_t *phase_step)
-{
- /* to improve precicsion calculations are done in U31.33 and converted
- * to U3.29 at the end */
- int64_t k1, k2, k3, k4, tmp;
- uint64_t n, d, os, os_p, od, od_p, oreq;
- unsigned rpa = 0;
- int64_t ip64, delta;
-
- if (dim_out % 3 == 0)
- rpa = !(dim_in % (dim_out / 3));
-
- n = ((uint64_t)dim_out) << 34;
- d = dim_in;
- if (!d)
- return -1;
- do_div(n, d);
- k3 = (n + 1) >> 1;
- if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31)) {
- DLOG("crap bad scale\n");
- return -1;
- }
- n = ((uint64_t)dim_in) << 34;
- d = (uint64_t)dim_out;
- if (!d)
- return -1;
- do_div(n, d);
- k1 = (n + 1) >> 1;
- k2 = (k1 - ONE) >> 1;
-
- *phase_init = (int)(k2 >> 4);
- k4 = (k3 - ONE) >> 1;
-
- if (rpa) {
- os = ((uint64_t)origin << 33) - ONE_HALF;
- tmp = (dim_out * os) + ONE_HALF;
- if (!dim_in)
- return -1;
- do_div(tmp, dim_in);
- od = tmp - ONE_HALF;
- } else {
- os = ((uint64_t)origin << 1) - 1;
- od = (((k3 * os) >> 1) + k4);
- }
-
- od_p = od & INT_MASK;
- if (od_p != od)
- od_p += ONE;
-
- if (rpa) {
- tmp = (dim_in * od_p) + ONE_HALF;
- if (!dim_in)
- return -1;
- do_div(tmp, dim_in);
- os_p = tmp - ONE_HALF;
- } else {
- os_p = ((k1 * (od_p >> 33)) + k2);
- }
-
- oreq = (os_p & INT_MASK) - ONE;
-
- ip64 = os_p - oreq;
- delta = ((int64_t)(origin) << 33) - oreq;
- ip64 -= delta;
- /* limit to valid range before the left shift */
- delta = (ip64 & (1LL << 63)) ? 4 : -4;
- delta <<= 33;
- while (abs((int)(ip64 >> 33)) > 4)
- ip64 += delta;
- *phase_init = (int)(ip64 >> 4);
- *phase_step = (uint32_t)(k1 >> 4);
- return 0;
-}
-
-static void load_scale_table(const struct mdp_info *mdp,
- struct mdp_table_entry *table, int len)
-{
- int i;
- for (i = 0; i < len; i++)
- mdp_writel(mdp, table[i].val, table[i].reg);
-}
-
-enum {
-IMG_LEFT,
-IMG_RIGHT,
-IMG_TOP,
-IMG_BOTTOM,
-};
-
-static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst,
- uint32_t *interp1, uint32_t *interp2,
- uint32_t *repeat1, uint32_t *repeat2) {
- if (src > 3 * dst) {
- *interp1 = 0;
- *interp2 = src - 1;
- *repeat1 = 0;
- *repeat2 = 0;
- } else if (src == 3 * dst) {
- *interp1 = 0;
- *interp2 = src;
- *repeat1 = 0;
- *repeat2 = 1;
- } else if (src > dst && src < 3 * dst) {
- *interp1 = -1;
- *interp2 = src;
- *repeat1 = 1;
- *repeat2 = 1;
- } else if (src == dst) {
- *interp1 = -1;
- *interp2 = src + 1;
- *repeat1 = 1;
- *repeat2 = 2;
- } else {
- *interp1 = -2;
- *interp2 = src + 1;
- *repeat1 = 2;
- *repeat2 = 2;
- }
- *interp1 += src_coord;
- *interp2 += src_coord;
-}
-
-static int get_edge_cond(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
- int32_t luma_interp[4];
- int32_t luma_repeat[4];
- int32_t chroma_interp[4];
- int32_t chroma_bound[4];
- int32_t chroma_repeat[4];
- uint32_t dst_w, dst_h;
-
- memset(&luma_interp, 0, sizeof(int32_t) * 4);
- memset(&luma_repeat, 0, sizeof(int32_t) * 4);
- memset(&chroma_interp, 0, sizeof(int32_t) * 4);
- memset(&chroma_bound, 0, sizeof(int32_t) * 4);
- memset(&chroma_repeat, 0, sizeof(int32_t) * 4);
- regs->edge = 0;
-
- if (req->flags & MDP_ROT_90) {
- dst_w = req->dst_rect.h;
- dst_h = req->dst_rect.w;
- } else {
- dst_w = req->dst_rect.w;
- dst_h = req->dst_rect.h;
- }
-
- if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) {
- get_edge_info(req->src_rect.h, req->src_rect.y, dst_h,
- &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM],
- &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]);
- get_edge_info(req->src_rect.w, req->src_rect.x, dst_w,
- &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT],
- &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]);
- } else {
- luma_interp[IMG_LEFT] = req->src_rect.x;
- luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
- luma_interp[IMG_TOP] = req->src_rect.y;
- luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
- luma_repeat[IMG_LEFT] = 0;
- luma_repeat[IMG_TOP] = 0;
- luma_repeat[IMG_RIGHT] = 0;
- luma_repeat[IMG_BOTTOM] = 0;
- }
-
- chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT];
- chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT];
- chroma_interp[IMG_TOP] = luma_interp[IMG_TOP];
- chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM];
-
- chroma_bound[IMG_LEFT] = req->src_rect.x;
- chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
- chroma_bound[IMG_TOP] = req->src_rect.y;
- chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
-
- if (IS_YCRCB(req->src.format)) {
- chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1;
- chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1;
-
- chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1;
- chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1;
- }
-
- if (req->src.format == MDP_Y_CBCR_H2V2 ||
- req->src.format == MDP_Y_CRCB_H2V2) {
- chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1;
- chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1)
- >> 1;
- chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1;
- chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1;
- }
-
- chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] -
- chroma_interp[IMG_LEFT];
- chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] -
- chroma_bound[IMG_RIGHT];
- chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] -
- chroma_interp[IMG_TOP];
- chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] -
- chroma_bound[IMG_BOTTOM];
-
- if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 ||
- chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 ||
- chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 ||
- chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 ||
- luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 ||
- luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 ||
- luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 ||
- luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3)
- return -1;
-
- regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA;
- regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA;
- regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA;
- regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA;
- regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA;
- regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA;
- regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA;
- regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA;
- return 0;
-}
-
-static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req,
- struct mdp_regs *regs)
-{
- uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y;
- uint32_t scale_factor_x, scale_factor_y;
- uint32_t downscale;
- uint32_t dst_w, dst_h;
-
- if (req->flags & MDP_ROT_90) {
- dst_w = req->dst_rect.h;
- dst_h = req->dst_rect.w;
- } else {
- dst_w = req->dst_rect.w;
- dst_h = req->dst_rect.h;
- }
- if ((req->src_rect.w == dst_w) && (req->src_rect.h == dst_h) &&
- !(req->flags & MDP_BLUR)) {
- regs->phasex_init = 0;
- regs->phasey_init = 0;
- regs->phasex_step = 0;
- regs->phasey_step = 0;
- return 0;
- }
-
- if (scale_params(req->src_rect.w, dst_w, 1, &phase_init_x,
- &phase_step_x) ||
- scale_params(req->src_rect.h, dst_h, 1, &phase_init_y,
- &phase_step_y))
- return -1;
-
- scale_factor_x = (dst_w * 10) / req->src_rect.w;
- scale_factor_y = (dst_h * 10) / req->src_rect.h;
-
- if (scale_factor_x > 8)
- downscale = MDP_DOWNSCALE_PT8TO1;
- else if (scale_factor_x > 6)
- downscale = MDP_DOWNSCALE_PT6TOPT8;
- else if (scale_factor_x > 4)
- downscale = MDP_DOWNSCALE_PT4TOPT6;
- else
- downscale = MDP_DOWNSCALE_PT2TOPT4;
- if (downscale != downscale_x_table) {
- load_scale_table(mdp, mdp_downscale_x_table[downscale], 64);
- downscale_x_table = downscale;
- }
-
- if (scale_factor_y > 8)
- downscale = MDP_DOWNSCALE_PT8TO1;
- else if (scale_factor_y > 6)
- downscale = MDP_DOWNSCALE_PT6TOPT8;
- else if (scale_factor_y > 4)
- downscale = MDP_DOWNSCALE_PT4TOPT6;
- else
- downscale = MDP_DOWNSCALE_PT2TOPT4;
- if (downscale != downscale_y_table) {
- load_scale_table(mdp, mdp_downscale_y_table[downscale], 64);
- downscale_y_table = downscale;
- }
-
- regs->phasex_init = phase_init_x;
- regs->phasey_init = phase_init_y;
- regs->phasex_step = phase_step_x;
- regs->phasey_step = phase_step_y;
- regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
- return 0;
-
-}
-
-static void blit_blur(const struct mdp_info *mdp, struct mdp_blit_req *req,
- struct mdp_regs *regs)
-{
- if (!(req->flags & MDP_BLUR))
- return;
-
- if (!(downscale_x_table == MDP_DOWNSCALE_BLUR &&
- downscale_y_table == MDP_DOWNSCALE_BLUR)) {
- load_scale_table(mdp, mdp_gaussian_blur_table, 128);
- downscale_x_table = MDP_DOWNSCALE_BLUR;
- downscale_y_table = MDP_DOWNSCALE_BLUR;
- }
-
- regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
-}
-
-
-#define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp)
-
-#define Y_TO_CRCB_RATIO(format) \
- ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ? 2 :\
- (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ? 1 : 1)
-
-static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp,
- uint32_t *len0, uint32_t *len1)
-{
- *len0 = IMG_LEN(rect->h, img->width, rect->w, bpp);
- if (IS_PSEUDOPLNR(img->format))
- *len1 = *len0/Y_TO_CRCB_RATIO(img->format);
- else
- *len1 = 0;
-}
-
-static int valid_src_dst(unsigned long src_start, unsigned long src_len,
- unsigned long dst_start, unsigned long dst_len,
- struct mdp_blit_req *req, struct mdp_regs *regs)
-{
- unsigned long src_min_ok = src_start;
- unsigned long src_max_ok = src_start + src_len;
- unsigned long dst_min_ok = dst_start;
- unsigned long dst_max_ok = dst_start + dst_len;
- uint32_t src0_len, src1_len, dst0_len, dst1_len;
- get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len,
- &src1_len);
- get_len(&req->dst, &req->dst_rect, regs->dst_bpp, &dst0_len,
- &dst1_len);
-
- if (regs->src0 < src_min_ok || regs->src0 > src_max_ok ||
- regs->src0 + src0_len > src_max_ok) {
- DLOG("invalid_src %x %x %lx %lx\n", regs->src0,
- src0_len, src_min_ok, src_max_ok);
- return 0;
- }
- if (regs->src_cfg & PPP_SRC_PLANE_PSEUDOPLNR) {
- if (regs->src1 < src_min_ok || regs->src1 > src_max_ok ||
- regs->src1 + src1_len > src_max_ok) {
- DLOG("invalid_src1");
- return 0;
- }
- }
- if (regs->dst0 < dst_min_ok || regs->dst0 > dst_max_ok ||
- regs->dst0 + dst0_len > dst_max_ok) {
- DLOG("invalid_dst");
- return 0;
- }
- if (regs->dst_cfg & PPP_SRC_PLANE_PSEUDOPLNR) {
- if (regs->dst1 < dst_min_ok || regs->dst1 > dst_max_ok ||
- regs->dst1 + dst1_len > dst_max_ok) {
- DLOG("invalid_dst1");
- return 0;
- }
- }
- return 1;
-}
-
-
-static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs,
- struct file *src_file, struct file *dst_file)
-{
-}
-
-static void get_chroma_addr(struct mdp_img *img, struct mdp_rect *rect,
- uint32_t base, uint32_t bpp, uint32_t cfg,
- uint32_t *addr, uint32_t *ystride)
-{
- uint32_t compress_v = Y_TO_CRCB_RATIO(img->format);
- uint32_t compress_h = 2;
- uint32_t offset;
-
- if (IS_PSEUDOPLNR(img->format)) {
- offset = (rect->x / compress_h) * compress_h;
- offset += rect->y == 0 ? 0 :
- ((rect->y + 1) / compress_v) * img->width;
- *addr = base + (img->width * img->height * bpp);
- *addr += offset * bpp;
- *ystride |= *ystride << 16;
- } else {
- *addr = 0;
- }
-}
-
-static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
- struct mdp_regs *regs, struct file *src_file,
- struct file *dst_file)
-{
- mdp_writel(mdp, 1, 0x060);
- mdp_writel(mdp, regs->src_rect, PPP_ADDR_SRC_ROI);
- mdp_writel(mdp, regs->src0, PPP_ADDR_SRC0);
- mdp_writel(mdp, regs->src1, PPP_ADDR_SRC1);
- mdp_writel(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE);
- mdp_writel(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG);
- mdp_writel(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN);
-
- mdp_writel(mdp, regs->op, PPP_ADDR_OPERATION);
- mdp_writel(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT);
- mdp_writel(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT);
- mdp_writel(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP);
- mdp_writel(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP);
-
- mdp_writel(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff),
- PPP_ADDR_ALPHA_TRANSP);
-
- mdp_writel(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG);
- mdp_writel(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN);
- mdp_writel(mdp, regs->dst_rect, PPP_ADDR_DST_ROI);
- mdp_writel(mdp, regs->dst0, PPP_ADDR_DST0);
- mdp_writel(mdp, regs->dst1, PPP_ADDR_DST1);
- mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE);
-
- mdp_writel(mdp, regs->edge, PPP_ADDR_EDGE);
- if (regs->op & PPP_OP_BLEND_ON) {
- mdp_writel(mdp, regs->dst0, PPP_ADDR_BG0);
- mdp_writel(mdp, regs->dst1, PPP_ADDR_BG1);
- mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_BG_YSTRIDE);
- mdp_writel(mdp, src_img_cfg[req->dst.format], PPP_ADDR_BG_CFG);
- mdp_writel(mdp, pack_pattern[req->dst.format],
- PPP_ADDR_BG_PACK_PATTERN);
- }
- flush_imgs(req, regs, src_file, dst_file);
- mdp_writel(mdp, 0x1000, MDP_DISPLAY0_START);
- return 0;
-}
-
-int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
- struct file *src_file, unsigned long src_start, unsigned long src_len,
- struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
-{
- struct mdp_regs regs = {0};
-
- if (unlikely(req->src.format >= MDP_IMGTYPE_LIMIT ||
- req->dst.format >= MDP_IMGTYPE_LIMIT)) {
- printk(KERN_ERR "mpd_ppp: img is of wrong format\n");
- return -EINVAL;
- }
-
- if (unlikely(req->src_rect.x > req->src.width ||
- req->src_rect.y > req->src.height ||
- req->dst_rect.x > req->dst.width ||
- req->dst_rect.y > req->dst.height)) {
- printk(KERN_ERR "mpd_ppp: img rect is outside of img!\n");
- return -EINVAL;
- }
-
- /* set the src image configuration */
- regs.src_cfg = src_img_cfg[req->src.format];
- regs.src_cfg |= (req->src_rect.x & 0x1) ? PPP_SRC_BPP_ROI_ODD_X : 0;
- regs.src_cfg |= (req->src_rect.y & 0x1) ? PPP_SRC_BPP_ROI_ODD_Y : 0;
- regs.src_rect = (req->src_rect.h << 16) | req->src_rect.w;
- regs.src_pack = pack_pattern[req->src.format];
-
- /* set the dest image configuration */
- regs.dst_cfg = dst_img_cfg[req->dst.format] | PPP_DST_OUT_SEL_AXI;
- regs.dst_rect = (req->dst_rect.h << 16) | req->dst_rect.w;
- regs.dst_pack = pack_pattern[req->dst.format];
-
- /* set src, bpp, start pixel and ystride */
- regs.src_bpp = bytes_per_pixel[req->src.format];
- regs.src0 = src_start + req->src.offset;
- regs.src_ystride = req->src.width * regs.src_bpp;
- get_chroma_addr(&req->src, &req->src_rect, regs.src0, regs.src_bpp,
- regs.src_cfg, &regs.src1, &regs.src_ystride);
- regs.src0 += (req->src_rect.x + (req->src_rect.y * req->src.width)) *
- regs.src_bpp;
-
- /* set dst, bpp, start pixel and ystride */
- regs.dst_bpp = bytes_per_pixel[req->dst.format];
- regs.dst0 = dst_start + req->dst.offset;
- regs.dst_ystride = req->dst.width * regs.dst_bpp;
- get_chroma_addr(&req->dst, &req->dst_rect, regs.dst0, regs.dst_bpp,
- regs.dst_cfg, &regs.dst1, &regs.dst_ystride);
- regs.dst0 += (req->dst_rect.x + (req->dst_rect.y * req->dst.width)) *
- regs.dst_bpp;
-
- if (!valid_src_dst(src_start, src_len, dst_start, dst_len, req,
- &regs)) {
- printk(KERN_ERR "mpd_ppp: final src or dst location is "
- "invalid, are you trying to make an image too large "
- "or to place it outside the screen?\n");
- return -EINVAL;
- }
-
- /* set up operation register */
- regs.op = 0;
- blit_rotate(req, &regs);
- blit_convert(req, &regs);
- if (req->flags & MDP_DITHER)
- regs.op |= PPP_OP_DITHER_EN;
- blit_blend(req, &regs);
- if (blit_scale(mdp, req, &regs)) {
- printk(KERN_ERR "mpd_ppp: error computing scale for img.\n");
- return -EINVAL;
- }
- blit_blur(mdp, req, &regs);
- regs.op |= dst_op_chroma[req->dst.format] |
- src_op_chroma[req->src.format];
-
- /* if the image is YCRYCB, the x and w must be even */
- if (unlikely(req->src.format == MDP_YCRYCB_H2V1)) {
- req->src_rect.x = req->src_rect.x & (~0x1);
- req->src_rect.w = req->src_rect.w & (~0x1);
- req->dst_rect.x = req->dst_rect.x & (~0x1);
- req->dst_rect.w = req->dst_rect.w & (~0x1);
- }
- if (get_edge_cond(req, &regs))
- return -EINVAL;
-
- send_blit(mdp, req, &regs, src_file, dst_file);
- return 0;
-}
diff --git a/drivers/video/fbdev/msm/mdp_scale_tables.c b/drivers/video/fbdev/msm/mdp_scale_tables.c
deleted file mode 100644
index 604783b2e17c..000000000000
--- a/drivers/video/fbdev/msm/mdp_scale_tables.c
+++ /dev/null
@@ -1,766 +0,0 @@
-/* drivers/video/msm_fb/mdp_scale_tables.c
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * 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 "mdp_scale_tables.h"
-#include "mdp_hw.h"
-
-struct mdp_table_entry mdp_upscale_table[] = {
- { 0x5fffc, 0x0 },
- { 0x50200, 0x7fc00000 },
- { 0x5fffc, 0xff80000d },
- { 0x50204, 0x7ec003f9 },
- { 0x5fffc, 0xfec0001c },
- { 0x50208, 0x7d4003f3 },
- { 0x5fffc, 0xfe40002b },
- { 0x5020c, 0x7b8003ed },
- { 0x5fffc, 0xfd80003c },
- { 0x50210, 0x794003e8 },
- { 0x5fffc, 0xfcc0004d },
- { 0x50214, 0x76c003e4 },
- { 0x5fffc, 0xfc40005f },
- { 0x50218, 0x73c003e0 },
- { 0x5fffc, 0xfb800071 },
- { 0x5021c, 0x708003de },
- { 0x5fffc, 0xfac00085 },
- { 0x50220, 0x6d0003db },
- { 0x5fffc, 0xfa000098 },
- { 0x50224, 0x698003d9 },
- { 0x5fffc, 0xf98000ac },
- { 0x50228, 0x654003d8 },
- { 0x5fffc, 0xf8c000c1 },
- { 0x5022c, 0x610003d7 },
- { 0x5fffc, 0xf84000d5 },
- { 0x50230, 0x5c8003d7 },
- { 0x5fffc, 0xf7c000e9 },
- { 0x50234, 0x580003d7 },
- { 0x5fffc, 0xf74000fd },
- { 0x50238, 0x534003d8 },
- { 0x5fffc, 0xf6c00112 },
- { 0x5023c, 0x4e8003d8 },
- { 0x5fffc, 0xf6800126 },
- { 0x50240, 0x494003da },
- { 0x5fffc, 0xf600013a },
- { 0x50244, 0x448003db },
- { 0x5fffc, 0xf600014d },
- { 0x50248, 0x3f4003dd },
- { 0x5fffc, 0xf5c00160 },
- { 0x5024c, 0x3a4003df },
- { 0x5fffc, 0xf5c00172 },
- { 0x50250, 0x354003e1 },
- { 0x5fffc, 0xf5c00184 },
- { 0x50254, 0x304003e3 },
- { 0x5fffc, 0xf6000195 },
- { 0x50258, 0x2b0003e6 },
- { 0x5fffc, 0xf64001a6 },
- { 0x5025c, 0x260003e8 },
- { 0x5fffc, 0xf6c001b4 },
- { 0x50260, 0x214003eb },
- { 0x5fffc, 0xf78001c2 },
- { 0x50264, 0x1c4003ee },
- { 0x5fffc, 0xf80001cf },
- { 0x50268, 0x17c003f1 },
- { 0x5fffc, 0xf90001db },
- { 0x5026c, 0x134003f3 },
- { 0x5fffc, 0xfa0001e5 },
- { 0x50270, 0xf0003f6 },
- { 0x5fffc, 0xfb4001ee },
- { 0x50274, 0xac003f9 },
- { 0x5fffc, 0xfcc001f5 },
- { 0x50278, 0x70003fb },
- { 0x5fffc, 0xfe4001fb },
- { 0x5027c, 0x34003fe },
-};
-
-static struct mdp_table_entry mdp_downscale_x_table_PT2TOPT4[] = {
- { 0x5fffc, 0x740008c },
- { 0x50280, 0x33800088 },
- { 0x5fffc, 0x800008e },
- { 0x50284, 0x33400084 },
- { 0x5fffc, 0x8400092 },
- { 0x50288, 0x33000080 },
- { 0x5fffc, 0x9000094 },
- { 0x5028c, 0x3300007b },
- { 0x5fffc, 0x9c00098 },
- { 0x50290, 0x32400077 },
- { 0x5fffc, 0xa40009b },
- { 0x50294, 0x32000073 },
- { 0x5fffc, 0xb00009d },
- { 0x50298, 0x31c0006f },
- { 0x5fffc, 0xbc000a0 },
- { 0x5029c, 0x3140006b },
- { 0x5fffc, 0xc8000a2 },
- { 0x502a0, 0x31000067 },
- { 0x5fffc, 0xd8000a5 },
- { 0x502a4, 0x30800062 },
- { 0x5fffc, 0xe4000a8 },
- { 0x502a8, 0x2fc0005f },
- { 0x5fffc, 0xec000aa },
- { 0x502ac, 0x2fc0005b },
- { 0x5fffc, 0xf8000ad },
- { 0x502b0, 0x2f400057 },
- { 0x5fffc, 0x108000b0 },
- { 0x502b4, 0x2e400054 },
- { 0x5fffc, 0x114000b2 },
- { 0x502b8, 0x2e000050 },
- { 0x5fffc, 0x124000b4 },
- { 0x502bc, 0x2d80004c },
- { 0x5fffc, 0x130000b6 },
- { 0x502c0, 0x2d000049 },
- { 0x5fffc, 0x140000b8 },
- { 0x502c4, 0x2c800045 },
- { 0x5fffc, 0x150000b9 },
- { 0x502c8, 0x2c000042 },
- { 0x5fffc, 0x15c000bd },
- { 0x502cc, 0x2b40003e },
- { 0x5fffc, 0x16c000bf },
- { 0x502d0, 0x2a80003b },
- { 0x5fffc, 0x17c000bf },
- { 0x502d4, 0x2a000039 },
- { 0x5fffc, 0x188000c2 },
- { 0x502d8, 0x29400036 },
- { 0x5fffc, 0x19c000c4 },
- { 0x502dc, 0x28800032 },
- { 0x5fffc, 0x1ac000c5 },
- { 0x502e0, 0x2800002f },
- { 0x5fffc, 0x1bc000c7 },
- { 0x502e4, 0x2740002c },
- { 0x5fffc, 0x1cc000c8 },
- { 0x502e8, 0x26c00029 },
- { 0x5fffc, 0x1dc000c9 },
- { 0x502ec, 0x26000027 },
- { 0x5fffc, 0x1ec000cc },
- { 0x502f0, 0x25000024 },
- { 0x5fffc, 0x200000cc },
- { 0x502f4, 0x24800021 },
- { 0x5fffc, 0x210000cd },
- { 0x502f8, 0x23800020 },
- { 0x5fffc, 0x220000ce },
- { 0x502fc, 0x2300001d },
-};
-
-static struct mdp_table_entry mdp_downscale_x_table_PT4TOPT6[] = {
- { 0x5fffc, 0x740008c },
- { 0x50280, 0x33800088 },
- { 0x5fffc, 0x800008e },
- { 0x50284, 0x33400084 },
- { 0x5fffc, 0x8400092 },
- { 0x50288, 0x33000080 },
- { 0x5fffc, 0x9000094 },
- { 0x5028c, 0x3300007b },
- { 0x5fffc, 0x9c00098 },
- { 0x50290, 0x32400077 },
- { 0x5fffc, 0xa40009b },
- { 0x50294, 0x32000073 },
- { 0x5fffc, 0xb00009d },
- { 0x50298, 0x31c0006f },
- { 0x5fffc, 0xbc000a0 },
- { 0x5029c, 0x3140006b },
- { 0x5fffc, 0xc8000a2 },
- { 0x502a0, 0x31000067 },
- { 0x5fffc, 0xd8000a5 },
- { 0x502a4, 0x30800062 },
- { 0x5fffc, 0xe4000a8 },
- { 0x502a8, 0x2fc0005f },
- { 0x5fffc, 0xec000aa },
- { 0x502ac, 0x2fc0005b },
- { 0x5fffc, 0xf8000ad },
- { 0x502b0, 0x2f400057 },
- { 0x5fffc, 0x108000b0 },
- { 0x502b4, 0x2e400054 },
- { 0x5fffc, 0x114000b2 },
- { 0x502b8, 0x2e000050 },
- { 0x5fffc, 0x124000b4 },
- { 0x502bc, 0x2d80004c },
- { 0x5fffc, 0x130000b6 },
- { 0x502c0, 0x2d000049 },
- { 0x5fffc, 0x140000b8 },
- { 0x502c4, 0x2c800045 },
- { 0x5fffc, 0x150000b9 },
- { 0x502c8, 0x2c000042 },
- { 0x5fffc, 0x15c000bd },
- { 0x502cc, 0x2b40003e },
- { 0x5fffc, 0x16c000bf },
- { 0x502d0, 0x2a80003b },
- { 0x5fffc, 0x17c000bf },
- { 0x502d4, 0x2a000039 },
- { 0x5fffc, 0x188000c2 },
- { 0x502d8, 0x29400036 },
- { 0x5fffc, 0x19c000c4 },
- { 0x502dc, 0x28800032 },
- { 0x5fffc, 0x1ac000c5 },
- { 0x502e0, 0x2800002f },
- { 0x5fffc, 0x1bc000c7 },
- { 0x502e4, 0x2740002c },
- { 0x5fffc, 0x1cc000c8 },
- { 0x502e8, 0x26c00029 },
- { 0x5fffc, 0x1dc000c9 },
- { 0x502ec, 0x26000027 },
- { 0x5fffc, 0x1ec000cc },
- { 0x502f0, 0x25000024 },
- { 0x5fffc, 0x200000cc },
- { 0x502f4, 0x24800021 },
- { 0x5fffc, 0x210000cd },
- { 0x502f8, 0x23800020 },
- { 0x5fffc, 0x220000ce },
- { 0x502fc, 0x2300001d },
-};
-
-static struct mdp_table_entry mdp_downscale_x_table_PT6TOPT8[] = {
- { 0x5fffc, 0xfe000070 },
- { 0x50280, 0x4bc00068 },
- { 0x5fffc, 0xfe000078 },
- { 0x50284, 0x4bc00060 },
- { 0x5fffc, 0xfe000080 },
- { 0x50288, 0x4b800059 },
- { 0x5fffc, 0xfe000089 },
- { 0x5028c, 0x4b000052 },
- { 0x5fffc, 0xfe400091 },
- { 0x50290, 0x4a80004b },
- { 0x5fffc, 0xfe40009a },
- { 0x50294, 0x4a000044 },
- { 0x5fffc, 0xfe8000a3 },
- { 0x50298, 0x4940003d },
- { 0x5fffc, 0xfec000ac },
- { 0x5029c, 0x48400037 },
- { 0x5fffc, 0xff0000b4 },
- { 0x502a0, 0x47800031 },
- { 0x5fffc, 0xff8000bd },
- { 0x502a4, 0x4640002b },
- { 0x5fffc, 0xc5 },
- { 0x502a8, 0x45000026 },
- { 0x5fffc, 0x8000ce },
- { 0x502ac, 0x43800021 },
- { 0x5fffc, 0x10000d6 },
- { 0x502b0, 0x4240001c },
- { 0x5fffc, 0x18000df },
- { 0x502b4, 0x40800018 },
- { 0x5fffc, 0x24000e6 },
- { 0x502b8, 0x3f000014 },
- { 0x5fffc, 0x30000ee },
- { 0x502bc, 0x3d400010 },
- { 0x5fffc, 0x40000f5 },
- { 0x502c0, 0x3b80000c },
- { 0x5fffc, 0x50000fc },
- { 0x502c4, 0x39800009 },
- { 0x5fffc, 0x6000102 },
- { 0x502c8, 0x37c00006 },
- { 0x5fffc, 0x7000109 },
- { 0x502cc, 0x35800004 },
- { 0x5fffc, 0x840010e },
- { 0x502d0, 0x33800002 },
- { 0x5fffc, 0x9800114 },
- { 0x502d4, 0x31400000 },
- { 0x5fffc, 0xac00119 },
- { 0x502d8, 0x2f4003fe },
- { 0x5fffc, 0xc40011e },
- { 0x502dc, 0x2d0003fc },
- { 0x5fffc, 0xdc00121 },
- { 0x502e0, 0x2b0003fb },
- { 0x5fffc, 0xf400125 },
- { 0x502e4, 0x28c003fa },
- { 0x5fffc, 0x11000128 },
- { 0x502e8, 0x268003f9 },
- { 0x5fffc, 0x12c0012a },
- { 0x502ec, 0x244003f9 },
- { 0x5fffc, 0x1480012c },
- { 0x502f0, 0x224003f8 },
- { 0x5fffc, 0x1640012e },
- { 0x502f4, 0x200003f8 },
- { 0x5fffc, 0x1800012f },
- { 0x502f8, 0x1e0003f8 },
- { 0x5fffc, 0x1a00012f },
- { 0x502fc, 0x1c0003f8 },
-};
-
-static struct mdp_table_entry mdp_downscale_x_table_PT8TO1[] = {
- { 0x5fffc, 0x0 },
- { 0x50280, 0x7fc00000 },
- { 0x5fffc, 0xff80000d },
- { 0x50284, 0x7ec003f9 },
- { 0x5fffc, 0xfec0001c },
- { 0x50288, 0x7d4003f3 },
- { 0x5fffc, 0xfe40002b },
- { 0x5028c, 0x7b8003ed },
- { 0x5fffc, 0xfd80003c },
- { 0x50290, 0x794003e8 },
- { 0x5fffc, 0xfcc0004d },
- { 0x50294, 0x76c003e4 },
- { 0x5fffc, 0xfc40005f },
- { 0x50298, 0x73c003e0 },
- { 0x5fffc, 0xfb800071 },
- { 0x5029c, 0x708003de },
- { 0x5fffc, 0xfac00085 },
- { 0x502a0, 0x6d0003db },
- { 0x5fffc, 0xfa000098 },
- { 0x502a4, 0x698003d9 },
- { 0x5fffc, 0xf98000ac },
- { 0x502a8, 0x654003d8 },
- { 0x5fffc, 0xf8c000c1 },
- { 0x502ac, 0x610003d7 },
- { 0x5fffc, 0xf84000d5 },
- { 0x502b0, 0x5c8003d7 },
- { 0x5fffc, 0xf7c000e9 },
- { 0x502b4, 0x580003d7 },
- { 0x5fffc, 0xf74000fd },
- { 0x502b8, 0x534003d8 },
- { 0x5fffc, 0xf6c00112 },
- { 0x502bc, 0x4e8003d8 },
- { 0x5fffc, 0xf6800126 },
- { 0x502c0, 0x494003da },
- { 0x5fffc, 0xf600013a },
- { 0x502c4, 0x448003db },
- { 0x5fffc, 0xf600014d },
- { 0x502c8, 0x3f4003dd },
- { 0x5fffc, 0xf5c00160 },
- { 0x502cc, 0x3a4003df },
- { 0x5fffc, 0xf5c00172 },
- { 0x502d0, 0x354003e1 },
- { 0x5fffc, 0xf5c00184 },
- { 0x502d4, 0x304003e3 },
- { 0x5fffc, 0xf6000195 },
- { 0x502d8, 0x2b0003e6 },
- { 0x5fffc, 0xf64001a6 },
- { 0x502dc, 0x260003e8 },
- { 0x5fffc, 0xf6c001b4 },
- { 0x502e0, 0x214003eb },
- { 0x5fffc, 0xf78001c2 },
- { 0x502e4, 0x1c4003ee },
- { 0x5fffc, 0xf80001cf },
- { 0x502e8, 0x17c003f1 },
- { 0x5fffc, 0xf90001db },
- { 0x502ec, 0x134003f3 },
- { 0x5fffc, 0xfa0001e5 },
- { 0x502f0, 0xf0003f6 },
- { 0x5fffc, 0xfb4001ee },
- { 0x502f4, 0xac003f9 },
- { 0x5fffc, 0xfcc001f5 },
- { 0x502f8, 0x70003fb },
- { 0x5fffc, 0xfe4001fb },
- { 0x502fc, 0x34003fe },
-};
-
-struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX] = {
- [MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_x_table_PT2TOPT4,
- [MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_x_table_PT4TOPT6,
- [MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_x_table_PT6TOPT8,
- [MDP_DOWNSCALE_PT8TO1] = mdp_downscale_x_table_PT8TO1,
-};
-
-static struct mdp_table_entry mdp_downscale_y_table_PT2TOPT4[] = {
- { 0x5fffc, 0x740008c },
- { 0x50300, 0x33800088 },
- { 0x5fffc, 0x800008e },
- { 0x50304, 0x33400084 },
- { 0x5fffc, 0x8400092 },
- { 0x50308, 0x33000080 },
- { 0x5fffc, 0x9000094 },
- { 0x5030c, 0x3300007b },
- { 0x5fffc, 0x9c00098 },
- { 0x50310, 0x32400077 },
- { 0x5fffc, 0xa40009b },
- { 0x50314, 0x32000073 },
- { 0x5fffc, 0xb00009d },
- { 0x50318, 0x31c0006f },
- { 0x5fffc, 0xbc000a0 },
- { 0x5031c, 0x3140006b },
- { 0x5fffc, 0xc8000a2 },
- { 0x50320, 0x31000067 },
- { 0x5fffc, 0xd8000a5 },
- { 0x50324, 0x30800062 },
- { 0x5fffc, 0xe4000a8 },
- { 0x50328, 0x2fc0005f },
- { 0x5fffc, 0xec000aa },
- { 0x5032c, 0x2fc0005b },
- { 0x5fffc, 0xf8000ad },
- { 0x50330, 0x2f400057 },
- { 0x5fffc, 0x108000b0 },
- { 0x50334, 0x2e400054 },
- { 0x5fffc, 0x114000b2 },
- { 0x50338, 0x2e000050 },
- { 0x5fffc, 0x124000b4 },
- { 0x5033c, 0x2d80004c },
- { 0x5fffc, 0x130000b6 },
- { 0x50340, 0x2d000049 },
- { 0x5fffc, 0x140000b8 },
- { 0x50344, 0x2c800045 },
- { 0x5fffc, 0x150000b9 },
- { 0x50348, 0x2c000042 },
- { 0x5fffc, 0x15c000bd },
- { 0x5034c, 0x2b40003e },
- { 0x5fffc, 0x16c000bf },
- { 0x50350, 0x2a80003b },
- { 0x5fffc, 0x17c000bf },
- { 0x50354, 0x2a000039 },
- { 0x5fffc, 0x188000c2 },
- { 0x50358, 0x29400036 },
- { 0x5fffc, 0x19c000c4 },
- { 0x5035c, 0x28800032 },
- { 0x5fffc, 0x1ac000c5 },
- { 0x50360, 0x2800002f },
- { 0x5fffc, 0x1bc000c7 },
- { 0x50364, 0x2740002c },
- { 0x5fffc, 0x1cc000c8 },
- { 0x50368, 0x26c00029 },
- { 0x5fffc, 0x1dc000c9 },
- { 0x5036c, 0x26000027 },
- { 0x5fffc, 0x1ec000cc },
- { 0x50370, 0x25000024 },
- { 0x5fffc, 0x200000cc },
- { 0x50374, 0x24800021 },
- { 0x5fffc, 0x210000cd },
- { 0x50378, 0x23800020 },
- { 0x5fffc, 0x220000ce },
- { 0x5037c, 0x2300001d },
-};
-
-static struct mdp_table_entry mdp_downscale_y_table_PT4TOPT6[] = {
- { 0x5fffc, 0x740008c },
- { 0x50300, 0x33800088 },
- { 0x5fffc, 0x800008e },
- { 0x50304, 0x33400084 },
- { 0x5fffc, 0x8400092 },
- { 0x50308, 0x33000080 },
- { 0x5fffc, 0x9000094 },
- { 0x5030c, 0x3300007b },
- { 0x5fffc, 0x9c00098 },
- { 0x50310, 0x32400077 },
- { 0x5fffc, 0xa40009b },
- { 0x50314, 0x32000073 },
- { 0x5fffc, 0xb00009d },
- { 0x50318, 0x31c0006f },
- { 0x5fffc, 0xbc000a0 },
- { 0x5031c, 0x3140006b },
- { 0x5fffc, 0xc8000a2 },
- { 0x50320, 0x31000067 },
- { 0x5fffc, 0xd8000a5 },
- { 0x50324, 0x30800062 },
- { 0x5fffc, 0xe4000a8 },
- { 0x50328, 0x2fc0005f },
- { 0x5fffc, 0xec000aa },
- { 0x5032c, 0x2fc0005b },
- { 0x5fffc, 0xf8000ad },
- { 0x50330, 0x2f400057 },
- { 0x5fffc, 0x108000b0 },
- { 0x50334, 0x2e400054 },
- { 0x5fffc, 0x114000b2 },
- { 0x50338, 0x2e000050 },
- { 0x5fffc, 0x124000b4 },
- { 0x5033c, 0x2d80004c },
- { 0x5fffc, 0x130000b6 },
- { 0x50340, 0x2d000049 },
- { 0x5fffc, 0x140000b8 },
- { 0x50344, 0x2c800045 },
- { 0x5fffc, 0x150000b9 },
- { 0x50348, 0x2c000042 },
- { 0x5fffc, 0x15c000bd },
- { 0x5034c, 0x2b40003e },
- { 0x5fffc, 0x16c000bf },
- { 0x50350, 0x2a80003b },
- { 0x5fffc, 0x17c000bf },
- { 0x50354, 0x2a000039 },
- { 0x5fffc, 0x188000c2 },
- { 0x50358, 0x29400036 },
- { 0x5fffc, 0x19c000c4 },
- { 0x5035c, 0x28800032 },
- { 0x5fffc, 0x1ac000c5 },
- { 0x50360, 0x2800002f },
- { 0x5fffc, 0x1bc000c7 },
- { 0x50364, 0x2740002c },
- { 0x5fffc, 0x1cc000c8 },
- { 0x50368, 0x26c00029 },
- { 0x5fffc, 0x1dc000c9 },
- { 0x5036c, 0x26000027 },
- { 0x5fffc, 0x1ec000cc },
- { 0x50370, 0x25000024 },
- { 0x5fffc, 0x200000cc },
- { 0x50374, 0x24800021 },
- { 0x5fffc, 0x210000cd },
- { 0x50378, 0x23800020 },
- { 0x5fffc, 0x220000ce },
- { 0x5037c, 0x2300001d },
-};
-
-static struct mdp_table_entry mdp_downscale_y_table_PT6TOPT8[] = {
- { 0x5fffc, 0xfe000070 },
- { 0x50300, 0x4bc00068 },
- { 0x5fffc, 0xfe000078 },
- { 0x50304, 0x4bc00060 },
- { 0x5fffc, 0xfe000080 },
- { 0x50308, 0x4b800059 },
- { 0x5fffc, 0xfe000089 },
- { 0x5030c, 0x4b000052 },
- { 0x5fffc, 0xfe400091 },
- { 0x50310, 0x4a80004b },
- { 0x5fffc, 0xfe40009a },
- { 0x50314, 0x4a000044 },
- { 0x5fffc, 0xfe8000a3 },
- { 0x50318, 0x4940003d },
- { 0x5fffc, 0xfec000ac },
- { 0x5031c, 0x48400037 },
- { 0x5fffc, 0xff0000b4 },
- { 0x50320, 0x47800031 },
- { 0x5fffc, 0xff8000bd },
- { 0x50324, 0x4640002b },
- { 0x5fffc, 0xc5 },
- { 0x50328, 0x45000026 },
- { 0x5fffc, 0x8000ce },
- { 0x5032c, 0x43800021 },
- { 0x5fffc, 0x10000d6 },
- { 0x50330, 0x4240001c },
- { 0x5fffc, 0x18000df },
- { 0x50334, 0x40800018 },
- { 0x5fffc, 0x24000e6 },
- { 0x50338, 0x3f000014 },
- { 0x5fffc, 0x30000ee },
- { 0x5033c, 0x3d400010 },
- { 0x5fffc, 0x40000f5 },
- { 0x50340, 0x3b80000c },
- { 0x5fffc, 0x50000fc },
- { 0x50344, 0x39800009 },
- { 0x5fffc, 0x6000102 },
- { 0x50348, 0x37c00006 },
- { 0x5fffc, 0x7000109 },
- { 0x5034c, 0x35800004 },
- { 0x5fffc, 0x840010e },
- { 0x50350, 0x33800002 },
- { 0x5fffc, 0x9800114 },
- { 0x50354, 0x31400000 },
- { 0x5fffc, 0xac00119 },
- { 0x50358, 0x2f4003fe },
- { 0x5fffc, 0xc40011e },
- { 0x5035c, 0x2d0003fc },
- { 0x5fffc, 0xdc00121 },
- { 0x50360, 0x2b0003fb },
- { 0x5fffc, 0xf400125 },
- { 0x50364, 0x28c003fa },
- { 0x5fffc, 0x11000128 },
- { 0x50368, 0x268003f9 },
- { 0x5fffc, 0x12c0012a },
- { 0x5036c, 0x244003f9 },
- { 0x5fffc, 0x1480012c },
- { 0x50370, 0x224003f8 },
- { 0x5fffc, 0x1640012e },
- { 0x50374, 0x200003f8 },
- { 0x5fffc, 0x1800012f },
- { 0x50378, 0x1e0003f8 },
- { 0x5fffc, 0x1a00012f },
- { 0x5037c, 0x1c0003f8 },
-};
-
-static struct mdp_table_entry mdp_downscale_y_table_PT8TO1[] = {
- { 0x5fffc, 0x0 },
- { 0x50300, 0x7fc00000 },
- { 0x5fffc, 0xff80000d },
- { 0x50304, 0x7ec003f9 },
- { 0x5fffc, 0xfec0001c },
- { 0x50308, 0x7d4003f3 },
- { 0x5fffc, 0xfe40002b },
- { 0x5030c, 0x7b8003ed },
- { 0x5fffc, 0xfd80003c },
- { 0x50310, 0x794003e8 },
- { 0x5fffc, 0xfcc0004d },
- { 0x50314, 0x76c003e4 },
- { 0x5fffc, 0xfc40005f },
- { 0x50318, 0x73c003e0 },
- { 0x5fffc, 0xfb800071 },
- { 0x5031c, 0x708003de },
- { 0x5fffc, 0xfac00085 },
- { 0x50320, 0x6d0003db },
- { 0x5fffc, 0xfa000098 },
- { 0x50324, 0x698003d9 },
- { 0x5fffc, 0xf98000ac },
- { 0x50328, 0x654003d8 },
- { 0x5fffc, 0xf8c000c1 },
- { 0x5032c, 0x610003d7 },
- { 0x5fffc, 0xf84000d5 },
- { 0x50330, 0x5c8003d7 },
- { 0x5fffc, 0xf7c000e9 },
- { 0x50334, 0x580003d7 },
- { 0x5fffc, 0xf74000fd },
- { 0x50338, 0x534003d8 },
- { 0x5fffc, 0xf6c00112 },
- { 0x5033c, 0x4e8003d8 },
- { 0x5fffc, 0xf6800126 },
- { 0x50340, 0x494003da },
- { 0x5fffc, 0xf600013a },
- { 0x50344, 0x448003db },
- { 0x5fffc, 0xf600014d },
- { 0x50348, 0x3f4003dd },
- { 0x5fffc, 0xf5c00160 },
- { 0x5034c, 0x3a4003df },
- { 0x5fffc, 0xf5c00172 },
- { 0x50350, 0x354003e1 },
- { 0x5fffc, 0xf5c00184 },
- { 0x50354, 0x304003e3 },
- { 0x5fffc, 0xf6000195 },
- { 0x50358, 0x2b0003e6 },
- { 0x5fffc, 0xf64001a6 },
- { 0x5035c, 0x260003e8 },
- { 0x5fffc, 0xf6c001b4 },
- { 0x50360, 0x214003eb },
- { 0x5fffc, 0xf78001c2 },
- { 0x50364, 0x1c4003ee },
- { 0x5fffc, 0xf80001cf },
- { 0x50368, 0x17c003f1 },
- { 0x5fffc, 0xf90001db },
- { 0x5036c, 0x134003f3 },
- { 0x5fffc, 0xfa0001e5 },
- { 0x50370, 0xf0003f6 },
- { 0x5fffc, 0xfb4001ee },
- { 0x50374, 0xac003f9 },
- { 0x5fffc, 0xfcc001f5 },
- { 0x50378, 0x70003fb },
- { 0x5fffc, 0xfe4001fb },
- { 0x5037c, 0x34003fe },
-};
-
-struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX] = {
- [MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_y_table_PT2TOPT4,
- [MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_y_table_PT4TOPT6,
- [MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_y_table_PT6TOPT8,
- [MDP_DOWNSCALE_PT8TO1] = mdp_downscale_y_table_PT8TO1,
-};
-
-struct mdp_table_entry mdp_gaussian_blur_table[] = {
- /* max variance */
- { 0x5fffc, 0x20000080 },
- { 0x50280, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50284, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50288, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x5028c, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50290, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50294, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50298, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x5029c, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502a0, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502a4, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502a8, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502ac, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502b0, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502b4, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502b8, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502bc, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502c0, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502c4, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502c8, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502cc, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502d0, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502d4, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502d8, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502dc, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502e0, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502e4, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502e8, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502ec, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502f0, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502f4, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502f8, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x502fc, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50300, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50304, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50308, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x5030c, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50310, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50314, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50318, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x5031c, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50320, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50324, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50328, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x5032c, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50330, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50334, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50338, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x5033c, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50340, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50344, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50348, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x5034c, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50350, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50354, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50358, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x5035c, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50360, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50364, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50368, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x5036c, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50370, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50374, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x50378, 0x20000080 },
- { 0x5fffc, 0x20000080 },
- { 0x5037c, 0x20000080 },
-};
diff --git a/drivers/video/fbdev/msm/mdp_scale_tables.h b/drivers/video/fbdev/msm/mdp_scale_tables.h
deleted file mode 100644
index 34077b1af603..000000000000
--- a/drivers/video/fbdev/msm/mdp_scale_tables.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* drivers/video/msm_fb/mdp_scale_tables.h
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#ifndef _MDP_SCALE_TABLES_H_
-#define _MDP_SCALE_TABLES_H_
-
-#include <linux/types.h>
-struct mdp_table_entry {
- uint32_t reg;
- uint32_t val;
-};
-
-extern struct mdp_table_entry mdp_upscale_table[64];
-
-enum {
- MDP_DOWNSCALE_PT2TOPT4,
- MDP_DOWNSCALE_PT4TOPT6,
- MDP_DOWNSCALE_PT6TOPT8,
- MDP_DOWNSCALE_PT8TO1,
- MDP_DOWNSCALE_MAX,
-};
-
-extern struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX];
-extern struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX];
-extern struct mdp_table_entry mdp_gaussian_blur_table[];
-
-#endif
diff --git a/drivers/video/fbdev/msm/msm_fb.c b/drivers/video/fbdev/msm/msm_fb.c
deleted file mode 100644
index 2979d7e72126..000000000000
--- a/drivers/video/fbdev/msm/msm_fb.c
+++ /dev/null
@@ -1,659 +0,0 @@
-/* drivers/video/msm/msm_fb.c
- *
- * Core MSM framebuffer driver.
- *
- * Copyright (C) 2007 Google Incorporated
- *
- * 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/platform_device.h>
-#include <linux/module.h>
-#include <linux/fb.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-
-#include <linux/freezer.h>
-#include <linux/wait.h>
-#include <linux/msm_mdp.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-#include <linux/platform_data/video-msm_fb.h>
-#include <linux/workqueue.h>
-#include <linux/clk.h>
-#include <linux/debugfs.h>
-#include <linux/dma-mapping.h>
-
-#define PRINT_FPS 0
-#define PRINT_BLIT_TIME 0
-
-#define SLEEPING 0x4
-#define UPDATING 0x3
-#define FULL_UPDATE_DONE 0x2
-#define WAKING 0x1
-#define AWAKE 0x0
-
-#define NONE 0
-#define SUSPEND_RESUME 0x1
-#define FPS 0x2
-#define BLIT_TIME 0x4
-#define SHOW_UPDATES 0x8
-
-#define DLOG(mask, fmt, args...) \
-do { \
- if (msmfb_debug_mask & mask) \
- printk(KERN_INFO "msmfb: "fmt, ##args); \
-} while (0)
-
-static int msmfb_debug_mask;
-module_param_named(msmfb_debug_mask, msmfb_debug_mask, int,
- S_IRUGO | S_IWUSR | S_IWGRP);
-
-struct mdp_device *mdp;
-
-struct msmfb_info {
- struct fb_info *fb;
- struct msm_panel_data *panel;
- int xres;
- int yres;
- unsigned output_format;
- unsigned yoffset;
- unsigned frame_requested;
- unsigned frame_done;
- int sleeping;
- unsigned update_frame;
- struct {
- int left;
- int top;
- int eright; /* exclusive */
- int ebottom; /* exclusive */
- } update_info;
- char *black;
-
- spinlock_t update_lock;
- struct mutex panel_init_lock;
- wait_queue_head_t frame_wq;
- struct work_struct resume_work;
- struct msmfb_callback dma_callback;
- struct msmfb_callback vsync_callback;
- struct hrtimer fake_vsync;
- ktime_t vsync_request_time;
-};
-
-static int msmfb_open(struct fb_info *info, int user)
-{
- return 0;
-}
-
-static int msmfb_release(struct fb_info *info, int user)
-{
- return 0;
-}
-
-/* Called from dma interrupt handler, must not sleep */
-static void msmfb_handle_dma_interrupt(struct msmfb_callback *callback)
-{
- unsigned long irq_flags;
- struct msmfb_info *msmfb = container_of(callback, struct msmfb_info,
- dma_callback);
-
- spin_lock_irqsave(&msmfb->update_lock, irq_flags);
- msmfb->frame_done = msmfb->frame_requested;
- if (msmfb->sleeping == UPDATING &&
- msmfb->frame_done == msmfb->update_frame) {
- DLOG(SUSPEND_RESUME, "full update completed\n");
- schedule_work(&msmfb->resume_work);
- }
- spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
- wake_up(&msmfb->frame_wq);
-}
-
-static int msmfb_start_dma(struct msmfb_info *msmfb)
-{
- uint32_t x, y, w, h;
- unsigned addr;
- unsigned long irq_flags;
- uint32_t yoffset;
- s64 time_since_request;
- struct msm_panel_data *panel = msmfb->panel;
-
- spin_lock_irqsave(&msmfb->update_lock, irq_flags);
- time_since_request = ktime_to_ns(ktime_sub(ktime_get(),
- msmfb->vsync_request_time));
- if (time_since_request > 20 * NSEC_PER_MSEC) {
- uint32_t us;
- us = do_div(time_since_request, NSEC_PER_MSEC) / NSEC_PER_USEC;
- printk(KERN_WARNING "msmfb_start_dma %lld.%03u ms after vsync "
- "request\n", time_since_request, us);
- }
- if (msmfb->frame_done == msmfb->frame_requested) {
- spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
- return -1;
- }
- if (msmfb->sleeping == SLEEPING) {
- DLOG(SUSPEND_RESUME, "tried to start dma while asleep\n");
- spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
- return -1;
- }
- x = msmfb->update_info.left;
- y = msmfb->update_info.top;
- w = msmfb->update_info.eright - x;
- h = msmfb->update_info.ebottom - y;
- yoffset = msmfb->yoffset;
- msmfb->update_info.left = msmfb->xres + 1;
- msmfb->update_info.top = msmfb->yres + 1;
- msmfb->update_info.eright = 0;
- msmfb->update_info.ebottom = 0;
- if (unlikely(w > msmfb->xres || h > msmfb->yres ||
- w == 0 || h == 0)) {
- printk(KERN_INFO "invalid update: %d %d %d "
- "%d\n", x, y, w, h);
- msmfb->frame_done = msmfb->frame_requested;
- goto error;
- }
- spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-
- addr = ((msmfb->xres * (yoffset + y) + x) * 2);
- mdp->dma(mdp, addr + msmfb->fb->fix.smem_start,
- msmfb->xres * 2, w, h, x, y, &msmfb->dma_callback,
- panel->interface_type);
- return 0;
-error:
- spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
- /* some clients need to clear their vsync interrupt */
- if (panel->clear_vsync)
- panel->clear_vsync(panel);
- wake_up(&msmfb->frame_wq);
- return 0;
-}
-
-/* Called from esync interrupt handler, must not sleep */
-static void msmfb_handle_vsync_interrupt(struct msmfb_callback *callback)
-{
- struct msmfb_info *msmfb = container_of(callback, struct msmfb_info,
- vsync_callback);
- msmfb_start_dma(msmfb);
-}
-
-static enum hrtimer_restart msmfb_fake_vsync(struct hrtimer *timer)
-{
- struct msmfb_info *msmfb = container_of(timer, struct msmfb_info,
- fake_vsync);
- msmfb_start_dma(msmfb);
- return HRTIMER_NORESTART;
-}
-
-static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top,
- uint32_t eright, uint32_t ebottom,
- uint32_t yoffset, int pan_display)
-{
- struct msmfb_info *msmfb = info->par;
- struct msm_panel_data *panel = msmfb->panel;
- unsigned long irq_flags;
- int sleeping;
- int retry = 1;
-
- DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n",
- left, top, eright, ebottom, yoffset, pan_display);
-restart:
- spin_lock_irqsave(&msmfb->update_lock, irq_flags);
-
- /* if we are sleeping, on a pan_display wait 10ms (to throttle back
- * drawing otherwise return */
- if (msmfb->sleeping == SLEEPING) {
- DLOG(SUSPEND_RESUME, "drawing while asleep\n");
- spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
- if (pan_display)
- wait_event_interruptible_timeout(msmfb->frame_wq,
- msmfb->sleeping != SLEEPING, HZ/10);
- return;
- }
-
- sleeping = msmfb->sleeping;
- /* on a full update, if the last frame has not completed, wait for it */
- if ((pan_display && msmfb->frame_requested != msmfb->frame_done) ||
- sleeping == UPDATING) {
- int ret;
- spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
- ret = wait_event_interruptible_timeout(msmfb->frame_wq,
- msmfb->frame_done == msmfb->frame_requested &&
- msmfb->sleeping != UPDATING, 5 * HZ);
- if (ret <= 0 && (msmfb->frame_requested != msmfb->frame_done ||
- msmfb->sleeping == UPDATING)) {
- if (retry && panel->request_vsync &&
- (sleeping == AWAKE)) {
- panel->request_vsync(panel,
- &msmfb->vsync_callback);
- retry = 0;
- printk(KERN_WARNING "msmfb_pan_display timeout "
- "rerequest vsync\n");
- } else {
- printk(KERN_WARNING "msmfb_pan_display timeout "
- "waiting for frame start, %d %d\n",
- msmfb->frame_requested,
- msmfb->frame_done);
- return;
- }
- }
- goto restart;
- }
-
-
- msmfb->frame_requested++;
- /* if necessary, update the y offset, if this is the
- * first full update on resume, set the sleeping state */
- if (pan_display) {
- msmfb->yoffset = yoffset;
- if (left == 0 && top == 0 && eright == info->var.xres &&
- ebottom == info->var.yres) {
- if (sleeping == WAKING) {
- msmfb->update_frame = msmfb->frame_requested;
- DLOG(SUSPEND_RESUME, "full update starting\n");
- msmfb->sleeping = UPDATING;
- }
- }
- }
-
- /* set the update request */
- if (left < msmfb->update_info.left)
- msmfb->update_info.left = left;
- if (top < msmfb->update_info.top)
- msmfb->update_info.top = top;
- if (eright > msmfb->update_info.eright)
- msmfb->update_info.eright = eright;
- if (ebottom > msmfb->update_info.ebottom)
- msmfb->update_info.ebottom = ebottom;
- DLOG(SHOW_UPDATES, "update queued %d %d %d %d %d\n",
- msmfb->update_info.left, msmfb->update_info.top,
- msmfb->update_info.eright, msmfb->update_info.ebottom,
- msmfb->yoffset);
- spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-
- /* if the panel is all the way on wait for vsync, otherwise sleep
- * for 16 ms (long enough for the dma to panel) and then begin dma */
- msmfb->vsync_request_time = ktime_get();
- if (panel->request_vsync && (sleeping == AWAKE)) {
- panel->request_vsync(panel, &msmfb->vsync_callback);
- } else {
- if (!hrtimer_active(&msmfb->fake_vsync)) {
- hrtimer_start(&msmfb->fake_vsync,
- ktime_set(0, NSEC_PER_SEC/60),
- HRTIMER_MODE_REL);
- }
- }
-}
-
-static void msmfb_update(struct fb_info *info, uint32_t left, uint32_t top,
- uint32_t eright, uint32_t ebottom)
-{
- msmfb_pan_update(info, left, top, eright, ebottom, 0, 0);
-}
-
-static void power_on_panel(struct work_struct *work)
-{
- struct msmfb_info *msmfb =
- container_of(work, struct msmfb_info, resume_work);
- struct msm_panel_data *panel = msmfb->panel;
- unsigned long irq_flags;
-
- mutex_lock(&msmfb->panel_init_lock);
- DLOG(SUSPEND_RESUME, "turning on panel\n");
- if (msmfb->sleeping == UPDATING) {
- if (panel->unblank(panel)) {
- printk(KERN_INFO "msmfb: panel unblank failed,"
- "not starting drawing\n");
- goto error;
- }
- spin_lock_irqsave(&msmfb->update_lock, irq_flags);
- msmfb->sleeping = AWAKE;
- wake_up(&msmfb->frame_wq);
- spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
- }
-error:
- mutex_unlock(&msmfb->panel_init_lock);
-}
-
-
-static int msmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
-{
- if ((var->xres != info->var.xres) ||
- (var->yres != info->var.yres) ||
- (var->xres_virtual != info->var.xres_virtual) ||
- (var->yres_virtual != info->var.yres_virtual) ||
- (var->xoffset != info->var.xoffset) ||
- (var->bits_per_pixel != info->var.bits_per_pixel) ||
- (var->grayscale != info->var.grayscale))
- return -EINVAL;
- return 0;
-}
-
-int msmfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
-{
- struct msmfb_info *msmfb = info->par;
- struct msm_panel_data *panel = msmfb->panel;
-
- /* "UPDT" */
- if ((panel->caps & MSMFB_CAP_PARTIAL_UPDATES) &&
- (var->reserved[0] == 0x54445055)) {
- msmfb_pan_update(info, var->reserved[1] & 0xffff,
- var->reserved[1] >> 16,
- var->reserved[2] & 0xffff,
- var->reserved[2] >> 16, var->yoffset, 1);
- } else {
- msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres,
- var->yoffset, 1);
- }
- return 0;
-}
-
-static void msmfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
-{
- cfb_fillrect(p, rect);
- msmfb_update(p, rect->dx, rect->dy, rect->dx + rect->width,
- rect->dy + rect->height);
-}
-
-static void msmfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
-{
- cfb_copyarea(p, area);
- msmfb_update(p, area->dx, area->dy, area->dx + area->width,
- area->dy + area->height);
-}
-
-static void msmfb_imageblit(struct fb_info *p, const struct fb_image *image)
-{
- cfb_imageblit(p, image);
- msmfb_update(p, image->dx, image->dy, image->dx + image->width,
- image->dy + image->height);
-}
-
-
-static int msmfb_blit(struct fb_info *info,
- void __user *p)
-{
- struct mdp_blit_req req;
- struct mdp_blit_req_list req_list;
- int i;
- int ret;
-
- if (copy_from_user(&req_list, p, sizeof(req_list)))
- return -EFAULT;
-
- for (i = 0; i < req_list.count; i++) {
- struct mdp_blit_req_list *list =
- (struct mdp_blit_req_list *)p;
- if (copy_from_user(&req, &list->req[i], sizeof(req)))
- return -EFAULT;
- ret = mdp->blit(mdp, info, &req);
- if (ret)
- return ret;
- }
- return 0;
-}
-
-
-DEFINE_MUTEX(mdp_ppp_lock);
-
-static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int ret;
-
- switch (cmd) {
- case MSMFB_GRP_DISP:
- mdp->set_grp_disp(mdp, arg);
- break;
- case MSMFB_BLIT:
- ret = msmfb_blit(p, argp);
- if (ret)
- return ret;
- break;
- default:
- printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd);
- return -EINVAL;
- }
- return 0;
-}
-
-static struct fb_ops msmfb_ops = {
- .owner = THIS_MODULE,
- .fb_open = msmfb_open,
- .fb_release = msmfb_release,
- .fb_check_var = msmfb_check_var,
- .fb_pan_display = msmfb_pan_display,
- .fb_fillrect = msmfb_fillrect,
- .fb_copyarea = msmfb_copyarea,
- .fb_imageblit = msmfb_imageblit,
- .fb_ioctl = msmfb_ioctl,
-};
-
-static unsigned PP[16];
-
-
-
-#define BITS_PER_PIXEL 16
-
-static void setup_fb_info(struct msmfb_info *msmfb)
-{
- struct fb_info *fb_info = msmfb->fb;
- int r;
-
- /* finish setting up the fb_info struct */
- strncpy(fb_info->fix.id, "msmfb", 16);
- fb_info->fix.ypanstep = 1;
-
- fb_info->fbops = &msmfb_ops;
- fb_info->flags = FBINFO_DEFAULT;
-
- fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
- fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
- fb_info->fix.line_length = msmfb->xres * 2;
-
- fb_info->var.xres = msmfb->xres;
- fb_info->var.yres = msmfb->yres;
- fb_info->var.width = msmfb->panel->fb_data->width;
- fb_info->var.height = msmfb->panel->fb_data->height;
- fb_info->var.xres_virtual = msmfb->xres;
- fb_info->var.yres_virtual = msmfb->yres * 2;
- fb_info->var.bits_per_pixel = BITS_PER_PIXEL;
- fb_info->var.accel_flags = 0;
-
- fb_info->var.yoffset = 0;
-
- if (msmfb->panel->caps & MSMFB_CAP_PARTIAL_UPDATES) {
- /*
- * Set the param in the fixed screen, so userspace can't
- * change it. This will be used to check for the
- * capability.
- */
- fb_info->fix.reserved[0] = 0x5444;
- fb_info->fix.reserved[1] = 0x5055;
-
- /*
- * This preloads the value so that if userspace doesn't
- * change it, it will be a full update
- */
- fb_info->var.reserved[0] = 0x54445055;
- fb_info->var.reserved[1] = 0;
- fb_info->var.reserved[2] = (uint16_t)msmfb->xres |
- ((uint32_t)msmfb->yres << 16);
- }
-
- fb_info->var.red.offset = 11;
- fb_info->var.red.length = 5;
- fb_info->var.red.msb_right = 0;
- fb_info->var.green.offset = 5;
- fb_info->var.green.length = 6;
- fb_info->var.green.msb_right = 0;
- fb_info->var.blue.offset = 0;
- fb_info->var.blue.length = 5;
- fb_info->var.blue.msb_right = 0;
-
- r = fb_alloc_cmap(&fb_info->cmap, 16, 0);
- fb_info->pseudo_palette = PP;
-
- PP[0] = 0;
- for (r = 1; r < 16; r++)
- PP[r] = 0xffffffff;
-}
-
-static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev)
-{
- struct fb_info *fb = msmfb->fb;
- struct resource *resource;
- unsigned long size = msmfb->xres * msmfb->yres *
- (BITS_PER_PIXEL >> 3) * 2;
- unsigned char *fbram;
-
- /* board file might have attached a resource describing an fb */
- resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!resource)
- return -EINVAL;
-
- /* check the resource is large enough to fit the fb */
- if (resource->end - resource->start < size) {
- printk(KERN_ERR "allocated resource is too small for "
- "fb\n");
- return -ENOMEM;
- }
- fb->fix.smem_start = resource->start;
- fb->fix.smem_len = resource_size(resource);
- fbram = ioremap(resource->start, resource_size(resource));
- if (fbram == NULL) {
- printk(KERN_ERR "msmfb: cannot allocate fbram!\n");
- return -ENOMEM;
- }
- fb->screen_base = fbram;
- return 0;
-}
-
-static int msmfb_probe(struct platform_device *pdev)
-{
- struct fb_info *fb;
- struct msmfb_info *msmfb;
- struct msm_panel_data *panel = pdev->dev.platform_data;
- int ret;
-
- if (!panel) {
- pr_err("msmfb_probe: no platform data\n");
- return -EINVAL;
- }
- if (!panel->fb_data) {
- pr_err("msmfb_probe: no fb_data\n");
- return -EINVAL;
- }
-
- fb = framebuffer_alloc(sizeof(struct msmfb_info), &pdev->dev);
- if (!fb)
- return -ENOMEM;
- msmfb = fb->par;
- msmfb->fb = fb;
- msmfb->panel = panel;
- msmfb->xres = panel->fb_data->xres;
- msmfb->yres = panel->fb_data->yres;
-
- ret = setup_fbmem(msmfb, pdev);
- if (ret)
- goto error_setup_fbmem;
-
- setup_fb_info(msmfb);
-
- spin_lock_init(&msmfb->update_lock);
- mutex_init(&msmfb->panel_init_lock);
- init_waitqueue_head(&msmfb->frame_wq);
- INIT_WORK(&msmfb->resume_work, power_on_panel);
- msmfb->black = devm_kzalloc(&pdev->dev,
- msmfb->fb->var.bits_per_pixel*msmfb->xres,
- GFP_KERNEL);
- if (!msmfb->black) {
- ret = -ENOMEM;
- goto error_register_framebuffer;
- }
-
- printk(KERN_INFO "msmfb_probe() installing %d x %d panel\n",
- msmfb->xres, msmfb->yres);
-
- msmfb->dma_callback.func = msmfb_handle_dma_interrupt;
- msmfb->vsync_callback.func = msmfb_handle_vsync_interrupt;
- hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
-
-
- msmfb->fake_vsync.function = msmfb_fake_vsync;
-
- ret = register_framebuffer(fb);
- if (ret)
- goto error_register_framebuffer;
-
- msmfb->sleeping = WAKING;
-
- platform_set_drvdata(pdev, msmfb);
-
- return 0;
-
-error_register_framebuffer:
- iounmap(fb->screen_base);
-error_setup_fbmem:
- framebuffer_release(msmfb->fb);
- return ret;
-}
-
-static int msmfb_remove(struct platform_device *pdev)
-{
- struct msmfb_info *msmfb;
-
- msmfb = platform_get_drvdata(pdev);
-
- unregister_framebuffer(msmfb->fb);
- iounmap(msmfb->fb->screen_base);
- framebuffer_release(msmfb->fb);
-
- return 0;
-}
-
-static struct platform_driver msm_panel_driver = {
- /* need to write remove */
- .probe = msmfb_probe,
- .remove = msmfb_remove,
- .driver = {.name = "msm_panel"},
-};
-
-
-static int msmfb_add_mdp_device(struct device *dev,
- struct class_interface *class_intf)
-{
- /* might need locking if mulitple mdp devices */
- if (mdp)
- return 0;
- mdp = container_of(dev, struct mdp_device, dev);
- return platform_driver_register(&msm_panel_driver);
-}
-
-static void msmfb_remove_mdp_device(struct device *dev,
- struct class_interface *class_intf)
-{
- /* might need locking if mulitple mdp devices */
- if (dev != &mdp->dev)
- return;
- platform_driver_unregister(&msm_panel_driver);
- mdp = NULL;
-}
-
-static struct class_interface msm_fb_interface = {
- .add_dev = &msmfb_add_mdp_device,
- .remove_dev = &msmfb_remove_mdp_device,
-};
-
-static int __init msmfb_init(void)
-{
- return register_mdp_client(&msm_fb_interface);
-}
-
-module_init(msmfb_init);
diff --git a/drivers/video/fbdev/mxsfb.c b/drivers/video/fbdev/mxsfb.c
index f8ac4a452f26..4e6608ceac09 100644
--- a/drivers/video/fbdev/mxsfb.c
+++ b/drivers/video/fbdev/mxsfb.c
@@ -316,6 +316,18 @@ static int mxsfb_check_var(struct fb_var_screeninfo *var,
return 0;
}
+static inline void mxsfb_enable_axi_clk(struct mxsfb_info *host)
+{
+ if (host->clk_axi)
+ clk_prepare_enable(host->clk_axi);
+}
+
+static inline void mxsfb_disable_axi_clk(struct mxsfb_info *host)
+{
+ if (host->clk_axi)
+ clk_disable_unprepare(host->clk_axi);
+}
+
static void mxsfb_enable_controller(struct fb_info *fb_info)
{
struct mxsfb_info *host = to_imxfb_host(fb_info);
@@ -333,14 +345,13 @@ static void mxsfb_enable_controller(struct fb_info *fb_info)
}
}
- if (host->clk_axi)
- clk_prepare_enable(host->clk_axi);
-
if (host->clk_disp_axi)
clk_prepare_enable(host->clk_disp_axi);
clk_prepare_enable(host->clk);
clk_set_rate(host->clk, PICOS2KHZ(fb_info->var.pixclock) * 1000U);
+ mxsfb_enable_axi_clk(host);
+
/* if it was disabled, re-enable the mode again */
writel(CTRL_DOTCLK_MODE, host->base + LCDC_CTRL + REG_SET);
@@ -380,11 +391,11 @@ static void mxsfb_disable_controller(struct fb_info *fb_info)
reg = readl(host->base + LCDC_VDCTRL4);
writel(reg & ~VDCTRL4_SYNC_SIGNALS_ON, host->base + LCDC_VDCTRL4);
+ mxsfb_disable_axi_clk(host);
+
clk_disable_unprepare(host->clk);
if (host->clk_disp_axi)
clk_disable_unprepare(host->clk_disp_axi);
- if (host->clk_axi)
- clk_disable_unprepare(host->clk_axi);
host->enabled = 0;
@@ -421,6 +432,8 @@ static int mxsfb_set_par(struct fb_info *fb_info)
mxsfb_disable_controller(fb_info);
}
+ mxsfb_enable_axi_clk(host);
+
/* clear the FIFOs */
writel(CTRL1_FIFO_CLEAR, host->base + LCDC_CTRL1 + REG_SET);
@@ -438,6 +451,7 @@ static int mxsfb_set_par(struct fb_info *fb_info)
ctrl |= CTRL_SET_WORD_LENGTH(3);
switch (host->ld_intf_width) {
case STMLCDIF_8BIT:
+ mxsfb_disable_axi_clk(host);
dev_err(&host->pdev->dev,
"Unsupported LCD bus width mapping\n");
return -EINVAL;
@@ -451,6 +465,7 @@ static int mxsfb_set_par(struct fb_info *fb_info)
writel(CTRL1_SET_BYTE_PACKAGING(0x7), host->base + LCDC_CTRL1);
break;
default:
+ mxsfb_disable_axi_clk(host);
dev_err(&host->pdev->dev, "Unhandled color depth of %u\n",
fb_info->var.bits_per_pixel);
return -EINVAL;
@@ -504,6 +519,8 @@ static int mxsfb_set_par(struct fb_info *fb_info)
fb_info->fix.line_length * fb_info->var.yoffset,
host->base + host->devdata->next_buf);
+ mxsfb_disable_axi_clk(host);
+
if (reenable)
mxsfb_enable_controller(fb_info);
@@ -582,10 +599,14 @@ static int mxsfb_pan_display(struct fb_var_screeninfo *var,
offset = fb_info->fix.line_length * var->yoffset;
+ mxsfb_enable_axi_clk(host);
+
/* update on next VSYNC */
writel(fb_info->fix.smem_start + offset,
host->base + host->devdata->next_buf);
+ mxsfb_disable_axi_clk(host);
+
return 0;
}
@@ -608,13 +629,17 @@ static int mxsfb_restore_mode(struct mxsfb_info *host,
unsigned line_count;
unsigned period;
unsigned long pa, fbsize;
- int bits_per_pixel, ofs;
+ int bits_per_pixel, ofs, ret = 0;
u32 transfer_count, vdctrl0, vdctrl2, vdctrl3, vdctrl4, ctrl;
+ mxsfb_enable_axi_clk(host);
+
/* Only restore the mode when the controller is running */
ctrl = readl(host->base + LCDC_CTRL);
- if (!(ctrl & CTRL_RUN))
- return -EINVAL;
+ if (!(ctrl & CTRL_RUN)) {
+ ret = -EINVAL;
+ goto err;
+ }
vdctrl0 = readl(host->base + LCDC_VDCTRL0);
vdctrl2 = readl(host->base + LCDC_VDCTRL2);
@@ -635,7 +660,8 @@ static int mxsfb_restore_mode(struct mxsfb_info *host,
break;
case 1:
default:
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
fb_info->var.bits_per_pixel = bits_per_pixel;
@@ -673,10 +699,14 @@ static int mxsfb_restore_mode(struct mxsfb_info *host,
pa = readl(host->base + host->devdata->cur_buf);
fbsize = fb_info->fix.line_length * vmode->yres;
- if (pa < fb_info->fix.smem_start)
- return -EINVAL;
- if (pa + fbsize > fb_info->fix.smem_start + fb_info->fix.smem_len)
- return -EINVAL;
+ if (pa < fb_info->fix.smem_start) {
+ ret = -EINVAL;
+ goto err;
+ }
+ if (pa + fbsize > fb_info->fix.smem_start + fb_info->fix.smem_len) {
+ ret = -EINVAL;
+ goto err;
+ }
ofs = pa - fb_info->fix.smem_start;
if (ofs) {
memmove(fb_info->screen_base, fb_info->screen_base + ofs, fbsize);
@@ -689,7 +719,11 @@ static int mxsfb_restore_mode(struct mxsfb_info *host,
clk_prepare_enable(host->clk);
host->enabled = 1;
- return 0;
+err:
+ if (ret)
+ mxsfb_disable_axi_clk(host);
+
+ return ret;
}
static int mxsfb_init_fbinfo_dt(struct mxsfb_info *host,
@@ -814,7 +848,7 @@ static void mxsfb_free_videomem(struct mxsfb_info *host)
free_pages_exact(fb_info->screen_base, fb_info->fix.smem_len);
}
-static struct platform_device_id mxsfb_devtype[] = {
+static const struct platform_device_id mxsfb_devtype[] = {
{
.name = "imx23-fb",
.driver_data = MXSFB_V3,
@@ -915,7 +949,9 @@ static int mxsfb_probe(struct platform_device *pdev)
}
if (!host->enabled) {
+ mxsfb_enable_axi_clk(host);
writel(0, host->base + LCDC_CTRL);
+ mxsfb_disable_axi_clk(host);
mxsfb_set_par(fb_info);
mxsfb_enable_controller(fb_info);
}
@@ -954,11 +990,15 @@ static void mxsfb_shutdown(struct platform_device *pdev)
struct fb_info *fb_info = platform_get_drvdata(pdev);
struct mxsfb_info *host = to_imxfb_host(fb_info);
+ mxsfb_enable_axi_clk(host);
+
/*
* Force stop the LCD controller as keeping it running during reboot
* might interfere with the BootROM's boot mode pads sampling.
*/
writel(CTRL_RUN, host->base + LCDC_CTRL + REG_CLR);
+
+ mxsfb_disable_axi_clk(host);
}
static struct platform_driver mxsfb_driver = {
diff --git a/drivers/video/fbdev/neofb.c b/drivers/video/fbdev/neofb.c
index 44f99a60bb9b..db023a97d1ea 100644
--- a/drivers/video/fbdev/neofb.c
+++ b/drivers/video/fbdev/neofb.c
@@ -71,11 +71,6 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pgtable.h>
-
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
#include <video/vga.h>
#include <video/neomagic.h>
@@ -1710,6 +1705,7 @@ static int neo_map_video(struct fb_info *info, struct pci_dev *dev,
int video_len)
{
//unsigned long addr;
+ struct neofb_par *par = info->par;
DBG("neo_map_video");
@@ -1723,7 +1719,7 @@ static int neo_map_video(struct fb_info *info, struct pci_dev *dev,
}
info->screen_base =
- ioremap(info->fix.smem_start, info->fix.smem_len);
+ ioremap_wc(info->fix.smem_start, info->fix.smem_len);
if (!info->screen_base) {
printk("neofb: unable to map screen memory\n");
release_mem_region(info->fix.smem_start,
@@ -1733,11 +1729,8 @@ static int neo_map_video(struct fb_info *info, struct pci_dev *dev,
printk(KERN_INFO "neofb: mapped framebuffer at %p\n",
info->screen_base);
-#ifdef CONFIG_MTRR
- ((struct neofb_par *)(info->par))->mtrr =
- mtrr_add(info->fix.smem_start, pci_resource_len(dev, 0),
- MTRR_TYPE_WRCOMB, 1);
-#endif
+ par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
+ pci_resource_len(dev, 0));
/* Clear framebuffer, it's all white in memory after boot */
memset_io(info->screen_base, 0, info->fix.smem_len);
@@ -1754,16 +1747,11 @@ static int neo_map_video(struct fb_info *info, struct pci_dev *dev,
static void neo_unmap_video(struct fb_info *info)
{
- DBG("neo_unmap_video");
+ struct neofb_par *par = info->par;
-#ifdef CONFIG_MTRR
- {
- struct neofb_par *par = info->par;
+ DBG("neo_unmap_video");
- mtrr_del(par->mtrr, info->fix.smem_start,
- info->fix.smem_len);
- }
-#endif
+ arch_phys_wc_del(par->wc_cookie);
iounmap(info->screen_base);
info->screen_base = NULL;
diff --git a/drivers/video/fbdev/nvidia/nv_type.h b/drivers/video/fbdev/nvidia/nv_type.h
index c03f7f55c76d..6ff321a36813 100644
--- a/drivers/video/fbdev/nvidia/nv_type.h
+++ b/drivers/video/fbdev/nvidia/nv_type.h
@@ -148,12 +148,7 @@ struct nvidia_par {
u32 forceCRTC;
u32 open_count;
u8 DDCBase;
-#ifdef CONFIG_MTRR
- struct {
- int vram;
- int vram_valid;
- } mtrr;
-#endif
+ int wc_cookie;
struct nvidia_i2c_chan chan[3];
volatile u32 __iomem *REGS;
diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c
index 4273c6ee8cf6..ce7dab7299fe 100644
--- a/drivers/video/fbdev/nvidia/nvidia.c
+++ b/drivers/video/fbdev/nvidia/nvidia.c
@@ -21,9 +21,6 @@
#include <linux/pci.h>
#include <linux/console.h>
#include <linux/backlight.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
#ifdef CONFIG_BOOTX_TEXT
#include <asm/btext.h>
#endif
@@ -76,9 +73,7 @@ static int paneltweak = 0;
static int vram = 0;
static int bpp = 8;
static int reverse_i2c;
-#ifdef CONFIG_MTRR
static bool nomtrr = false;
-#endif
#ifdef CONFIG_PMAC_BACKLIGHT
static int backlight = 1;
#else
@@ -1361,7 +1356,8 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
par->CursorStart = par->FbUsableSize + (32 * 1024);
- info->screen_base = ioremap(nvidiafb_fix.smem_start, par->FbMapSize);
+ info->screen_base = ioremap_wc(nvidiafb_fix.smem_start,
+ par->FbMapSize);
info->screen_size = par->FbUsableSize;
nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
@@ -1372,20 +1368,9 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
par->FbStart = info->screen_base;
-#ifdef CONFIG_MTRR
- if (!nomtrr) {
- par->mtrr.vram = mtrr_add(nvidiafb_fix.smem_start,
- par->RamAmountKBytes * 1024,
- MTRR_TYPE_WRCOMB, 1);
- if (par->mtrr.vram < 0) {
- printk(KERN_ERR PFX "unable to setup MTRR\n");
- } else {
- par->mtrr.vram_valid = 1;
- /* let there be speed */
- printk(KERN_INFO PFX "MTRR set to ON\n");
- }
- }
-#endif /* CONFIG_MTRR */
+ if (!nomtrr)
+ par->wc_cookie = arch_phys_wc_add(nvidiafb_fix.smem_start,
+ par->RamAmountKBytes * 1024);
info->fbops = &nvidia_fb_ops;
info->fix = nvidiafb_fix;
@@ -1443,13 +1428,7 @@ static void nvidiafb_remove(struct pci_dev *pd)
unregister_framebuffer(info);
nvidia_bl_exit(par);
-
-#ifdef CONFIG_MTRR
- if (par->mtrr.vram_valid)
- mtrr_del(par->mtrr.vram, info->fix.smem_start,
- info->fix.smem_len);
-#endif /* CONFIG_MTRR */
-
+ arch_phys_wc_del(par->wc_cookie);
iounmap(info->screen_base);
fb_destroy_modedb(info->monspecs.modedb);
nvidia_delete_i2c_busses(par);
@@ -1501,10 +1480,8 @@ static int nvidiafb_setup(char *options)
vram = simple_strtoul(this_opt+5, NULL, 0);
} else if (!strncmp(this_opt, "backlight:", 10)) {
backlight = simple_strtoul(this_opt+10, NULL, 0);
-#ifdef CONFIG_MTRR
} else if (!strncmp(this_opt, "nomtrr", 6)) {
nomtrr = true;
-#endif
} else if (!strncmp(this_opt, "fpdither:", 9)) {
fpdither = simple_strtol(this_opt+9, NULL, 0);
} else if (!strncmp(this_opt, "bpp:", 4)) {
@@ -1592,11 +1569,9 @@ MODULE_PARM_DESC(bpp, "pixel width in bits"
"(default=8)");
module_param(reverse_i2c, int, 0);
MODULE_PARM_DESC(reverse_i2c, "reverse port assignment of the i2c bus");
-#ifdef CONFIG_MTRR
module_param(nomtrr, bool, false);
MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
"(default=0)");
-#endif
MODULE_AUTHOR("Antonino Daplas");
MODULE_DESCRIPTION("Framebuffer driver for nVidia graphics chipset");
diff --git a/drivers/video/fbdev/omap/Kconfig b/drivers/video/fbdev/omap/Kconfig
index 18c4cb0d5690..29d250da8a3e 100644
--- a/drivers/video/fbdev/omap/Kconfig
+++ b/drivers/video/fbdev/omap/Kconfig
@@ -42,7 +42,7 @@ config FB_OMAP_LCD_MIPID
config FB_OMAP_LCD_H3
bool "TPS65010 LCD controller on OMAP-H3"
depends on MACH_OMAP_H3
- depends on TPS65010
+ depends on TPS65010=y
default y
help
Say Y here if you want to have support for the LCD on the
diff --git a/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c b/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c
index 84a6b3367124..a14d993f719d 100644
--- a/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c
+++ b/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c
@@ -201,15 +201,9 @@ static int opa362_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ddata);
- gpio = devm_gpiod_get(&pdev->dev, "enable");
- if (IS_ERR(gpio)) {
- if (PTR_ERR(gpio) != -ENOENT)
- return PTR_ERR(gpio);
-
- gpio = NULL;
- } else {
- gpiod_direction_output(gpio, 0);
- }
+ gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
ddata->enable_gpio = gpio;
diff --git a/drivers/video/fbdev/omap2/displays-new/panel-dpi.c b/drivers/video/fbdev/omap2/displays-new/panel-dpi.c
index eb8fd8140ad0..f7be3489f744 100644
--- a/drivers/video/fbdev/omap2/displays-new/panel-dpi.c
+++ b/drivers/video/fbdev/omap2/displays-new/panel-dpi.c
@@ -209,16 +209,9 @@ static int panel_dpi_probe_of(struct platform_device *pdev)
struct videomode vm;
struct gpio_desc *gpio;
- gpio = devm_gpiod_get(&pdev->dev, "enable");
-
- if (IS_ERR(gpio)) {
- if (PTR_ERR(gpio) != -ENOENT)
- return PTR_ERR(gpio);
- else
- gpio = NULL;
- } else {
- gpiod_direction_output(gpio, 0);
- }
+ gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
ddata->enable_gpio = gpio;
diff --git a/drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c b/drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c
index 9974a37a11af..6a1b6a89a928 100644
--- a/drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c
+++ b/drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c
@@ -285,15 +285,14 @@ static int lb035q02_probe_of(struct spi_device *spi)
struct omap_dss_device *in;
struct gpio_desc *gpio;
- gpio = devm_gpiod_get(&spi->dev, "enable");
+ gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(gpio)) {
dev_err(&spi->dev, "failed to parse enable gpio\n");
return PTR_ERR(gpio);
- } else {
- gpiod_direction_output(gpio, 0);
- ddata->enable_gpio = gpio;
}
+ ddata->enable_gpio = gpio;
+
ddata->backlight_gpio = -ENOENT;
in = omapdss_of_find_source_for_first_ep(node);
diff --git a/drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c b/drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c
index eae263702964..abfd1f6e3327 100644
--- a/drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c
+++ b/drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c
@@ -268,17 +268,12 @@ static int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
const char *desc, struct gpio_desc **gpiod)
{
struct gpio_desc *gd;
- int r;
*gpiod = NULL;
- gd = devm_gpiod_get_index(dev, desc, index);
+ gd = devm_gpiod_get_index(dev, desc, index, GPIOD_OUT_LOW);
if (IS_ERR(gd))
- return PTR_ERR(gd) == -ENOENT ? 0 : PTR_ERR(gd);
-
- r = gpiod_direction_output(gd, val);
- if (r)
- return r;
+ return PTR_ERR(gd);
*gpiod = gd;
return 0;
diff --git a/drivers/video/fbdev/omap2/dss/core.c b/drivers/video/fbdev/omap2/dss/core.c
index 16751755d433..54eeb507f9b3 100644
--- a/drivers/video/fbdev/omap2/dss/core.c
+++ b/drivers/video/fbdev/omap2/dss/core.c
@@ -50,8 +50,6 @@ static char *def_disp_name;
module_param_named(def_disp, def_disp_name, charp, 0);
MODULE_PARM_DESC(def_disp, "default display name");
-static bool dss_initialized;
-
const char *omapdss_get_default_display_name(void)
{
return core.default_display_name;
@@ -65,12 +63,6 @@ enum omapdss_version omapdss_get_version(void)
}
EXPORT_SYMBOL(omapdss_get_version);
-bool omapdss_is_initialized(void)
-{
- return dss_initialized;
-}
-EXPORT_SYMBOL(omapdss_is_initialized);
-
struct platform_device *dss_get_core_pdev(void)
{
return core.pdev;
@@ -253,6 +245,8 @@ static struct platform_driver omap_dss_driver = {
/* INIT */
static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
+ dss_init_platform_driver,
+ dispc_init_platform_driver,
#ifdef CONFIG_OMAP2_DSS_DSI
dsi_init_platform_driver,
#endif
@@ -276,32 +270,32 @@ static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
#endif
};
-static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = {
-#ifdef CONFIG_OMAP2_DSS_DSI
- dsi_uninit_platform_driver,
+static void (*dss_output_drv_unreg_funcs[])(void) = {
+#ifdef CONFIG_OMAP5_DSS_HDMI
+ hdmi5_uninit_platform_driver,
#endif
-#ifdef CONFIG_OMAP2_DSS_DPI
- dpi_uninit_platform_driver,
+#ifdef CONFIG_OMAP4_DSS_HDMI
+ hdmi4_uninit_platform_driver,
#endif
-#ifdef CONFIG_OMAP2_DSS_SDI
- sdi_uninit_platform_driver,
+#ifdef CONFIG_OMAP2_DSS_VENC
+ venc_uninit_platform_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_RFBI
rfbi_uninit_platform_driver,
#endif
-#ifdef CONFIG_OMAP2_DSS_VENC
- venc_uninit_platform_driver,
+#ifdef CONFIG_OMAP2_DSS_SDI
+ sdi_uninit_platform_driver,
#endif
-#ifdef CONFIG_OMAP4_DSS_HDMI
- hdmi4_uninit_platform_driver,
+#ifdef CONFIG_OMAP2_DSS_DPI
+ dpi_uninit_platform_driver,
#endif
-#ifdef CONFIG_OMAP5_DSS_HDMI
- hdmi5_uninit_platform_driver,
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_uninit_platform_driver,
#endif
+ dispc_uninit_platform_driver,
+ dss_uninit_platform_driver,
};
-static bool dss_output_drv_loaded[ARRAY_SIZE(dss_output_drv_reg_funcs)];
-
static int __init omap_dss_init(void)
{
int r;
@@ -311,35 +305,20 @@ static int __init omap_dss_init(void)
if (r)
return r;
- r = dss_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize DSS platform driver\n");
- goto err_dss;
- }
-
- r = dispc_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize dispc platform driver\n");
- goto err_dispc;
- }
-
- /*
- * It's ok if the output-driver register fails. It happens, for example,
- * when there is no output-device (e.g. SDI for OMAP4).
- */
for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) {
r = dss_output_drv_reg_funcs[i]();
- if (r == 0)
- dss_output_drv_loaded[i] = true;
+ if (r)
+ goto err_reg;
}
- dss_initialized = true;
-
return 0;
-err_dispc:
- dss_uninit_platform_driver();
-err_dss:
+err_reg:
+ for (i = ARRAY_SIZE(dss_output_drv_reg_funcs) - i;
+ i < ARRAY_SIZE(dss_output_drv_reg_funcs);
+ ++i)
+ dss_output_drv_unreg_funcs[i]();
+
platform_driver_unregister(&omap_dss_driver);
return r;
@@ -349,13 +328,8 @@ static void __exit omap_dss_exit(void)
{
int i;
- for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i) {
- if (dss_output_drv_loaded[i])
- dss_output_drv_unreg_funcs[i]();
- }
-
- dispc_uninit_platform_driver();
- dss_uninit_platform_driver();
+ for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i)
+ dss_output_drv_unreg_funcs[i]();
platform_driver_unregister(&omap_dss_driver);
}
diff --git a/drivers/video/fbdev/omap2/dss/dispc.c b/drivers/video/fbdev/omap2/dss/dispc.c
index f4fc77d9d3bf..be716c9ffb88 100644
--- a/drivers/video/fbdev/omap2/dss/dispc.c
+++ b/drivers/video/fbdev/omap2/dss/dispc.c
@@ -39,6 +39,7 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/of.h>
+#include <linux/component.h>
#include <video/omapdss.h>
@@ -95,6 +96,9 @@ struct dispc_features {
bool mstandby_workaround:1;
bool set_max_preload:1;
+
+ /* PIXEL_INC is not added to the last pixel of a line */
+ bool last_pixel_inc_missing:1;
};
#define DISPC_MAX_NR_FIFOS 5
@@ -1741,6 +1745,15 @@ static void dispc_ovl_set_rotation_attrs(enum omap_plane plane, u8 rotation,
row_repeat = false;
}
+ /*
+ * OMAP4/5 Errata i631:
+ * NV12 in 1D mode must use ROTATION=1. Otherwise DSS will fetch extra
+ * rows beyond the framebuffer, which may cause OCP error.
+ */
+ if (color_mode == OMAP_DSS_COLOR_NV12 &&
+ rotation_type != OMAP_DSS_ROT_TILER)
+ vidrot = 1;
+
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12);
if (dss_has_feature(FEAT_ROWREPEATENABLE))
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane),
@@ -2154,7 +2167,7 @@ static unsigned long calc_core_clk_five_taps(unsigned long pclk,
if (height > out_height) {
unsigned int ppl = mgr_timings->x_res;
- tmp = pclk * height * out_width;
+ tmp = (u64)pclk * height * out_width;
do_div(tmp, 2 * out_height * ppl);
core_clk = tmp;
@@ -2162,14 +2175,14 @@ static unsigned long calc_core_clk_five_taps(unsigned long pclk,
if (ppl == out_width)
return 0;
- tmp = pclk * (height - 2 * out_height) * out_width;
+ tmp = (u64)pclk * (height - 2 * out_height) * out_width;
do_div(tmp, 2 * out_height * (ppl - out_width));
core_clk = max_t(u32, core_clk, tmp);
}
}
if (width > out_width) {
- tmp = pclk * width;
+ tmp = (u64)pclk * width;
do_div(tmp, out_width);
core_clk = max_t(u32, core_clk, tmp);
@@ -2267,6 +2280,11 @@ static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk,
}
} while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);
+ if (error) {
+ DSSERR("failed to find scaling settings\n");
+ return -EINVAL;
+ }
+
if (in_width > maxsinglelinewidth) {
DSSERR("Cannot scale max input width exceeded");
return -EINVAL;
@@ -2283,7 +2301,6 @@ static int dispc_ovl_calc_scaling_34xx(unsigned long pclk, unsigned long lclk,
{
int error;
u16 in_width, in_height;
- int min_factor = min(*decim_x, *decim_y);
const int maxsinglelinewidth =
dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
@@ -2317,20 +2334,32 @@ again:
error = (error || in_width > maxsinglelinewidth * 2 ||
(in_width > maxsinglelinewidth && *five_taps) ||
!*core_clk || *core_clk > dispc_core_clk_rate());
- if (error) {
- if (*decim_x == *decim_y) {
- *decim_x = min_factor;
- ++*decim_y;
+
+ if (!error) {
+ /* verify that we're inside the limits of scaler */
+ if (in_width / 4 > out_width)
+ error = 1;
+
+ if (*five_taps) {
+ if (in_height / 4 > out_height)
+ error = 1;
} else {
- swap(*decim_x, *decim_y);
- if (*decim_x < *decim_y)
- ++*decim_x;
+ if (in_height / 2 > out_height)
+ error = 1;
}
}
+
+ if (error)
+ ++*decim_y;
} while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);
- if (check_horiz_timing_omap3(pclk, lclk, mgr_timings, pos_x, width,
- height, out_width, out_height, *five_taps)) {
+ if (error) {
+ DSSERR("failed to find scaling settings\n");
+ return -EINVAL;
+ }
+
+ if (check_horiz_timing_omap3(pclk, lclk, mgr_timings, pos_x, in_width,
+ in_height, out_width, out_height, *five_taps)) {
DSSERR("horizontal timing too tight\n");
return -EINVAL;
}
@@ -2390,6 +2419,9 @@ static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk,
return 0;
}
+#define DIV_FRAC(dividend, divisor) \
+ ((dividend) * 100 / (divisor) - ((dividend) / (divisor) * 100))
+
static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk,
enum omap_overlay_caps caps,
const struct omap_video_timings *mgr_timings,
@@ -2449,8 +2481,19 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk,
if (ret)
return ret;
- DSSDBG("required core clk rate = %lu Hz\n", core_clk);
- DSSDBG("current core clk rate = %lu Hz\n", dispc_core_clk_rate());
+ DSSDBG("%dx%d -> %dx%d (%d.%02d x %d.%02d), decim %dx%d %dx%d (%d.%02d x %d.%02d), taps %d, req clk %lu, cur clk %lu\n",
+ width, height,
+ out_width, out_height,
+ out_width / width, DIV_FRAC(out_width, width),
+ out_height / height, DIV_FRAC(out_height, height),
+
+ decim_x, decim_y,
+ width / decim_x, height / decim_y,
+ out_width / (width / decim_x), DIV_FRAC(out_width, width / decim_x),
+ out_height / (height / decim_y), DIV_FRAC(out_height, height / decim_y),
+
+ *five_taps ? 5 : 3,
+ core_clk, dispc_core_clk_rate());
if (!core_clk || core_clk > dispc_core_clk_rate()) {
DSSERR("failed to set up scaling, "
@@ -2533,6 +2576,21 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER)
return -EINVAL;
+ switch (color_mode) {
+ case OMAP_DSS_COLOR_YUV2:
+ case OMAP_DSS_COLOR_UYVY:
+ case OMAP_DSS_COLOR_NV12:
+ if (in_width & 1) {
+ DSSERR("input width %d is not even for YUV format\n",
+ in_width);
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
out_width = out_width == 0 ? width : out_width;
out_height = out_height == 0 ? height : out_height;
@@ -2563,6 +2621,27 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
in_width = in_width / x_predecim;
in_height = in_height / y_predecim;
+ if (x_predecim > 1 || y_predecim > 1)
+ DSSDBG("predecimation %d x %x, new input size %d x %d\n",
+ x_predecim, y_predecim, in_width, in_height);
+
+ switch (color_mode) {
+ case OMAP_DSS_COLOR_YUV2:
+ case OMAP_DSS_COLOR_UYVY:
+ case OMAP_DSS_COLOR_NV12:
+ if (in_width & 1) {
+ DSSDBG("predecimated input width is not even for YUV format\n");
+ DSSDBG("adjusting input width %d -> %d\n",
+ in_width, in_width & ~1);
+
+ in_width &= ~1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
if (color_mode == OMAP_DSS_COLOR_YUV2 ||
color_mode == OMAP_DSS_COLOR_UYVY ||
color_mode == OMAP_DSS_COLOR_NV12)
@@ -2632,6 +2711,9 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
dispc_ovl_set_ba1_uv(plane, p_uv_addr + offset1);
}
+ if (dispc.feat->last_pixel_inc_missing)
+ row_inc += pix_inc - 1;
+
dispc_ovl_set_row_inc(plane, row_inc);
dispc_ovl_set_pix_inc(plane, pix_inc);
@@ -3692,7 +3774,7 @@ static void _omap_dispc_initial_config(void)
dispc_init_mflag();
}
-static const struct dispc_features omap24xx_dispc_feats __initconst = {
+static const struct dispc_features omap24xx_dispc_feats = {
.sw_start = 5,
.fp_start = 15,
.bp_start = 27,
@@ -3709,9 +3791,10 @@ static const struct dispc_features omap24xx_dispc_feats __initconst = {
.num_fifos = 3,
.no_framedone_tv = true,
.set_max_preload = false,
+ .last_pixel_inc_missing = true,
};
-static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = {
+static const struct dispc_features omap34xx_rev1_0_dispc_feats = {
.sw_start = 5,
.fp_start = 15,
.bp_start = 27,
@@ -3729,9 +3812,10 @@ static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = {
.num_fifos = 3,
.no_framedone_tv = true,
.set_max_preload = false,
+ .last_pixel_inc_missing = true,
};
-static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = {
+static const struct dispc_features omap34xx_rev3_0_dispc_feats = {
.sw_start = 7,
.fp_start = 19,
.bp_start = 31,
@@ -3749,9 +3833,10 @@ static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = {
.num_fifos = 3,
.no_framedone_tv = true,
.set_max_preload = false,
+ .last_pixel_inc_missing = true,
};
-static const struct dispc_features omap44xx_dispc_feats __initconst = {
+static const struct dispc_features omap44xx_dispc_feats = {
.sw_start = 7,
.fp_start = 19,
.bp_start = 31,
@@ -3771,7 +3856,7 @@ static const struct dispc_features omap44xx_dispc_feats __initconst = {
.set_max_preload = true,
};
-static const struct dispc_features omap54xx_dispc_feats __initconst = {
+static const struct dispc_features omap54xx_dispc_feats = {
.sw_start = 7,
.fp_start = 19,
.bp_start = 31,
@@ -3792,7 +3877,7 @@ static const struct dispc_features omap54xx_dispc_feats __initconst = {
.set_max_preload = true,
};
-static int __init dispc_init_features(struct platform_device *pdev)
+static int dispc_init_features(struct platform_device *pdev)
{
const struct dispc_features *src;
struct dispc_features *dst;
@@ -3882,8 +3967,9 @@ void dispc_free_irq(void *dev_id)
EXPORT_SYMBOL(dispc_free_irq);
/* DISPC HW IP initialisation */
-static int __init omap_dispchw_probe(struct platform_device *pdev)
+static int dispc_bind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
u32 rev;
int r = 0;
struct resource *dispc_mem;
@@ -3955,12 +4041,27 @@ err_runtime_get:
return r;
}
-static int __exit omap_dispchw_remove(struct platform_device *pdev)
+static void dispc_unbind(struct device *dev, struct device *master,
+ void *data)
{
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_disable(dev);
dss_uninit_overlay_managers();
+}
+
+static const struct component_ops dispc_component_ops = {
+ .bind = dispc_bind,
+ .unbind = dispc_unbind,
+};
+static int dispc_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &dispc_component_ops);
+}
+
+static int dispc_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &dispc_component_ops);
return 0;
}
@@ -4013,7 +4114,8 @@ static const struct of_device_id dispc_of_match[] = {
};
static struct platform_driver omap_dispchw_driver = {
- .remove = __exit_p(omap_dispchw_remove),
+ .probe = dispc_probe,
+ .remove = dispc_remove,
.driver = {
.name = "omapdss_dispc",
.pm = &dispc_pm_ops,
@@ -4024,10 +4126,10 @@ static struct platform_driver omap_dispchw_driver = {
int __init dispc_init_platform_driver(void)
{
- return platform_driver_probe(&omap_dispchw_driver, omap_dispchw_probe);
+ return platform_driver_register(&omap_dispchw_driver);
}
-void __exit dispc_uninit_platform_driver(void)
+void dispc_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_dispchw_driver);
}
diff --git a/drivers/video/fbdev/omap2/dss/display-sysfs.c b/drivers/video/fbdev/omap2/dss/display-sysfs.c
index 12186557a9d4..6ad0991f8259 100644
--- a/drivers/video/fbdev/omap2/dss/display-sysfs.c
+++ b/drivers/video/fbdev/omap2/dss/display-sysfs.c
@@ -324,7 +324,7 @@ int display_init_sysfs(struct platform_device *pdev)
for_each_dss_dev(dssdev) {
r = kobject_init_and_add(&dssdev->kobj, &display_ktype,
- &pdev->dev.kobj, dssdev->alias);
+ &pdev->dev.kobj, "%s", dssdev->alias);
if (r) {
DSSERR("failed to create sysfs files\n");
omap_dss_put_device(dssdev);
diff --git a/drivers/video/fbdev/omap2/dss/dpi.c b/drivers/video/fbdev/omap2/dss/dpi.c
index f83e7b030249..fb45b6432968 100644
--- a/drivers/video/fbdev/omap2/dss/dpi.c
+++ b/drivers/video/fbdev/omap2/dss/dpi.c
@@ -32,6 +32,7 @@
#include <linux/string.h>
#include <linux/of.h>
#include <linux/clk.h>
+#include <linux/component.h>
#include <video/omapdss.h>
@@ -731,7 +732,7 @@ static void dpi_init_output(struct platform_device *pdev)
omapdss_register_output(out);
}
-static void __exit dpi_uninit_output(struct platform_device *pdev)
+static void dpi_uninit_output(struct platform_device *pdev)
{
struct dpi_data *dpi = dpi_get_data_from_pdev(pdev);
struct omap_dss_device *out = &dpi->output;
@@ -775,7 +776,7 @@ static void dpi_init_output_port(struct platform_device *pdev,
omapdss_register_output(out);
}
-static void __exit dpi_uninit_output_port(struct device_node *port)
+static void dpi_uninit_output_port(struct device_node *port)
{
struct dpi_data *dpi = port->data;
struct omap_dss_device *out = &dpi->output;
@@ -783,8 +784,9 @@ static void __exit dpi_uninit_output_port(struct device_node *port)
omapdss_unregister_output(out);
}
-static int omap_dpi_probe(struct platform_device *pdev)
+static int dpi_bind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct dpi_data *dpi;
dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);
@@ -802,16 +804,32 @@ static int omap_dpi_probe(struct platform_device *pdev)
return 0;
}
-static int __exit omap_dpi_remove(struct platform_device *pdev)
+static void dpi_unbind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
+
dpi_uninit_output(pdev);
+}
+
+static const struct component_ops dpi_component_ops = {
+ .bind = dpi_bind,
+ .unbind = dpi_unbind,
+};
+static int dpi_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &dpi_component_ops);
+}
+
+static int dpi_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &dpi_component_ops);
return 0;
}
static struct platform_driver omap_dpi_driver = {
- .probe = omap_dpi_probe,
- .remove = __exit_p(omap_dpi_remove),
+ .probe = dpi_probe,
+ .remove = dpi_remove,
.driver = {
.name = "omapdss_dpi",
.suppress_bind_attrs = true,
@@ -823,12 +841,12 @@ int __init dpi_init_platform_driver(void)
return platform_driver_register(&omap_dpi_driver);
}
-void __exit dpi_uninit_platform_driver(void)
+void dpi_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_dpi_driver);
}
-int __init dpi_init_port(struct platform_device *pdev, struct device_node *port)
+int dpi_init_port(struct platform_device *pdev, struct device_node *port)
{
struct dpi_data *dpi;
struct device_node *ep;
@@ -870,7 +888,7 @@ err_datalines:
return r;
}
-void __exit dpi_uninit_port(struct device_node *port)
+void dpi_uninit_port(struct device_node *port)
{
struct dpi_data *dpi = port->data;
diff --git a/drivers/video/fbdev/omap2/dss/dsi.c b/drivers/video/fbdev/omap2/dss/dsi.c
index 28b0bc11669d..b3606def5b7b 100644
--- a/drivers/video/fbdev/omap2/dss/dsi.c
+++ b/drivers/video/fbdev/omap2/dss/dsi.c
@@ -40,6 +40,7 @@
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/component.h>
#include <video/omapdss.h>
#include <video/mipi_display.h>
@@ -5274,8 +5275,9 @@ static int dsi_init_pll_data(struct platform_device *dsidev)
}
/* DSI1 HW IP initialisation */
-static int omap_dsihw_probe(struct platform_device *dsidev)
+static int dsi_bind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *dsidev = to_platform_device(dev);
u32 rev;
int r, i;
struct dsi_data *dsi;
@@ -5484,8 +5486,9 @@ err_runtime_get:
return r;
}
-static int __exit omap_dsihw_remove(struct platform_device *dsidev)
+static void dsi_unbind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *dsidev = to_platform_device(dev);
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
of_platform_depopulate(&dsidev->dev);
@@ -5502,7 +5505,21 @@ static int __exit omap_dsihw_remove(struct platform_device *dsidev)
regulator_disable(dsi->vdds_dsi_reg);
dsi->vdds_dsi_enabled = false;
}
+}
+
+static const struct component_ops dsi_component_ops = {
+ .bind = dsi_bind,
+ .unbind = dsi_unbind,
+};
+static int dsi_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &dsi_component_ops);
+}
+
+static int dsi_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &dsi_component_ops);
return 0;
}
@@ -5569,8 +5586,8 @@ static const struct of_device_id dsi_of_match[] = {
};
static struct platform_driver omap_dsihw_driver = {
- .probe = omap_dsihw_probe,
- .remove = __exit_p(omap_dsihw_remove),
+ .probe = dsi_probe,
+ .remove = dsi_remove,
.driver = {
.name = "omapdss_dsi",
.pm = &dsi_pm_ops,
@@ -5584,7 +5601,7 @@ int __init dsi_init_platform_driver(void)
return platform_driver_register(&omap_dsihw_driver);
}
-void __exit dsi_uninit_platform_driver(void)
+void dsi_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_dsihw_driver);
}
diff --git a/drivers/video/fbdev/omap2/dss/dss.c b/drivers/video/fbdev/omap2/dss/dss.c
index 7f978b6a34e8..612b093831d5 100644
--- a/drivers/video/fbdev/omap2/dss/dss.c
+++ b/drivers/video/fbdev/omap2/dss/dss.c
@@ -39,6 +39,7 @@
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/suspend.h>
+#include <linux/component.h>
#include <video/omapdss.h>
@@ -111,6 +112,14 @@ static const char * const dss_generic_clk_source_names[] = {
[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI] = "DSI_PLL2_HSDIV_DSI",
};
+static bool dss_initialized;
+
+bool omapdss_is_initialized(void)
+{
+ return dss_initialized;
+}
+EXPORT_SYMBOL(omapdss_is_initialized);
+
static inline void dss_write_reg(const struct dss_reg idx, u32 val)
{
__raw_writel(val, dss.base + idx.idx);
@@ -811,7 +820,7 @@ static const enum omap_display_type dra7xx_ports[] = {
OMAP_DISPLAY_TYPE_DPI,
};
-static const struct dss_features omap24xx_dss_feats __initconst = {
+static const struct dss_features omap24xx_dss_feats = {
/*
* fck div max is really 16, but the divider range has gaps. The range
* from 1 to 6 has no gaps, so let's use that as a max.
@@ -824,7 +833,7 @@ static const struct dss_features omap24xx_dss_feats __initconst = {
.num_ports = ARRAY_SIZE(omap2plus_ports),
};
-static const struct dss_features omap34xx_dss_feats __initconst = {
+static const struct dss_features omap34xx_dss_feats = {
.fck_div_max = 16,
.dss_fck_multiplier = 2,
.parent_clk_name = "dpll4_ck",
@@ -833,7 +842,7 @@ static const struct dss_features omap34xx_dss_feats __initconst = {
.num_ports = ARRAY_SIZE(omap34xx_ports),
};
-static const struct dss_features omap3630_dss_feats __initconst = {
+static const struct dss_features omap3630_dss_feats = {
.fck_div_max = 32,
.dss_fck_multiplier = 1,
.parent_clk_name = "dpll4_ck",
@@ -842,7 +851,7 @@ static const struct dss_features omap3630_dss_feats __initconst = {
.num_ports = ARRAY_SIZE(omap2plus_ports),
};
-static const struct dss_features omap44xx_dss_feats __initconst = {
+static const struct dss_features omap44xx_dss_feats = {
.fck_div_max = 32,
.dss_fck_multiplier = 1,
.parent_clk_name = "dpll_per_x2_ck",
@@ -851,7 +860,7 @@ static const struct dss_features omap44xx_dss_feats __initconst = {
.num_ports = ARRAY_SIZE(omap2plus_ports),
};
-static const struct dss_features omap54xx_dss_feats __initconst = {
+static const struct dss_features omap54xx_dss_feats = {
.fck_div_max = 64,
.dss_fck_multiplier = 1,
.parent_clk_name = "dpll_per_x2_ck",
@@ -860,7 +869,7 @@ static const struct dss_features omap54xx_dss_feats __initconst = {
.num_ports = ARRAY_SIZE(omap2plus_ports),
};
-static const struct dss_features am43xx_dss_feats __initconst = {
+static const struct dss_features am43xx_dss_feats = {
.fck_div_max = 0,
.dss_fck_multiplier = 0,
.parent_clk_name = NULL,
@@ -869,7 +878,7 @@ static const struct dss_features am43xx_dss_feats __initconst = {
.num_ports = ARRAY_SIZE(omap2plus_ports),
};
-static const struct dss_features dra7xx_dss_feats __initconst = {
+static const struct dss_features dra7xx_dss_feats = {
.fck_div_max = 64,
.dss_fck_multiplier = 1,
.parent_clk_name = "dpll_per_x2_ck",
@@ -878,7 +887,7 @@ static const struct dss_features dra7xx_dss_feats __initconst = {
.num_ports = ARRAY_SIZE(dra7xx_ports),
};
-static int __init dss_init_features(struct platform_device *pdev)
+static int dss_init_features(struct platform_device *pdev)
{
const struct dss_features *src;
struct dss_features *dst;
@@ -932,7 +941,7 @@ static int __init dss_init_features(struct platform_device *pdev)
return 0;
}
-static int __init dss_init_ports(struct platform_device *pdev)
+static int dss_init_ports(struct platform_device *pdev)
{
struct device_node *parent = pdev->dev.of_node;
struct device_node *port;
@@ -976,7 +985,7 @@ static int __init dss_init_ports(struct platform_device *pdev)
return 0;
}
-static void __exit dss_uninit_ports(struct platform_device *pdev)
+static void dss_uninit_ports(struct platform_device *pdev)
{
struct device_node *parent = pdev->dev.of_node;
struct device_node *port;
@@ -1018,14 +1027,74 @@ static void __exit dss_uninit_ports(struct platform_device *pdev)
} while ((port = omapdss_of_get_next_port(parent, port)) != NULL);
}
+static int dss_video_pll_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct regulator *pll_regulator;
+ int r;
+
+ if (!np)
+ return 0;
+
+ if (of_property_read_bool(np, "syscon-pll-ctrl")) {
+ dss.syscon_pll_ctrl = syscon_regmap_lookup_by_phandle(np,
+ "syscon-pll-ctrl");
+ if (IS_ERR(dss.syscon_pll_ctrl)) {
+ dev_err(&pdev->dev,
+ "failed to get syscon-pll-ctrl regmap\n");
+ return PTR_ERR(dss.syscon_pll_ctrl);
+ }
+
+ if (of_property_read_u32_index(np, "syscon-pll-ctrl", 1,
+ &dss.syscon_pll_ctrl_offset)) {
+ dev_err(&pdev->dev,
+ "failed to get syscon-pll-ctrl offset\n");
+ return -EINVAL;
+ }
+ }
+
+ pll_regulator = devm_regulator_get(&pdev->dev, "vdda_video");
+ if (IS_ERR(pll_regulator)) {
+ r = PTR_ERR(pll_regulator);
+
+ switch (r) {
+ case -ENOENT:
+ pll_regulator = NULL;
+ break;
+
+ case -EPROBE_DEFER:
+ return -EPROBE_DEFER;
+
+ default:
+ DSSERR("can't get DPLL VDDA regulator\n");
+ return r;
+ }
+ }
+
+ if (of_property_match_string(np, "reg-names", "pll1") >= 0) {
+ dss.video1_pll = dss_video_pll_init(pdev, 0, pll_regulator);
+ if (IS_ERR(dss.video1_pll))
+ return PTR_ERR(dss.video1_pll);
+ }
+
+ if (of_property_match_string(np, "reg-names", "pll2") >= 0) {
+ dss.video2_pll = dss_video_pll_init(pdev, 1, pll_regulator);
+ if (IS_ERR(dss.video2_pll)) {
+ dss_video_pll_uninit(dss.video1_pll);
+ return PTR_ERR(dss.video2_pll);
+ }
+ }
+
+ return 0;
+}
+
/* DSS HW IP initialisation */
-static int __init omap_dsshw_probe(struct platform_device *pdev)
+static int dss_bind(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct resource *dss_mem;
- struct device_node *np = pdev->dev.of_node;
u32 rev;
int r;
- struct regulator *pll_regulator;
dss.pdev = pdev;
@@ -1054,6 +1123,14 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
if (r)
goto err_setup_clocks;
+ r = dss_video_pll_probe(pdev);
+ if (r)
+ goto err_pll_init;
+
+ r = dss_init_ports(pdev);
+ if (r)
+ goto err_init_ports;
+
pm_runtime_enable(&pdev->dev);
r = dss_runtime_get();
@@ -1078,86 +1155,48 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
- dss_init_ports(pdev);
-
- if (np && of_property_read_bool(np, "syscon-pll-ctrl")) {
- dss.syscon_pll_ctrl = syscon_regmap_lookup_by_phandle(np,
- "syscon-pll-ctrl");
- if (IS_ERR(dss.syscon_pll_ctrl)) {
- dev_err(&pdev->dev,
- "failed to get syscon-pll-ctrl regmap\n");
- return PTR_ERR(dss.syscon_pll_ctrl);
- }
-
- if (of_property_read_u32_index(np, "syscon-pll-ctrl", 1,
- &dss.syscon_pll_ctrl_offset)) {
- dev_err(&pdev->dev,
- "failed to get syscon-pll-ctrl offset\n");
- return -EINVAL;
- }
- }
-
- pll_regulator = devm_regulator_get(&pdev->dev, "vdda_video");
- if (IS_ERR(pll_regulator)) {
- r = PTR_ERR(pll_regulator);
-
- switch (r) {
- case -ENOENT:
- pll_regulator = NULL;
- break;
-
- case -EPROBE_DEFER:
- return -EPROBE_DEFER;
-
- default:
- DSSERR("can't get DPLL VDDA regulator\n");
- return r;
- }
- }
-
- if (of_property_match_string(np, "reg-names", "pll1") >= 0) {
- dss.video1_pll = dss_video_pll_init(pdev, 0, pll_regulator);
- if (IS_ERR(dss.video1_pll)) {
- r = PTR_ERR(dss.video1_pll);
- goto err_pll_init;
- }
- }
-
- if (of_property_match_string(np, "reg-names", "pll2") >= 0) {
- dss.video2_pll = dss_video_pll_init(pdev, 1, pll_regulator);
- if (IS_ERR(dss.video2_pll)) {
- r = PTR_ERR(dss.video2_pll);
- goto err_pll_init;
- }
- }
-
rev = dss_read_reg(DSS_REVISION);
printk(KERN_INFO "OMAP DSS rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
dss_runtime_put();
+ r = component_bind_all(&pdev->dev, NULL);
+ if (r)
+ goto err_component;
+
dss_debugfs_create_file("dss", dss_dump_regs);
pm_set_vt_switch(0);
+ dss_initialized = true;
+
return 0;
-err_pll_init:
+err_component:
+err_runtime_get:
+ pm_runtime_disable(&pdev->dev);
+ dss_uninit_ports(pdev);
+err_init_ports:
if (dss.video1_pll)
dss_video_pll_uninit(dss.video1_pll);
if (dss.video2_pll)
dss_video_pll_uninit(dss.video2_pll);
-err_runtime_get:
- pm_runtime_disable(&pdev->dev);
+err_pll_init:
err_setup_clocks:
dss_put_clocks();
return r;
}
-static int __exit omap_dsshw_remove(struct platform_device *pdev)
+static void dss_unbind(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ dss_initialized = false;
+
+ component_unbind_all(&pdev->dev, NULL);
+
if (dss.video1_pll)
dss_video_pll_uninit(dss.video1_pll);
@@ -1169,10 +1208,49 @@ static int __exit omap_dsshw_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
dss_put_clocks();
+}
+
+static const struct component_master_ops dss_component_ops = {
+ .bind = dss_bind,
+ .unbind = dss_unbind,
+};
+
+static int dss_component_compare(struct device *dev, void *data)
+{
+ struct device *child = data;
+ return dev == child;
+}
+
+static int dss_add_child_component(struct device *dev, void *data)
+{
+ struct component_match **match = data;
+
+ component_match_add(dev->parent, match, dss_component_compare, dev);
+
+ return 0;
+}
+
+static int dss_probe(struct platform_device *pdev)
+{
+ struct component_match *match = NULL;
+ int r;
+
+ /* add all the child devices as components */
+ device_for_each_child(&pdev->dev, &match, dss_add_child_component);
+
+ r = component_master_add_with_match(&pdev->dev, &dss_component_ops, match);
+ if (r)
+ return r;
return 0;
}
+static int dss_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &dss_component_ops);
+ return 0;
+}
+
static int dss_runtime_suspend(struct device *dev)
{
dss_save_context();
@@ -1215,7 +1293,8 @@ static const struct of_device_id dss_of_match[] = {
MODULE_DEVICE_TABLE(of, dss_of_match);
static struct platform_driver omap_dsshw_driver = {
- .remove = __exit_p(omap_dsshw_remove),
+ .probe = dss_probe,
+ .remove = dss_remove,
.driver = {
.name = "omapdss_dss",
.pm = &dss_pm_ops,
@@ -1226,7 +1305,7 @@ static struct platform_driver omap_dsshw_driver = {
int __init dss_init_platform_driver(void)
{
- return platform_driver_probe(&omap_dsshw_driver, omap_dsshw_probe);
+ return platform_driver_register(&omap_dsshw_driver);
}
void dss_uninit_platform_driver(void)
diff --git a/drivers/video/fbdev/omap2/dss/dss.h b/drivers/video/fbdev/omap2/dss/dss.h
index 4812eee2622a..2406bcdb831a 100644
--- a/drivers/video/fbdev/omap2/dss/dss.h
+++ b/drivers/video/fbdev/omap2/dss/dss.h
@@ -309,18 +309,18 @@ bool dss_div_calc(unsigned long pck, unsigned long fck_min,
/* SDI */
int sdi_init_platform_driver(void) __init;
-void sdi_uninit_platform_driver(void) __exit;
+void sdi_uninit_platform_driver(void);
#ifdef CONFIG_OMAP2_DSS_SDI
-int sdi_init_port(struct platform_device *pdev, struct device_node *port) __init;
-void sdi_uninit_port(struct device_node *port) __exit;
+int sdi_init_port(struct platform_device *pdev, struct device_node *port);
+void sdi_uninit_port(struct device_node *port);
#else
-static inline int __init sdi_init_port(struct platform_device *pdev,
+static inline int sdi_init_port(struct platform_device *pdev,
struct device_node *port)
{
return 0;
}
-static inline void __exit sdi_uninit_port(struct device_node *port)
+static inline void sdi_uninit_port(struct device_node *port)
{
}
#endif
@@ -333,7 +333,7 @@ struct dentry;
struct file_operations;
int dsi_init_platform_driver(void) __init;
-void dsi_uninit_platform_driver(void) __exit;
+void dsi_uninit_platform_driver(void);
void dsi_dump_clocks(struct seq_file *s);
@@ -350,25 +350,25 @@ static inline u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
/* DPI */
int dpi_init_platform_driver(void) __init;
-void dpi_uninit_platform_driver(void) __exit;
+void dpi_uninit_platform_driver(void);
#ifdef CONFIG_OMAP2_DSS_DPI
-int dpi_init_port(struct platform_device *pdev, struct device_node *port) __init;
-void dpi_uninit_port(struct device_node *port) __exit;
+int dpi_init_port(struct platform_device *pdev, struct device_node *port);
+void dpi_uninit_port(struct device_node *port);
#else
-static inline int __init dpi_init_port(struct platform_device *pdev,
+static inline int dpi_init_port(struct platform_device *pdev,
struct device_node *port)
{
return 0;
}
-static inline void __exit dpi_uninit_port(struct device_node *port)
+static inline void dpi_uninit_port(struct device_node *port)
{
}
#endif
/* DISPC */
int dispc_init_platform_driver(void) __init;
-void dispc_uninit_platform_driver(void) __exit;
+void dispc_uninit_platform_driver(void);
void dispc_dump_clocks(struct seq_file *s);
void dispc_enable_sidle(void);
@@ -418,18 +418,18 @@ int dispc_wb_setup(const struct omap_dss_writeback_info *wi,
/* VENC */
int venc_init_platform_driver(void) __init;
-void venc_uninit_platform_driver(void) __exit;
+void venc_uninit_platform_driver(void);
/* HDMI */
int hdmi4_init_platform_driver(void) __init;
-void hdmi4_uninit_platform_driver(void) __exit;
+void hdmi4_uninit_platform_driver(void);
int hdmi5_init_platform_driver(void) __init;
-void hdmi5_uninit_platform_driver(void) __exit;
+void hdmi5_uninit_platform_driver(void);
/* RFBI */
int rfbi_init_platform_driver(void) __init;
-void rfbi_uninit_platform_driver(void) __exit;
+void rfbi_uninit_platform_driver(void);
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
diff --git a/drivers/video/fbdev/omap2/dss/hdmi4.c b/drivers/video/fbdev/omap2/dss/hdmi4.c
index 916d47978f41..6d3aa3f51c20 100644
--- a/drivers/video/fbdev/omap2/dss/hdmi4.c
+++ b/drivers/video/fbdev/omap2/dss/hdmi4.c
@@ -32,6 +32,7 @@
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/component.h>
#include <video/omapdss.h>
#include <sound/omap-hdmi-audio.h>
@@ -229,9 +230,9 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
err_mgr_enable:
hdmi_wp_video_stop(&hdmi.wp);
err_vid_enable:
-err_phy_cfg:
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
+err_phy_cfg:
err_pll_cfg:
dss_pll_disable(&hdmi.pll.pll);
err_pll_enable:
@@ -646,8 +647,9 @@ static int hdmi_audio_register(struct device *dev)
}
/* HDMI HW IP initialisation */
-static int omapdss_hdmihw_probe(struct platform_device *pdev)
+static int hdmi4_bind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
int r;
int irq;
@@ -713,8 +715,10 @@ err:
return r;
}
-static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
+static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
+
if (hdmi.audio_pdev)
platform_device_unregister(hdmi.audio_pdev);
@@ -723,7 +727,21 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
hdmi_pll_uninit(&hdmi.pll);
pm_runtime_disable(&pdev->dev);
+}
+
+static const struct component_ops hdmi4_component_ops = {
+ .bind = hdmi4_bind,
+ .unbind = hdmi4_unbind,
+};
+static int hdmi4_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &hdmi4_component_ops);
+}
+
+static int hdmi4_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &hdmi4_component_ops);
return 0;
}
@@ -756,8 +774,8 @@ static const struct of_device_id hdmi_of_match[] = {
};
static struct platform_driver omapdss_hdmihw_driver = {
- .probe = omapdss_hdmihw_probe,
- .remove = __exit_p(omapdss_hdmihw_remove),
+ .probe = hdmi4_probe,
+ .remove = hdmi4_remove,
.driver = {
.name = "omapdss_hdmi",
.pm = &hdmi_pm_ops,
@@ -771,7 +789,7 @@ int __init hdmi4_init_platform_driver(void)
return platform_driver_register(&omapdss_hdmihw_driver);
}
-void __exit hdmi4_uninit_platform_driver(void)
+void hdmi4_uninit_platform_driver(void)
{
platform_driver_unregister(&omapdss_hdmihw_driver);
}
diff --git a/drivers/video/fbdev/omap2/dss/hdmi4_core.c b/drivers/video/fbdev/omap2/dss/hdmi4_core.c
index 7eafea5b8e19..fa72e735dad2 100644
--- a/drivers/video/fbdev/omap2/dss/hdmi4_core.c
+++ b/drivers/video/fbdev/omap2/dss/hdmi4_core.c
@@ -654,6 +654,13 @@ static void hdmi_core_audio_infoframe_cfg(struct hdmi_core_data *core,
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), info_aud->db3);
sum += info_aud->db3;
+ /*
+ * The OMAP HDMI IP requires to use the 8-channel channel code when
+ * transmitting more than two channels.
+ */
+ if (info_aud->db4_ca != 0x00)
+ info_aud->db4_ca = 0x13;
+
hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), info_aud->db4_ca);
sum += info_aud->db4_ca;
@@ -795,7 +802,9 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
/*
* the HDMI IP needs to enable four stereo channels when transmitting
- * more than 2 audio channels
+ * more than 2 audio channels. Similarly, the channel count in the
+ * Audio InfoFrame has to match the sample_present bits (some channels
+ * are padded with zeroes)
*/
if (channel_count == 2) {
audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL;
@@ -807,6 +816,7 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
HDMI_AUDIO_I2S_SD1_EN | HDMI_AUDIO_I2S_SD2_EN |
HDMI_AUDIO_I2S_SD3_EN;
acore.layout = HDMI_AUDIO_LAYOUT_8CH;
+ audio->cea->db1_ct_cc = 7;
}
acore.en_spdif = false;
diff --git a/drivers/video/fbdev/omap2/dss/hdmi5.c b/drivers/video/fbdev/omap2/dss/hdmi5.c
index 3f0b34a7031a..7f875788edbc 100644
--- a/drivers/video/fbdev/omap2/dss/hdmi5.c
+++ b/drivers/video/fbdev/omap2/dss/hdmi5.c
@@ -37,6 +37,7 @@
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/component.h>
#include <video/omapdss.h>
#include <sound/omap-hdmi-audio.h>
@@ -681,8 +682,9 @@ static int hdmi_audio_register(struct device *dev)
}
/* HDMI HW IP initialisation */
-static int omapdss_hdmihw_probe(struct platform_device *pdev)
+static int hdmi5_bind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
int r;
int irq;
@@ -748,8 +750,10 @@ err:
return r;
}
-static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
+static void hdmi5_unbind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
+
if (hdmi.audio_pdev)
platform_device_unregister(hdmi.audio_pdev);
@@ -758,7 +762,21 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
hdmi_pll_uninit(&hdmi.pll);
pm_runtime_disable(&pdev->dev);
+}
+
+static const struct component_ops hdmi5_component_ops = {
+ .bind = hdmi5_bind,
+ .unbind = hdmi5_unbind,
+};
+static int hdmi5_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &hdmi5_component_ops);
+}
+
+static int hdmi5_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &hdmi5_component_ops);
return 0;
}
@@ -792,8 +810,8 @@ static const struct of_device_id hdmi_of_match[] = {
};
static struct platform_driver omapdss_hdmihw_driver = {
- .probe = omapdss_hdmihw_probe,
- .remove = __exit_p(omapdss_hdmihw_remove),
+ .probe = hdmi5_probe,
+ .remove = hdmi5_remove,
.driver = {
.name = "omapdss_hdmi5",
.pm = &hdmi_pm_ops,
@@ -807,7 +825,7 @@ int __init hdmi5_init_platform_driver(void)
return platform_driver_register(&omapdss_hdmihw_driver);
}
-void __exit hdmi5_uninit_platform_driver(void)
+void hdmi5_uninit_platform_driver(void)
{
platform_driver_unregister(&omapdss_hdmihw_driver);
}
diff --git a/drivers/video/fbdev/omap2/dss/hdmi5_core.c b/drivers/video/fbdev/omap2/dss/hdmi5_core.c
index bfc0c4c297d6..8ea531d2652c 100644
--- a/drivers/video/fbdev/omap2/dss/hdmi5_core.c
+++ b/drivers/video/fbdev/omap2/dss/hdmi5_core.c
@@ -790,7 +790,9 @@ static void hdmi5_core_audio_infoframe_cfg(struct hdmi_core_data *core,
hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF1, info_aud->db2_sf_ss);
hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF2, info_aud->db4_ca);
- hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF3, info_aud->db5_dminh_lsv);
+ hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF3,
+ (info_aud->db5_dminh_lsv & CEA861_AUDIO_INFOFRAME_DB5_DM_INH) >> 3 |
+ (info_aud->db5_dminh_lsv & CEA861_AUDIO_INFOFRAME_DB5_LSV));
}
int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
@@ -870,6 +872,7 @@ int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES;
audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS;
audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT;
+ audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST;
/* only LPCM atm */
audio_format.type = HDMI_AUDIO_TYPE_LPCM;
diff --git a/drivers/video/fbdev/omap2/dss/hdmi_wp.c b/drivers/video/fbdev/omap2/dss/hdmi_wp.c
index c15377e242cc..7c544bc56fb5 100644
--- a/drivers/video/fbdev/omap2/dss/hdmi_wp.c
+++ b/drivers/video/fbdev/omap2/dss/hdmi_wp.c
@@ -110,7 +110,23 @@ int hdmi_wp_video_start(struct hdmi_wp_data *wp)
void hdmi_wp_video_stop(struct hdmi_wp_data *wp)
{
+ int i;
+
+ hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, HDMI_IRQ_VIDEO_FRAME_DONE);
+
REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, false, 31, 31);
+
+ for (i = 0; i < 50; ++i) {
+ u32 v;
+
+ msleep(20);
+
+ v = hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS_RAW);
+ if (v & HDMI_IRQ_VIDEO_FRAME_DONE)
+ return;
+ }
+
+ DSSERR("no HDMI FRAMEDONE when disabling output\n");
}
void hdmi_wp_video_config_format(struct hdmi_wp_data *wp,
diff --git a/drivers/video/fbdev/omap2/dss/rfbi.c b/drivers/video/fbdev/omap2/dss/rfbi.c
index 065effca9236..1525a494d057 100644
--- a/drivers/video/fbdev/omap2/dss/rfbi.c
+++ b/drivers/video/fbdev/omap2/dss/rfbi.c
@@ -36,6 +36,7 @@
#include <linux/semaphore.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/component.h>
#include <video/omapdss.h>
#include "dss.h"
@@ -938,7 +939,7 @@ static void rfbi_init_output(struct platform_device *pdev)
omapdss_register_output(out);
}
-static void __exit rfbi_uninit_output(struct platform_device *pdev)
+static void rfbi_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &rfbi.output;
@@ -946,8 +947,9 @@ static void __exit rfbi_uninit_output(struct platform_device *pdev)
}
/* RFBI HW IP initialisation */
-static int omap_rfbihw_probe(struct platform_device *pdev)
+static int rfbi_bind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
u32 rev;
struct resource *rfbi_mem;
struct clk *clk;
@@ -1005,8 +1007,10 @@ err_runtime_get:
return r;
}
-static int __exit omap_rfbihw_remove(struct platform_device *pdev)
+static void rfbi_unbind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
+
rfbi_uninit_output(pdev);
pm_runtime_disable(&pdev->dev);
@@ -1014,6 +1018,22 @@ static int __exit omap_rfbihw_remove(struct platform_device *pdev)
return 0;
}
+static const struct component_ops rfbi_component_ops = {
+ .bind = rfbi_bind,
+ .unbind = rfbi_unbind,
+};
+
+static int rfbi_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &rfbi_component_ops);
+}
+
+static int rfbi_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &rfbi_component_ops);
+ return 0;
+}
+
static int rfbi_runtime_suspend(struct device *dev)
{
dispc_runtime_put();
@@ -1038,8 +1058,8 @@ static const struct dev_pm_ops rfbi_pm_ops = {
};
static struct platform_driver omap_rfbihw_driver = {
- .probe = omap_rfbihw_probe,
- .remove = __exit_p(omap_rfbihw_remove),
+ .probe = rfbi_probe,
+ .remove = rfbi_remove,
.driver = {
.name = "omapdss_rfbi",
.pm = &rfbi_pm_ops,
@@ -1052,7 +1072,7 @@ int __init rfbi_init_platform_driver(void)
return platform_driver_register(&omap_rfbihw_driver);
}
-void __exit rfbi_uninit_platform_driver(void)
+void rfbi_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_rfbihw_driver);
}
diff --git a/drivers/video/fbdev/omap2/dss/sdi.c b/drivers/video/fbdev/omap2/dss/sdi.c
index 5c2ccab5a958..5843580a1deb 100644
--- a/drivers/video/fbdev/omap2/dss/sdi.c
+++ b/drivers/video/fbdev/omap2/dss/sdi.c
@@ -27,6 +27,7 @@
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/of.h>
+#include <linux/component.h>
#include <video/omapdss.h>
#include "dss.h"
@@ -350,15 +351,17 @@ static void sdi_init_output(struct platform_device *pdev)
omapdss_register_output(out);
}
-static void __exit sdi_uninit_output(struct platform_device *pdev)
+static void sdi_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &sdi.output;
omapdss_unregister_output(out);
}
-static int omap_sdi_probe(struct platform_device *pdev)
+static int sdi_bind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
+
sdi.pdev = pdev;
sdi_init_output(pdev);
@@ -366,16 +369,32 @@ static int omap_sdi_probe(struct platform_device *pdev)
return 0;
}
-static int __exit omap_sdi_remove(struct platform_device *pdev)
+static void sdi_unbind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
+
sdi_uninit_output(pdev);
+}
+
+static const struct component_ops sdi_component_ops = {
+ .bind = sdi_bind,
+ .unbind = sdi_unbind,
+};
+static int sdi_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &sdi_component_ops);
+}
+
+static int sdi_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &sdi_component_ops);
return 0;
}
static struct platform_driver omap_sdi_driver = {
- .probe = omap_sdi_probe,
- .remove = __exit_p(omap_sdi_remove),
+ .probe = sdi_probe,
+ .remove = sdi_remove,
.driver = {
.name = "omapdss_sdi",
.suppress_bind_attrs = true,
@@ -387,12 +406,12 @@ int __init sdi_init_platform_driver(void)
return platform_driver_register(&omap_sdi_driver);
}
-void __exit sdi_uninit_platform_driver(void)
+void sdi_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_sdi_driver);
}
-int __init sdi_init_port(struct platform_device *pdev, struct device_node *port)
+int sdi_init_port(struct platform_device *pdev, struct device_node *port)
{
struct device_node *ep;
u32 datapairs;
@@ -426,7 +445,7 @@ err_datapairs:
return r;
}
-void __exit sdi_uninit_port(struct device_node *port)
+void sdi_uninit_port(struct device_node *port)
{
if (!sdi.port_initialized)
return;
diff --git a/drivers/video/fbdev/omap2/dss/venc.c b/drivers/video/fbdev/omap2/dss/venc.c
index ef7fd925e7f2..99ca268c1cdd 100644
--- a/drivers/video/fbdev/omap2/dss/venc.c
+++ b/drivers/video/fbdev/omap2/dss/venc.c
@@ -35,6 +35,7 @@
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
+#include <linux/component.h>
#include <video/omapdss.h>
@@ -802,7 +803,7 @@ static void venc_init_output(struct platform_device *pdev)
omapdss_register_output(out);
}
-static void __exit venc_uninit_output(struct platform_device *pdev)
+static void venc_uninit_output(struct platform_device *pdev)
{
struct omap_dss_device *out = &venc.output;
@@ -852,8 +853,9 @@ err:
}
/* VENC HW IP initialisation */
-static int omap_venchw_probe(struct platform_device *pdev)
+static int venc_bind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
u8 rev_id;
struct resource *venc_mem;
int r;
@@ -912,12 +914,28 @@ err_runtime_get:
return r;
}
-static int __exit omap_venchw_remove(struct platform_device *pdev)
+static void venc_unbind(struct device *dev, struct device *master, void *data)
{
+ struct platform_device *pdev = to_platform_device(dev);
+
venc_uninit_output(pdev);
pm_runtime_disable(&pdev->dev);
+}
+static const struct component_ops venc_component_ops = {
+ .bind = venc_bind,
+ .unbind = venc_unbind,
+};
+
+static int venc_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &venc_component_ops);
+}
+
+static int venc_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &venc_component_ops);
return 0;
}
@@ -950,7 +968,6 @@ static const struct dev_pm_ops venc_pm_ops = {
.runtime_resume = venc_runtime_resume,
};
-
static const struct of_device_id venc_of_match[] = {
{ .compatible = "ti,omap2-venc", },
{ .compatible = "ti,omap3-venc", },
@@ -959,8 +976,8 @@ static const struct of_device_id venc_of_match[] = {
};
static struct platform_driver omap_venchw_driver = {
- .probe = omap_venchw_probe,
- .remove = __exit_p(omap_venchw_remove),
+ .probe = venc_probe,
+ .remove = venc_remove,
.driver = {
.name = "omapdss_venc",
.pm = &venc_pm_ops,
@@ -974,7 +991,7 @@ int __init venc_init_platform_driver(void)
return platform_driver_register(&omap_venchw_driver);
}
-void __exit venc_uninit_platform_driver(void)
+void venc_uninit_platform_driver(void)
{
platform_driver_unregister(&omap_venchw_driver);
}
diff --git a/drivers/video/fbdev/pm2fb.c b/drivers/video/fbdev/pm2fb.c
index 3b85b647bc10..aa8d28880912 100644
--- a/drivers/video/fbdev/pm2fb.c
+++ b/drivers/video/fbdev/pm2fb.c
@@ -38,10 +38,6 @@
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/pci.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
#include <video/permedia2.h>
#include <video/cvisionppc.h>
@@ -81,10 +77,7 @@ static char *mode_option;
static bool lowhsync;
static bool lowvsync;
static bool noaccel;
-/* mtrr option */
-#ifdef CONFIG_MTRR
static bool nomtrr;
-#endif
/*
* The hardware state of the graphics card that isn't part of the
@@ -100,7 +93,7 @@ struct pm2fb_par
u32 mem_control; /* MemControl reg at probe */
u32 boot_address; /* BootAddress reg at probe */
u32 palette[16];
- int mtrr_handle;
+ int wc_cookie;
};
/*
@@ -1637,21 +1630,16 @@ static int pm2fb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_exit_mmio;
}
info->screen_base =
- ioremap_nocache(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
+ ioremap_wc(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
if (!info->screen_base) {
printk(KERN_WARNING "pm2fb: Can't ioremap smem area.\n");
release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
goto err_exit_mmio;
}
-#ifdef CONFIG_MTRR
- default_par->mtrr_handle = -1;
if (!nomtrr)
- default_par->mtrr_handle =
- mtrr_add(pm2fb_fix.smem_start,
- pm2fb_fix.smem_len,
- MTRR_TYPE_WRCOMB, 1);
-#endif
+ default_par->wc_cookie = arch_phys_wc_add(pm2fb_fix.smem_start,
+ pm2fb_fix.smem_len);
info->fbops = &pm2fb_ops;
info->fix = pm2fb_fix;
@@ -1733,12 +1721,7 @@ static void pm2fb_remove(struct pci_dev *pdev)
struct pm2fb_par *par = info->par;
unregister_framebuffer(info);
-
-#ifdef CONFIG_MTRR
- if (par->mtrr_handle >= 0)
- mtrr_del(par->mtrr_handle, info->fix.smem_start,
- info->fix.smem_len);
-#endif /* CONFIG_MTRR */
+ arch_phys_wc_del(par->wc_cookie);
iounmap(info->screen_base);
release_mem_region(fix->smem_start, fix->smem_len);
iounmap(par->v_regs);
@@ -1791,10 +1774,8 @@ static int __init pm2fb_setup(char *options)
lowvsync = 1;
else if (!strncmp(this_opt, "hwcursor=", 9))
hwcursor = simple_strtoul(this_opt + 9, NULL, 0);
-#ifdef CONFIG_MTRR
else if (!strncmp(this_opt, "nomtrr", 6))
nomtrr = 1;
-#endif
else if (!strncmp(this_opt, "noaccel", 7))
noaccel = 1;
else
@@ -1847,10 +1828,8 @@ MODULE_PARM_DESC(noaccel, "Disable acceleration");
module_param(hwcursor, int, 0644);
MODULE_PARM_DESC(hwcursor, "Enable hardware cursor "
"(1=enable, 0=disable, default=1)");
-#ifdef CONFIG_MTRR
module_param(nomtrr, bool, 0);
MODULE_PARM_DESC(nomtrr, "Disable MTRR support (0 or 1=disabled) (default=0)");
-#endif
MODULE_AUTHOR("Jim Hague <jim.hague@acm.org>");
MODULE_DESCRIPTION("Permedia2 framebuffer device driver");
diff --git a/drivers/video/fbdev/pm3fb.c b/drivers/video/fbdev/pm3fb.c
index 77b99ed39ad0..6ff5077a2e15 100644
--- a/drivers/video/fbdev/pm3fb.c
+++ b/drivers/video/fbdev/pm3fb.c
@@ -32,9 +32,6 @@
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/pci.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
#include <video/pm3fb.h>
@@ -58,11 +55,7 @@
static int hwcursor = 1;
static char *mode_option;
static bool noaccel;
-
-/* mtrr option */
-#ifdef CONFIG_MTRR
static bool nomtrr;
-#endif
/*
* This structure defines the hardware state of the graphics card. Normally
@@ -76,7 +69,7 @@ struct pm3_par {
u32 video; /* video flags before blanking */
u32 base; /* screen base in 128 bits unit */
u32 palette[16];
- int mtrr_handle;
+ int wc_cookie;
};
/*
@@ -1374,8 +1367,8 @@ static int pm3fb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
printk(KERN_WARNING "pm3fb: Can't reserve smem.\n");
goto err_exit_mmio;
}
- info->screen_base =
- ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
+ info->screen_base = ioremap_wc(pm3fb_fix.smem_start,
+ pm3fb_fix.smem_len);
if (!info->screen_base) {
printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n");
release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
@@ -1383,12 +1376,9 @@ static int pm3fb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
}
info->screen_size = pm3fb_fix.smem_len;
-#ifdef CONFIG_MTRR
if (!nomtrr)
- par->mtrr_handle = mtrr_add(pm3fb_fix.smem_start,
- pm3fb_fix.smem_len,
- MTRR_TYPE_WRCOMB, 1);
-#endif
+ par->wc_cookie = arch_phys_wc_add(pm3fb_fix.smem_start,
+ pm3fb_fix.smem_len);
info->fbops = &pm3fb_ops;
par->video = PM3_READ_REG(par, PM3VideoControl);
@@ -1478,11 +1468,7 @@ static void pm3fb_remove(struct pci_dev *dev)
unregister_framebuffer(info);
fb_dealloc_cmap(&info->cmap);
-#ifdef CONFIG_MTRR
- if (par->mtrr_handle >= 0)
- mtrr_del(par->mtrr_handle, info->fix.smem_start,
- info->fix.smem_len);
-#endif /* CONFIG_MTRR */
+ arch_phys_wc_del(par->wc_cookie);
iounmap(info->screen_base);
release_mem_region(fix->smem_start, fix->smem_len);
iounmap(par->v_regs);
@@ -1533,10 +1519,8 @@ static int __init pm3fb_setup(char *options)
noaccel = 1;
else if (!strncmp(this_opt, "hwcursor=", 9))
hwcursor = simple_strtoul(this_opt + 9, NULL, 0);
-#ifdef CONFIG_MTRR
else if (!strncmp(this_opt, "nomtrr", 6))
nomtrr = 1;
-#endif
else
mode_option = this_opt;
}
@@ -1577,10 +1561,8 @@ MODULE_PARM_DESC(noaccel, "Disable acceleration");
module_param(hwcursor, int, 0644);
MODULE_PARM_DESC(hwcursor, "Enable hardware cursor "
"(1=enable, 0=disable, default=1)");
-#ifdef CONFIG_MTRR
module_param(nomtrr, bool, 0);
MODULE_PARM_DESC(nomtrr, "Disable MTRR support (0 or 1=disabled) (default=0)");
-#endif
MODULE_DESCRIPTION("Permedia3 framebuffer device driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/riva/fbdev.c b/drivers/video/fbdev/riva/fbdev.c
index 294a80908c8c..f1ad2747064b 100644
--- a/drivers/video/fbdev/riva/fbdev.c
+++ b/drivers/video/fbdev/riva/fbdev.c
@@ -41,9 +41,6 @@
#include <linux/pci.h>
#include <linux/backlight.h>
#include <linux/bitrev.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/machdep.h>
#include <asm/backlight.h>
@@ -204,9 +201,7 @@ MODULE_DEVICE_TABLE(pci, rivafb_pci_tbl);
static int flatpanel = -1; /* Autodetect later */
static int forceCRTC = -1;
static bool noaccel = 0;
-#ifdef CONFIG_MTRR
static bool nomtrr = 0;
-#endif
#ifdef CONFIG_PMAC_BACKLIGHT
static int backlight = 1;
#else
@@ -2010,28 +2005,18 @@ static int rivafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
rivafb_fix.smem_len = riva_get_memlen(default_par) * 1024;
default_par->dclk_max = riva_get_maxdclk(default_par) * 1000;
- info->screen_base = ioremap(rivafb_fix.smem_start,
- rivafb_fix.smem_len);
+ info->screen_base = ioremap_wc(rivafb_fix.smem_start,
+ rivafb_fix.smem_len);
if (!info->screen_base) {
printk(KERN_ERR PFX "cannot ioremap FB base\n");
ret = -EIO;
goto err_iounmap_pramin;
}
-#ifdef CONFIG_MTRR
- if (!nomtrr) {
- default_par->mtrr.vram = mtrr_add(rivafb_fix.smem_start,
- rivafb_fix.smem_len,
- MTRR_TYPE_WRCOMB, 1);
- if (default_par->mtrr.vram < 0) {
- printk(KERN_ERR PFX "unable to setup MTRR\n");
- } else {
- default_par->mtrr.vram_valid = 1;
- /* let there be speed */
- printk(KERN_INFO PFX "RIVA MTRR set to ON\n");
- }
- }
-#endif /* CONFIG_MTRR */
+ if (!nomtrr)
+ default_par->wc_cookie =
+ arch_phys_wc_add(rivafb_fix.smem_start,
+ rivafb_fix.smem_len);
info->fbops = &riva_fb_ops;
info->fix = rivafb_fix;
@@ -2105,13 +2090,7 @@ static void rivafb_remove(struct pci_dev *pd)
unregister_framebuffer(info);
riva_bl_exit(info);
-
-#ifdef CONFIG_MTRR
- if (par->mtrr.vram_valid)
- mtrr_del(par->mtrr.vram, info->fix.smem_start,
- info->fix.smem_len);
-#endif /* CONFIG_MTRR */
-
+ arch_phys_wc_del(par->wc_cookie);
iounmap(par->ctrl_base);
iounmap(info->screen_base);
if (par->riva.Architecture == NV_ARCH_03)
@@ -2150,10 +2129,8 @@ static int rivafb_setup(char *options)
flatpanel = 1;
} else if (!strncmp(this_opt, "backlight:", 10)) {
backlight = simple_strtoul(this_opt+10, NULL, 0);
-#ifdef CONFIG_MTRR
} else if (!strncmp(this_opt, "nomtrr", 6)) {
nomtrr = 1;
-#endif
} else if (!strncmp(this_opt, "strictmode", 10)) {
strictmode = 1;
} else if (!strncmp(this_opt, "noaccel", 7)) {
@@ -2209,10 +2186,8 @@ module_param(flatpanel, int, 0);
MODULE_PARM_DESC(flatpanel, "Enables experimental flat panel support for some chipsets. (0 or 1=enabled) (default=0)");
module_param(forceCRTC, int, 0);
MODULE_PARM_DESC(forceCRTC, "Forces usage of a particular CRTC in case autodetection fails. (0 or 1) (default=autodetect)");
-#ifdef CONFIG_MTRR
module_param(nomtrr, bool, 0);
MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) (default=0)");
-#endif
module_param(strictmode, bool, 0);
MODULE_PARM_DESC(strictmode, "Only use video modes from EDID");
diff --git a/drivers/video/fbdev/riva/rivafb.h b/drivers/video/fbdev/riva/rivafb.h
index d9f107b704c6..61fd37ca490a 100644
--- a/drivers/video/fbdev/riva/rivafb.h
+++ b/drivers/video/fbdev/riva/rivafb.h
@@ -61,9 +61,7 @@ struct riva_par {
int FlatPanel;
struct pci_dev *pdev;
int cursor_reset;
-#ifdef CONFIG_MTRR
- struct { int vram; int vram_valid; } mtrr;
-#endif
+ int wc_cookie;
struct riva_i2c_chan chan[3];
};
diff --git a/drivers/video/fbdev/savage/savagefb.h b/drivers/video/fbdev/savage/savagefb.h
index 8ff4ab1cb69b..aba04afe712d 100644
--- a/drivers/video/fbdev/savage/savagefb.h
+++ b/drivers/video/fbdev/savage/savagefb.h
@@ -213,9 +213,7 @@ struct savagefb_par {
void __iomem *vbase;
u32 pbase;
u32 len;
-#ifdef CONFIG_MTRR
- int mtrr;
-#endif
+ int wc_cookie;
} video;
struct {
diff --git a/drivers/video/fbdev/savage/savagefb_driver.c b/drivers/video/fbdev/savage/savagefb_driver.c
index 4dbf45f3b21a..6c77ab09b0b2 100644
--- a/drivers/video/fbdev/savage/savagefb_driver.c
+++ b/drivers/video/fbdev/savage/savagefb_driver.c
@@ -57,10 +57,6 @@
#include <asm/irq.h>
#include <asm/pgtable.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
#include "savagefb.h"
@@ -1775,7 +1771,7 @@ static int savage_map_video(struct fb_info *info, int video_len)
par->video.pbase = pci_resource_start(par->pcidev, resource);
par->video.len = video_len;
- par->video.vbase = ioremap(par->video.pbase, par->video.len);
+ par->video.vbase = ioremap_wc(par->video.pbase, par->video.len);
if (!par->video.vbase) {
printk("savagefb: unable to map screen memory\n");
@@ -1787,11 +1783,7 @@ static int savage_map_video(struct fb_info *info, int video_len)
info->fix.smem_start = par->video.pbase;
info->fix.smem_len = par->video.len - par->cob_size;
info->screen_base = par->video.vbase;
-
-#ifdef CONFIG_MTRR
- par->video.mtrr = mtrr_add(par->video.pbase, video_len,
- MTRR_TYPE_WRCOMB, 1);
-#endif
+ par->video.wc_cookie = arch_phys_wc_add(par->video.pbase, video_len);
/* Clear framebuffer, it's all white in memory after boot */
memset_io(par->video.vbase, 0, par->video.len);
@@ -1806,10 +1798,7 @@ static void savage_unmap_video(struct fb_info *info)
DBG("savage_unmap_video");
if (par->video.vbase) {
-#ifdef CONFIG_MTRR
- mtrr_del(par->video.mtrr, par->video.pbase, par->video.len);
-#endif
-
+ arch_phys_wc_del(par->video.wc_cookie);
iounmap(par->video.vbase);
par->video.vbase = NULL;
info->screen_base = NULL;
diff --git a/drivers/video/fbdev/sis/sis.h b/drivers/video/fbdev/sis/sis.h
index 1987f1b7212f..ea1d1c9640bf 100644
--- a/drivers/video/fbdev/sis/sis.h
+++ b/drivers/video/fbdev/sis/sis.h
@@ -458,7 +458,7 @@ struct sis_video_info {
unsigned char *bios_abase;
- int mtrr;
+ int wc_cookie;
u32 sisfb_mem;
diff --git a/drivers/video/fbdev/sis/sis_main.c b/drivers/video/fbdev/sis/sis_main.c
index fcf610edf217..e92303823a4b 100644
--- a/drivers/video/fbdev/sis/sis_main.c
+++ b/drivers/video/fbdev/sis/sis_main.c
@@ -53,9 +53,6 @@
#include <linux/types.h>
#include <linux/uaccess.h>
#include <asm/io.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
#include "sis.h"
#include "sis_main.h"
@@ -4130,13 +4127,13 @@ static void sisfb_post_map_vram(struct sis_video_info *ivideo,
if (*mapsize < (min << 20))
return;
- ivideo->video_vbase = ioremap(ivideo->video_base, (*mapsize));
+ ivideo->video_vbase = ioremap_wc(ivideo->video_base, (*mapsize));
if(!ivideo->video_vbase) {
printk(KERN_ERR
"sisfb: Unable to map maximum video RAM for size detection\n");
(*mapsize) >>= 1;
- while((!(ivideo->video_vbase = ioremap(ivideo->video_base, (*mapsize))))) {
+ while((!(ivideo->video_vbase = ioremap_wc(ivideo->video_base, (*mapsize))))) {
(*mapsize) >>= 1;
if((*mapsize) < (min << 20))
break;
@@ -6186,7 +6183,7 @@ static int sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto error_2;
}
- ivideo->video_vbase = ioremap(ivideo->video_base, ivideo->video_size);
+ ivideo->video_vbase = ioremap_wc(ivideo->video_base, ivideo->video_size);
ivideo->SiS_Pr.VideoMemoryAddress = ivideo->video_vbase;
if(!ivideo->video_vbase) {
printk(KERN_ERR "sisfb: Fatal error: Unable to map framebuffer memory\n");
@@ -6254,8 +6251,6 @@ error_3: vfree(ivideo->bios_abase);
ivideo->SiS_Pr.VideoMemoryAddress += ivideo->video_offset;
ivideo->SiS_Pr.VideoMemorySize = ivideo->sisfb_mem;
- ivideo->mtrr = -1;
-
ivideo->vbflags = 0;
ivideo->lcddefmodeidx = DEFAULT_LCDMODE;
ivideo->tvdefmodeidx = DEFAULT_TVMODE;
@@ -6443,14 +6438,8 @@ error_3: vfree(ivideo->bios_abase);
printk(KERN_DEBUG "sisfb: Initial vbflags 0x%x\n", (int)ivideo->vbflags);
-#ifdef CONFIG_MTRR
- ivideo->mtrr = mtrr_add(ivideo->video_base, ivideo->video_size,
- MTRR_TYPE_WRCOMB, 1);
- if(ivideo->mtrr < 0) {
- printk(KERN_DEBUG "sisfb: Failed to add MTRRs\n");
- }
-#endif
-
+ ivideo->wc_cookie = arch_phys_wc_add(ivideo->video_base,
+ ivideo->video_size);
if(register_framebuffer(sis_fb_info) < 0) {
printk(KERN_ERR "sisfb: Fatal error: Failed to register framebuffer\n");
ret = -EINVAL;
@@ -6507,11 +6496,7 @@ static void sisfb_remove(struct pci_dev *pdev)
pci_dev_put(ivideo->nbridge);
-#ifdef CONFIG_MTRR
- /* Release MTRR region */
- if(ivideo->mtrr >= 0)
- mtrr_del(ivideo->mtrr, ivideo->video_base, ivideo->video_size);
-#endif
+ arch_phys_wc_del(ivideo->wc_cookie);
/* If device was disabled when starting, disable
* it when quitting.
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index f7ed6d9016f7..3e153c06131a 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -7,6 +7,7 @@
*/
#include <linux/module.h>
+#include <linux/backlight.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/fb.h>
@@ -38,22 +39,43 @@
#define SSD1307FB_SET_COM_PINS_CONFIG 0xda
#define SSD1307FB_SET_VCOMH 0xdb
+#define MAX_CONTRAST 255
+
+#define REFRESHRATE 1
+
+static u_int refreshrate = REFRESHRATE;
+module_param(refreshrate, uint, 0);
+
struct ssd1307fb_par;
-struct ssd1307fb_ops {
- int (*init)(struct ssd1307fb_par *);
- int (*remove)(struct ssd1307fb_par *);
+struct ssd1307fb_deviceinfo {
+ u32 default_vcomh;
+ u32 default_dclk_div;
+ u32 default_dclk_frq;
+ int need_pwm;
+ int need_chargepump;
};
struct ssd1307fb_par {
+ u32 com_invdir;
+ u32 com_lrremap;
+ u32 com_offset;
+ u32 com_seq;
+ u32 contrast;
+ u32 dclk_div;
+ u32 dclk_frq;
+ struct ssd1307fb_deviceinfo *device_info;
struct i2c_client *client;
u32 height;
struct fb_info *info;
- struct ssd1307fb_ops *ops;
u32 page_offset;
+ u32 prechargep1;
+ u32 prechargep2;
struct pwm_device *pwm;
u32 pwm_period;
int reset;
+ u32 seg_remap;
+ u32 vcomh;
u32 width;
};
@@ -213,6 +235,16 @@ static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
return count;
}
+static int ssd1307fb_blank(int blank_mode, struct fb_info *info)
+{
+ struct ssd1307fb_par *par = info->par;
+
+ if (blank_mode != FB_BLANK_UNBLANK)
+ return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
+ else
+ return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
+}
+
static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
struct ssd1307fb_par *par = info->par;
@@ -238,6 +270,7 @@ static struct fb_ops ssd1307fb_ops = {
.owner = THIS_MODULE,
.fb_read = fb_sys_read,
.fb_write = ssd1307fb_write,
+ .fb_blank = ssd1307fb_blank,
.fb_fillrect = ssd1307fb_fillrect,
.fb_copyarea = ssd1307fb_copyarea,
.fb_imageblit = ssd1307fb_imageblit,
@@ -249,74 +282,46 @@ static void ssd1307fb_deferred_io(struct fb_info *info,
ssd1307fb_update_display(info->par);
}
-static struct fb_deferred_io ssd1307fb_defio = {
- .delay = HZ,
- .deferred_io = ssd1307fb_deferred_io,
-};
-
-static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
+static int ssd1307fb_init(struct ssd1307fb_par *par)
{
int ret;
+ u32 precharge, dclk, com_invdir, compins;
- par->pwm = pwm_get(&par->client->dev, NULL);
- if (IS_ERR(par->pwm)) {
- dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
- return PTR_ERR(par->pwm);
- }
-
- par->pwm_period = pwm_get_period(par->pwm);
- /* Enable the PWM */
- pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
- pwm_enable(par->pwm);
-
- dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
- par->pwm->pwm, par->pwm_period);
-
- /* Map column 127 of the OLED to segment 0 */
- ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
- if (ret < 0)
- return ret;
-
- /* Turn on the display */
- ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
-{
- pwm_disable(par->pwm);
- pwm_put(par->pwm);
- return 0;
-}
+ if (par->device_info->need_pwm) {
+ par->pwm = pwm_get(&par->client->dev, NULL);
+ if (IS_ERR(par->pwm)) {
+ dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
+ return PTR_ERR(par->pwm);
+ }
-static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
- .init = ssd1307fb_ssd1307_init,
- .remove = ssd1307fb_ssd1307_remove,
-};
+ par->pwm_period = pwm_get_period(par->pwm);
+ /* Enable the PWM */
+ pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+ pwm_enable(par->pwm);
-static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
-{
- int ret;
+ dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
+ par->pwm->pwm, par->pwm_period);
+ };
/* Set initial contrast */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
if (ret < 0)
return ret;
- ret = ssd1307fb_write_cmd(par->client, 0x7f);
- if (ret < 0)
- return ret;
-
- /* Set COM direction */
- ret = ssd1307fb_write_cmd(par->client, 0xc8);
+ ret = ssd1307fb_write_cmd(par->client, par->contrast);
if (ret < 0)
return ret;
/* Set segment re-map */
- ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+ if (par->seg_remap) {
+ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+ if (ret < 0)
+ return ret;
+ };
+
+ /* Set COM direction */
+ com_invdir = 0xc0 | (par->com_invdir & 0x1) << 3;
+ ret = ssd1307fb_write_cmd(par->client, com_invdir);
if (ret < 0)
return ret;
@@ -334,7 +339,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
- ret = ssd1307fb_write_cmd(par->client, 0x20);
+ ret = ssd1307fb_write_cmd(par->client, par->com_offset);
if (ret < 0)
return ret;
@@ -343,7 +348,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
- ret = ssd1307fb_write_cmd(par->client, 0xf0);
+ dclk = ((par->dclk_div - 1) & 0xf) | (par->dclk_frq & 0xf) << 4;
+ ret = ssd1307fb_write_cmd(par->client, dclk);
if (ret < 0)
return ret;
@@ -352,7 +358,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
- ret = ssd1307fb_write_cmd(par->client, 0x22);
+ precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4;
+ ret = ssd1307fb_write_cmd(par->client, precharge);
if (ret < 0)
return ret;
@@ -361,7 +368,9 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
- ret = ssd1307fb_write_cmd(par->client, 0x22);
+ compins = 0x02 | !(par->com_seq & 0x1) << 4
+ | (par->com_lrremap & 0x1) << 5;
+ ret = ssd1307fb_write_cmd(par->client, compins);
if (ret < 0)
return ret;
@@ -370,7 +379,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
- ret = ssd1307fb_write_cmd(par->client, 0x49);
+ ret = ssd1307fb_write_cmd(par->client, par->vcomh);
if (ret < 0)
return ret;
@@ -379,7 +388,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
- ret = ssd1307fb_write_cmd(par->client, 0x14);
+ ret = ssd1307fb_write_cmd(par->client,
+ (par->device_info->need_chargepump & 0x1 << 2) & 0x14);
if (ret < 0)
return ret;
@@ -393,6 +403,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
+ /* Set column range */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
if (ret < 0)
return ret;
@@ -405,6 +416,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
+ /* Set page range */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
if (ret < 0)
return ret;
@@ -426,18 +438,75 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
return 0;
}
-static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
- .init = ssd1307fb_ssd1306_init,
+static int ssd1307fb_update_bl(struct backlight_device *bdev)
+{
+ struct ssd1307fb_par *par = bl_get_data(bdev);
+ int ret;
+ int brightness = bdev->props.brightness;
+
+ par->contrast = brightness;
+
+ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
+ if (ret < 0)
+ return ret;
+ ret = ssd1307fb_write_cmd(par->client, par->contrast);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int ssd1307fb_get_brightness(struct backlight_device *bdev)
+{
+ struct ssd1307fb_par *par = bl_get_data(bdev);
+
+ return par->contrast;
+}
+
+static int ssd1307fb_check_fb(struct backlight_device *bdev,
+ struct fb_info *info)
+{
+ return (info->bl_dev == bdev);
+}
+
+static const struct backlight_ops ssd1307fb_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = ssd1307fb_update_bl,
+ .get_brightness = ssd1307fb_get_brightness,
+ .check_fb = ssd1307fb_check_fb,
+};
+
+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1305_deviceinfo = {
+ .default_vcomh = 0x34,
+ .default_dclk_div = 1,
+ .default_dclk_frq = 7,
+};
+
+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
+ .default_vcomh = 0x20,
+ .default_dclk_div = 1,
+ .default_dclk_frq = 8,
+ .need_chargepump = 1,
+};
+
+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
+ .default_vcomh = 0x20,
+ .default_dclk_div = 2,
+ .default_dclk_frq = 12,
+ .need_pwm = 1,
};
static const struct of_device_id ssd1307fb_of_match[] = {
{
+ .compatible = "solomon,ssd1305fb-i2c",
+ .data = (void *)&ssd1307fb_ssd1305_deviceinfo,
+ },
+ {
.compatible = "solomon,ssd1306fb-i2c",
- .data = (void *)&ssd1307fb_ssd1306_ops,
+ .data = (void *)&ssd1307fb_ssd1306_deviceinfo,
},
{
.compatible = "solomon,ssd1307fb-i2c",
- .data = (void *)&ssd1307fb_ssd1307_ops,
+ .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
},
{},
};
@@ -446,8 +515,11 @@ MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
static int ssd1307fb_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct backlight_device *bl;
+ char bl_name[12];
struct fb_info *info;
struct device_node *node = client->dev.of_node;
+ struct fb_deferred_io *ssd1307fb_defio;
u32 vmem_size;
struct ssd1307fb_par *par;
u8 *vmem;
@@ -468,8 +540,8 @@ static int ssd1307fb_probe(struct i2c_client *client,
par->info = info;
par->client = client;
- par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match,
- &client->dev)->data;
+ par->device_info = (struct ssd1307fb_deviceinfo *)of_match_device(
+ ssd1307fb_of_match, &client->dev)->data;
par->reset = of_get_named_gpio(client->dev.of_node,
"reset-gpios", 0);
@@ -487,19 +559,51 @@ static int ssd1307fb_probe(struct i2c_client *client,
if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
par->page_offset = 1;
+ if (of_property_read_u32(node, "solomon,com-offset", &par->com_offset))
+ par->com_offset = 0;
+
+ if (of_property_read_u32(node, "solomon,prechargep1", &par->prechargep1))
+ par->prechargep1 = 2;
+
+ if (of_property_read_u32(node, "solomon,prechargep2", &par->prechargep2))
+ par->prechargep2 = 2;
+
+ par->seg_remap = !of_property_read_bool(node, "solomon,segment-no-remap");
+ par->com_seq = of_property_read_bool(node, "solomon,com-seq");
+ par->com_lrremap = of_property_read_bool(node, "solomon,com-lrremap");
+ par->com_invdir = of_property_read_bool(node, "solomon,com-invdir");
+
+ par->contrast = 127;
+ par->vcomh = par->device_info->default_vcomh;
+
+ /* Setup display timing */
+ par->dclk_div = par->device_info->default_dclk_div;
+ par->dclk_frq = par->device_info->default_dclk_frq;
+
vmem_size = par->width * par->height / 8;
- vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
+ vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ get_order(vmem_size));
if (!vmem) {
dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
ret = -ENOMEM;
goto fb_alloc_error;
}
+ ssd1307fb_defio = devm_kzalloc(&client->dev, sizeof(struct fb_deferred_io), GFP_KERNEL);
+ if (!ssd1307fb_defio) {
+ dev_err(&client->dev, "Couldn't allocate deferred io.\n");
+ ret = -ENOMEM;
+ goto fb_alloc_error;
+ }
+
+ ssd1307fb_defio->delay = HZ / refreshrate;
+ ssd1307fb_defio->deferred_io = ssd1307fb_deferred_io;
+
info->fbops = &ssd1307fb_ops;
info->fix = ssd1307fb_fix;
info->fix.line_length = par->width / 8;
- info->fbdefio = &ssd1307fb_defio;
+ info->fbdefio = ssd1307fb_defio;
info->var = ssd1307fb_var;
info->var.xres = par->width;
@@ -515,7 +619,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
info->var.blue.offset = 0;
info->screen_base = (u8 __force __iomem *)vmem;
- info->fix.smem_start = (unsigned long)vmem;
+ info->fix.smem_start = __pa(vmem);
info->fix.smem_len = vmem_size;
fb_deferred_io_init(info);
@@ -538,11 +642,9 @@ static int ssd1307fb_probe(struct i2c_client *client,
gpio_set_value(par->reset, 1);
udelay(4);
- if (par->ops->init) {
- ret = par->ops->init(par);
- if (ret)
- goto reset_oled_error;
- }
+ ret = ssd1307fb_init(par);
+ if (ret)
+ goto reset_oled_error;
ret = register_framebuffer(info);
if (ret) {
@@ -550,13 +652,30 @@ static int ssd1307fb_probe(struct i2c_client *client,
goto panel_init_error;
}
+ snprintf(bl_name, sizeof(bl_name), "ssd1307fb%d", info->node);
+ bl = backlight_device_register(bl_name, &client->dev, par,
+ &ssd1307fb_bl_ops, NULL);
+ if (IS_ERR(bl)) {
+ dev_err(&client->dev, "unable to register backlight device: %ld\n",
+ PTR_ERR(bl));
+ goto bl_init_error;
+ }
+
+ bl->props.brightness = par->contrast;
+ bl->props.max_brightness = MAX_CONTRAST;
+ info->bl_dev = bl;
+
dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
return 0;
+bl_init_error:
+ unregister_framebuffer(info);
panel_init_error:
- if (par->ops->remove)
- par->ops->remove(par);
+ if (par->device_info->need_pwm) {
+ pwm_disable(par->pwm);
+ pwm_put(par->pwm);
+ };
reset_oled_error:
fb_deferred_io_cleanup(info);
fb_alloc_error:
@@ -569,16 +688,24 @@ static int ssd1307fb_remove(struct i2c_client *client)
struct fb_info *info = i2c_get_clientdata(client);
struct ssd1307fb_par *par = info->par;
+ ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
+
+ backlight_device_unregister(info->bl_dev);
+
unregister_framebuffer(info);
- if (par->ops->remove)
- par->ops->remove(par);
+ if (par->device_info->need_pwm) {
+ pwm_disable(par->pwm);
+ pwm_put(par->pwm);
+ };
fb_deferred_io_cleanup(info);
+ __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
framebuffer_release(info);
return 0;
}
static const struct i2c_device_id ssd1307fb_i2c_id[] = {
+ { "ssd1305fb", 0 },
{ "ssd1306fb", 0 },
{ "ssd1307fb", 0 },
{ }
diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c
index f761fe375f5b..621fa441a6db 100644
--- a/drivers/video/fbdev/tdfxfb.c
+++ b/drivers/video/fbdev/tdfxfb.c
@@ -78,24 +78,6 @@
#define DPRINTK(a, b...) pr_debug("fb: %s: " a, __func__ , ## b)
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#else
-/* duplicate asm/mtrr.h defines to work on archs without mtrr */
-#define MTRR_TYPE_WRCOMB 1
-
-static inline int mtrr_add(unsigned long base, unsigned long size,
- unsigned int type, char increment)
-{
- return -ENODEV;
-}
-static inline int mtrr_del(int reg, unsigned long base,
- unsigned long size)
-{
- return -ENODEV;
-}
-#endif
-
#define BANSHEE_MAX_PIXCLOCK 270000
#define VOODOO3_MAX_PIXCLOCK 300000
#define VOODOO5_MAX_PIXCLOCK 350000
@@ -167,7 +149,6 @@ static int nopan;
static int nowrap = 1; /* not implemented (yet) */
static int hwcursor = 1;
static char *mode_option;
-/* mtrr option */
static bool nomtrr;
/* -------------------------------------------------------------------------
@@ -1454,8 +1435,8 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_err_regbase;
}
- info->screen_base = ioremap_nocache(info->fix.smem_start,
- info->fix.smem_len);
+ info->screen_base = ioremap_wc(info->fix.smem_start,
+ info->fix.smem_len);
if (!info->screen_base) {
printk(KERN_ERR "fb: Can't remap %s framebuffer.\n",
info->fix.id);
@@ -1473,11 +1454,9 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
printk(KERN_INFO "fb: %s memory = %dK\n", info->fix.id,
info->fix.smem_len >> 10);
- default_par->mtrr_handle = -1;
if (!nomtrr)
- default_par->mtrr_handle =
- mtrr_add(info->fix.smem_start, info->fix.smem_len,
- MTRR_TYPE_WRCOMB, 1);
+ default_par->wc_cookie= arch_phys_wc_add(info->fix.smem_start,
+ info->fix.smem_len);
info->fix.ypanstep = nopan ? 0 : 1;
info->fix.ywrapstep = nowrap ? 0 : 1;
@@ -1566,9 +1545,7 @@ out_err_iobase:
#ifdef CONFIG_FB_3DFX_I2C
tdfxfb_delete_i2c_busses(default_par);
#endif
- if (default_par->mtrr_handle >= 0)
- mtrr_del(default_par->mtrr_handle, info->fix.smem_start,
- info->fix.smem_len);
+ arch_phys_wc_del(default_par->wc_cookie);
release_region(pci_resource_start(pdev, 2),
pci_resource_len(pdev, 2));
out_err_screenbase:
@@ -1604,10 +1581,8 @@ static void __init tdfxfb_setup(char *options)
nowrap = 1;
} else if (!strncmp(this_opt, "hwcursor=", 9)) {
hwcursor = simple_strtoul(this_opt + 9, NULL, 0);
-#ifdef CONFIG_MTRR
} else if (!strncmp(this_opt, "nomtrr", 6)) {
nomtrr = 1;
-#endif
} else {
mode_option = this_opt;
}
@@ -1633,9 +1608,7 @@ static void tdfxfb_remove(struct pci_dev *pdev)
#ifdef CONFIG_FB_3DFX_I2C
tdfxfb_delete_i2c_busses(par);
#endif
- if (par->mtrr_handle >= 0)
- mtrr_del(par->mtrr_handle, info->fix.smem_start,
- info->fix.smem_len);
+ arch_phys_wc_del(par->wc_cookie);
iounmap(par->regbase_virt);
iounmap(info->screen_base);
@@ -1677,10 +1650,8 @@ MODULE_PARM_DESC(hwcursor, "Enable hardware cursor "
"(1=enable, 0=disable, default=1)");
module_param(mode_option, charp, 0);
MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
-#ifdef CONFIG_MTRR
module_param(nomtrr, bool, 0);
MODULE_PARM_DESC(nomtrr, "Disable MTRR support (default: enabled)");
-#endif
module_init(tdfxfb_init);
module_exit(tdfxfb_exit);
diff --git a/drivers/video/fbdev/vesafb.c b/drivers/video/fbdev/vesafb.c
index d79a0ac49fc7..528fe917dd49 100644
--- a/drivers/video/fbdev/vesafb.c
+++ b/drivers/video/fbdev/vesafb.c
@@ -19,16 +19,20 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/screen_info.h>
+#include <linux/io.h>
#include <video/vga.h>
-#include <asm/io.h>
-#include <asm/mtrr.h>
#define dac_reg (0x3c8)
#define dac_val (0x3c9)
/* --------------------------------------------------------------------- */
+struct vesafb_par {
+ u32 pseudo_palette[256];
+ int wc_cookie;
+};
+
static struct fb_var_screeninfo vesafb_defined = {
.activate = FB_ACTIVATE_NOW,
.height = -1,
@@ -175,7 +179,10 @@ static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
static void vesafb_destroy(struct fb_info *info)
{
+ struct vesafb_par *par = info->par;
+
fb_dealloc_cmap(&info->cmap);
+ arch_phys_wc_del(par->wc_cookie);
if (info->screen_base)
iounmap(info->screen_base);
release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size);
@@ -228,6 +235,7 @@ static int vesafb_setup(char *options)
static int vesafb_probe(struct platform_device *dev)
{
struct fb_info *info;
+ struct vesafb_par *par;
int i, err;
unsigned int size_vmode;
unsigned int size_remap;
@@ -291,14 +299,14 @@ static int vesafb_probe(struct platform_device *dev)
spaces our resource handlers simply don't know about */
}
- info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev);
+ info = framebuffer_alloc(sizeof(struct vesafb_par), &dev->dev);
if (!info) {
release_mem_region(vesafb_fix.smem_start, size_total);
return -ENOMEM;
}
platform_set_drvdata(dev, info);
- info->pseudo_palette = info->par;
- info->par = NULL;
+ par = info->par;
+ info->pseudo_palette = par->pseudo_palette;
/* set vesafb aperture size for generic probing */
info->apertures = alloc_apertures(1);
@@ -404,60 +412,27 @@ static int vesafb_probe(struct platform_device *dev)
* region already (FIXME) */
request_region(0x3c0, 32, "vesafb");
-#ifdef CONFIG_MTRR
- if (mtrr) {
+ if (mtrr == 3) {
unsigned int temp_size = size_total;
- unsigned int type = 0;
-
- switch (mtrr) {
- case 1:
- type = MTRR_TYPE_UNCACHABLE;
- break;
- case 2:
- type = MTRR_TYPE_WRBACK;
- break;
- case 3:
- type = MTRR_TYPE_WRCOMB;
- break;
- case 4:
- type = MTRR_TYPE_WRTHROUGH;
- break;
- default:
- type = 0;
- break;
- }
- if (type) {
- int rc;
+ /* Find the largest power-of-two */
+ temp_size = roundup_pow_of_two(temp_size);
- /* Find the largest power-of-two */
- temp_size = roundup_pow_of_two(temp_size);
+ /* Try and find a power of two to add */
+ do {
+ par->wc_cookie =
+ arch_phys_wc_add(vesafb_fix.smem_start,
+ temp_size);
+ temp_size >>= 1;
+ } while (temp_size >= PAGE_SIZE && par->wc_cookie < 0);
- /* Try and find a power of two to add */
- do {
- rc = mtrr_add(vesafb_fix.smem_start, temp_size,
- type, 1);
- temp_size >>= 1;
- } while (temp_size >= PAGE_SIZE && rc == -EINVAL);
- }
- }
-#endif
-
- switch (mtrr) {
- case 1: /* uncachable */
- info->screen_base = ioremap_nocache(vesafb_fix.smem_start, vesafb_fix.smem_len);
- break;
- case 2: /* write-back */
- info->screen_base = ioremap_cache(vesafb_fix.smem_start, vesafb_fix.smem_len);
- break;
- case 3: /* write-combining */
info->screen_base = ioremap_wc(vesafb_fix.smem_start, vesafb_fix.smem_len);
- break;
- case 4: /* write-through */
- default:
+ } else {
+ if (mtrr && mtrr != 3)
+ WARN_ONCE(1, "Only MTRR_TYPE_WRCOMB (3) make sense\n");
info->screen_base = ioremap(vesafb_fix.smem_start, vesafb_fix.smem_len);
- break;
}
+
if (!info->screen_base) {
printk(KERN_ERR
"vesafb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n",
@@ -492,6 +467,7 @@ static int vesafb_probe(struct platform_device *dev)
fb_info(info, "%s frame buffer device\n", info->fix.id);
return 0;
err:
+ arch_phys_wc_del(par->wc_cookie);
if (info->screen_base)
iounmap(info->screen_base);
framebuffer_release(info);
diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
index e894eb278d83..5447b8186332 100644
--- a/drivers/virtio/virtio_pci_common.c
+++ b/drivers/virtio/virtio_pci_common.c
@@ -423,6 +423,7 @@ int vp_set_vq_affinity(struct virtqueue *vq, int cpu)
if (cpu == -1)
irq_set_affinity_hint(irq, NULL);
else {
+ cpumask_clear(mask);
cpumask_set_cpu(cpu, mask);
irq_set_affinity_hint(irq, mask);
}
@@ -501,9 +502,6 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
INIT_LIST_HEAD(&vp_dev->virtqueues);
spin_lock_init(&vp_dev->lock);
- /* Disable MSI/MSIX to bring device to a known good state. */
- pci_msi_off(pci_dev);
-
/* enable the device */
rc = pci_enable_device(pci_dev);
if (rc)
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index e5e7c5505de7..262647bbc614 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -470,6 +470,18 @@ config SIRFSOC_WATCHDOG
Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
the watchdog triggers the system will be reset.
+config ST_LPC_WATCHDOG
+ tristate "STMicroelectronics LPC Watchdog"
+ depends on ARCH_STI
+ depends on OF
+ select WATCHDOG_CORE
+ help
+ Say Y here to include STMicroelectronics Low Power Controller
+ (LPC) based Watchdog timer support.
+
+ To compile this driver as a module, choose M here: the
+ module will be called st_lpc_wdt.
+
config TEGRA_WATCHDOG
tristate "Tegra watchdog"
depends on (ARCH_TEGRA || COMPILE_TEST) && HAS_IOMEM
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 5c19294d1c30..d98768c7d928 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
+obj-$(CONFIG_ST_LPC_WATCHDOG) += st_lpc_wdt.o
obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o
obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c
new file mode 100644
index 000000000000..f32be155212a
--- /dev/null
+++ b/drivers/watchdog/st_lpc_wdt.c
@@ -0,0 +1,344 @@
+/*
+ * ST's LPC Watchdog
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ * Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+#include <dt-bindings/mfd/st-lpc.h>
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF 0x410
+#define LPC_LPA_START_OFF 0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF 0x510
+
+static struct watchdog_device st_wdog_dev;
+
+struct st_wdog_syscfg {
+ unsigned int reset_type_reg;
+ unsigned int reset_type_mask;
+ unsigned int enable_reg;
+ unsigned int enable_mask;
+};
+
+struct st_wdog {
+ void __iomem *base;
+ struct device *dev;
+ struct regmap *regmap;
+ struct st_wdog_syscfg *syscfg;
+ struct clk *clk;
+ unsigned long clkrate;
+ bool warm_reset;
+};
+
+static struct st_wdog_syscfg stid127_syscfg = {
+ .reset_type_reg = 0x004,
+ .reset_type_mask = BIT(2),
+ .enable_reg = 0x000,
+ .enable_mask = BIT(2),
+};
+
+static struct st_wdog_syscfg stih415_syscfg = {
+ .reset_type_reg = 0x0B8,
+ .reset_type_mask = BIT(6),
+ .enable_reg = 0x0B4,
+ .enable_mask = BIT(7),
+};
+
+static struct st_wdog_syscfg stih416_syscfg = {
+ .reset_type_reg = 0x88C,
+ .reset_type_mask = BIT(6),
+ .enable_reg = 0x888,
+ .enable_mask = BIT(7),
+};
+
+static struct st_wdog_syscfg stih407_syscfg = {
+ .enable_reg = 0x204,
+ .enable_mask = BIT(19),
+};
+
+static const struct of_device_id st_wdog_match[] = {
+ {
+ .compatible = "st,stih407-lpc",
+ .data = &stih407_syscfg,
+ },
+ {
+ .compatible = "st,stih416-lpc",
+ .data = &stih416_syscfg,
+ },
+ {
+ .compatible = "st,stih415-lpc",
+ .data = &stih415_syscfg,
+ },
+ {
+ .compatible = "st,stid127-lpc",
+ .data = &stid127_syscfg,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, st_wdog_match);
+
+static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
+{
+ /* Type of watchdog reset - 0: Cold 1: Warm */
+ if (st_wdog->syscfg->reset_type_reg)
+ regmap_update_bits(st_wdog->regmap,
+ st_wdog->syscfg->reset_type_reg,
+ st_wdog->syscfg->reset_type_mask,
+ st_wdog->warm_reset);
+
+ /* Mask/unmask watchdog reset */
+ regmap_update_bits(st_wdog->regmap,
+ st_wdog->syscfg->enable_reg,
+ st_wdog->syscfg->enable_mask,
+ enable ? 0 : st_wdog->syscfg->enable_mask);
+}
+
+static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
+{
+ unsigned long clkrate = st_wdog->clkrate;
+
+ writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
+ writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
+}
+
+static int st_wdog_start(struct watchdog_device *wdd)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
+
+ return 0;
+}
+
+static int st_wdog_stop(struct watchdog_device *wdd)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
+
+ return 0;
+}
+
+static int st_wdog_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ wdd->timeout = timeout;
+ st_wdog_load_timer(st_wdog, timeout);
+
+ return 0;
+}
+
+static int st_wdog_keepalive(struct watchdog_device *wdd)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+ st_wdog_load_timer(st_wdog, wdd->timeout);
+
+ return 0;
+}
+
+static const struct watchdog_info st_wdog_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "ST LPC WDT",
+};
+
+static const struct watchdog_ops st_wdog_ops = {
+ .owner = THIS_MODULE,
+ .start = st_wdog_start,
+ .stop = st_wdog_stop,
+ .ping = st_wdog_keepalive,
+ .set_timeout = st_wdog_set_timeout,
+};
+
+static struct watchdog_device st_wdog_dev = {
+ .info = &st_wdog_info,
+ .ops = &st_wdog_ops,
+};
+
+static int st_wdog_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device_node *np = pdev->dev.of_node;
+ struct st_wdog *st_wdog;
+ struct regmap *regmap;
+ struct resource *res;
+ struct clk *clk;
+ void __iomem *base;
+ uint32_t mode;
+ int ret;
+
+ ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+ if (ret) {
+ dev_err(&pdev->dev, "An LPC mode must be provided\n");
+ return -EINVAL;
+ }
+
+ /* LPC can either run in RTC or WDT mode */
+ if (mode != ST_LPC_MODE_WDT)
+ return -ENODEV;
+
+ st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
+ if (!st_wdog)
+ return -ENOMEM;
+
+ match = of_match_device(st_wdog_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Couldn't match device\n");
+ return -ENODEV;
+ }
+ st_wdog->syscfg = (struct st_wdog_syscfg *)match->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(regmap)) {
+ dev_err(&pdev->dev, "No syscfg phandle specified\n");
+ return PTR_ERR(regmap);
+ }
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Unable to request clock\n");
+ return PTR_ERR(clk);
+ }
+
+ st_wdog->dev = &pdev->dev;
+ st_wdog->base = base;
+ st_wdog->clk = clk;
+ st_wdog->regmap = regmap;
+ st_wdog->warm_reset = of_property_read_bool(np, "st,warm_reset");
+ st_wdog->clkrate = clk_get_rate(st_wdog->clk);
+
+ if (!st_wdog->clkrate) {
+ dev_err(&pdev->dev, "Unable to fetch clock rate\n");
+ return -EINVAL;
+ }
+ st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to enable clock\n");
+ return ret;
+ }
+
+ watchdog_set_drvdata(&st_wdog_dev, st_wdog);
+ watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
+
+ /* Init Watchdog timeout with value in DT */
+ ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
+ clk_disable_unprepare(clk);
+ return ret;
+ }
+
+ ret = watchdog_register_device(&st_wdog_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to register watchdog\n");
+ clk_disable_unprepare(clk);
+ return ret;
+ }
+
+ st_wdog_setup(st_wdog, true);
+
+ dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
+ st_wdog->warm_reset ? "warm" : "cold");
+
+ return ret;
+}
+
+static int st_wdog_remove(struct platform_device *pdev)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+ st_wdog_setup(st_wdog, false);
+ watchdog_unregister_device(&st_wdog_dev);
+ clk_disable_unprepare(st_wdog->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int st_wdog_suspend(struct device *dev)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+ if (watchdog_active(&st_wdog_dev))
+ st_wdog_stop(&st_wdog_dev);
+
+ st_wdog_setup(st_wdog, false);
+
+ clk_disable(st_wdog->clk);
+
+ return 0;
+}
+
+static int st_wdog_resume(struct device *dev)
+{
+ struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+ int ret;
+
+ ret = clk_enable(st_wdog->clk);
+ if (ret) {
+ dev_err(dev, "Unable to re-enable clock\n");
+ watchdog_unregister_device(&st_wdog_dev);
+ clk_unprepare(st_wdog->clk);
+ return ret;
+ }
+
+ st_wdog_setup(st_wdog, true);
+
+ if (watchdog_active(&st_wdog_dev)) {
+ st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
+ st_wdog_start(&st_wdog_dev);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
+ st_wdog_suspend,
+ st_wdog_resume);
+
+static struct platform_driver st_wdog_driver = {
+ .driver = {
+ .name = "st-lpc-wdt",
+ .pm = &st_wdog_pm_ops,
+ .of_match_table = st_wdog_match,
+ },
+ .probe = st_wdog_probe,
+ .remove = st_wdog_remove,
+};
+module_platform_driver(st_wdog_driver);
+
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_DESCRIPTION("ST LPC Watchdog Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/xen/events/events_2l.c b/drivers/xen/events/events_2l.c
index 5db43fc100a4..7dd46312c180 100644
--- a/drivers/xen/events/events_2l.c
+++ b/drivers/xen/events/events_2l.c
@@ -345,6 +345,15 @@ irqreturn_t xen_debug_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static void evtchn_2l_resume(void)
+{
+ int i;
+
+ for_each_online_cpu(i)
+ memset(per_cpu(cpu_evtchn_mask, i), 0, sizeof(xen_ulong_t) *
+ EVTCHN_2L_NR_CHANNELS/BITS_PER_EVTCHN_WORD);
+}
+
static const struct evtchn_ops evtchn_ops_2l = {
.max_channels = evtchn_2l_max_channels,
.nr_channels = evtchn_2l_max_channels,
@@ -356,6 +365,7 @@ static const struct evtchn_ops evtchn_ops_2l = {
.mask = evtchn_2l_mask,
.unmask = evtchn_2l_unmask,
.handle_events = evtchn_2l_handle_events,
+ .resume = evtchn_2l_resume,
};
void __init xen_evtchn_2l_init(void)
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index 70fba973a107..38387950490e 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -529,8 +529,8 @@ static unsigned int __startup_pirq(unsigned int irq)
if (rc)
goto err;
- bind_evtchn_to_cpu(evtchn, 0);
info->evtchn = evtchn;
+ bind_evtchn_to_cpu(evtchn, 0);
rc = xen_evtchn_port_setup(info);
if (rc)
@@ -957,7 +957,7 @@ unsigned xen_evtchn_nr_channels(void)
}
EXPORT_SYMBOL_GPL(xen_evtchn_nr_channels);
-int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
+int bind_virq_to_irq(unsigned int virq, unsigned int cpu, bool percpu)
{
struct evtchn_bind_virq bind_virq;
int evtchn, irq, ret;
@@ -971,8 +971,12 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
if (irq < 0)
goto out;
- irq_set_chip_and_handler_name(irq, &xen_percpu_chip,
- handle_percpu_irq, "virq");
+ if (percpu)
+ irq_set_chip_and_handler_name(irq, &xen_percpu_chip,
+ handle_percpu_irq, "virq");
+ else
+ irq_set_chip_and_handler_name(irq, &xen_dynamic_chip,
+ handle_edge_irq, "virq");
bind_virq.virq = virq;
bind_virq.vcpu = cpu;
@@ -1062,7 +1066,7 @@ int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu,
{
int irq, retval;
- irq = bind_virq_to_irq(virq, cpu);
+ irq = bind_virq_to_irq(virq, cpu, irqflags & IRQF_PERCPU);
if (irq < 0)
return irq;
retval = request_irq(irq, handler, irqflags, devname, dev_id);
@@ -1279,8 +1283,9 @@ void rebind_evtchn_irq(int evtchn, int irq)
mutex_unlock(&irq_mapping_update_lock);
- /* new event channels are always bound to cpu 0 */
- irq_set_affinity(irq, cpumask_of(0));
+ bind_evtchn_to_cpu(evtchn, info->cpu);
+ /* This will be deferred until interrupt is processed */
+ irq_set_affinity(irq, cpumask_of(info->cpu));
/* Unmask the event channel. */
enable_irq(irq);
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index d5bb1a33d0a3..89274850741b 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -327,30 +327,10 @@ static int map_grant_pages(struct grant_map *map)
return err;
}
-struct unmap_grant_pages_callback_data
-{
- struct completion completion;
- int result;
-};
-
-static void unmap_grant_callback(int result,
- struct gntab_unmap_queue_data *data)
-{
- struct unmap_grant_pages_callback_data* d = data->data;
-
- d->result = result;
- complete(&d->completion);
-}
-
static int __unmap_grant_pages(struct grant_map *map, int offset, int pages)
{
int i, err = 0;
struct gntab_unmap_queue_data unmap_data;
- struct unmap_grant_pages_callback_data data;
-
- init_completion(&data.completion);
- unmap_data.data = &data;
- unmap_data.done= &unmap_grant_callback;
if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) {
int pgno = (map->notify.addr >> PAGE_SHIFT);
@@ -367,11 +347,9 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages)
unmap_data.pages = map->pages + offset;
unmap_data.count = pages;
- gnttab_unmap_refs_async(&unmap_data);
-
- wait_for_completion(&data.completion);
- if (data.result)
- return data.result;
+ err = gnttab_unmap_refs_sync(&unmap_data);
+ if (err)
+ return err;
for (i = 0; i < pages; i++) {
if (map->unmap_ops[offset+i].status)
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 17972fbacddc..b1c7170e5c9e 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -123,6 +123,11 @@ struct gnttab_ops {
int (*query_foreign_access)(grant_ref_t ref);
};
+struct unmap_refs_callback_data {
+ struct completion completion;
+ int result;
+};
+
static struct gnttab_ops *gnttab_interface;
static int grant_table_version;
@@ -863,6 +868,29 @@ void gnttab_unmap_refs_async(struct gntab_unmap_queue_data* item)
}
EXPORT_SYMBOL_GPL(gnttab_unmap_refs_async);
+static void unmap_refs_callback(int result,
+ struct gntab_unmap_queue_data *data)
+{
+ struct unmap_refs_callback_data *d = data->data;
+
+ d->result = result;
+ complete(&d->completion);
+}
+
+int gnttab_unmap_refs_sync(struct gntab_unmap_queue_data *item)
+{
+ struct unmap_refs_callback_data data;
+
+ init_completion(&data.completion);
+ item->data = &data;
+ item->done = &unmap_refs_callback;
+ gnttab_unmap_refs_async(item);
+ wait_for_completion(&data.completion);
+
+ return data.result;
+}
+EXPORT_SYMBOL_GPL(gnttab_unmap_refs_sync);
+
static int gnttab_map_frames_v1(xen_pfn_t *frames, unsigned int nr_gframes)
{
int rc;
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
index bf1940706422..9e6a85104a20 100644
--- a/drivers/xen/manage.c
+++ b/drivers/xen/manage.c
@@ -131,6 +131,8 @@ static void do_suspend(void)
goto out_resume;
}
+ xen_arch_suspend();
+
si.cancelled = 1;
err = stop_machine(xen_suspend, &si, cpumask_of(0));
@@ -148,11 +150,12 @@ static void do_suspend(void)
si.cancelled = 1;
}
+ xen_arch_resume();
+
out_resume:
- if (!si.cancelled) {
- xen_arch_resume();
+ if (!si.cancelled)
xs_resume();
- } else
+ else
xs_suspend_cancel();
dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index 810ad419e34c..4c549323c605 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -235,7 +235,7 @@ retry:
#define SLABS_PER_PAGE (1 << (PAGE_SHIFT - IO_TLB_SHIFT))
#define IO_TLB_MIN_SLABS ((1<<20) >> IO_TLB_SHIFT)
while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) {
- xen_io_tlb_start = (void *)__get_free_pages(__GFP_NOWARN, order);
+ xen_io_tlb_start = (void *)xen_get_swiotlb_free_pages(order);
if (xen_io_tlb_start)
break;
order--;
diff --git a/drivers/xen/xen-acpi-cpuhotplug.c b/drivers/xen/xen-acpi-cpuhotplug.c
index 3e62ee4b3b66..f4a369429553 100644
--- a/drivers/xen/xen-acpi-cpuhotplug.c
+++ b/drivers/xen/xen-acpi-cpuhotplug.c
@@ -46,13 +46,7 @@ static int xen_acpi_processor_enable(struct acpi_device *device)
unsigned long long value;
union acpi_object object = { 0 };
struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
- struct acpi_processor *pr;
-
- pr = acpi_driver_data(device);
- if (!pr) {
- pr_err(PREFIX "Cannot find driver data\n");
- return -EINVAL;
- }
+ struct acpi_processor *pr = acpi_driver_data(device);
if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) {
/* Declared with "Processor" statement; match ProcessorID */
@@ -77,7 +71,7 @@ static int xen_acpi_processor_enable(struct acpi_device *device)
pr->id = xen_pcpu_id(pr->acpi_id);
- if ((int)pr->id < 0)
+ if (invalid_logical_cpuid(pr->id))
/* This cpu is not presented at hypervisor, try to hotadd it */
if (ACPI_FAILURE(xen_acpi_cpu_hotadd(pr))) {
pr_err(PREFIX "Hotadd CPU (acpi_id = %d) failed.\n",
@@ -226,7 +220,7 @@ static acpi_status xen_acpi_cpu_hotadd(struct acpi_processor *pr)
return AE_ERROR;
pr->id = xen_hotadd_cpu(pr);
- if ((int)pr->id < 0)
+ if (invalid_logical_cpuid(pr->id))
return AE_ERROR;
/*
diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c
index 75fe3d466515..9c234209d8b5 100644
--- a/drivers/xen/xen-pciback/conf_space.c
+++ b/drivers/xen/xen-pciback/conf_space.c
@@ -16,8 +16,8 @@
#include "conf_space.h"
#include "conf_space_quirks.h"
-bool permissive;
-module_param(permissive, bool, 0644);
+bool xen_pcibk_permissive;
+module_param_named(permissive, xen_pcibk_permissive, bool, 0644);
/* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word,
* xen_pcibk_write_config_word, and xen_pcibk_write_config_byte are created. */
@@ -262,7 +262,7 @@ int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value)
* This means that some fields may still be read-only because
* they have entries in the config_field list that intercept
* the write and do nothing. */
- if (dev_data->permissive || permissive) {
+ if (dev_data->permissive || xen_pcibk_permissive) {
switch (size) {
case 1:
err = pci_write_config_byte(dev, offset,
diff --git a/drivers/xen/xen-pciback/conf_space.h b/drivers/xen/xen-pciback/conf_space.h
index 2e1d73d1d5d0..62461a8ba1d6 100644
--- a/drivers/xen/xen-pciback/conf_space.h
+++ b/drivers/xen/xen-pciback/conf_space.h
@@ -64,7 +64,7 @@ struct config_field_entry {
void *data;
};
-extern bool permissive;
+extern bool xen_pcibk_permissive;
#define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset)
diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c
index c2260a0456c9..ad3d17d29c81 100644
--- a/drivers/xen/xen-pciback/conf_space_header.c
+++ b/drivers/xen/xen-pciback/conf_space_header.c
@@ -118,7 +118,7 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
cmd->val = value;
- if (!permissive && (!dev_data || !dev_data->permissive))
+ if (!xen_pcibk_permissive && (!dev_data || !dev_data->permissive))
return 0;
/* Only allow the guest to control certain bits. */
diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c
index b7f51504f85a..39223c3e99ad 100644
--- a/drivers/xen/xen-scsiback.c
+++ b/drivers/xen/xen-scsiback.c
@@ -49,10 +49,7 @@
#include <generated/utsrelease.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_dbg.h>
-#include <scsi/scsi_eh.h>
-#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_host.h> /* SG_ALL */
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index 564b31584860..5390a674b5e3 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -57,6 +57,7 @@
#include <xen/xen.h>
#include <xen/xenbus.h>
#include <xen/events.h>
+#include <xen/xen-ops.h>
#include <xen/page.h>
#include <xen/hvm.h>
@@ -735,6 +736,30 @@ static int __init xenstored_local_init(void)
return err;
}
+static int xenbus_resume_cb(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ int err = 0;
+
+ if (xen_hvm_domain()) {
+ uint64_t v;
+
+ err = hvm_get_parameter(HVM_PARAM_STORE_EVTCHN, &v);
+ if (!err && v)
+ xen_store_evtchn = v;
+ else
+ pr_warn("Cannot update xenstore event channel: %d\n",
+ err);
+ } else
+ xen_store_evtchn = xen_start_info->store_evtchn;
+
+ return err;
+}
+
+static struct notifier_block xenbus_resume_nb = {
+ .notifier_call = xenbus_resume_cb,
+};
+
static int __init xenbus_init(void)
{
int err = 0;
@@ -793,6 +818,10 @@ static int __init xenbus_init(void)
goto out_error;
}
+ if ((xen_store_domain_type != XS_LOCAL) &&
+ (xen_store_domain_type != XS_UNKNOWN))
+ xen_resume_notifier_register(&xenbus_resume_nb);
+
#ifdef CONFIG_XEN_COMPAT_XENFS
/*
* Create xenfs mountpoint in /proc for compatibility with