summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/Kconfig8
-rw-r--r--drivers/acpi/dock.c6
-rw-r--r--drivers/acpi/osl.c3
-rw-r--r--drivers/acpi/thermal.c2
-rw-r--r--drivers/acpi/utils.c3
-rw-r--r--drivers/acpi/video.c8
-rw-r--r--drivers/base/power/domain.c13
-rw-r--r--drivers/base/regmap/regmap.c3
-rw-r--r--drivers/block/drbd/drbd_receiver.c12
-rw-r--r--drivers/block/loop.c8
-rw-r--r--drivers/block/nbd.c48
-rw-r--r--drivers/block/nvme-core.c684
-rw-r--r--drivers/block/nvme-scsi.c43
-rw-r--r--drivers/char/Kconfig2
-rw-r--r--drivers/char/virtio_console.c4
-rw-r--r--drivers/cpufreq/Kconfig.arm6
-rw-r--r--drivers/cpufreq/Kconfig.powerpc8
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c2
-rw-r--r--drivers/cpufreq/at32ap-cpufreq.c2
-rw-r--r--drivers/cpufreq/cris-artpec3-cpufreq.c6
-rw-r--r--drivers/cpufreq/cris-etraxfs-cpufreq.c6
-rw-r--r--drivers/cpufreq/elanfreq.c18
-rw-r--r--drivers/cpufreq/exynos4210-cpufreq.c12
-rw-r--r--drivers/cpufreq/exynos4x12-cpufreq.c30
-rw-r--r--drivers/cpufreq/exynos5250-cpufreq.c34
-rw-r--r--drivers/cpufreq/freq_table.c11
-rw-r--r--drivers/cpufreq/ia64-acpi-cpufreq.c3
-rw-r--r--drivers/cpufreq/kirkwood-cpufreq.c6
-rw-r--r--drivers/cpufreq/longhaul.c2
-rw-r--r--drivers/cpufreq/loongson2_cpufreq.c2
-rw-r--r--drivers/cpufreq/maple-cpufreq.c6
-rw-r--r--drivers/cpufreq/p4-clockmod.c20
-rw-r--r--drivers/cpufreq/pasemi-cpufreq.c12
-rw-r--r--drivers/cpufreq/pmac32-cpufreq.c6
-rw-r--r--drivers/cpufreq/pmac64-cpufreq.c6
-rw-r--r--drivers/cpufreq/powernow-k6.c18
-rw-r--r--drivers/cpufreq/powernow-k8.c5
-rw-r--r--drivers/cpufreq/powernv-cpufreq.c341
-rw-r--r--drivers/cpufreq/ppc-corenet-cpufreq.c1
-rw-r--r--drivers/cpufreq/ppc_cbe_cpufreq.c18
-rw-r--r--drivers/cpufreq/s3c2416-cpufreq.c20
-rw-r--r--drivers/cpufreq/s3c24xx-cpufreq.c4
-rw-r--r--drivers/cpufreq/s3c64xx-cpufreq.c26
-rw-r--r--drivers/cpufreq/s5pv210-cpufreq.c12
-rw-r--r--drivers/cpufreq/sc520_freq.c6
-rw-r--r--drivers/cpufreq/spear-cpufreq.c7
-rw-r--r--drivers/cpufreq/speedstep-ich.c6
-rw-r--r--drivers/cpufreq/speedstep-smi.c6
-rw-r--r--drivers/cpufreq/unicore2-cpufreq.c2
-rw-r--r--drivers/cpuidle/sysfs.c3
-rw-r--r--drivers/dma/Kconfig21
-rw-r--r--drivers/dma/Makefile2
-rw-r--r--drivers/dma/acpi-dma.c17
-rw-r--r--drivers/dma/at_hdmac.c1
-rw-r--r--drivers/dma/cppi41.c7
-rw-r--r--drivers/dma/dmaengine.c9
-rw-r--r--drivers/dma/dmatest.c4
-rw-r--r--drivers/dma/dw/core.c21
-rw-r--r--drivers/dma/dw/pci.c36
-rw-r--r--drivers/dma/dw/regs.h4
-rw-r--r--drivers/dma/edma.c5
-rw-r--r--drivers/dma/fsl-edma.c975
-rw-r--r--drivers/dma/imx-dma.c13
-rw-r--r--drivers/dma/mmp_pdma.c8
-rw-r--r--drivers/dma/mmp_tdma.c50
-rw-r--r--drivers/dma/omap-dma.c18
-rw-r--r--drivers/dma/pch_dma.c4
-rw-r--r--drivers/dma/qcom_bam_dma.c1111
-rw-r--r--drivers/dma/s3c24xx-dma.c2
-rw-r--r--drivers/dma/sh/Kconfig6
-rw-r--r--drivers/dma/sh/Makefile1
-rw-r--r--drivers/dma/sh/rcar-audmapp.c320
-rw-r--r--drivers/dma/sh/shdma-base.c10
-rw-r--r--drivers/dma/sh/shdma-of.c3
-rw-r--r--drivers/dma/sh/shdmac.c13
-rw-r--r--drivers/dma/sh/sudmac.c4
-rw-r--r--drivers/dma/sirf-dma.c23
-rw-r--r--drivers/firmware/efi/efi-stub-helper.c6
-rw-r--r--drivers/hwmon/Kconfig8
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/ibmpowernv.c529
-rw-r--r--drivers/i2c/busses/Kconfig27
-rw-r--r--drivers/i2c/busses/Makefile3
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c2
-rw-r--r--drivers/i2c/busses/i2c-ali1563.c2
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c2
-rw-r--r--drivers/i2c/busses/i2c-amd756.c2
-rw-r--r--drivers/i2c/busses/i2c-amd8111.c2
-rw-r--r--drivers/i2c/busses/i2c-at91.c12
-rw-r--r--drivers/i2c/busses/i2c-bcm2835.c26
-rw-r--r--drivers/i2c/busses/i2c-bfin-twi.c20
-rw-r--r--drivers/i2c/busses/i2c-cadence.c905
-rw-r--r--drivers/i2c/busses/i2c-davinci.c2
-rw-r--r--drivers/i2c/busses/i2c-designware-core.c27
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h2
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c125
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c9
-rw-r--r--drivers/i2c/busses/i2c-efm32.c481
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c2
-rw-r--r--drivers/i2c/busses/i2c-exynos5.c4
-rw-r--r--drivers/i2c/busses/i2c-gpio.c3
-rw-r--r--drivers/i2c/busses/i2c-hydra.c2
-rw-r--r--drivers/i2c/busses/i2c-i801.c5
-rw-r--r--drivers/i2c/busses/i2c-ismt.c2
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c83
-rw-r--r--drivers/i2c/busses/i2c-mxs.c18
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c2
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c253
-rw-r--r--drivers/i2c/busses/i2c-ocores.c2
-rw-r--r--drivers/i2c/busses/i2c-omap.c8
-rw-r--r--drivers/i2c/busses/i2c-pasemi.c2
-rw-r--r--drivers/i2c/busses/i2c-piix4.c2
-rw-r--r--drivers/i2c/busses/i2c-pxa-pci.c2
-rw-r--r--drivers/i2c/busses/i2c-qup.c768
-rw-r--r--drivers/i2c/busses/i2c-rcar.c3
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c39
-rw-r--r--drivers/i2c/busses/i2c-sirf.c2
-rw-r--r--drivers/i2c/busses/i2c-sis5595.c2
-rw-r--r--drivers/i2c/busses/i2c-sis630.c2
-rw-r--r--drivers/i2c/busses/i2c-sis96x.c2
-rw-r--r--drivers/i2c/busses/i2c-st.c2
-rw-r--r--drivers/i2c/busses/i2c-stu300.c2
-rw-r--r--drivers/i2c/busses/i2c-tegra.c2
-rw-r--r--drivers/i2c/busses/i2c-via.c2
-rw-r--r--drivers/i2c/busses/i2c-viapro.c2
-rw-r--r--drivers/i2c/busses/i2c-xiic.c2
-rw-r--r--drivers/i2c/busses/scx200_acb.c2
-rw-r--r--drivers/i2c/i2c-core.c67
-rw-r--r--drivers/idle/intel_idle.c204
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c828
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.h38
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.c27
-rw-r--r--drivers/leds/Kconfig4
-rw-r--r--drivers/leds/led-core.c6
-rw-r--r--drivers/leds/led-triggers.c6
-rw-r--r--drivers/leds/leds-88pm860x.c1
-rw-r--r--drivers/leds/leds-adp5520.c1
-rw-r--r--drivers/leds/leds-asic3.c1
-rw-r--r--drivers/leds/leds-blinkm.c3
-rw-r--r--drivers/leds/leds-clevo-mail.c5
-rw-r--r--drivers/leds/leds-cobalt-qube.c1
-rw-r--r--drivers/leds/leds-da903x.c1
-rw-r--r--drivers/leds/leds-da9052.c1
-rw-r--r--drivers/leds/leds-fsg.c1
-rw-r--r--drivers/leds/leds-gpio.c6
-rw-r--r--drivers/leds/leds-hp6xx.c1
-rw-r--r--drivers/leds/leds-lm3533.c1
-rw-r--r--drivers/leds/leds-lp5521.c1
-rw-r--r--drivers/leds/leds-lp5523.c1
-rw-r--r--drivers/leds/leds-lp5562.c7
-rw-r--r--drivers/leds/leds-lt3593.c1
-rw-r--r--drivers/leds/leds-mc13783.c223
-rw-r--r--drivers/leds/leds-netxbig.c1
-rw-r--r--drivers/leds/leds-ns2.c1
-rw-r--r--drivers/leds/leds-ot200.c1
-rw-r--r--drivers/leds/leds-pwm.c24
-rw-r--r--drivers/leds/leds-s3c24xx.c1
-rw-r--r--drivers/leds/leds-ss4200.c4
-rw-r--r--drivers/leds/leds-wm831x-status.c1
-rw-r--r--drivers/leds/leds-wm8350.c1
-rw-r--r--drivers/leds/trigger/ledtrig-cpu.c24
-rw-r--r--drivers/md/bitmap.c1
-rw-r--r--drivers/md/md.c65
-rw-r--r--drivers/md/md.h1
-rw-r--r--drivers/md/raid1.c17
-rw-r--r--drivers/md/raid5.c28
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/Kconfig2
-rw-r--r--drivers/media/dvb-frontends/lgdt3305.c1
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.c8
-rw-r--r--drivers/media/platform/ti-vpe/vpe.c45
-rw-r--r--drivers/media/rc/img-ir/img-ir-hw.c15
-rw-r--r--drivers/media/rc/img-ir/img-ir-nec.c27
-rw-r--r--drivers/media/rc/ir-nec-decoder.c5
-rw-r--r--drivers/media/rc/keymaps/rc-tivo.c86
-rw-r--r--drivers/media/rc/rc-main.c98
-rw-r--r--drivers/media/tuners/r820t.c3
-rw-r--r--drivers/media/tuners/tuner-xc2028.c1
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c2
-rw-r--r--drivers/media/usb/gspca/jpeg.h4
-rw-r--r--drivers/media/usb/stk1160/stk1160-ac97.c2
-rw-r--r--drivers/mfd/rtsx_pcr.c132
-rw-r--r--drivers/mmc/card/block.c181
-rw-r--r--drivers/mmc/core/Kconfig15
-rw-r--r--drivers/mmc/core/bus.c12
-rw-r--r--drivers/mmc/core/core.c87
-rw-r--r--drivers/mmc/core/host.c18
-rw-r--r--drivers/mmc/core/mmc.c65
-rw-r--r--drivers/mmc/core/mmc_ops.c64
-rw-r--r--drivers/mmc/core/sd.c23
-rw-r--r--drivers/mmc/core/slot-gpio.c180
-rw-r--r--drivers/mmc/host/Kconfig23
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/davinci_mmc.c4
-rw-r--r--drivers/mmc/host/dw_mmc-k3.c2
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c12
-rw-r--r--drivers/mmc/host/dw_mmc-socfpga.c138
-rw-r--r--drivers/mmc/host/dw_mmc.c2
-rw-r--r--drivers/mmc/host/dw_mmc.h3
-rw-r--r--drivers/mmc/host/mmci.c54
-rw-r--r--drivers/mmc/host/mmci.h2
-rw-r--r--drivers/mmc/host/omap.c93
-rw-r--r--drivers/mmc/host/omap_hsmmc.c242
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c529
-rw-r--r--drivers/mmc/host/sdhci-acpi.c80
-rw-r--r--drivers/mmc/host/sdhci-bcm-kona.c39
-rw-r--r--drivers/mmc/host/sdhci-dove.c2
-rw-r--r--drivers/mmc/host/sdhci-msm.c618
-rw-r--r--drivers/mmc/host/sdhci-pci.c20
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c68
-rw-r--r--drivers/mmc/host/sdhci-s3c.c170
-rw-r--r--drivers/mmc/host/sdhci-spear.c199
-rw-r--r--drivers/mmc/host/sdhci.c24
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c50
-rw-r--r--drivers/mmc/host/tmio_mmc.c30
-rw-r--r--drivers/mmc/host/tmio_mmc.h7
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c7
-rw-r--r--drivers/mmc/host/ushc.c2
-rw-r--r--drivers/mmc/host/wmt-sdmmc.c4
-rw-r--r--drivers/net/ntb_netdev.c27
-rw-r--r--drivers/ntb/ntb_hw.c192
-rw-r--r--drivers/ntb/ntb_hw.h8
-rw-r--r--drivers/ntb/ntb_transport.c20
-rw-r--r--drivers/platform/x86/Kconfig22
-rw-r--r--drivers/platform/x86/Makefile2
-rw-r--r--drivers/platform/x86/alienware-wmi.c565
-rw-r--r--drivers/platform/x86/fujitsu-tablet.c65
-rw-r--r--drivers/platform/x86/intel_baytrail.c224
-rw-r--r--drivers/platform/x86/intel_baytrail.h90
-rw-r--r--drivers/platform/x86/panasonic-laptop.c11
-rw-r--r--drivers/platform/x86/sony-laptop.c539
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c227
-rw-r--r--drivers/platform/x86/toshiba_acpi.c635
-rw-r--r--drivers/regulator/Kconfig9
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/bcm590xx-regulator.c1
-rw-r--r--drivers/regulator/pbias-regulator.c255
-rw-r--r--drivers/regulator/s2mpa01.c12
-rw-r--r--drivers/regulator/s2mps11.c12
-rw-r--r--drivers/regulator/s5m8767.c1
-rw-r--r--drivers/remoteproc/da8xx_remoteproc.c16
-rw-r--r--drivers/remoteproc/ste_modem_rproc.c4
-rw-r--r--drivers/scsi/Kconfig3
-rw-r--r--drivers/scsi/qla2xxx/tcm_qla2xxx.c18
-rw-r--r--drivers/scsi/scsi.c9
-rw-r--r--drivers/scsi/scsi_lib.c4
-rw-r--r--drivers/scsi/scsi_pm.c128
-rw-r--r--drivers/scsi/scsi_priv.h2
-rw-r--r--drivers/scsi/scsi_scan.c2
-rw-r--r--drivers/scsi/sd.c1
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--drivers/spi/spi-fsl-espi.c3
-rw-r--r--drivers/spi/spi-fsl-spi.c3
-rw-r--r--drivers/spi/spi-mpc512x-psc.c3
-rw-r--r--drivers/spi/spi-mpc52xx-psc.c3
-rw-r--r--drivers/spi/spi-mpc52xx.c6
-rw-r--r--drivers/spi/spi-omap2-mcspi.c26
-rw-r--r--drivers/spi/spi-sh.c6
-rw-r--r--drivers/spi/spi-txx9.c3
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c60
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c24
-rw-r--r--drivers/staging/lustre/lustre/llite/symlink.c23
-rw-r--r--drivers/staging/media/msi3101/msi001.c2
-rw-r--r--drivers/staging/media/msi3101/sdr-msi3101.c15
-rw-r--r--drivers/staging/usbip/stub_dev.c8
-rw-r--r--drivers/staging/usbip/usbip_common.c25
-rw-r--r--drivers/staging/usbip/usbip_common.h1
-rw-r--r--drivers/staging/usbip/vhci_hcd.c4
-rw-r--r--drivers/staging/usbip/vhci_sysfs.c6
-rw-r--r--drivers/target/iscsi/iscsi_target.c33
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.c14
-rw-r--r--drivers/target/iscsi/iscsi_target_core.h5
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c4
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c21
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.h1
-rw-r--r--drivers/target/iscsi/iscsi_target_util.c4
-rw-r--r--drivers/target/iscsi/iscsi_target_util.h1
-rw-r--r--drivers/target/loopback/tcm_loop.c12
-rw-r--r--drivers/target/sbp/sbp_target.c8
-rw-r--r--drivers/target/target_core_alua.c95
-rw-r--r--drivers/target/target_core_configfs.c4
-rw-r--r--drivers/target/target_core_file.c40
-rw-r--r--drivers/target/target_core_iblock.c5
-rw-r--r--drivers/target/target_core_rd.c14
-rw-r--r--drivers/target/target_core_sbc.c178
-rw-r--r--drivers/target/target_core_spc.c49
-rw-r--r--drivers/target/target_core_tmr.c23
-rw-r--r--drivers/target/target_core_transport.c92
-rw-r--r--drivers/target/tcm_fc/tcm_fc.h13
-rw-r--r--drivers/target/tcm_fc/tfc_cmd.c5
-rw-r--r--drivers/target/tcm_fc/tfc_conf.c76
-rw-r--r--drivers/target/tcm_fc/tfc_sess.c10
-rw-r--r--drivers/thermal/imx_thermal.c39
-rw-r--r--drivers/thermal/rcar_thermal.c9
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-bandgap.c6
-rw-r--r--drivers/tty/hvc/hvc_opal.c22
-rw-r--r--drivers/tty/tty_audit.c3
-rw-r--r--drivers/usb/gadget/tcm_usb_gadget.c8
-rw-r--r--drivers/vhost/net.c14
-rw-r--r--drivers/vhost/scsi.c9
-rw-r--r--drivers/video/backlight/backlight.c2
-rw-r--r--drivers/video/backlight/gpio_backlight.c58
-rw-r--r--drivers/video/backlight/lm3639_bl.c17
303 files changed, 12837 insertions, 4117 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index c205653e9644..ab686b310100 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -31,10 +31,14 @@ menuconfig ACPI
ACPI CA, see:
<http://acpica.org/>
- ACPI is an open industry specification co-developed by
- Hewlett-Packard, Intel, Microsoft, Phoenix, and Toshiba.
+ ACPI is an open industry specification originally co-developed by
+ Hewlett-Packard, Intel, Microsoft, Phoenix, and Toshiba. Currently,
+ it is developed by the ACPI Specification Working Group (ASWG) under
+ the UEFI Forum and any UEFI member can join the ASWG and contribute
+ to the ACPI specification.
The specification is available at:
<http://www.acpi.info>
+ <http://www.uefi.org/acpi/specs>
if ACPI
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index f0fc6260266b..d9339b442a4e 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -51,12 +51,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
" the driver to wait for userspace to write the undock sysfs file "
" before undocking");
-static const struct acpi_device_id dock_device_ids[] = {
- {"LNXDOCK", 0},
- {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, dock_device_ids);
-
struct dock_station {
acpi_handle handle;
unsigned long last_dock_time;
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index f7fd72ac69cf..6776c599816f 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -1219,10 +1219,9 @@ acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle)
{
struct semaphore *sem = NULL;
- sem = acpi_os_allocate(sizeof(struct semaphore));
+ sem = acpi_os_allocate_zeroed(sizeof(struct semaphore));
if (!sem)
return AE_NO_MEMORY;
- memset(sem, 0, sizeof(struct semaphore));
sema_init(sem, initial_units);
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 964068553334..c1e31a41f949 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -344,7 +344,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
tz->trips.hot.flags.valid = 1;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Found hot threshold [%lu]\n",
- tz->trips.critical.temperature));
+ tz->trips.hot.temperature));
}
}
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index 0f5f78fa6545..bba526148583 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -164,11 +164,10 @@ acpi_extract_package(union acpi_object *package,
* Validate output buffer.
*/
if (buffer->length == ACPI_ALLOCATE_BUFFER) {
- buffer->pointer = ACPI_ALLOCATE(size_required);
+ buffer->pointer = ACPI_ALLOCATE_ZEROED(size_required);
if (!buffer->pointer)
return AE_NO_MEMORY;
buffer->length = size_required;
- memset(buffer->pointer, 0, size_required);
} else {
if (buffer->length < size_required) {
buffer->length = size_required;
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 48c7e8af9c96..8b6990e417ec 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -488,6 +488,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
},
},
{
+ .callback = video_set_use_native_backlight,
+ .ident = "Thinkpad Helix",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix"),
+ },
+ },
+ {
.callback = video_set_use_native_backlight,
.ident = "Dell Inspiron 7520",
.matches = {
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 6f54962aae1d..ae098a261fcd 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -705,6 +705,14 @@ static int pm_genpd_runtime_resume(struct device *dev)
return 0;
}
+static bool pd_ignore_unused;
+static int __init pd_ignore_unused_setup(char *__unused)
+{
+ pd_ignore_unused = true;
+ return 1;
+}
+__setup("pd_ignore_unused", pd_ignore_unused_setup);
+
/**
* pm_genpd_poweroff_unused - Power off all PM domains with no devices in use.
*/
@@ -712,6 +720,11 @@ void pm_genpd_poweroff_unused(void)
{
struct generic_pm_domain *genpd;
+ if (pd_ignore_unused) {
+ pr_warn("genpd: Not disabling unused power domains\n");
+ return;
+ }
+
mutex_lock(&gpd_list_lock);
list_for_each_entry(genpd, &gpd_list, gpd_list_node)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index d0a072463a04..63e30ef096e2 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -761,10 +761,11 @@ skip_format_initialization:
if (ret != 0)
goto err_range;
- if (dev)
+ if (dev) {
ret = regmap_attach_dev(dev, map, config);
if (ret != 0)
goto err_regcache;
+ }
return map;
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index 18c76e84d540..68e3992e8838 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -469,24 +469,14 @@ static void drbd_wait_ee_list_empty(struct drbd_device *device,
static int drbd_recv_short(struct socket *sock, void *buf, size_t size, int flags)
{
- mm_segment_t oldfs;
struct kvec iov = {
.iov_base = buf,
.iov_len = size,
};
struct msghdr msg = {
- .msg_iovlen = 1,
- .msg_iov = (struct iovec *)&iov,
.msg_flags = (flags ? flags : MSG_WAITALL | MSG_NOSIGNAL)
};
- int rv;
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
- rv = sock_recvmsg(sock, &msg, size, msg.msg_flags);
- set_fs(oldfs);
-
- return rv;
+ return kernel_recvmsg(sock, &msg, &iov, 1, size, msg.msg_flags);
}
static int drbd_recv(struct drbd_connection *connection, void *buf, size_t size)
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 66e8c3b94ef3..f70a230a2945 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -237,7 +237,7 @@ static int __do_lo_send_write(struct file *file,
file_end_write(file);
if (likely(bw == len))
return 0;
- printk(KERN_ERR "loop: Write error at byte offset %llu, length %i.\n",
+ printk_ratelimited(KERN_ERR "loop: Write error at byte offset %llu, length %i.\n",
(unsigned long long)pos, len);
if (bw >= 0)
bw = -EIO;
@@ -277,7 +277,7 @@ static int do_lo_send_write(struct loop_device *lo, struct bio_vec *bvec,
return __do_lo_send_write(lo->lo_backing_file,
page_address(page), bvec->bv_len,
pos);
- printk(KERN_ERR "loop: Transfer error at byte offset %llu, "
+ printk_ratelimited(KERN_ERR "loop: Transfer error at byte offset %llu, "
"length %i.\n", (unsigned long long)pos, bvec->bv_len);
if (ret > 0)
ret = -EIO;
@@ -316,7 +316,7 @@ static int lo_send(struct loop_device *lo, struct bio *bio, loff_t pos)
out:
return ret;
fail:
- printk(KERN_ERR "loop: Failed to allocate temporary page for write.\n");
+ printk_ratelimited(KERN_ERR "loop: Failed to allocate temporary page for write.\n");
ret = -ENOMEM;
goto out;
}
@@ -345,7 +345,7 @@ lo_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
size = p->bsize;
if (lo_do_transfer(lo, READ, page, buf->offset, p->page, p->offset, size, IV)) {
- printk(KERN_ERR "loop: transfer error block %ld\n",
+ printk_ratelimited(KERN_ERR "loop: transfer error block %ld\n",
page->index);
size = -EINVAL;
}
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 55298db36b2d..3a70ea2f7cd6 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -630,37 +630,29 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
}
case NBD_CLEAR_SOCK: {
- struct file *file;
-
+ struct socket *sock = nbd->sock;
nbd->sock = NULL;
- file = nbd->file;
- nbd->file = NULL;
nbd_clear_que(nbd);
BUG_ON(!list_empty(&nbd->queue_head));
BUG_ON(!list_empty(&nbd->waiting_queue));
kill_bdev(bdev);
- if (file)
- fput(file);
+ if (sock)
+ sockfd_put(sock);
return 0;
}
case NBD_SET_SOCK: {
- struct file *file;
- if (nbd->file)
+ struct socket *sock;
+ int err;
+ if (nbd->sock)
return -EBUSY;
- file = fget(arg);
- if (file) {
- struct inode *inode = file_inode(file);
- if (S_ISSOCK(inode->i_mode)) {
- nbd->file = file;
- nbd->sock = SOCKET_I(inode);
- if (max_part > 0)
- bdev->bd_invalidated = 1;
- nbd->disconnect = 0; /* we're connected now */
- return 0;
- } else {
- fput(file);
- }
+ sock = sockfd_lookup(arg, &err);
+ if (sock) {
+ nbd->sock = sock;
+ if (max_part > 0)
+ bdev->bd_invalidated = 1;
+ nbd->disconnect = 0; /* we're connected now */
+ return 0;
}
return -EINVAL;
}
@@ -697,12 +689,12 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
case NBD_DO_IT: {
struct task_struct *thread;
- struct file *file;
+ struct socket *sock;
int error;
if (nbd->pid)
return -EBUSY;
- if (!nbd->file)
+ if (!nbd->sock)
return -EINVAL;
mutex_unlock(&nbd->tx_lock);
@@ -731,15 +723,15 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
if (error)
return error;
sock_shutdown(nbd, 0);
- file = nbd->file;
- nbd->file = NULL;
+ sock = nbd->sock;
+ nbd->sock = NULL;
nbd_clear_que(nbd);
dev_warn(disk_to_dev(nbd->disk), "queue cleared\n");
kill_bdev(bdev);
queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue);
set_device_ro(bdev, false);
- if (file)
- fput(file);
+ if (sock)
+ sockfd_put(sock);
nbd->flags = 0;
nbd->bytesize = 0;
bdev->bd_inode->i_size = 0;
@@ -875,9 +867,7 @@ static int __init nbd_init(void)
for (i = 0; i < nbds_max; i++) {
struct gendisk *disk = nbd_dev[i].disk;
- nbd_dev[i].file = NULL;
nbd_dev[i].magic = NBD_MAGIC;
- nbd_dev[i].flags = 0;
INIT_LIST_HEAD(&nbd_dev[i].waiting_queue);
spin_lock_init(&nbd_dev[i].queue_lock);
INIT_LIST_HEAD(&nbd_dev[i].queue_head);
diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c
index da085ff10d25..7c64fa756cce 100644
--- a/drivers/block/nvme-core.c
+++ b/drivers/block/nvme-core.c
@@ -1,6 +1,6 @@
/*
* NVM Express device driver
- * Copyright (c) 2011, Intel Corporation.
+ * Copyright (c) 2011-2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -20,10 +20,12 @@
#include <linux/bio.h>
#include <linux/bitops.h>
#include <linux/blkdev.h>
+#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/genhd.h>
+#include <linux/hdreg.h>
#include <linux/idr.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -35,6 +37,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
+#include <linux/percpu.h>
#include <linux/poison.h>
#include <linux/ptrace.h>
#include <linux/sched.h>
@@ -47,6 +50,11 @@
#define SQ_SIZE(depth) (depth * sizeof(struct nvme_command))
#define CQ_SIZE(depth) (depth * sizeof(struct nvme_completion))
#define ADMIN_TIMEOUT (60 * HZ)
+#define IOD_TIMEOUT (4 * NVME_IO_TIMEOUT)
+
+unsigned char io_timeout = 30;
+module_param(io_timeout, byte, 0644);
+MODULE_PARM_DESC(io_timeout, "timeout in seconds for I/O");
static int nvme_major;
module_param(nvme_major, int, 0);
@@ -58,6 +66,7 @@ static DEFINE_SPINLOCK(dev_list_lock);
static LIST_HEAD(dev_list);
static struct task_struct *nvme_thread;
static struct workqueue_struct *nvme_workq;
+static wait_queue_head_t nvme_kthread_wait;
static void nvme_reset_failed_dev(struct work_struct *ws);
@@ -74,6 +83,7 @@ struct async_cmd_info {
* commands and one for I/O commands).
*/
struct nvme_queue {
+ struct rcu_head r_head;
struct device *q_dmadev;
struct nvme_dev *dev;
char irqname[24]; /* nvme4294967295-65535\0 */
@@ -85,6 +95,7 @@ struct nvme_queue {
wait_queue_head_t sq_full;
wait_queue_t sq_cong_wait;
struct bio_list sq_cong;
+ struct list_head iod_bio;
u32 __iomem *q_db;
u16 q_depth;
u16 cq_vector;
@@ -95,6 +106,7 @@ struct nvme_queue {
u8 cq_phase;
u8 cqe_seen;
u8 q_suspended;
+ cpumask_var_t cpu_mask;
struct async_cmd_info cmdinfo;
unsigned long cmdid_data[];
};
@@ -118,7 +130,7 @@ static inline void _nvme_check_size(void)
BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512);
}
-typedef void (*nvme_completion_fn)(struct nvme_dev *, void *,
+typedef void (*nvme_completion_fn)(struct nvme_queue *, void *,
struct nvme_completion *);
struct nvme_cmd_info {
@@ -190,7 +202,7 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx,
#define CMD_CTX_FLUSH (0x318 + CMD_CTX_BASE)
#define CMD_CTX_ABORT (0x31C + CMD_CTX_BASE)
-static void special_completion(struct nvme_dev *dev, void *ctx,
+static void special_completion(struct nvme_queue *nvmeq, void *ctx,
struct nvme_completion *cqe)
{
if (ctx == CMD_CTX_CANCELLED)
@@ -198,26 +210,26 @@ static void special_completion(struct nvme_dev *dev, void *ctx,
if (ctx == CMD_CTX_FLUSH)
return;
if (ctx == CMD_CTX_ABORT) {
- ++dev->abort_limit;
+ ++nvmeq->dev->abort_limit;
return;
}
if (ctx == CMD_CTX_COMPLETED) {
- dev_warn(&dev->pci_dev->dev,
+ dev_warn(nvmeq->q_dmadev,
"completed id %d twice on queue %d\n",
cqe->command_id, le16_to_cpup(&cqe->sq_id));
return;
}
if (ctx == CMD_CTX_INVALID) {
- dev_warn(&dev->pci_dev->dev,
+ dev_warn(nvmeq->q_dmadev,
"invalid id %d completed on queue %d\n",
cqe->command_id, le16_to_cpup(&cqe->sq_id));
return;
}
- dev_warn(&dev->pci_dev->dev, "Unknown special completion %p\n", ctx);
+ dev_warn(nvmeq->q_dmadev, "Unknown special completion %p\n", ctx);
}
-static void async_completion(struct nvme_dev *dev, void *ctx,
+static void async_completion(struct nvme_queue *nvmeq, void *ctx,
struct nvme_completion *cqe)
{
struct async_cmd_info *cmdinfo = ctx;
@@ -262,14 +274,34 @@ static void *cancel_cmdid(struct nvme_queue *nvmeq, int cmdid,
return ctx;
}
-struct nvme_queue *get_nvmeq(struct nvme_dev *dev)
+static struct nvme_queue *raw_nvmeq(struct nvme_dev *dev, int qid)
+{
+ return rcu_dereference_raw(dev->queues[qid]);
+}
+
+static struct nvme_queue *get_nvmeq(struct nvme_dev *dev) __acquires(RCU)
+{
+ unsigned queue_id = get_cpu_var(*dev->io_queue);
+ rcu_read_lock();
+ return rcu_dereference(dev->queues[queue_id]);
+}
+
+static void put_nvmeq(struct nvme_queue *nvmeq) __releases(RCU)
{
- return dev->queues[get_cpu() + 1];
+ rcu_read_unlock();
+ put_cpu_var(nvmeq->dev->io_queue);
}
-void put_nvmeq(struct nvme_queue *nvmeq)
+static struct nvme_queue *lock_nvmeq(struct nvme_dev *dev, int q_idx)
+ __acquires(RCU)
{
- put_cpu();
+ rcu_read_lock();
+ return rcu_dereference(dev->queues[q_idx]);
+}
+
+static void unlock_nvmeq(struct nvme_queue *nvmeq) __releases(RCU)
+{
+ rcu_read_unlock();
}
/**
@@ -284,6 +316,10 @@ static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd)
unsigned long flags;
u16 tail;
spin_lock_irqsave(&nvmeq->q_lock, flags);
+ if (nvmeq->q_suspended) {
+ spin_unlock_irqrestore(&nvmeq->q_lock, flags);
+ return -EBUSY;
+ }
tail = nvmeq->sq_tail;
memcpy(&nvmeq->sq_cmds[tail], cmd, sizeof(*cmd));
if (++tail == nvmeq->q_depth)
@@ -323,6 +359,7 @@ nvme_alloc_iod(unsigned nseg, unsigned nbytes, gfp_t gfp)
iod->npages = -1;
iod->length = nbytes;
iod->nents = 0;
+ iod->first_dma = 0ULL;
iod->start_time = jiffies;
}
@@ -371,19 +408,31 @@ static void nvme_end_io_acct(struct bio *bio, unsigned long start_time)
part_stat_unlock();
}
-static void bio_completion(struct nvme_dev *dev, void *ctx,
+static void bio_completion(struct nvme_queue *nvmeq, void *ctx,
struct nvme_completion *cqe)
{
struct nvme_iod *iod = ctx;
struct bio *bio = iod->private;
u16 status = le16_to_cpup(&cqe->status) >> 1;
+ if (unlikely(status)) {
+ if (!(status & NVME_SC_DNR ||
+ bio->bi_rw & REQ_FAILFAST_MASK) &&
+ (jiffies - iod->start_time) < IOD_TIMEOUT) {
+ if (!waitqueue_active(&nvmeq->sq_full))
+ add_wait_queue(&nvmeq->sq_full,
+ &nvmeq->sq_cong_wait);
+ list_add_tail(&iod->node, &nvmeq->iod_bio);
+ wake_up(&nvmeq->sq_full);
+ return;
+ }
+ }
if (iod->nents) {
- dma_unmap_sg(&dev->pci_dev->dev, iod->sg, iod->nents,
+ dma_unmap_sg(nvmeq->q_dmadev, iod->sg, iod->nents,
bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
nvme_end_io_acct(bio, iod->start_time);
}
- nvme_free_iod(dev, iod);
+ nvme_free_iod(nvmeq->dev, iod);
if (status)
bio_endio(bio, -EIO);
else
@@ -391,8 +440,8 @@ static void bio_completion(struct nvme_dev *dev, void *ctx,
}
/* length is in bytes. gfp flags indicates whether we may sleep. */
-int nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd,
- struct nvme_iod *iod, int total_len, gfp_t gfp)
+int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod, int total_len,
+ gfp_t gfp)
{
struct dma_pool *pool;
int length = total_len;
@@ -405,7 +454,6 @@ int nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd,
dma_addr_t prp_dma;
int nprps, i;
- cmd->prp1 = cpu_to_le64(dma_addr);
length -= (PAGE_SIZE - offset);
if (length <= 0)
return total_len;
@@ -420,7 +468,7 @@ int nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd,
}
if (length <= PAGE_SIZE) {
- cmd->prp2 = cpu_to_le64(dma_addr);
+ iod->first_dma = dma_addr;
return total_len;
}
@@ -435,13 +483,12 @@ int nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd,
prp_list = dma_pool_alloc(pool, gfp, &prp_dma);
if (!prp_list) {
- cmd->prp2 = cpu_to_le64(dma_addr);
+ iod->first_dma = dma_addr;
iod->npages = -1;
return (total_len - length) + PAGE_SIZE;
}
list[0] = prp_list;
iod->first_dma = prp_dma;
- cmd->prp2 = cpu_to_le64(prp_dma);
i = 0;
for (;;) {
if (i == PAGE_SIZE / 8) {
@@ -480,10 +527,11 @@ static int nvme_split_and_submit(struct bio *bio, struct nvme_queue *nvmeq,
bio_chain(split, bio);
- if (bio_list_empty(&nvmeq->sq_cong))
+ if (!waitqueue_active(&nvmeq->sq_full))
add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait);
bio_list_add(&nvmeq->sq_cong, split);
bio_list_add(&nvmeq->sq_cong, bio);
+ wake_up(&nvmeq->sq_full);
return 0;
}
@@ -536,25 +584,13 @@ static int nvme_map_bio(struct nvme_queue *nvmeq, struct nvme_iod *iod,
return length;
}
-/*
- * We reuse the small pool to allocate the 16-byte range here as it is not
- * worth having a special pool for these or additional cases to handle freeing
- * the iod.
- */
static int nvme_submit_discard(struct nvme_queue *nvmeq, struct nvme_ns *ns,
struct bio *bio, struct nvme_iod *iod, int cmdid)
{
- struct nvme_dsm_range *range;
+ struct nvme_dsm_range *range =
+ (struct nvme_dsm_range *)iod_list(iod)[0];
struct nvme_command *cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail];
- range = dma_pool_alloc(nvmeq->dev->prp_small_pool, GFP_ATOMIC,
- &iod->first_dma);
- if (!range)
- return -ENOMEM;
-
- iod_list(iod)[0] = (__le64 *)range;
- iod->npages = 0;
-
range->cattr = cpu_to_le32(0);
range->nlb = cpu_to_le32(bio->bi_iter.bi_size >> ns->lba_shift);
range->slba = cpu_to_le64(nvme_block_nr(ns, bio->bi_iter.bi_sector));
@@ -601,44 +637,22 @@ int nvme_submit_flush_data(struct nvme_queue *nvmeq, struct nvme_ns *ns)
return nvme_submit_flush(nvmeq, ns, cmdid);
}
-/*
- * Called with local interrupts disabled and the q_lock held. May not sleep.
- */
-static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns,
- struct bio *bio)
+static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod)
{
+ struct bio *bio = iod->private;
+ struct nvme_ns *ns = bio->bi_bdev->bd_disk->private_data;
struct nvme_command *cmnd;
- struct nvme_iod *iod;
- enum dma_data_direction dma_dir;
- int cmdid, length, result;
+ int cmdid;
u16 control;
u32 dsmgmt;
- int psegs = bio_phys_segments(ns->queue, bio);
-
- if ((bio->bi_rw & REQ_FLUSH) && psegs) {
- result = nvme_submit_flush_data(nvmeq, ns);
- if (result)
- return result;
- }
- result = -ENOMEM;
- iod = nvme_alloc_iod(psegs, bio->bi_iter.bi_size, GFP_ATOMIC);
- if (!iod)
- goto nomem;
- iod->private = bio;
-
- result = -EBUSY;
cmdid = alloc_cmdid(nvmeq, iod, bio_completion, NVME_IO_TIMEOUT);
if (unlikely(cmdid < 0))
- goto free_iod;
+ return cmdid;
- if (bio->bi_rw & REQ_DISCARD) {
- result = nvme_submit_discard(nvmeq, ns, bio, iod, cmdid);
- if (result)
- goto free_cmdid;
- return result;
- }
- if ((bio->bi_rw & REQ_FLUSH) && !psegs)
+ if (bio->bi_rw & REQ_DISCARD)
+ return nvme_submit_discard(nvmeq, ns, bio, iod, cmdid);
+ if ((bio->bi_rw & REQ_FLUSH) && !iod->nents)
return nvme_submit_flush(nvmeq, ns, cmdid);
control = 0;
@@ -652,42 +666,85 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns,
dsmgmt |= NVME_RW_DSM_FREQ_PREFETCH;
cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail];
-
memset(cmnd, 0, sizeof(*cmnd));
- if (bio_data_dir(bio)) {
- cmnd->rw.opcode = nvme_cmd_write;
- dma_dir = DMA_TO_DEVICE;
- } else {
- cmnd->rw.opcode = nvme_cmd_read;
- dma_dir = DMA_FROM_DEVICE;
- }
-
- result = nvme_map_bio(nvmeq, iod, bio, dma_dir, psegs);
- if (result <= 0)
- goto free_cmdid;
- length = result;
+ cmnd->rw.opcode = bio_data_dir(bio) ? nvme_cmd_write : nvme_cmd_read;
cmnd->rw.command_id = cmdid;
cmnd->rw.nsid = cpu_to_le32(ns->ns_id);
- length = nvme_setup_prps(nvmeq->dev, &cmnd->common, iod, length,
- GFP_ATOMIC);
+ cmnd->rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
+ cmnd->rw.prp2 = cpu_to_le64(iod->first_dma);
cmnd->rw.slba = cpu_to_le64(nvme_block_nr(ns, bio->bi_iter.bi_sector));
- cmnd->rw.length = cpu_to_le16((length >> ns->lba_shift) - 1);
+ cmnd->rw.length =
+ cpu_to_le16((bio->bi_iter.bi_size >> ns->lba_shift) - 1);
cmnd->rw.control = cpu_to_le16(control);
cmnd->rw.dsmgmt = cpu_to_le32(dsmgmt);
- nvme_start_io_acct(bio);
if (++nvmeq->sq_tail == nvmeq->q_depth)
nvmeq->sq_tail = 0;
writel(nvmeq->sq_tail, nvmeq->q_db);
return 0;
+}
+
+/*
+ * Called with local interrupts disabled and the q_lock held. May not sleep.
+ */
+static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns,
+ struct bio *bio)
+{
+ struct nvme_iod *iod;
+ int psegs = bio_phys_segments(ns->queue, bio);
+ int result;
+
+ if ((bio->bi_rw & REQ_FLUSH) && psegs) {
+ result = nvme_submit_flush_data(nvmeq, ns);
+ if (result)
+ return result;
+ }
+
+ iod = nvme_alloc_iod(psegs, bio->bi_iter.bi_size, GFP_ATOMIC);
+ if (!iod)
+ return -ENOMEM;
+
+ iod->private = bio;
+ if (bio->bi_rw & REQ_DISCARD) {
+ void *range;
+ /*
+ * We reuse the small pool to allocate the 16-byte range here
+ * as it is not worth having a special pool for these or
+ * additional cases to handle freeing the iod.
+ */
+ range = dma_pool_alloc(nvmeq->dev->prp_small_pool,
+ GFP_ATOMIC,
+ &iod->first_dma);
+ if (!range) {
+ result = -ENOMEM;
+ goto free_iod;
+ }
+ iod_list(iod)[0] = (__le64 *)range;
+ iod->npages = 0;
+ } else if (psegs) {
+ result = nvme_map_bio(nvmeq, iod, bio,
+ bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE,
+ psegs);
+ if (result <= 0)
+ goto free_iod;
+ if (nvme_setup_prps(nvmeq->dev, iod, result, GFP_ATOMIC) !=
+ result) {
+ result = -ENOMEM;
+ goto free_iod;
+ }
+ nvme_start_io_acct(bio);
+ }
+ if (unlikely(nvme_submit_iod(nvmeq, iod))) {
+ if (!waitqueue_active(&nvmeq->sq_full))
+ add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait);
+ list_add_tail(&iod->node, &nvmeq->iod_bio);
+ }
+ return 0;
- free_cmdid:
- free_cmdid(nvmeq, cmdid, NULL);
free_iod:
nvme_free_iod(nvmeq->dev, iod);
- nomem:
return result;
}
@@ -711,7 +768,7 @@ static int nvme_process_cq(struct nvme_queue *nvmeq)
}
ctx = free_cmdid(nvmeq, cqe.command_id, &fn);
- fn(nvmeq->dev, ctx, &cqe);
+ fn(nvmeq, ctx, &cqe);
}
/* If the controller ignores the cq head doorbell and continuously
@@ -747,7 +804,7 @@ static void nvme_make_request(struct request_queue *q, struct bio *bio)
if (!nvmeq->q_suspended && bio_list_empty(&nvmeq->sq_cong))
result = nvme_submit_bio_queue(nvmeq, ns, bio);
if (unlikely(result)) {
- if (bio_list_empty(&nvmeq->sq_cong))
+ if (!waitqueue_active(&nvmeq->sq_full))
add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait);
bio_list_add(&nvmeq->sq_cong, bio);
}
@@ -791,7 +848,7 @@ struct sync_cmd_info {
int status;
};
-static void sync_completion(struct nvme_dev *dev, void *ctx,
+static void sync_completion(struct nvme_queue *nvmeq, void *ctx,
struct nvme_completion *cqe)
{
struct sync_cmd_info *cmdinfo = ctx;
@@ -804,27 +861,46 @@ static void sync_completion(struct nvme_dev *dev, void *ctx,
* Returns 0 on success. If the result is negative, it's a Linux error code;
* if the result is positive, it's an NVM Express status code
*/
-int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd,
+static int nvme_submit_sync_cmd(struct nvme_dev *dev, int q_idx,
+ struct nvme_command *cmd,
u32 *result, unsigned timeout)
{
- int cmdid;
+ int cmdid, ret;
struct sync_cmd_info cmdinfo;
+ struct nvme_queue *nvmeq;
+
+ nvmeq = lock_nvmeq(dev, q_idx);
+ if (!nvmeq) {
+ unlock_nvmeq(nvmeq);
+ return -ENODEV;
+ }
cmdinfo.task = current;
cmdinfo.status = -EINTR;
- cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion,
- timeout);
- if (cmdid < 0)
+ cmdid = alloc_cmdid(nvmeq, &cmdinfo, sync_completion, timeout);
+ if (cmdid < 0) {
+ unlock_nvmeq(nvmeq);
return cmdid;
+ }
cmd->common.command_id = cmdid;
set_current_state(TASK_KILLABLE);
- nvme_submit_cmd(nvmeq, cmd);
+ ret = nvme_submit_cmd(nvmeq, cmd);
+ if (ret) {
+ free_cmdid(nvmeq, cmdid, NULL);
+ unlock_nvmeq(nvmeq);
+ set_current_state(TASK_RUNNING);
+ return ret;
+ }
+ unlock_nvmeq(nvmeq);
schedule_timeout(timeout);
if (cmdinfo.status == -EINTR) {
- nvme_abort_command(nvmeq, cmdid);
+ nvmeq = lock_nvmeq(dev, q_idx);
+ if (nvmeq)
+ nvme_abort_command(nvmeq, cmdid);
+ unlock_nvmeq(nvmeq);
return -EINTR;
}
@@ -845,20 +921,26 @@ static int nvme_submit_async_cmd(struct nvme_queue *nvmeq,
return cmdid;
cmdinfo->status = -EINTR;
cmd->common.command_id = cmdid;
- nvme_submit_cmd(nvmeq, cmd);
- return 0;
+ return nvme_submit_cmd(nvmeq, cmd);
}
int nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd,
u32 *result)
{
- return nvme_submit_sync_cmd(dev->queues[0], cmd, result, ADMIN_TIMEOUT);
+ return nvme_submit_sync_cmd(dev, 0, cmd, result, ADMIN_TIMEOUT);
+}
+
+int nvme_submit_io_cmd(struct nvme_dev *dev, struct nvme_command *cmd,
+ u32 *result)
+{
+ return nvme_submit_sync_cmd(dev, smp_processor_id() + 1, cmd, result,
+ NVME_IO_TIMEOUT);
}
static int nvme_submit_admin_cmd_async(struct nvme_dev *dev,
struct nvme_command *cmd, struct async_cmd_info *cmdinfo)
{
- return nvme_submit_async_cmd(dev->queues[0], cmd, cmdinfo,
+ return nvme_submit_async_cmd(raw_nvmeq(dev, 0), cmd, cmdinfo,
ADMIN_TIMEOUT);
}
@@ -985,6 +1067,7 @@ static void nvme_abort_cmd(int cmdid, struct nvme_queue *nvmeq)
struct nvme_command cmd;
struct nvme_dev *dev = nvmeq->dev;
struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
+ struct nvme_queue *adminq;
if (!nvmeq->qid || info[cmdid].aborted) {
if (work_busy(&dev->reset_work))
@@ -1001,7 +1084,8 @@ static void nvme_abort_cmd(int cmdid, struct nvme_queue *nvmeq)
if (!dev->abort_limit)
return;
- a_cmdid = alloc_cmdid(dev->queues[0], CMD_CTX_ABORT, special_completion,
+ adminq = rcu_dereference(dev->queues[0]);
+ a_cmdid = alloc_cmdid(adminq, CMD_CTX_ABORT, special_completion,
ADMIN_TIMEOUT);
if (a_cmdid < 0)
return;
@@ -1018,7 +1102,7 @@ static void nvme_abort_cmd(int cmdid, struct nvme_queue *nvmeq)
dev_warn(nvmeq->q_dmadev, "Aborting I/O %d QID %d\n", cmdid,
nvmeq->qid);
- nvme_submit_cmd(dev->queues[0], &cmd);
+ nvme_submit_cmd(adminq, &cmd);
}
/**
@@ -1051,23 +1135,38 @@ static void nvme_cancel_ios(struct nvme_queue *nvmeq, bool timeout)
dev_warn(nvmeq->q_dmadev, "Cancelling I/O %d QID %d\n", cmdid,
nvmeq->qid);
ctx = cancel_cmdid(nvmeq, cmdid, &fn);
- fn(nvmeq->dev, ctx, &cqe);
+ fn(nvmeq, ctx, &cqe);
}
}
-static void nvme_free_queue(struct nvme_queue *nvmeq)
+static void nvme_free_queue(struct rcu_head *r)
{
+ struct nvme_queue *nvmeq = container_of(r, struct nvme_queue, r_head);
+
spin_lock_irq(&nvmeq->q_lock);
while (bio_list_peek(&nvmeq->sq_cong)) {
struct bio *bio = bio_list_pop(&nvmeq->sq_cong);
bio_endio(bio, -EIO);
}
+ while (!list_empty(&nvmeq->iod_bio)) {
+ static struct nvme_completion cqe = {
+ .status = cpu_to_le16(
+ (NVME_SC_ABORT_REQ | NVME_SC_DNR) << 1),
+ };
+ struct nvme_iod *iod = list_first_entry(&nvmeq->iod_bio,
+ struct nvme_iod,
+ node);
+ list_del(&iod->node);
+ bio_completion(nvmeq, iod, &cqe);
+ }
spin_unlock_irq(&nvmeq->q_lock);
dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth),
(void *)nvmeq->cqes, nvmeq->cq_dma_addr);
dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth),
nvmeq->sq_cmds, nvmeq->sq_dma_addr);
+ if (nvmeq->qid)
+ free_cpumask_var(nvmeq->cpu_mask);
kfree(nvmeq);
}
@@ -1076,9 +1175,10 @@ static void nvme_free_queues(struct nvme_dev *dev, int lowest)
int i;
for (i = dev->queue_count - 1; i >= lowest; i--) {
- nvme_free_queue(dev->queues[i]);
+ struct nvme_queue *nvmeq = raw_nvmeq(dev, i);
+ rcu_assign_pointer(dev->queues[i], NULL);
+ call_rcu(&nvmeq->r_head, nvme_free_queue);
dev->queue_count--;
- dev->queues[i] = NULL;
}
}
@@ -1098,6 +1198,7 @@ static int nvme_suspend_queue(struct nvme_queue *nvmeq)
return 1;
}
nvmeq->q_suspended = 1;
+ nvmeq->dev->online_queues--;
spin_unlock_irq(&nvmeq->q_lock);
irq_set_affinity_hint(vector, NULL);
@@ -1116,7 +1217,7 @@ static void nvme_clear_queue(struct nvme_queue *nvmeq)
static void nvme_disable_queue(struct nvme_dev *dev, int qid)
{
- struct nvme_queue *nvmeq = dev->queues[qid];
+ struct nvme_queue *nvmeq = raw_nvmeq(dev, qid);
if (!nvmeq)
return;
@@ -1152,6 +1253,9 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
if (!nvmeq->sq_cmds)
goto free_cqdma;
+ if (qid && !zalloc_cpumask_var(&nvmeq->cpu_mask, GFP_KERNEL))
+ goto free_sqdma;
+
nvmeq->q_dmadev = dmadev;
nvmeq->dev = dev;
snprintf(nvmeq->irqname, sizeof(nvmeq->irqname), "nvme%dq%d",
@@ -1162,15 +1266,20 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
init_waitqueue_head(&nvmeq->sq_full);
init_waitqueue_entry(&nvmeq->sq_cong_wait, nvme_thread);
bio_list_init(&nvmeq->sq_cong);
+ INIT_LIST_HEAD(&nvmeq->iod_bio);
nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride];
nvmeq->q_depth = depth;
nvmeq->cq_vector = vector;
nvmeq->qid = qid;
nvmeq->q_suspended = 1;
dev->queue_count++;
+ rcu_assign_pointer(dev->queues[qid], nvmeq);
return nvmeq;
+ free_sqdma:
+ dma_free_coherent(dmadev, SQ_SIZE(depth), (void *)nvmeq->sq_cmds,
+ nvmeq->sq_dma_addr);
free_cqdma:
dma_free_coherent(dmadev, CQ_SIZE(depth), (void *)nvmeq->cqes,
nvmeq->cq_dma_addr);
@@ -1203,6 +1312,7 @@ static void nvme_init_queue(struct nvme_queue *nvmeq, u16 qid)
memset((void *)nvmeq->cqes, 0, CQ_SIZE(nvmeq->q_depth));
nvme_cancel_ios(nvmeq, false);
nvmeq->q_suspended = 0;
+ dev->online_queues++;
}
static int nvme_create_queue(struct nvme_queue *nvmeq, int qid)
@@ -1311,12 +1421,11 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
if (result < 0)
return result;
- nvmeq = dev->queues[0];
+ nvmeq = raw_nvmeq(dev, 0);
if (!nvmeq) {
nvmeq = nvme_alloc_queue(dev, 0, 64, 0);
if (!nvmeq)
return -ENOMEM;
- dev->queues[0] = nvmeq;
}
aqa = nvmeq->q_depth - 1;
@@ -1418,7 +1527,6 @@ void nvme_unmap_user_pages(struct nvme_dev *dev, int write,
static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
{
struct nvme_dev *dev = ns->dev;
- struct nvme_queue *nvmeq;
struct nvme_user_io io;
struct nvme_command c;
unsigned length, meta_len;
@@ -1492,22 +1600,14 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
c.rw.metadata = cpu_to_le64(meta_dma_addr);
}
- length = nvme_setup_prps(dev, &c.common, iod, length, GFP_KERNEL);
+ length = nvme_setup_prps(dev, iod, length, GFP_KERNEL);
+ c.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
+ c.rw.prp2 = cpu_to_le64(iod->first_dma);
- nvmeq = get_nvmeq(dev);
- /*
- * Since nvme_submit_sync_cmd sleeps, we can't keep preemption
- * disabled. We may be preempted at any point, and be rescheduled
- * to a different CPU. That will cause cacheline bouncing, but no
- * additional races since q_lock already protects against other CPUs.
- */
- put_nvmeq(nvmeq);
if (length != (io.nblocks + 1) << ns->lba_shift)
status = -ENOMEM;
- else if (!nvmeq || nvmeq->q_suspended)
- status = -EBUSY;
else
- status = nvme_submit_sync_cmd(nvmeq, &c, NULL, NVME_IO_TIMEOUT);
+ status = nvme_submit_io_cmd(dev, &c, NULL);
if (meta_len) {
if (status == NVME_SC_SUCCESS && !(io.opcode & 1)) {
@@ -1572,8 +1672,9 @@ static int nvme_user_admin_cmd(struct nvme_dev *dev,
length);
if (IS_ERR(iod))
return PTR_ERR(iod);
- length = nvme_setup_prps(dev, &c.common, iod, length,
- GFP_KERNEL);
+ length = nvme_setup_prps(dev, iod, length, GFP_KERNEL);
+ c.common.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
+ c.common.prp2 = cpu_to_le64(iod->first_dma);
}
timeout = cmd.timeout_ms ? msecs_to_jiffies(cmd.timeout_ms) :
@@ -1581,8 +1682,7 @@ static int nvme_user_admin_cmd(struct nvme_dev *dev,
if (length != cmd.data_len)
status = -ENOMEM;
else
- status = nvme_submit_sync_cmd(dev->queues[0], &c, &cmd.result,
- timeout);
+ status = nvme_submit_sync_cmd(dev, 0, &c, &cmd.result, timeout);
if (cmd.data_len) {
nvme_unmap_user_pages(dev, cmd.opcode & 1, iod);
@@ -1653,25 +1753,51 @@ static void nvme_release(struct gendisk *disk, fmode_t mode)
kref_put(&dev->kref, nvme_free_dev);
}
+static int nvme_getgeo(struct block_device *bd, struct hd_geometry *geo)
+{
+ /* some standard values */
+ geo->heads = 1 << 6;
+ geo->sectors = 1 << 5;
+ geo->cylinders = get_capacity(bd->bd_disk) >> 11;
+ return 0;
+}
+
static const struct block_device_operations nvme_fops = {
.owner = THIS_MODULE,
.ioctl = nvme_ioctl,
.compat_ioctl = nvme_compat_ioctl,
.open = nvme_open,
.release = nvme_release,
+ .getgeo = nvme_getgeo,
};
+static void nvme_resubmit_iods(struct nvme_queue *nvmeq)
+{
+ struct nvme_iod *iod, *next;
+
+ list_for_each_entry_safe(iod, next, &nvmeq->iod_bio, node) {
+ if (unlikely(nvme_submit_iod(nvmeq, iod)))
+ break;
+ list_del(&iod->node);
+ if (bio_list_empty(&nvmeq->sq_cong) &&
+ list_empty(&nvmeq->iod_bio))
+ remove_wait_queue(&nvmeq->sq_full,
+ &nvmeq->sq_cong_wait);
+ }
+}
+
static void nvme_resubmit_bios(struct nvme_queue *nvmeq)
{
while (bio_list_peek(&nvmeq->sq_cong)) {
struct bio *bio = bio_list_pop(&nvmeq->sq_cong);
struct nvme_ns *ns = bio->bi_bdev->bd_disk->private_data;
- if (bio_list_empty(&nvmeq->sq_cong))
+ if (bio_list_empty(&nvmeq->sq_cong) &&
+ list_empty(&nvmeq->iod_bio))
remove_wait_queue(&nvmeq->sq_full,
&nvmeq->sq_cong_wait);
if (nvme_submit_bio_queue(nvmeq, ns, bio)) {
- if (bio_list_empty(&nvmeq->sq_cong))
+ if (!waitqueue_active(&nvmeq->sq_full))
add_wait_queue(&nvmeq->sq_full,
&nvmeq->sq_cong_wait);
bio_list_add_head(&nvmeq->sq_cong, bio);
@@ -1700,8 +1826,10 @@ static int nvme_kthread(void *data)
queue_work(nvme_workq, &dev->reset_work);
continue;
}
+ rcu_read_lock();
for (i = 0; i < dev->queue_count; i++) {
- struct nvme_queue *nvmeq = dev->queues[i];
+ struct nvme_queue *nvmeq =
+ rcu_dereference(dev->queues[i]);
if (!nvmeq)
continue;
spin_lock_irq(&nvmeq->q_lock);
@@ -1710,9 +1838,11 @@ static int nvme_kthread(void *data)
nvme_process_cq(nvmeq);
nvme_cancel_ios(nvmeq, true);
nvme_resubmit_bios(nvmeq);
+ nvme_resubmit_iods(nvmeq);
unlock:
spin_unlock_irq(&nvmeq->q_lock);
}
+ rcu_read_unlock();
}
spin_unlock(&dev_list_lock);
schedule_timeout(round_jiffies_relative(HZ));
@@ -1787,6 +1917,143 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid,
return NULL;
}
+static int nvme_find_closest_node(int node)
+{
+ int n, val, min_val = INT_MAX, best_node = node;
+
+ for_each_online_node(n) {
+ if (n == node)
+ continue;
+ val = node_distance(node, n);
+ if (val < min_val) {
+ min_val = val;
+ best_node = n;
+ }
+ }
+ return best_node;
+}
+
+static void nvme_set_queue_cpus(cpumask_t *qmask, struct nvme_queue *nvmeq,
+ int count)
+{
+ int cpu;
+ for_each_cpu(cpu, qmask) {
+ if (cpumask_weight(nvmeq->cpu_mask) >= count)
+ break;
+ if (!cpumask_test_and_set_cpu(cpu, nvmeq->cpu_mask))
+ *per_cpu_ptr(nvmeq->dev->io_queue, cpu) = nvmeq->qid;
+ }
+}
+
+static void nvme_add_cpus(cpumask_t *mask, const cpumask_t *unassigned_cpus,
+ const cpumask_t *new_mask, struct nvme_queue *nvmeq, int cpus_per_queue)
+{
+ int next_cpu;
+ for_each_cpu(next_cpu, new_mask) {
+ cpumask_or(mask, mask, get_cpu_mask(next_cpu));
+ cpumask_or(mask, mask, topology_thread_cpumask(next_cpu));
+ cpumask_and(mask, mask, unassigned_cpus);
+ nvme_set_queue_cpus(mask, nvmeq, cpus_per_queue);
+ }
+}
+
+static void nvme_create_io_queues(struct nvme_dev *dev)
+{
+ unsigned i, max;
+
+ max = min(dev->max_qid, num_online_cpus());
+ for (i = dev->queue_count; i <= max; i++)
+ if (!nvme_alloc_queue(dev, i, dev->q_depth, i - 1))
+ break;
+
+ max = min(dev->queue_count - 1, num_online_cpus());
+ for (i = dev->online_queues; i <= max; i++)
+ if (nvme_create_queue(raw_nvmeq(dev, i), i))
+ break;
+}
+
+/*
+ * If there are fewer queues than online cpus, this will try to optimally
+ * assign a queue to multiple cpus by grouping cpus that are "close" together:
+ * thread siblings, core, socket, closest node, then whatever else is
+ * available.
+ */
+static void nvme_assign_io_queues(struct nvme_dev *dev)
+{
+ unsigned cpu, cpus_per_queue, queues, remainder, i;
+ cpumask_var_t unassigned_cpus;
+
+ nvme_create_io_queues(dev);
+
+ queues = min(dev->online_queues - 1, num_online_cpus());
+ if (!queues)
+ return;
+
+ cpus_per_queue = num_online_cpus() / queues;
+ remainder = queues - (num_online_cpus() - queues * cpus_per_queue);
+
+ if (!alloc_cpumask_var(&unassigned_cpus, GFP_KERNEL))
+ return;
+
+ cpumask_copy(unassigned_cpus, cpu_online_mask);
+ cpu = cpumask_first(unassigned_cpus);
+ for (i = 1; i <= queues; i++) {
+ struct nvme_queue *nvmeq = lock_nvmeq(dev, i);
+ cpumask_t mask;
+
+ cpumask_clear(nvmeq->cpu_mask);
+ if (!cpumask_weight(unassigned_cpus)) {
+ unlock_nvmeq(nvmeq);
+ break;
+ }
+
+ mask = *get_cpu_mask(cpu);
+ nvme_set_queue_cpus(&mask, nvmeq, cpus_per_queue);
+ if (cpus_weight(mask) < cpus_per_queue)
+ nvme_add_cpus(&mask, unassigned_cpus,
+ topology_thread_cpumask(cpu),
+ nvmeq, cpus_per_queue);
+ if (cpus_weight(mask) < cpus_per_queue)
+ nvme_add_cpus(&mask, unassigned_cpus,
+ topology_core_cpumask(cpu),
+ nvmeq, cpus_per_queue);
+ if (cpus_weight(mask) < cpus_per_queue)
+ nvme_add_cpus(&mask, unassigned_cpus,
+ cpumask_of_node(cpu_to_node(cpu)),
+ nvmeq, cpus_per_queue);
+ if (cpus_weight(mask) < cpus_per_queue)
+ nvme_add_cpus(&mask, unassigned_cpus,
+ cpumask_of_node(
+ nvme_find_closest_node(
+ cpu_to_node(cpu))),
+ nvmeq, cpus_per_queue);
+ if (cpus_weight(mask) < cpus_per_queue)
+ nvme_add_cpus(&mask, unassigned_cpus,
+ unassigned_cpus,
+ nvmeq, cpus_per_queue);
+
+ WARN(cpumask_weight(nvmeq->cpu_mask) != cpus_per_queue,
+ "nvme%d qid:%d mis-matched queue-to-cpu assignment\n",
+ dev->instance, i);
+
+ irq_set_affinity_hint(dev->entry[nvmeq->cq_vector].vector,
+ nvmeq->cpu_mask);
+ cpumask_andnot(unassigned_cpus, unassigned_cpus,
+ nvmeq->cpu_mask);
+ cpu = cpumask_next(cpu, unassigned_cpus);
+ if (remainder && !--remainder)
+ cpus_per_queue++;
+ unlock_nvmeq(nvmeq);
+ }
+ WARN(cpumask_weight(unassigned_cpus), "nvme%d unassigned online cpus\n",
+ dev->instance);
+ i = 0;
+ cpumask_andnot(unassigned_cpus, cpu_possible_mask, cpu_online_mask);
+ for_each_cpu(cpu, unassigned_cpus)
+ *per_cpu_ptr(dev->io_queue, cpu) = (i++ % queues) + 1;
+ free_cpumask_var(unassigned_cpus);
+}
+
static int set_queue_count(struct nvme_dev *dev, int count)
{
int status;
@@ -1805,13 +2072,26 @@ static size_t db_bar_size(struct nvme_dev *dev, unsigned nr_io_queues)
return 4096 + ((nr_io_queues + 1) * 8 * dev->db_stride);
}
+static int nvme_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ struct nvme_dev *dev = container_of(self, struct nvme_dev, nb);
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_DEAD:
+ nvme_assign_io_queues(dev);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
static int nvme_setup_io_queues(struct nvme_dev *dev)
{
- struct nvme_queue *adminq = dev->queues[0];
+ struct nvme_queue *adminq = raw_nvmeq(dev, 0);
struct pci_dev *pdev = dev->pci_dev;
- int result, cpu, i, vecs, nr_io_queues, size, q_depth;
+ int result, i, vecs, nr_io_queues, size;
- nr_io_queues = num_online_cpus();
+ nr_io_queues = num_possible_cpus();
result = set_queue_count(dev, nr_io_queues);
if (result < 0)
return result;
@@ -1830,7 +2110,7 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
size = db_bar_size(dev, nr_io_queues);
} while (1);
dev->dbs = ((void __iomem *)dev->bar) + 4096;
- dev->queues[0]->q_db = dev->dbs;
+ adminq->q_db = dev->dbs;
}
/* Deregister the admin queue's interrupt */
@@ -1856,6 +2136,7 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
* number of interrupts.
*/
nr_io_queues = vecs;
+ dev->max_qid = nr_io_queues;
result = queue_request_irq(dev, adminq, adminq->irqname);
if (result) {
@@ -1864,49 +2145,13 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
}
/* Free previously allocated queues that are no longer usable */
- spin_lock(&dev_list_lock);
- for (i = dev->queue_count - 1; i > nr_io_queues; i--) {
- struct nvme_queue *nvmeq = dev->queues[i];
-
- spin_lock_irq(&nvmeq->q_lock);
- nvme_cancel_ios(nvmeq, false);
- spin_unlock_irq(&nvmeq->q_lock);
-
- nvme_free_queue(nvmeq);
- dev->queue_count--;
- dev->queues[i] = NULL;
- }
- spin_unlock(&dev_list_lock);
-
- cpu = cpumask_first(cpu_online_mask);
- for (i = 0; i < nr_io_queues; i++) {
- irq_set_affinity_hint(dev->entry[i].vector, get_cpu_mask(cpu));
- cpu = cpumask_next(cpu, cpu_online_mask);
- }
-
- q_depth = min_t(int, NVME_CAP_MQES(readq(&dev->bar->cap)) + 1,
- NVME_Q_DEPTH);
- for (i = dev->queue_count - 1; i < nr_io_queues; i++) {
- dev->queues[i + 1] = nvme_alloc_queue(dev, i + 1, q_depth, i);
- if (!dev->queues[i + 1]) {
- result = -ENOMEM;
- goto free_queues;
- }
- }
-
- for (; i < num_possible_cpus(); i++) {
- int target = i % rounddown_pow_of_two(dev->queue_count - 1);
- dev->queues[i + 1] = dev->queues[target + 1];
- }
+ nvme_free_queues(dev, nr_io_queues + 1);
+ nvme_assign_io_queues(dev);
- for (i = 1; i < dev->queue_count; i++) {
- result = nvme_create_queue(dev->queues[i], i);
- if (result) {
- for (--i; i > 0; i--)
- nvme_disable_queue(dev, i);
- goto free_queues;
- }
- }
+ dev->nb.notifier_call = &nvme_cpu_notify;
+ result = register_hotcpu_notifier(&dev->nb);
+ if (result)
+ goto free_queues;
return 0;
@@ -1985,6 +2230,7 @@ static int nvme_dev_add(struct nvme_dev *dev)
static int nvme_dev_map(struct nvme_dev *dev)
{
+ u64 cap;
int bars, result = -ENOMEM;
struct pci_dev *pdev = dev->pci_dev;
@@ -2008,7 +2254,9 @@ static int nvme_dev_map(struct nvme_dev *dev)
result = -ENODEV;
goto unmap;
}
- dev->db_stride = 1 << NVME_CAP_STRIDE(readq(&dev->bar->cap));
+ cap = readq(&dev->bar->cap);
+ dev->q_depth = min_t(int, NVME_CAP_MQES(cap) + 1, NVME_Q_DEPTH);
+ dev->db_stride = 1 << NVME_CAP_STRIDE(cap);
dev->dbs = ((void __iomem *)dev->bar) + 4096;
return 0;
@@ -2164,7 +2412,7 @@ static void nvme_disable_io_queues(struct nvme_dev *dev)
atomic_set(&dq.refcount, 0);
dq.worker = &worker;
for (i = dev->queue_count - 1; i > 0; i--) {
- struct nvme_queue *nvmeq = dev->queues[i];
+ struct nvme_queue *nvmeq = raw_nvmeq(dev, i);
if (nvme_suspend_queue(nvmeq))
continue;
@@ -2177,19 +2425,38 @@ static void nvme_disable_io_queues(struct nvme_dev *dev)
kthread_stop(kworker_task);
}
+/*
+* Remove the node from the device list and check
+* for whether or not we need to stop the nvme_thread.
+*/
+static void nvme_dev_list_remove(struct nvme_dev *dev)
+{
+ struct task_struct *tmp = NULL;
+
+ spin_lock(&dev_list_lock);
+ list_del_init(&dev->node);
+ if (list_empty(&dev_list) && !IS_ERR_OR_NULL(nvme_thread)) {
+ tmp = nvme_thread;
+ nvme_thread = NULL;
+ }
+ spin_unlock(&dev_list_lock);
+
+ if (tmp)
+ kthread_stop(tmp);
+}
+
static void nvme_dev_shutdown(struct nvme_dev *dev)
{
int i;
dev->initialized = 0;
+ unregister_hotcpu_notifier(&dev->nb);
- spin_lock(&dev_list_lock);
- list_del_init(&dev->node);
- spin_unlock(&dev_list_lock);
+ nvme_dev_list_remove(dev);
if (!dev->bar || (dev->bar && readl(&dev->bar->csts) == -1)) {
for (i = dev->queue_count - 1; i >= 0; i--) {
- struct nvme_queue *nvmeq = dev->queues[i];
+ struct nvme_queue *nvmeq = raw_nvmeq(dev, i);
nvme_suspend_queue(nvmeq);
nvme_clear_queue(nvmeq);
}
@@ -2282,6 +2549,7 @@ static void nvme_free_dev(struct kref *kref)
struct nvme_dev *dev = container_of(kref, struct nvme_dev, kref);
nvme_free_namespaces(dev);
+ free_percpu(dev->io_queue);
kfree(dev->queues);
kfree(dev->entry);
kfree(dev);
@@ -2325,6 +2593,7 @@ static const struct file_operations nvme_dev_fops = {
static int nvme_dev_start(struct nvme_dev *dev)
{
int result;
+ bool start_thread = false;
result = nvme_dev_map(dev);
if (result)
@@ -2335,9 +2604,24 @@ static int nvme_dev_start(struct nvme_dev *dev)
goto unmap;
spin_lock(&dev_list_lock);
+ if (list_empty(&dev_list) && IS_ERR_OR_NULL(nvme_thread)) {
+ start_thread = true;
+ nvme_thread = NULL;
+ }
list_add(&dev->node, &dev_list);
spin_unlock(&dev_list_lock);
+ if (start_thread) {
+ nvme_thread = kthread_run(nvme_kthread, NULL, "nvme");
+ wake_up(&nvme_kthread_wait);
+ } else
+ wait_event_killable(nvme_kthread_wait, nvme_thread);
+
+ if (IS_ERR_OR_NULL(nvme_thread)) {
+ result = nvme_thread ? PTR_ERR(nvme_thread) : -EINTR;
+ goto disable;
+ }
+
result = nvme_setup_io_queues(dev);
if (result && result != -EBUSY)
goto disable;
@@ -2346,9 +2630,7 @@ static int nvme_dev_start(struct nvme_dev *dev)
disable:
nvme_disable_queue(dev, 0);
- spin_lock(&dev_list_lock);
- list_del_init(&dev->node);
- spin_unlock(&dev_list_lock);
+ nvme_dev_list_remove(dev);
unmap:
nvme_dev_unmap(dev);
return result;
@@ -2367,18 +2649,10 @@ static int nvme_remove_dead_ctrl(void *arg)
static void nvme_remove_disks(struct work_struct *ws)
{
- int i;
struct nvme_dev *dev = container_of(ws, struct nvme_dev, reset_work);
nvme_dev_remove(dev);
- spin_lock(&dev_list_lock);
- for (i = dev->queue_count - 1; i > 0; i--) {
- BUG_ON(!dev->queues[i] || !dev->queues[i]->q_suspended);
- nvme_free_queue(dev->queues[i]);
- dev->queue_count--;
- dev->queues[i] = NULL;
- }
- spin_unlock(&dev_list_lock);
+ nvme_free_queues(dev, 1);
}
static int nvme_dev_resume(struct nvme_dev *dev)
@@ -2441,6 +2715,9 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
GFP_KERNEL);
if (!dev->queues)
goto free;
+ dev->io_queue = alloc_percpu(unsigned short);
+ if (!dev->io_queue)
+ goto free;
INIT_LIST_HEAD(&dev->namespaces);
dev->reset_workfn = nvme_reset_failed_dev;
@@ -2455,6 +2732,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (result)
goto release;
+ kref_init(&dev->kref);
result = nvme_dev_start(dev);
if (result) {
if (result == -EBUSY)
@@ -2462,7 +2740,6 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto release_pools;
}
- kref_init(&dev->kref);
result = nvme_dev_add(dev);
if (result)
goto shutdown;
@@ -2491,6 +2768,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
release:
nvme_release_instance(dev);
free:
+ free_percpu(dev->io_queue);
kfree(dev->queues);
kfree(dev->entry);
kfree(dev);
@@ -2517,6 +2795,7 @@ static void nvme_remove(struct pci_dev *pdev)
nvme_dev_remove(dev);
nvme_dev_shutdown(dev);
nvme_free_queues(dev, 0);
+ rcu_barrier();
nvme_release_instance(dev);
nvme_release_prp_pools(dev);
kref_put(&dev->kref, nvme_free_dev);
@@ -2529,6 +2808,7 @@ static void nvme_remove(struct pci_dev *pdev)
#define nvme_slot_reset NULL
#define nvme_error_resume NULL
+#ifdef CONFIG_PM_SLEEP
static int nvme_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -2549,6 +2829,7 @@ static int nvme_resume(struct device *dev)
}
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(nvme_dev_pm_ops, nvme_suspend, nvme_resume);
@@ -2563,7 +2844,7 @@ static const struct pci_error_handlers nvme_err_handler = {
/* Move to pci_ids.h later */
#define PCI_CLASS_STORAGE_EXPRESS 0x010802
-static DEFINE_PCI_DEVICE_TABLE(nvme_id_table) = {
+static const struct pci_device_id nvme_id_table[] = {
{ PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) },
{ 0, }
};
@@ -2585,14 +2866,11 @@ static int __init nvme_init(void)
{
int result;
- nvme_thread = kthread_run(nvme_kthread, NULL, "nvme");
- if (IS_ERR(nvme_thread))
- return PTR_ERR(nvme_thread);
+ init_waitqueue_head(&nvme_kthread_wait);
- result = -ENOMEM;
nvme_workq = create_singlethread_workqueue("nvme");
if (!nvme_workq)
- goto kill_kthread;
+ return -ENOMEM;
result = register_blkdev(nvme_major, "nvme");
if (result < 0)
@@ -2609,8 +2887,6 @@ static int __init nvme_init(void)
unregister_blkdev(nvme_major, "nvme");
kill_workq:
destroy_workqueue(nvme_workq);
- kill_kthread:
- kthread_stop(nvme_thread);
return result;
}
@@ -2619,11 +2895,11 @@ static void __exit nvme_exit(void)
pci_unregister_driver(&nvme_driver);
unregister_blkdev(nvme_major, "nvme");
destroy_workqueue(nvme_workq);
- kthread_stop(nvme_thread);
+ BUG_ON(nvme_thread && !IS_ERR(nvme_thread));
}
MODULE_AUTHOR("Matthew Wilcox <willy@linux.intel.com>");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.8");
+MODULE_VERSION("0.9");
module_init(nvme_init);
module_exit(nvme_exit);
diff --git a/drivers/block/nvme-scsi.c b/drivers/block/nvme-scsi.c
index 4a0ceb64e269..2c3f5be06da1 100644
--- a/drivers/block/nvme-scsi.c
+++ b/drivers/block/nvme-scsi.c
@@ -1562,13 +1562,14 @@ static int nvme_trans_send_fw_cmd(struct nvme_ns *ns, struct sg_io_hdr *hdr,
res = PTR_ERR(iod);
goto out;
}
- length = nvme_setup_prps(dev, &c.common, iod, tot_len,
- GFP_KERNEL);
+ length = nvme_setup_prps(dev, iod, tot_len, GFP_KERNEL);
if (length != tot_len) {
res = -ENOMEM;
goto out_unmap;
}
+ c.dlfw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
+ c.dlfw.prp2 = cpu_to_le64(iod->first_dma);
c.dlfw.numd = cpu_to_le32((tot_len/BYTES_TO_DWORDS) - 1);
c.dlfw.offset = cpu_to_le32(offset/BYTES_TO_DWORDS);
} else if (opcode == nvme_admin_activate_fw) {
@@ -2033,7 +2034,6 @@ static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr,
int res = SNTI_TRANSLATION_SUCCESS;
int nvme_sc;
struct nvme_dev *dev = ns->dev;
- struct nvme_queue *nvmeq;
u32 num_cmds;
struct nvme_iod *iod;
u64 unit_len;
@@ -2045,7 +2045,7 @@ static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr,
struct nvme_command c;
u8 opcode = (is_write ? nvme_cmd_write : nvme_cmd_read);
u16 control;
- u32 max_blocks = nvme_block_nr(ns, dev->max_hw_sectors);
+ u32 max_blocks = queue_max_hw_sectors(ns->queue);
num_cmds = nvme_trans_io_get_num_cmds(hdr, cdb_info, max_blocks);
@@ -2093,8 +2093,7 @@ static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr,
res = PTR_ERR(iod);
goto out;
}
- retcode = nvme_setup_prps(dev, &c.common, iod, unit_len,
- GFP_KERNEL);
+ retcode = nvme_setup_prps(dev, iod, unit_len, GFP_KERNEL);
if (retcode != unit_len) {
nvme_unmap_user_pages(dev,
(is_write) ? DMA_TO_DEVICE : DMA_FROM_DEVICE,
@@ -2103,21 +2102,12 @@ static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr,
res = -ENOMEM;
goto out;
}
+ c.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
+ c.rw.prp2 = cpu_to_le64(iod->first_dma);
nvme_offset += unit_num_blocks;
- nvmeq = get_nvmeq(dev);
- /*
- * Since nvme_submit_sync_cmd sleeps, we can't keep
- * preemption disabled. We may be preempted at any
- * point, and be rescheduled to a different CPU. That
- * will cause cacheline bouncing, but no additional
- * races since q_lock already protects against other
- * CPUs.
- */
- put_nvmeq(nvmeq);
- nvme_sc = nvme_submit_sync_cmd(nvmeq, &c, NULL,
- NVME_IO_TIMEOUT);
+ nvme_sc = nvme_submit_io_cmd(dev, &c, NULL);
if (nvme_sc != NVME_SC_SUCCESS) {
nvme_unmap_user_pages(dev,
(is_write) ? DMA_TO_DEVICE : DMA_FROM_DEVICE,
@@ -2644,7 +2634,6 @@ static int nvme_trans_start_stop(struct nvme_ns *ns, struct sg_io_hdr *hdr,
{
int res = SNTI_TRANSLATION_SUCCESS;
int nvme_sc;
- struct nvme_queue *nvmeq;
struct nvme_command c;
u8 immed, pcmod, pc, no_flush, start;
@@ -2671,10 +2660,7 @@ static int nvme_trans_start_stop(struct nvme_ns *ns, struct sg_io_hdr *hdr,
c.common.opcode = nvme_cmd_flush;
c.common.nsid = cpu_to_le32(ns->ns_id);
- nvmeq = get_nvmeq(ns->dev);
- put_nvmeq(nvmeq);
- nvme_sc = nvme_submit_sync_cmd(nvmeq, &c, NULL, NVME_IO_TIMEOUT);
-
+ nvme_sc = nvme_submit_io_cmd(ns->dev, &c, NULL);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
goto out;
@@ -2697,15 +2683,12 @@ static int nvme_trans_synchronize_cache(struct nvme_ns *ns,
int res = SNTI_TRANSLATION_SUCCESS;
int nvme_sc;
struct nvme_command c;
- struct nvme_queue *nvmeq;
memset(&c, 0, sizeof(c));
c.common.opcode = nvme_cmd_flush;
c.common.nsid = cpu_to_le32(ns->ns_id);
- nvmeq = get_nvmeq(ns->dev);
- put_nvmeq(nvmeq);
- nvme_sc = nvme_submit_sync_cmd(nvmeq, &c, NULL, NVME_IO_TIMEOUT);
+ nvme_sc = nvme_submit_io_cmd(ns->dev, &c, NULL);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
@@ -2872,7 +2855,6 @@ static int nvme_trans_unmap(struct nvme_ns *ns, struct sg_io_hdr *hdr,
struct nvme_dev *dev = ns->dev;
struct scsi_unmap_parm_list *plist;
struct nvme_dsm_range *range;
- struct nvme_queue *nvmeq;
struct nvme_command c;
int i, nvme_sc, res = -ENOMEM;
u16 ndesc, list_len;
@@ -2914,10 +2896,7 @@ static int nvme_trans_unmap(struct nvme_ns *ns, struct sg_io_hdr *hdr,
c.dsm.nr = cpu_to_le32(ndesc - 1);
c.dsm.attributes = cpu_to_le32(NVME_DSMGMT_AD);
- nvmeq = get_nvmeq(dev);
- put_nvmeq(nvmeq);
-
- nvme_sc = nvme_submit_sync_cmd(nvmeq, &c, NULL, NVME_IO_TIMEOUT);
+ nvme_sc = nvme_submit_io_cmd(dev, &c, NULL);
res = nvme_trans_status_code(hdr, nvme_sc);
dma_free_coherent(&dev->pci_dev->dev, ndesc * sizeof(*range),
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 1386749b48ff..fbae63e3d304 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -408,7 +408,7 @@ config APPLICOM
config SONYPI
tristate "Sony Vaio Programmable I/O Control Device support"
- depends on X86 && PCI && INPUT && !64BIT
+ depends on X86_32 && PCI && INPUT
---help---
This driver enables access to the Sony Programmable I/O Control
Device which can be found in many (all ?) Sony Vaio laptops.
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 6928d094451d..60aafb8a1f2e 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -901,9 +901,9 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
if (len + offset > PAGE_SIZE)
len = PAGE_SIZE - offset;
- src = buf->ops->map(pipe, buf, 1);
+ src = kmap_atomic(buf->page);
memcpy(page_address(page) + offset, src + buf->offset, len);
- buf->ops->unmap(pipe, buf, src);
+ kunmap_atomic(src);
sg_set_page(&(sgl->sg[sgl->n]), page, len, offset);
}
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 1e2b9db563ec..0e9cce82844b 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -30,7 +30,7 @@ config ARM_EXYNOS_CPUFREQ
config ARM_EXYNOS4210_CPUFREQ
bool "SAMSUNG EXYNOS4210"
- depends on CPU_EXYNOS4210
+ depends on CPU_EXYNOS4210 && !ARCH_MULTIPLATFORM
default y
select ARM_EXYNOS_CPUFREQ
help
@@ -41,7 +41,7 @@ config ARM_EXYNOS4210_CPUFREQ
config ARM_EXYNOS4X12_CPUFREQ
bool "SAMSUNG EXYNOS4x12"
- depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
+ depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM
default y
select ARM_EXYNOS_CPUFREQ
help
@@ -52,7 +52,7 @@ config ARM_EXYNOS4X12_CPUFREQ
config ARM_EXYNOS5250_CPUFREQ
bool "SAMSUNG EXYNOS5250"
- depends on SOC_EXYNOS5250
+ depends on SOC_EXYNOS5250 && !ARCH_MULTIPLATFORM
default y
select ARM_EXYNOS_CPUFREQ
help
diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc
index ca0021a96e19..72564b701b4a 100644
--- a/drivers/cpufreq/Kconfig.powerpc
+++ b/drivers/cpufreq/Kconfig.powerpc
@@ -54,3 +54,11 @@ config PPC_PASEMI_CPUFREQ
help
This adds the support for frequency switching on PA Semi
PWRficient processors.
+
+config POWERNV_CPUFREQ
+ tristate "CPU frequency scaling for IBM POWERNV platform"
+ depends on PPC_POWERNV
+ default y
+ help
+ This adds support for CPU frequency switching on IBM POWERNV
+ platform
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 74945652dd7a..0dbb963c1aef 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o
obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o
obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o
obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o
+obj-$(CONFIG_POWERNV_CPUFREQ) += powernv-cpufreq.o
##################################################################################
# Other platform drivers
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index d5eaedbe464f..000e4e0afd7e 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -754,7 +754,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
goto err_unreg;
}
- data->freq_table = kmalloc(sizeof(*data->freq_table) *
+ data->freq_table = kzalloc(sizeof(*data->freq_table) *
(perf->state_count+1), GFP_KERNEL);
if (!data->freq_table) {
result = -ENOMEM;
diff --git a/drivers/cpufreq/at32ap-cpufreq.c b/drivers/cpufreq/at32ap-cpufreq.c
index a1c79f549edb..7b612c8bb09e 100644
--- a/drivers/cpufreq/at32ap-cpufreq.c
+++ b/drivers/cpufreq/at32ap-cpufreq.c
@@ -52,7 +52,7 @@ static int at32_set_target(struct cpufreq_policy *policy, unsigned int index)
static int at32_cpufreq_driver_init(struct cpufreq_policy *policy)
{
unsigned int frequency, rate, min_freq;
- static struct clk *cpuclk;
+ struct clk *cpuclk;
int retval, steps, i;
if (policy->cpu != 0)
diff --git a/drivers/cpufreq/cris-artpec3-cpufreq.c b/drivers/cpufreq/cris-artpec3-cpufreq.c
index d4573032cbbc..601b88c490cf 100644
--- a/drivers/cpufreq/cris-artpec3-cpufreq.c
+++ b/drivers/cpufreq/cris-artpec3-cpufreq.c
@@ -15,9 +15,9 @@ static struct notifier_block cris_sdram_freq_notifier_block = {
};
static struct cpufreq_frequency_table cris_freq_table[] = {
- {0x01, 6000},
- {0x02, 200000},
- {0, CPUFREQ_TABLE_END},
+ {0, 0x01, 6000},
+ {0, 0x02, 200000},
+ {0, 0, CPUFREQ_TABLE_END},
};
static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu)
diff --git a/drivers/cpufreq/cris-etraxfs-cpufreq.c b/drivers/cpufreq/cris-etraxfs-cpufreq.c
index 13c3361437f7..22b2cdde74d9 100644
--- a/drivers/cpufreq/cris-etraxfs-cpufreq.c
+++ b/drivers/cpufreq/cris-etraxfs-cpufreq.c
@@ -15,9 +15,9 @@ static struct notifier_block cris_sdram_freq_notifier_block = {
};
static struct cpufreq_frequency_table cris_freq_table[] = {
- {0x01, 6000},
- {0x02, 200000},
- {0, CPUFREQ_TABLE_END},
+ {0, 0x01, 6000},
+ {0, 0x02, 200000},
+ {0, 0, CPUFREQ_TABLE_END},
};
static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu)
diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c
index c987e94708f5..7f5d2a68c353 100644
--- a/drivers/cpufreq/elanfreq.c
+++ b/drivers/cpufreq/elanfreq.c
@@ -56,15 +56,15 @@ static struct s_elan_multiplier elan_multiplier[] = {
};
static struct cpufreq_frequency_table elanfreq_table[] = {
- {0, 1000},
- {1, 2000},
- {2, 4000},
- {3, 8000},
- {4, 16000},
- {5, 33000},
- {6, 66000},
- {7, 99000},
- {0, CPUFREQ_TABLE_END},
+ {0, 0, 1000},
+ {0, 1, 2000},
+ {0, 2, 4000},
+ {0, 3, 8000},
+ {0, 4, 16000},
+ {0, 5, 33000},
+ {0, 6, 66000},
+ {0, 7, 99000},
+ {0, 0, CPUFREQ_TABLE_END},
};
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c
index 40d84c43d8f4..6384e5b9a347 100644
--- a/drivers/cpufreq/exynos4210-cpufreq.c
+++ b/drivers/cpufreq/exynos4210-cpufreq.c
@@ -29,12 +29,12 @@ static unsigned int exynos4210_volt_table[] = {
};
static struct cpufreq_frequency_table exynos4210_freq_table[] = {
- {L0, 1200 * 1000},
- {L1, 1000 * 1000},
- {L2, 800 * 1000},
- {L3, 500 * 1000},
- {L4, 200 * 1000},
- {0, CPUFREQ_TABLE_END},
+ {0, L0, 1200 * 1000},
+ {0, L1, 1000 * 1000},
+ {0, L2, 800 * 1000},
+ {0, L3, 500 * 1000},
+ {0, L4, 200 * 1000},
+ {0, 0, CPUFREQ_TABLE_END},
};
static struct apll_freq apll_freq_4210[] = {
diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c
index 7c11ace3b3fc..466c76ad335b 100644
--- a/drivers/cpufreq/exynos4x12-cpufreq.c
+++ b/drivers/cpufreq/exynos4x12-cpufreq.c
@@ -30,21 +30,21 @@ static unsigned int exynos4x12_volt_table[] = {
};
static struct cpufreq_frequency_table exynos4x12_freq_table[] = {
- {CPUFREQ_BOOST_FREQ, 1500 * 1000},
- {L1, 1400 * 1000},
- {L2, 1300 * 1000},
- {L3, 1200 * 1000},
- {L4, 1100 * 1000},
- {L5, 1000 * 1000},
- {L6, 900 * 1000},
- {L7, 800 * 1000},
- {L8, 700 * 1000},
- {L9, 600 * 1000},
- {L10, 500 * 1000},
- {L11, 400 * 1000},
- {L12, 300 * 1000},
- {L13, 200 * 1000},
- {0, CPUFREQ_TABLE_END},
+ {CPUFREQ_BOOST_FREQ, L0, 1500 * 1000},
+ {0, L1, 1400 * 1000},
+ {0, L2, 1300 * 1000},
+ {0, L3, 1200 * 1000},
+ {0, L4, 1100 * 1000},
+ {0, L5, 1000 * 1000},
+ {0, L6, 900 * 1000},
+ {0, L7, 800 * 1000},
+ {0, L8, 700 * 1000},
+ {0, L9, 600 * 1000},
+ {0, L10, 500 * 1000},
+ {0, L11, 400 * 1000},
+ {0, L12, 300 * 1000},
+ {0, L13, 200 * 1000},
+ {0, 0, CPUFREQ_TABLE_END},
};
static struct apll_freq *apll_freq_4x12;
diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c
index 5f90b82a4082..363a0b3fe1b1 100644
--- a/drivers/cpufreq/exynos5250-cpufreq.c
+++ b/drivers/cpufreq/exynos5250-cpufreq.c
@@ -34,23 +34,23 @@ static unsigned int exynos5250_volt_table[] = {
};
static struct cpufreq_frequency_table exynos5250_freq_table[] = {
- {L0, 1700 * 1000},
- {L1, 1600 * 1000},
- {L2, 1500 * 1000},
- {L3, 1400 * 1000},
- {L4, 1300 * 1000},
- {L5, 1200 * 1000},
- {L6, 1100 * 1000},
- {L7, 1000 * 1000},
- {L8, 900 * 1000},
- {L9, 800 * 1000},
- {L10, 700 * 1000},
- {L11, 600 * 1000},
- {L12, 500 * 1000},
- {L13, 400 * 1000},
- {L14, 300 * 1000},
- {L15, 200 * 1000},
- {0, CPUFREQ_TABLE_END},
+ {0, L0, 1700 * 1000},
+ {0, L1, 1600 * 1000},
+ {0, L2, 1500 * 1000},
+ {0, L3, 1400 * 1000},
+ {0, L4, 1300 * 1000},
+ {0, L5, 1200 * 1000},
+ {0, L6, 1100 * 1000},
+ {0, L7, 1000 * 1000},
+ {0, L8, 900 * 1000},
+ {0, L9, 800 * 1000},
+ {0, L10, 700 * 1000},
+ {0, L11, 600 * 1000},
+ {0, L12, 500 * 1000},
+ {0, L13, 400 * 1000},
+ {0, L14, 300 * 1000},
+ {0, L15, 200 * 1000},
+ {0, 0, CPUFREQ_TABLE_END},
};
static struct apll_freq apll_freq_5250[] = {
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
index 65a477075b3f..08e7bbcf6d73 100644
--- a/drivers/cpufreq/freq_table.c
+++ b/drivers/cpufreq/freq_table.c
@@ -33,11 +33,10 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
continue;
}
if (!cpufreq_boost_enabled()
- && table[i].driver_data == CPUFREQ_BOOST_FREQ)
+ && (table[i].flags & CPUFREQ_BOOST_FREQ))
continue;
- pr_debug("table entry %u: %u kHz, %u driver_data\n",
- i, freq, table[i].driver_data);
+ pr_debug("table entry %u: %u kHz\n", i, freq);
if (freq < min_freq)
min_freq = freq;
if (freq > max_freq)
@@ -175,8 +174,8 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
} else
*index = optimal.driver_data;
- pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency,
- table[*index].driver_data);
+ pr_debug("target index is %u, freq is:%u kHz\n", *index,
+ table[*index].frequency);
return 0;
}
@@ -230,7 +229,7 @@ static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
* show_boost = false and driver_data != BOOST freq
* display NON BOOST freqs
*/
- if (show_boost ^ (table[i].driver_data == CPUFREQ_BOOST_FREQ))
+ if (show_boost ^ (table[i].flags & CPUFREQ_BOOST_FREQ))
continue;
count += sprintf(&buf[count], "%d ", table[i].frequency);
diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c
index a22b5d182e0e..c30aaa6a54e8 100644
--- a/drivers/cpufreq/ia64-acpi-cpufreq.c
+++ b/drivers/cpufreq/ia64-acpi-cpufreq.c
@@ -254,7 +254,7 @@ acpi_cpufreq_cpu_init (
}
/* alloc freq_table */
- data->freq_table = kmalloc(sizeof(*data->freq_table) *
+ data->freq_table = kzalloc(sizeof(*data->freq_table) *
(data->acpi_data.state_count + 1),
GFP_KERNEL);
if (!data->freq_table) {
@@ -275,7 +275,6 @@ acpi_cpufreq_cpu_init (
/* table init */
for (i = 0; i <= data->acpi_data.state_count; i++)
{
- data->freq_table[i].driver_data = i;
if (i < data->acpi_data.state_count) {
data->freq_table[i].frequency =
data->acpi_data.states[i].core_frequency * 1000;
diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c
index 3d114bc5a97a..37a480680cd0 100644
--- a/drivers/cpufreq/kirkwood-cpufreq.c
+++ b/drivers/cpufreq/kirkwood-cpufreq.c
@@ -43,9 +43,9 @@ static struct priv
* table.
*/
static struct cpufreq_frequency_table kirkwood_freq_table[] = {
- {STATE_CPU_FREQ, 0}, /* CPU uses cpuclk */
- {STATE_DDR_FREQ, 0}, /* CPU uses ddrclk */
- {0, CPUFREQ_TABLE_END},
+ {0, STATE_CPU_FREQ, 0}, /* CPU uses cpuclk */
+ {0, STATE_DDR_FREQ, 0}, /* CPU uses ddrclk */
+ {0, 0, CPUFREQ_TABLE_END},
};
static unsigned int kirkwood_cpufreq_get_cpu_frequency(unsigned int cpu)
diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c
index 5c440f87ba8a..d00e5d1abd25 100644
--- a/drivers/cpufreq/longhaul.c
+++ b/drivers/cpufreq/longhaul.c
@@ -475,7 +475,7 @@ static int longhaul_get_ranges(void)
return -EINVAL;
}
- longhaul_table = kmalloc((numscales + 1) * sizeof(*longhaul_table),
+ longhaul_table = kzalloc((numscales + 1) * sizeof(*longhaul_table),
GFP_KERNEL);
if (!longhaul_table)
return -ENOMEM;
diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c
index a3588d61d933..f0bc31f5db27 100644
--- a/drivers/cpufreq/loongson2_cpufreq.c
+++ b/drivers/cpufreq/loongson2_cpufreq.c
@@ -69,7 +69,7 @@ static int loongson2_cpufreq_target(struct cpufreq_policy *policy,
static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
- static struct clk *cpuclk;
+ struct clk *cpuclk;
int i;
unsigned long rate;
int ret;
diff --git a/drivers/cpufreq/maple-cpufreq.c b/drivers/cpufreq/maple-cpufreq.c
index c4dfa42a75ac..cc3408fc073f 100644
--- a/drivers/cpufreq/maple-cpufreq.c
+++ b/drivers/cpufreq/maple-cpufreq.c
@@ -59,9 +59,9 @@
#define CPUFREQ_LOW 1
static struct cpufreq_frequency_table maple_cpu_freqs[] = {
- {CPUFREQ_HIGH, 0},
- {CPUFREQ_LOW, 0},
- {0, CPUFREQ_TABLE_END},
+ {0, CPUFREQ_HIGH, 0},
+ {0, CPUFREQ_LOW, 0},
+ {0, 0, CPUFREQ_TABLE_END},
};
/* Power mode data is an array of the 32 bits PCR values to use for
diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c
index 74f593e70e19..529cfd92158f 100644
--- a/drivers/cpufreq/p4-clockmod.c
+++ b/drivers/cpufreq/p4-clockmod.c
@@ -92,16 +92,16 @@ static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate)
static struct cpufreq_frequency_table p4clockmod_table[] = {
- {DC_RESV, CPUFREQ_ENTRY_INVALID},
- {DC_DFLT, 0},
- {DC_25PT, 0},
- {DC_38PT, 0},
- {DC_50PT, 0},
- {DC_64PT, 0},
- {DC_75PT, 0},
- {DC_88PT, 0},
- {DC_DISABLE, 0},
- {DC_RESV, CPUFREQ_TABLE_END},
+ {0, DC_RESV, CPUFREQ_ENTRY_INVALID},
+ {0, DC_DFLT, 0},
+ {0, DC_25PT, 0},
+ {0, DC_38PT, 0},
+ {0, DC_50PT, 0},
+ {0, DC_64PT, 0},
+ {0, DC_75PT, 0},
+ {0, DC_88PT, 0},
+ {0, DC_DISABLE, 0},
+ {0, DC_RESV, CPUFREQ_TABLE_END},
};
diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c
index 6a2b7d3e85a7..84c84b5f0f3a 100644
--- a/drivers/cpufreq/pasemi-cpufreq.c
+++ b/drivers/cpufreq/pasemi-cpufreq.c
@@ -60,12 +60,12 @@ static int current_astate;
/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */
static struct cpufreq_frequency_table pas_freqs[] = {
- {0, 0},
- {1, 0},
- {2, 0},
- {3, 0},
- {4, 0},
- {0, CPUFREQ_TABLE_END},
+ {0, 0, 0},
+ {0, 1, 0},
+ {0, 2, 0},
+ {0, 3, 0},
+ {0, 4, 0},
+ {0, 0, CPUFREQ_TABLE_END},
};
/*
diff --git a/drivers/cpufreq/pmac32-cpufreq.c b/drivers/cpufreq/pmac32-cpufreq.c
index cf55d202f332..7615180d7ee3 100644
--- a/drivers/cpufreq/pmac32-cpufreq.c
+++ b/drivers/cpufreq/pmac32-cpufreq.c
@@ -81,9 +81,9 @@ static int is_pmu_based;
#define CPUFREQ_LOW 1
static struct cpufreq_frequency_table pmac_cpu_freqs[] = {
- {CPUFREQ_HIGH, 0},
- {CPUFREQ_LOW, 0},
- {0, CPUFREQ_TABLE_END},
+ {0, CPUFREQ_HIGH, 0},
+ {0, CPUFREQ_LOW, 0},
+ {0, 0, CPUFREQ_TABLE_END},
};
static inline void local_delay(unsigned long ms)
diff --git a/drivers/cpufreq/pmac64-cpufreq.c b/drivers/cpufreq/pmac64-cpufreq.c
index 6a338f8c3860..8bc422977b5b 100644
--- a/drivers/cpufreq/pmac64-cpufreq.c
+++ b/drivers/cpufreq/pmac64-cpufreq.c
@@ -65,9 +65,9 @@
#define CPUFREQ_LOW 1
static struct cpufreq_frequency_table g5_cpu_freqs[] = {
- {CPUFREQ_HIGH, 0},
- {CPUFREQ_LOW, 0},
- {0, CPUFREQ_TABLE_END},
+ {0, CPUFREQ_HIGH, 0},
+ {0, CPUFREQ_LOW, 0},
+ {0, 0, CPUFREQ_TABLE_END},
};
/* Power mode data is an array of the 32 bits PCR values to use for
diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c
index 62c6f2e5afce..49f120e1bc7b 100644
--- a/drivers/cpufreq/powernow-k6.c
+++ b/drivers/cpufreq/powernow-k6.c
@@ -37,15 +37,15 @@ MODULE_PARM_DESC(bus_frequency, "Bus frequency in kHz");
/* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
static struct cpufreq_frequency_table clock_ratio[] = {
- {60, /* 110 -> 6.0x */ 0},
- {55, /* 011 -> 5.5x */ 0},
- {50, /* 001 -> 5.0x */ 0},
- {45, /* 000 -> 4.5x */ 0},
- {40, /* 010 -> 4.0x */ 0},
- {35, /* 111 -> 3.5x */ 0},
- {30, /* 101 -> 3.0x */ 0},
- {20, /* 100 -> 2.0x */ 0},
- {0, CPUFREQ_TABLE_END}
+ {0, 60, /* 110 -> 6.0x */ 0},
+ {0, 55, /* 011 -> 5.5x */ 0},
+ {0, 50, /* 001 -> 5.0x */ 0},
+ {0, 45, /* 000 -> 4.5x */ 0},
+ {0, 40, /* 010 -> 4.0x */ 0},
+ {0, 35, /* 111 -> 3.5x */ 0},
+ {0, 30, /* 101 -> 3.0x */ 0},
+ {0, 20, /* 100 -> 2.0x */ 0},
+ {0, 0, CPUFREQ_TABLE_END}
};
static const u8 index_to_register[8] = { 6, 3, 1, 0, 2, 7, 5, 4 };
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c
index 770a9e1b3468..1b6ae6b57c11 100644
--- a/drivers/cpufreq/powernow-k8.c
+++ b/drivers/cpufreq/powernow-k8.c
@@ -623,7 +623,7 @@ static int fill_powernow_table(struct powernow_k8_data *data,
if (check_pst_table(data, pst, maxvid))
return -EINVAL;
- powernow_table = kmalloc((sizeof(*powernow_table)
+ powernow_table = kzalloc((sizeof(*powernow_table)
* (data->numps + 1)), GFP_KERNEL);
if (!powernow_table) {
printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
@@ -793,7 +793,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
}
/* fill in data->powernow_table */
- powernow_table = kmalloc((sizeof(*powernow_table)
+ powernow_table = kzalloc((sizeof(*powernow_table)
* (data->acpi_data.state_count + 1)), GFP_KERNEL);
if (!powernow_table) {
pr_debug("powernow_table memory alloc failure\n");
@@ -810,7 +810,6 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
powernow_table[data->acpi_data.state_count].frequency =
CPUFREQ_TABLE_END;
- powernow_table[data->acpi_data.state_count].driver_data = 0;
data->powernow_table = powernow_table;
if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu)
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c
new file mode 100644
index 000000000000..9edccc63245d
--- /dev/null
+++ b/drivers/cpufreq/powernv-cpufreq.c
@@ -0,0 +1,341 @@
+/*
+ * POWERNV cpufreq driver for the IBM POWER processors
+ *
+ * (C) Copyright IBM 2014
+ *
+ * Author: Vaidyanathan Srinivasan <svaidy at linux.vnet.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
+ * the Free Software Foundation; either version 2, 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) "powernv-cpufreq: " fmt
+
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/cpumask.h>
+#include <linux/module.h>
+#include <linux/cpufreq.h>
+#include <linux/smp.h>
+#include <linux/of.h>
+
+#include <asm/cputhreads.h>
+#include <asm/reg.h>
+
+#define POWERNV_MAX_PSTATES 256
+
+static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1];
+
+/*
+ * Note: The set of pstates consists of contiguous integers, the
+ * smallest of which is indicated by powernv_pstate_info.min, the
+ * largest of which is indicated by powernv_pstate_info.max.
+ *
+ * The nominal pstate is the highest non-turbo pstate in this
+ * platform. This is indicated by powernv_pstate_info.nominal.
+ */
+static struct powernv_pstate_info {
+ int min;
+ int max;
+ int nominal;
+ int nr_pstates;
+} powernv_pstate_info;
+
+/*
+ * Initialize the freq table based on data obtained
+ * from the firmware passed via device-tree
+ */
+static int init_powernv_pstates(void)
+{
+ struct device_node *power_mgt;
+ int i, pstate_min, pstate_max, pstate_nominal, nr_pstates = 0;
+ const __be32 *pstate_ids, *pstate_freqs;
+ u32 len_ids, len_freqs;
+
+ power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
+ if (!power_mgt) {
+ pr_warn("power-mgt node not found\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(power_mgt, "ibm,pstate-min", &pstate_min)) {
+ pr_warn("ibm,pstate-min node not found\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(power_mgt, "ibm,pstate-max", &pstate_max)) {
+ pr_warn("ibm,pstate-max node not found\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(power_mgt, "ibm,pstate-nominal",
+ &pstate_nominal)) {
+ pr_warn("ibm,pstate-nominal not found\n");
+ return -ENODEV;
+ }
+ pr_info("cpufreq pstate min %d nominal %d max %d\n", pstate_min,
+ pstate_nominal, pstate_max);
+
+ pstate_ids = of_get_property(power_mgt, "ibm,pstate-ids", &len_ids);
+ if (!pstate_ids) {
+ pr_warn("ibm,pstate-ids not found\n");
+ return -ENODEV;
+ }
+
+ pstate_freqs = of_get_property(power_mgt, "ibm,pstate-frequencies-mhz",
+ &len_freqs);
+ if (!pstate_freqs) {
+ pr_warn("ibm,pstate-frequencies-mhz not found\n");
+ return -ENODEV;
+ }
+
+ WARN_ON(len_ids != len_freqs);
+ nr_pstates = min(len_ids, len_freqs) / sizeof(u32);
+ if (!nr_pstates) {
+ pr_warn("No PStates found\n");
+ return -ENODEV;
+ }
+
+ pr_debug("NR PStates %d\n", nr_pstates);
+ for (i = 0; i < nr_pstates; i++) {
+ u32 id = be32_to_cpu(pstate_ids[i]);
+ u32 freq = be32_to_cpu(pstate_freqs[i]);
+
+ pr_debug("PState id %d freq %d MHz\n", id, freq);
+ powernv_freqs[i].frequency = freq * 1000; /* kHz */
+ powernv_freqs[i].driver_data = id;
+ }
+ /* End of list marker entry */
+ powernv_freqs[i].frequency = CPUFREQ_TABLE_END;
+
+ powernv_pstate_info.min = pstate_min;
+ powernv_pstate_info.max = pstate_max;
+ powernv_pstate_info.nominal = pstate_nominal;
+ powernv_pstate_info.nr_pstates = nr_pstates;
+
+ return 0;
+}
+
+/* Returns the CPU frequency corresponding to the pstate_id. */
+static unsigned int pstate_id_to_freq(int pstate_id)
+{
+ int i;
+
+ i = powernv_pstate_info.max - pstate_id;
+ BUG_ON(i >= powernv_pstate_info.nr_pstates || i < 0);
+
+ return powernv_freqs[i].frequency;
+}
+
+/*
+ * cpuinfo_nominal_freq_show - Show the nominal CPU frequency as indicated by
+ * the firmware
+ */
+static ssize_t cpuinfo_nominal_freq_show(struct cpufreq_policy *policy,
+ char *buf)
+{
+ return sprintf(buf, "%u\n",
+ pstate_id_to_freq(powernv_pstate_info.nominal));
+}
+
+struct freq_attr cpufreq_freq_attr_cpuinfo_nominal_freq =
+ __ATTR_RO(cpuinfo_nominal_freq);
+
+static struct freq_attr *powernv_cpu_freq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ &cpufreq_freq_attr_cpuinfo_nominal_freq,
+ NULL,
+};
+
+/* Helper routines */
+
+/* Access helpers to power mgt SPR */
+
+static inline unsigned long get_pmspr(unsigned long sprn)
+{
+ switch (sprn) {
+ case SPRN_PMCR:
+ return mfspr(SPRN_PMCR);
+
+ case SPRN_PMICR:
+ return mfspr(SPRN_PMICR);
+
+ case SPRN_PMSR:
+ return mfspr(SPRN_PMSR);
+ }
+ BUG();
+}
+
+static inline void set_pmspr(unsigned long sprn, unsigned long val)
+{
+ switch (sprn) {
+ case SPRN_PMCR:
+ mtspr(SPRN_PMCR, val);
+ return;
+
+ case SPRN_PMICR:
+ mtspr(SPRN_PMICR, val);
+ return;
+ }
+ BUG();
+}
+
+/*
+ * Use objects of this type to query/update
+ * pstates on a remote CPU via smp_call_function.
+ */
+struct powernv_smp_call_data {
+ unsigned int freq;
+ int pstate_id;
+};
+
+/*
+ * powernv_read_cpu_freq: Reads the current frequency on this CPU.
+ *
+ * Called via smp_call_function.
+ *
+ * Note: The caller of the smp_call_function should pass an argument of
+ * the type 'struct powernv_smp_call_data *' along with this function.
+ *
+ * The current frequency on this CPU will be returned via
+ * ((struct powernv_smp_call_data *)arg)->freq;
+ */
+static void powernv_read_cpu_freq(void *arg)
+{
+ unsigned long pmspr_val;
+ s8 local_pstate_id;
+ struct powernv_smp_call_data *freq_data = arg;
+
+ pmspr_val = get_pmspr(SPRN_PMSR);
+
+ /*
+ * The local pstate id corresponds bits 48..55 in the PMSR.
+ * Note: Watch out for the sign!
+ */
+ local_pstate_id = (pmspr_val >> 48) & 0xFF;
+ freq_data->pstate_id = local_pstate_id;
+ freq_data->freq = pstate_id_to_freq(freq_data->pstate_id);
+
+ pr_debug("cpu %d pmsr %016lX pstate_id %d frequency %d kHz\n",
+ raw_smp_processor_id(), pmspr_val, freq_data->pstate_id,
+ freq_data->freq);
+}
+
+/*
+ * powernv_cpufreq_get: Returns the CPU frequency as reported by the
+ * firmware for CPU 'cpu'. This value is reported through the sysfs
+ * file cpuinfo_cur_freq.
+ */
+unsigned int powernv_cpufreq_get(unsigned int cpu)
+{
+ struct powernv_smp_call_data freq_data;
+
+ smp_call_function_any(cpu_sibling_mask(cpu), powernv_read_cpu_freq,
+ &freq_data, 1);
+
+ return freq_data.freq;
+}
+
+/*
+ * set_pstate: Sets the pstate on this CPU.
+ *
+ * This is called via an smp_call_function.
+ *
+ * The caller must ensure that freq_data is of the type
+ * (struct powernv_smp_call_data *) and the pstate_id which needs to be set
+ * on this CPU should be present in freq_data->pstate_id.
+ */
+static void set_pstate(void *freq_data)
+{
+ unsigned long val;
+ unsigned long pstate_ul =
+ ((struct powernv_smp_call_data *) freq_data)->pstate_id;
+
+ val = get_pmspr(SPRN_PMCR);
+ val = val & 0x0000FFFFFFFFFFFFULL;
+
+ pstate_ul = pstate_ul & 0xFF;
+
+ /* Set both global(bits 56..63) and local(bits 48..55) PStates */
+ val = val | (pstate_ul << 56) | (pstate_ul << 48);
+
+ pr_debug("Setting cpu %d pmcr to %016lX\n",
+ raw_smp_processor_id(), val);
+ set_pmspr(SPRN_PMCR, val);
+}
+
+/*
+ * powernv_cpufreq_target_index: Sets the frequency corresponding to
+ * the cpufreq table entry indexed by new_index on the cpus in the
+ * mask policy->cpus
+ */
+static int powernv_cpufreq_target_index(struct cpufreq_policy *policy,
+ unsigned int new_index)
+{
+ struct powernv_smp_call_data freq_data;
+
+ freq_data.pstate_id = powernv_freqs[new_index].driver_data;
+
+ /*
+ * Use smp_call_function to send IPI and execute the
+ * mtspr on target CPU. We could do that without IPI
+ * if current CPU is within policy->cpus (core)
+ */
+ smp_call_function_any(policy->cpus, set_pstate, &freq_data, 1);
+
+ return 0;
+}
+
+static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+ int base, i;
+
+ base = cpu_first_thread_sibling(policy->cpu);
+
+ for (i = 0; i < threads_per_core; i++)
+ cpumask_set_cpu(base + i, policy->cpus);
+
+ return cpufreq_table_validate_and_show(policy, powernv_freqs);
+}
+
+static struct cpufreq_driver powernv_cpufreq_driver = {
+ .name = "powernv-cpufreq",
+ .flags = CPUFREQ_CONST_LOOPS,
+ .init = powernv_cpufreq_cpu_init,
+ .verify = cpufreq_generic_frequency_table_verify,
+ .target_index = powernv_cpufreq_target_index,
+ .get = powernv_cpufreq_get,
+ .attr = powernv_cpu_freq_attr,
+};
+
+static int __init powernv_cpufreq_init(void)
+{
+ int rc = 0;
+
+ /* Discover pstates from device tree and init */
+ rc = init_powernv_pstates();
+ if (rc) {
+ pr_info("powernv-cpufreq disabled. System does not support PState control\n");
+ return rc;
+ }
+
+ return cpufreq_register_driver(&powernv_cpufreq_driver);
+}
+module_init(powernv_cpufreq_init);
+
+static void __exit powernv_cpufreq_exit(void)
+{
+ cpufreq_unregister_driver(&powernv_cpufreq_driver);
+}
+module_exit(powernv_cpufreq_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vaidyanathan Srinivasan <svaidy at linux.vnet.ibm.com>");
diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c
index 3bd9123e7026..b7e677be1df0 100644
--- a/drivers/cpufreq/ppc-corenet-cpufreq.c
+++ b/drivers/cpufreq/ppc-corenet-cpufreq.c
@@ -13,7 +13,6 @@
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/errno.h>
-#include <sysdev/fsl_soc.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c
index af7b1cabd1e7..5be8a48dba74 100644
--- a/drivers/cpufreq/ppc_cbe_cpufreq.c
+++ b/drivers/cpufreq/ppc_cbe_cpufreq.c
@@ -32,15 +32,15 @@
/* the CBE supports an 8 step frequency scaling */
static struct cpufreq_frequency_table cbe_freqs[] = {
- {1, 0},
- {2, 0},
- {3, 0},
- {4, 0},
- {5, 0},
- {6, 0},
- {8, 0},
- {10, 0},
- {0, CPUFREQ_TABLE_END},
+ {0, 1, 0},
+ {0, 2, 0},
+ {0, 3, 0},
+ {0, 4, 0},
+ {0, 5, 0},
+ {0, 6, 0},
+ {0, 8, 0},
+ {0, 10, 0},
+ {0, 0, CPUFREQ_TABLE_END},
};
/*
diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c
index 826b8be23099..4626f90559b5 100644
--- a/drivers/cpufreq/s3c2416-cpufreq.c
+++ b/drivers/cpufreq/s3c2416-cpufreq.c
@@ -72,19 +72,19 @@ static struct s3c2416_dvfs s3c2416_dvfs_table[] = {
#endif
static struct cpufreq_frequency_table s3c2416_freq_table[] = {
- { SOURCE_HCLK, FREQ_DVS },
- { SOURCE_ARMDIV, 133333 },
- { SOURCE_ARMDIV, 266666 },
- { SOURCE_ARMDIV, 400000 },
- { 0, CPUFREQ_TABLE_END },
+ { 0, SOURCE_HCLK, FREQ_DVS },
+ { 0, SOURCE_ARMDIV, 133333 },
+ { 0, SOURCE_ARMDIV, 266666 },
+ { 0, SOURCE_ARMDIV, 400000 },
+ { 0, 0, CPUFREQ_TABLE_END },
};
static struct cpufreq_frequency_table s3c2450_freq_table[] = {
- { SOURCE_HCLK, FREQ_DVS },
- { SOURCE_ARMDIV, 133500 },
- { SOURCE_ARMDIV, 267000 },
- { SOURCE_ARMDIV, 534000 },
- { 0, CPUFREQ_TABLE_END },
+ { 0, SOURCE_HCLK, FREQ_DVS },
+ { 0, SOURCE_ARMDIV, 133500 },
+ { 0, SOURCE_ARMDIV, 267000 },
+ { 0, SOURCE_ARMDIV, 534000 },
+ { 0, 0, CPUFREQ_TABLE_END },
};
static unsigned int s3c2416_cpufreq_get_speed(unsigned int cpu)
diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c
index a3dc192d21f9..be1b2b5c9753 100644
--- a/drivers/cpufreq/s3c24xx-cpufreq.c
+++ b/drivers/cpufreq/s3c24xx-cpufreq.c
@@ -586,7 +586,7 @@ static int s3c_cpufreq_build_freq(void)
size = cpu_cur.info->calc_freqtable(&cpu_cur, NULL, 0);
size++;
- ftab = kmalloc(sizeof(*ftab) * size, GFP_KERNEL);
+ ftab = kzalloc(sizeof(*ftab) * size, GFP_KERNEL);
if (!ftab) {
printk(KERN_ERR "%s: no memory for tables\n", __func__);
return -ENOMEM;
@@ -664,7 +664,7 @@ int __init s3c_plltab_register(struct cpufreq_frequency_table *plls,
size = sizeof(*vals) * (plls_no + 1);
- vals = kmalloc(size, GFP_KERNEL);
+ vals = kzalloc(size, GFP_KERNEL);
if (vals) {
memcpy(vals, plls, size);
pll_reg = vals;
diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c
index c4226de079ab..ff7d3ecb85f0 100644
--- a/drivers/cpufreq/s3c64xx-cpufreq.c
+++ b/drivers/cpufreq/s3c64xx-cpufreq.c
@@ -37,19 +37,19 @@ static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = {
};
static struct cpufreq_frequency_table s3c64xx_freq_table[] = {
- { 0, 66000 },
- { 0, 100000 },
- { 0, 133000 },
- { 1, 200000 },
- { 1, 222000 },
- { 1, 266000 },
- { 2, 333000 },
- { 2, 400000 },
- { 2, 532000 },
- { 2, 533000 },
- { 3, 667000 },
- { 4, 800000 },
- { 0, CPUFREQ_TABLE_END },
+ { 0, 0, 66000 },
+ { 0, 0, 100000 },
+ { 0, 0, 133000 },
+ { 0, 1, 200000 },
+ { 0, 1, 222000 },
+ { 0, 1, 266000 },
+ { 0, 2, 333000 },
+ { 0, 2, 400000 },
+ { 0, 2, 532000 },
+ { 0, 2, 533000 },
+ { 0, 3, 667000 },
+ { 0, 4, 800000 },
+ { 0, 0, CPUFREQ_TABLE_END },
};
#endif
diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c
index 72421534fff5..ab2c1a40d437 100644
--- a/drivers/cpufreq/s5pv210-cpufreq.c
+++ b/drivers/cpufreq/s5pv210-cpufreq.c
@@ -64,12 +64,12 @@ enum s5pv210_dmc_port {
};
static struct cpufreq_frequency_table s5pv210_freq_table[] = {
- {L0, 1000*1000},
- {L1, 800*1000},
- {L2, 400*1000},
- {L3, 200*1000},
- {L4, 100*1000},
- {0, CPUFREQ_TABLE_END},
+ {0, L0, 1000*1000},
+ {0, L1, 800*1000},
+ {0, L2, 400*1000},
+ {0, L3, 200*1000},
+ {0, L4, 100*1000},
+ {0, 0, CPUFREQ_TABLE_END},
};
static struct regulator *arm_regulator;
diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c
index 69371bf0886d..ac84e4818014 100644
--- a/drivers/cpufreq/sc520_freq.c
+++ b/drivers/cpufreq/sc520_freq.c
@@ -33,9 +33,9 @@ static __u8 __iomem *cpuctl;
#define PFX "sc520_freq: "
static struct cpufreq_frequency_table sc520_freq_table[] = {
- {0x01, 100000},
- {0x02, 133000},
- {0, CPUFREQ_TABLE_END},
+ {0, 0x01, 100000},
+ {0, 0x02, 133000},
+ {0, 0, CPUFREQ_TABLE_END},
};
static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c
index 4cfdcff8a310..38678396636d 100644
--- a/drivers/cpufreq/spear-cpufreq.c
+++ b/drivers/cpufreq/spear-cpufreq.c
@@ -195,18 +195,15 @@ static int spear_cpufreq_probe(struct platform_device *pdev)
cnt = prop->length / sizeof(u32);
val = prop->value;
- freq_tbl = kmalloc(sizeof(*freq_tbl) * (cnt + 1), GFP_KERNEL);
+ freq_tbl = kzalloc(sizeof(*freq_tbl) * (cnt + 1), GFP_KERNEL);
if (!freq_tbl) {
ret = -ENOMEM;
goto out_put_node;
}
- for (i = 0; i < cnt; i++) {
- freq_tbl[i].driver_data = i;
+ for (i = 0; i < cnt; i++)
freq_tbl[i].frequency = be32_to_cpup(val++);
- }
- freq_tbl[i].driver_data = i;
freq_tbl[i].frequency = CPUFREQ_TABLE_END;
spear_cpufreq.freq_tbl = freq_tbl;
diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c
index 394ac159312a..1a07b5904ed5 100644
--- a/drivers/cpufreq/speedstep-ich.c
+++ b/drivers/cpufreq/speedstep-ich.c
@@ -49,9 +49,9 @@ static u32 pmbase;
* are in kHz for the time being.
*/
static struct cpufreq_frequency_table speedstep_freqs[] = {
- {SPEEDSTEP_HIGH, 0},
- {SPEEDSTEP_LOW, 0},
- {0, CPUFREQ_TABLE_END},
+ {0, SPEEDSTEP_HIGH, 0},
+ {0, SPEEDSTEP_LOW, 0},
+ {0, 0, CPUFREQ_TABLE_END},
};
diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c
index db5d274dc13a..8635eec96da5 100644
--- a/drivers/cpufreq/speedstep-smi.c
+++ b/drivers/cpufreq/speedstep-smi.c
@@ -42,9 +42,9 @@ static enum speedstep_processor speedstep_processor;
* are in kHz for the time being.
*/
static struct cpufreq_frequency_table speedstep_freqs[] = {
- {SPEEDSTEP_HIGH, 0},
- {SPEEDSTEP_LOW, 0},
- {0, CPUFREQ_TABLE_END},
+ {0, SPEEDSTEP_HIGH, 0},
+ {0, SPEEDSTEP_LOW, 0},
+ {0, 0, CPUFREQ_TABLE_END},
};
#define GET_SPEEDSTEP_OWNER 0
diff --git a/drivers/cpufreq/unicore2-cpufreq.c b/drivers/cpufreq/unicore2-cpufreq.c
index 13be802b6170..8d045afa7fb4 100644
--- a/drivers/cpufreq/unicore2-cpufreq.c
+++ b/drivers/cpufreq/unicore2-cpufreq.c
@@ -45,7 +45,7 @@ static int ucv2_target(struct cpufreq_policy *policy,
freqs.new = target_freq;
cpufreq_freq_transition_begin(policy, &freqs);
- ret = clk_set_rate(policy->mclk, target_freq * 1000);
+ ret = clk_set_rate(policy->clk, target_freq * 1000);
cpufreq_freq_transition_end(policy, &freqs, ret);
return ret;
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index e918b6d0caf7..efe2f175168f 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -293,6 +293,7 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \
}
define_show_state_function(exit_latency)
+define_show_state_function(target_residency)
define_show_state_function(power_usage)
define_show_state_ull_function(usage)
define_show_state_ull_function(time)
@@ -304,6 +305,7 @@ define_store_state_ull_function(disable)
define_one_state_ro(name, show_state_name);
define_one_state_ro(desc, show_state_desc);
define_one_state_ro(latency, show_state_exit_latency);
+define_one_state_ro(residency, show_state_target_residency);
define_one_state_ro(power, show_state_power_usage);
define_one_state_ro(usage, show_state_usage);
define_one_state_ro(time, show_state_time);
@@ -313,6 +315,7 @@ static struct attribute *cpuidle_state_default_attrs[] = {
&attr_name.attr,
&attr_desc.attr,
&attr_latency.attr,
+ &attr_residency.attr,
&attr_power.attr,
&attr_usage.attr,
&attr_time.attr,
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 605b016bcea4..ba06d1d2f99e 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -308,7 +308,7 @@ config DMA_OMAP
config DMA_BCM2835
tristate "BCM2835 DMA engine support"
- depends on (ARCH_BCM2835 || MACH_BCM2708)
+ depends on ARCH_BCM2835
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
@@ -350,6 +350,16 @@ config MOXART_DMA
select DMA_VIRTUAL_CHANNELS
help
Enable support for the MOXA ART SoC DMA controller.
+
+config FSL_EDMA
+ tristate "Freescale eDMA engine support"
+ depends on OF
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Support the Freescale eDMA engine with programmable channel
+ multiplexing capability for DMA request sources(slot).
+ This module can be found on Freescale Vybrid and LS-1 SoCs.
config DMA_ENGINE
bool
@@ -401,4 +411,13 @@ config DMATEST
config DMA_ENGINE_RAID
bool
+config QCOM_BAM_DMA
+ tristate "QCOM BAM DMA support"
+ depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ ---help---
+ Enable support for the QCOM BAM DMA controller. This controller
+ provides DMA capabilities for a variety of on-chip devices.
+
endif
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index a029d0f4a1be..5150c82c9caf 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -44,3 +44,5 @@ obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
obj-$(CONFIG_TI_CPPI41) += cppi41.o
obj-$(CONFIG_K3_DMA) += k3dma.o
obj-$(CONFIG_MOXART_DMA) += moxart-dma.o
+obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
+obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o
diff --git a/drivers/dma/acpi-dma.c b/drivers/dma/acpi-dma.c
index 1e506afa33f5..de361a156b34 100644
--- a/drivers/dma/acpi-dma.c
+++ b/drivers/dma/acpi-dma.c
@@ -13,6 +13,7 @@
*/
#include <linux/device.h>
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/mutex.h>
@@ -265,7 +266,7 @@ EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_register);
*/
void devm_acpi_dma_controller_free(struct device *dev)
{
- WARN_ON(devres_destroy(dev, devm_acpi_dma_release, NULL, NULL));
+ WARN_ON(devres_release(dev, devm_acpi_dma_release, NULL, NULL));
}
EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_free);
@@ -343,7 +344,7 @@ static int acpi_dma_parse_fixed_dma(struct acpi_resource *res, void *data)
* @index: index of FixedDMA descriptor for @dev
*
* Return:
- * Pointer to appropriate dma channel on success or NULL on error.
+ * Pointer to appropriate dma channel on success or an error pointer.
*/
struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev,
size_t index)
@@ -358,10 +359,10 @@ struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev,
/* Check if the device was enumerated by ACPI */
if (!dev || !ACPI_HANDLE(dev))
- return NULL;
+ return ERR_PTR(-ENODEV);
if (acpi_bus_get_device(ACPI_HANDLE(dev), &adev))
- return NULL;
+ return ERR_PTR(-ENODEV);
memset(&pdata, 0, sizeof(pdata));
pdata.index = index;
@@ -376,7 +377,7 @@ struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev,
acpi_dev_free_resource_list(&resource_list);
if (dma_spec->slave_id < 0 || dma_spec->chan_id < 0)
- return NULL;
+ return ERR_PTR(-ENODEV);
mutex_lock(&acpi_dma_lock);
@@ -399,7 +400,7 @@ struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev,
}
mutex_unlock(&acpi_dma_lock);
- return chan;
+ return chan ? chan : ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_index);
@@ -413,7 +414,7 @@ EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_index);
* the first FixedDMA descriptor is TX and second is RX.
*
* Return:
- * Pointer to appropriate dma channel on success or NULL on error.
+ * Pointer to appropriate dma channel on success or an error pointer.
*/
struct dma_chan *acpi_dma_request_slave_chan_by_name(struct device *dev,
const char *name)
@@ -425,7 +426,7 @@ struct dma_chan *acpi_dma_request_slave_chan_by_name(struct device *dev,
else if (!strcmp(name, "rx"))
index = 1;
else
- return NULL;
+ return ERR_PTR(-ENODEV);
return acpi_dma_request_slave_chan_by_index(dev, index);
}
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index e2c04dc81e2a..c13a3bb0f594 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -1569,7 +1569,6 @@ static int at_dma_remove(struct platform_device *pdev)
/* Disable interrupts */
atc_disable_chan_irq(atdma, chan->chan_id);
- tasklet_disable(&atchan->tasklet);
tasklet_kill(&atchan->tasklet);
list_del(&chan->device_node);
diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c
index c18aebf7d5aa..d028f36ae655 100644
--- a/drivers/dma/cppi41.c
+++ b/drivers/dma/cppi41.c
@@ -620,12 +620,15 @@ static int cppi41_stop_chan(struct dma_chan *chan)
u32 desc_phys;
int ret;
+ desc_phys = lower_32_bits(c->desc_phys);
+ desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc);
+ if (!cdd->chan_busy[desc_num])
+ return 0;
+
ret = cppi41_tear_down_chan(c);
if (ret)
return ret;
- desc_phys = lower_32_bits(c->desc_phys);
- desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc);
WARN_ON(!cdd->chan_busy[desc_num]);
cdd->chan_busy[desc_num] = NULL;
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index ed610b497518..a886713937fd 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -627,18 +627,13 @@ EXPORT_SYMBOL_GPL(__dma_request_channel);
struct dma_chan *dma_request_slave_channel_reason(struct device *dev,
const char *name)
{
- struct dma_chan *chan;
-
/* If device-tree is present get slave info from here */
if (dev->of_node)
return of_dma_request_slave_channel(dev->of_node, name);
/* If device was enumerated by ACPI get slave info from here */
- if (ACPI_HANDLE(dev)) {
- chan = acpi_dma_request_slave_chan_by_name(dev, name);
- if (chan)
- return chan;
- }
+ if (ACPI_HANDLE(dev))
+ return acpi_dma_request_slave_chan_by_name(dev, name);
return ERR_PTR(-ENODEV);
}
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index 05b6dea770a4..e27cec25c59e 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -340,7 +340,7 @@ static unsigned int min_odd(unsigned int x, unsigned int y)
static void result(const char *err, unsigned int n, unsigned int src_off,
unsigned int dst_off, unsigned int len, unsigned long data)
{
- pr_info("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)",
+ pr_info("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)\n",
current->comm, n, err, src_off, dst_off, len, data);
}
@@ -348,7 +348,7 @@ static void dbg_result(const char *err, unsigned int n, unsigned int src_off,
unsigned int dst_off, unsigned int len,
unsigned long data)
{
- pr_debug("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)",
+ pr_debug("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)\n",
current->comm, n, err, src_off, dst_off, len, data);
}
diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index 13ac3f240e79..cfdbb92aae1d 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -33,8 +33,8 @@
* of which use ARM any more). See the "Databook" from Synopsys for
* information beyond what licensees probably provide.
*
- * The driver has currently been tested only with the Atmel AT32AP7000,
- * which does not support descriptor writeback.
+ * The driver has been tested with the Atmel AT32AP7000, which does not
+ * support descriptor writeback.
*/
static inline bool is_request_line_unset(struct dw_dma_chan *dwc)
@@ -1479,7 +1479,6 @@ static void dw_dma_off(struct dw_dma *dw)
int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)
{
struct dw_dma *dw;
- size_t size;
bool autocfg;
unsigned int dw_params;
unsigned int nr_channels;
@@ -1487,6 +1486,13 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)
int err;
int i;
+ dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL);
+ if (!dw)
+ return -ENOMEM;
+
+ dw->regs = chip->regs;
+ chip->dw = dw;
+
dw_params = dma_read_byaddr(chip->regs, DW_PARAMS);
autocfg = dw_params >> DW_PARAMS_EN & 0x1;
@@ -1509,9 +1515,9 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)
else
nr_channels = pdata->nr_channels;
- size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan);
- dw = devm_kzalloc(chip->dev, size, GFP_KERNEL);
- if (!dw)
+ dw->chan = devm_kcalloc(chip->dev, nr_channels, sizeof(*dw->chan),
+ GFP_KERNEL);
+ if (!dw->chan)
return -ENOMEM;
dw->clk = devm_clk_get(chip->dev, "hclk");
@@ -1519,9 +1525,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata)
return PTR_ERR(dw->clk);
clk_prepare_enable(dw->clk);
- dw->regs = chip->regs;
- chip->dw = dw;
-
/* Get hardware configuration parameters */
if (autocfg) {
max_blk_size = dma_readl(dw, MAX_BLK_SIZE);
diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c
index e89fc24b8293..fec59f1a77bb 100644
--- a/drivers/dma/dw/pci.c
+++ b/drivers/dma/dw/pci.c
@@ -75,6 +75,36 @@ static void dw_pci_remove(struct pci_dev *pdev)
dev_warn(&pdev->dev, "can't remove device properly: %d\n", ret);
}
+#ifdef CONFIG_PM_SLEEP
+
+static int dw_pci_suspend_late(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct dw_dma_chip *chip = pci_get_drvdata(pci);
+
+ return dw_dma_suspend(chip);
+};
+
+static int dw_pci_resume_early(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct dw_dma_chip *chip = pci_get_drvdata(pci);
+
+ return dw_dma_resume(chip);
+};
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define dw_pci_suspend_late NULL
+#define dw_pci_resume_early NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops dw_pci_dev_pm_ops = {
+ .suspend_late = dw_pci_suspend_late,
+ .resume_early = dw_pci_resume_early,
+};
+
static DEFINE_PCI_DEVICE_TABLE(dw_pci_id_table) = {
/* Medfield */
{ PCI_VDEVICE(INTEL, 0x0827), (kernel_ulong_t)&dw_pci_pdata },
@@ -83,6 +113,9 @@ static DEFINE_PCI_DEVICE_TABLE(dw_pci_id_table) = {
/* BayTrail */
{ PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_pci_pdata },
{ PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_pci_pdata },
+
+ /* Haswell */
+ { PCI_VDEVICE(INTEL, 0x9c60), (kernel_ulong_t)&dw_pci_pdata },
{ }
};
MODULE_DEVICE_TABLE(pci, dw_pci_id_table);
@@ -92,6 +125,9 @@ static struct pci_driver dw_pci_driver = {
.id_table = dw_pci_id_table,
.probe = dw_pci_probe,
.remove = dw_pci_remove,
+ .driver = {
+ .pm = &dw_pci_dev_pm_ops,
+ },
};
module_pci_driver(dw_pci_driver);
diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h
index deb4274f80f4..bb98d3e91e8b 100644
--- a/drivers/dma/dw/regs.h
+++ b/drivers/dma/dw/regs.h
@@ -252,13 +252,13 @@ struct dw_dma {
struct tasklet_struct tasklet;
struct clk *clk;
+ /* channels */
+ struct dw_dma_chan *chan;
u8 all_chan_mask;
/* hardware configuration */
unsigned char nr_masters;
unsigned char data_width[4];
-
- struct dw_dma_chan chan[0];
};
static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw)
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c
index cd8da451d199..cd04eb7b182e 100644
--- a/drivers/dma/edma.c
+++ b/drivers/dma/edma.c
@@ -539,6 +539,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
edma_alloc_slot(EDMA_CTLR(echan->ch_num),
EDMA_SLOT_ANY);
if (echan->slot[i] < 0) {
+ kfree(edesc);
dev_err(dev, "Failed to allocate slot\n");
return NULL;
}
@@ -553,8 +554,10 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic(
ret = edma_config_pset(chan, &edesc->pset[i], src_addr,
dst_addr, burst, dev_width, period_len,
direction);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(edesc);
return NULL;
+ }
if (direction == DMA_DEV_TO_MEM)
dst_addr += period_len;
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
new file mode 100644
index 000000000000..381e793184ba
--- /dev/null
+++ b/drivers/dma/fsl-edma.c
@@ -0,0 +1,975 @@
+/*
+ * drivers/dma/fsl-edma.c
+ *
+ * Copyright 2013-2014 Freescale Semiconductor, Inc.
+ *
+ * Driver for the Freescale eDMA engine with flexible channel multiplexing
+ * capability for DMA request sources. The eDMA block can be found on some
+ * Vybrid and Layerscape SoCs.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_dma.h>
+
+#include "virt-dma.h"
+
+#define EDMA_CR 0x00
+#define EDMA_ES 0x04
+#define EDMA_ERQ 0x0C
+#define EDMA_EEI 0x14
+#define EDMA_SERQ 0x1B
+#define EDMA_CERQ 0x1A
+#define EDMA_SEEI 0x19
+#define EDMA_CEEI 0x18
+#define EDMA_CINT 0x1F
+#define EDMA_CERR 0x1E
+#define EDMA_SSRT 0x1D
+#define EDMA_CDNE 0x1C
+#define EDMA_INTR 0x24
+#define EDMA_ERR 0x2C
+
+#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x))
+#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x))
+#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x))
+#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x))
+#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x))
+#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x))
+#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x))
+#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x))
+#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x))
+#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x))
+#define EDMA_TCD_CSR(x) (0x101C + 32 * (x))
+#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x))
+#define EDMA_TCD_BITER(x) (0x101E + 32 * (x))
+
+#define EDMA_CR_EDBG BIT(1)
+#define EDMA_CR_ERCA BIT(2)
+#define EDMA_CR_ERGA BIT(3)
+#define EDMA_CR_HOE BIT(4)
+#define EDMA_CR_HALT BIT(5)
+#define EDMA_CR_CLM BIT(6)
+#define EDMA_CR_EMLM BIT(7)
+#define EDMA_CR_ECX BIT(16)
+#define EDMA_CR_CX BIT(17)
+
+#define EDMA_SEEI_SEEI(x) ((x) & 0x1F)
+#define EDMA_CEEI_CEEI(x) ((x) & 0x1F)
+#define EDMA_CINT_CINT(x) ((x) & 0x1F)
+#define EDMA_CERR_CERR(x) ((x) & 0x1F)
+
+#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007))
+#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3)
+#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8)
+#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11)
+#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000)
+#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100)
+#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200)
+#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300)
+#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500)
+#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000)
+#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001)
+#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002)
+#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003)
+#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005)
+
+#define EDMA_TCD_SOFF_SOFF(x) (x)
+#define EDMA_TCD_NBYTES_NBYTES(x) (x)
+#define EDMA_TCD_SLAST_SLAST(x) (x)
+#define EDMA_TCD_DADDR_DADDR(x) (x)
+#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF)
+#define EDMA_TCD_DOFF_DOFF(x) (x)
+#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x)
+#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF)
+
+#define EDMA_TCD_CSR_START BIT(0)
+#define EDMA_TCD_CSR_INT_MAJOR BIT(1)
+#define EDMA_TCD_CSR_INT_HALF BIT(2)
+#define EDMA_TCD_CSR_D_REQ BIT(3)
+#define EDMA_TCD_CSR_E_SG BIT(4)
+#define EDMA_TCD_CSR_E_LINK BIT(5)
+#define EDMA_TCD_CSR_ACTIVE BIT(6)
+#define EDMA_TCD_CSR_DONE BIT(7)
+
+#define EDMAMUX_CHCFG_DIS 0x0
+#define EDMAMUX_CHCFG_ENBL 0x80
+#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F)
+
+#define DMAMUX_NR 2
+
+#define FSL_EDMA_BUSWIDTHS BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
+ BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)
+
+struct fsl_edma_hw_tcd {
+ u32 saddr;
+ u16 soff;
+ u16 attr;
+ u32 nbytes;
+ u32 slast;
+ u32 daddr;
+ u16 doff;
+ u16 citer;
+ u32 dlast_sga;
+ u16 csr;
+ u16 biter;
+};
+
+struct fsl_edma_sw_tcd {
+ dma_addr_t ptcd;
+ struct fsl_edma_hw_tcd *vtcd;
+};
+
+struct fsl_edma_slave_config {
+ enum dma_transfer_direction dir;
+ enum dma_slave_buswidth addr_width;
+ u32 dev_addr;
+ u32 burst;
+ u32 attr;
+};
+
+struct fsl_edma_chan {
+ struct virt_dma_chan vchan;
+ enum dma_status status;
+ struct fsl_edma_engine *edma;
+ struct fsl_edma_desc *edesc;
+ struct fsl_edma_slave_config fsc;
+ struct dma_pool *tcd_pool;
+};
+
+struct fsl_edma_desc {
+ struct virt_dma_desc vdesc;
+ struct fsl_edma_chan *echan;
+ bool iscyclic;
+ unsigned int n_tcds;
+ struct fsl_edma_sw_tcd tcd[];
+};
+
+struct fsl_edma_engine {
+ struct dma_device dma_dev;
+ void __iomem *membase;
+ void __iomem *muxbase[DMAMUX_NR];
+ struct clk *muxclk[DMAMUX_NR];
+ struct mutex fsl_edma_mutex;
+ u32 n_chans;
+ int txirq;
+ int errirq;
+ bool big_endian;
+ struct fsl_edma_chan chans[];
+};
+
+/*
+ * R/W functions for big- or little-endian registers
+ * the eDMA controller's endian is independent of the CPU core's endian.
+ */
+
+static u16 edma_readw(struct fsl_edma_engine *edma, void __iomem *addr)
+{
+ if (edma->big_endian)
+ return ioread16be(addr);
+ else
+ return ioread16(addr);
+}
+
+static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr)
+{
+ if (edma->big_endian)
+ return ioread32be(addr);
+ else
+ return ioread32(addr);
+}
+
+static void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *addr)
+{
+ iowrite8(val, addr);
+}
+
+static void edma_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr)
+{
+ if (edma->big_endian)
+ iowrite16be(val, addr);
+ else
+ iowrite16(val, addr);
+}
+
+static void edma_writel(struct fsl_edma_engine *edma, u32 val, void __iomem *addr)
+{
+ if (edma->big_endian)
+ iowrite32be(val, addr);
+ else
+ iowrite32(val, addr);
+}
+
+static struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct fsl_edma_chan, vchan.chan);
+}
+
+static struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd)
+{
+ return container_of(vd, struct fsl_edma_desc, vdesc);
+}
+
+static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
+{
+ void __iomem *addr = fsl_chan->edma->membase;
+ u32 ch = fsl_chan->vchan.chan.chan_id;
+
+ edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI);
+ edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ);
+}
+
+static void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
+{
+ void __iomem *addr = fsl_chan->edma->membase;
+ u32 ch = fsl_chan->vchan.chan.chan_id;
+
+ edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ);
+ edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI);
+}
+
+static void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
+ unsigned int slot, bool enable)
+{
+ u32 ch = fsl_chan->vchan.chan.chan_id;
+ void __iomem *muxaddr = fsl_chan->edma->muxbase[ch / DMAMUX_NR];
+ unsigned chans_per_mux, ch_off;
+
+ chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR;
+ ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux;
+
+ if (enable)
+ edma_writeb(fsl_chan->edma,
+ EDMAMUX_CHCFG_ENBL | EDMAMUX_CHCFG_SOURCE(slot),
+ muxaddr + ch_off);
+ else
+ edma_writeb(fsl_chan->edma, EDMAMUX_CHCFG_DIS, muxaddr + ch_off);
+}
+
+static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width)
+{
+ switch (addr_width) {
+ case 1:
+ return EDMA_TCD_ATTR_SSIZE_8BIT | EDMA_TCD_ATTR_DSIZE_8BIT;
+ case 2:
+ return EDMA_TCD_ATTR_SSIZE_16BIT | EDMA_TCD_ATTR_DSIZE_16BIT;
+ case 4:
+ return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT;
+ case 8:
+ return EDMA_TCD_ATTR_SSIZE_64BIT | EDMA_TCD_ATTR_DSIZE_64BIT;
+ default:
+ return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT;
+ }
+}
+
+static void fsl_edma_free_desc(struct virt_dma_desc *vdesc)
+{
+ struct fsl_edma_desc *fsl_desc;
+ int i;
+
+ fsl_desc = to_fsl_edma_desc(vdesc);
+ for (i = 0; i < fsl_desc->n_tcds; i++)
+ dma_pool_free(fsl_desc->echan->tcd_pool,
+ fsl_desc->tcd[i].vtcd,
+ fsl_desc->tcd[i].ptcd);
+ kfree(fsl_desc);
+}
+
+static int fsl_edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ struct dma_slave_config *cfg = (void *)arg;
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ switch (cmd) {
+ case DMA_TERMINATE_ALL:
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ fsl_edma_disable_request(fsl_chan);
+ fsl_chan->edesc = NULL;
+ vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+ vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
+ return 0;
+
+ case DMA_SLAVE_CONFIG:
+ fsl_chan->fsc.dir = cfg->direction;
+ if (cfg->direction == DMA_DEV_TO_MEM) {
+ fsl_chan->fsc.dev_addr = cfg->src_addr;
+ fsl_chan->fsc.addr_width = cfg->src_addr_width;
+ fsl_chan->fsc.burst = cfg->src_maxburst;
+ fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width);
+ } else if (cfg->direction == DMA_MEM_TO_DEV) {
+ fsl_chan->fsc.dev_addr = cfg->dst_addr;
+ fsl_chan->fsc.addr_width = cfg->dst_addr_width;
+ fsl_chan->fsc.burst = cfg->dst_maxburst;
+ fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width);
+ } else {
+ return -EINVAL;
+ }
+ return 0;
+
+ case DMA_PAUSE:
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ if (fsl_chan->edesc) {
+ fsl_edma_disable_request(fsl_chan);
+ fsl_chan->status = DMA_PAUSED;
+ }
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+ return 0;
+
+ case DMA_RESUME:
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ if (fsl_chan->edesc) {
+ fsl_edma_enable_request(fsl_chan);
+ fsl_chan->status = DMA_IN_PROGRESS;
+ }
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+ return 0;
+
+ default:
+ return -ENXIO;
+ }
+}
+
+static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
+ struct virt_dma_desc *vdesc, bool in_progress)
+{
+ struct fsl_edma_desc *edesc = fsl_chan->edesc;
+ void __iomem *addr = fsl_chan->edma->membase;
+ u32 ch = fsl_chan->vchan.chan.chan_id;
+ enum dma_transfer_direction dir = fsl_chan->fsc.dir;
+ dma_addr_t cur_addr, dma_addr;
+ size_t len, size;
+ int i;
+
+ /* calculate the total size in this desc */
+ for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++)
+ len += edma_readl(fsl_chan->edma, &(edesc->tcd[i].vtcd->nbytes))
+ * edma_readw(fsl_chan->edma, &(edesc->tcd[i].vtcd->biter));
+
+ if (!in_progress)
+ return len;
+
+ if (dir == DMA_MEM_TO_DEV)
+ cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_SADDR(ch));
+ else
+ cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_DADDR(ch));
+
+ /* figure out the finished and calculate the residue */
+ for (i = 0; i < fsl_chan->edesc->n_tcds; i++) {
+ size = edma_readl(fsl_chan->edma, &(edesc->tcd[i].vtcd->nbytes))
+ * edma_readw(fsl_chan->edma, &(edesc->tcd[i].vtcd->biter));
+ if (dir == DMA_MEM_TO_DEV)
+ dma_addr = edma_readl(fsl_chan->edma,
+ &(edesc->tcd[i].vtcd->saddr));
+ else
+ dma_addr = edma_readl(fsl_chan->edma,
+ &(edesc->tcd[i].vtcd->daddr));
+
+ len -= size;
+ if (cur_addr > dma_addr && cur_addr < dma_addr + size) {
+ len += dma_addr + size - cur_addr;
+ break;
+ }
+ }
+
+ return len;
+}
+
+static enum dma_status fsl_edma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ struct virt_dma_desc *vdesc;
+ enum dma_status status;
+ unsigned long flags;
+
+ status = dma_cookie_status(chan, cookie, txstate);
+ if (status == DMA_COMPLETE)
+ return status;
+
+ if (!txstate)
+ return fsl_chan->status;
+
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ vdesc = vchan_find_desc(&fsl_chan->vchan, cookie);
+ if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie)
+ txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, true);
+ else if (vdesc)
+ txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, false);
+ else
+ txstate->residue = 0;
+
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+
+ return fsl_chan->status;
+}
+
+static void fsl_edma_set_tcd_params(struct fsl_edma_chan *fsl_chan,
+ u32 src, u32 dst, u16 attr, u16 soff, u32 nbytes,
+ u32 slast, u16 citer, u16 biter, u32 doff, u32 dlast_sga,
+ u16 csr)
+{
+ void __iomem *addr = fsl_chan->edma->membase;
+ u32 ch = fsl_chan->vchan.chan.chan_id;
+
+ /*
+ * TCD parameters have been swapped in fill_tcd_params(),
+ * so just write them to registers in the cpu endian here
+ */
+ writew(0, addr + EDMA_TCD_CSR(ch));
+ writel(src, addr + EDMA_TCD_SADDR(ch));
+ writel(dst, addr + EDMA_TCD_DADDR(ch));
+ writew(attr, addr + EDMA_TCD_ATTR(ch));
+ writew(soff, addr + EDMA_TCD_SOFF(ch));
+ writel(nbytes, addr + EDMA_TCD_NBYTES(ch));
+ writel(slast, addr + EDMA_TCD_SLAST(ch));
+ writew(citer, addr + EDMA_TCD_CITER(ch));
+ writew(biter, addr + EDMA_TCD_BITER(ch));
+ writew(doff, addr + EDMA_TCD_DOFF(ch));
+ writel(dlast_sga, addr + EDMA_TCD_DLAST_SGA(ch));
+ writew(csr, addr + EDMA_TCD_CSR(ch));
+}
+
+static void fill_tcd_params(struct fsl_edma_engine *edma,
+ struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst,
+ u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer,
+ u16 biter, u16 doff, u32 dlast_sga, bool major_int,
+ bool disable_req, bool enable_sg)
+{
+ u16 csr = 0;
+
+ /*
+ * eDMA hardware SGs require the TCD parameters stored in memory
+ * the same endian as the eDMA module so that they can be loaded
+ * automatically by the engine
+ */
+ edma_writel(edma, src, &(tcd->saddr));
+ edma_writel(edma, dst, &(tcd->daddr));
+ edma_writew(edma, attr, &(tcd->attr));
+ edma_writew(edma, EDMA_TCD_SOFF_SOFF(soff), &(tcd->soff));
+ edma_writel(edma, EDMA_TCD_NBYTES_NBYTES(nbytes), &(tcd->nbytes));
+ edma_writel(edma, EDMA_TCD_SLAST_SLAST(slast), &(tcd->slast));
+ edma_writew(edma, EDMA_TCD_CITER_CITER(citer), &(tcd->citer));
+ edma_writew(edma, EDMA_TCD_DOFF_DOFF(doff), &(tcd->doff));
+ edma_writel(edma, EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga), &(tcd->dlast_sga));
+ edma_writew(edma, EDMA_TCD_BITER_BITER(biter), &(tcd->biter));
+ if (major_int)
+ csr |= EDMA_TCD_CSR_INT_MAJOR;
+
+ if (disable_req)
+ csr |= EDMA_TCD_CSR_D_REQ;
+
+ if (enable_sg)
+ csr |= EDMA_TCD_CSR_E_SG;
+
+ edma_writew(edma, csr, &(tcd->csr));
+}
+
+static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan,
+ int sg_len)
+{
+ struct fsl_edma_desc *fsl_desc;
+ int i;
+
+ fsl_desc = kzalloc(sizeof(*fsl_desc) + sizeof(struct fsl_edma_sw_tcd) * sg_len,
+ GFP_NOWAIT);
+ if (!fsl_desc)
+ return NULL;
+
+ fsl_desc->echan = fsl_chan;
+ fsl_desc->n_tcds = sg_len;
+ for (i = 0; i < sg_len; i++) {
+ fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool,
+ GFP_NOWAIT, &fsl_desc->tcd[i].ptcd);
+ if (!fsl_desc->tcd[i].vtcd)
+ goto err;
+ }
+ return fsl_desc;
+
+err:
+ while (--i >= 0)
+ dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd,
+ fsl_desc->tcd[i].ptcd);
+ kfree(fsl_desc);
+ return NULL;
+}
+
+static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
+ struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
+ size_t period_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ struct fsl_edma_desc *fsl_desc;
+ dma_addr_t dma_buf_next;
+ int sg_len, i;
+ u32 src_addr, dst_addr, last_sg, nbytes;
+ u16 soff, doff, iter;
+
+ if (!is_slave_direction(fsl_chan->fsc.dir))
+ return NULL;
+
+ sg_len = buf_len / period_len;
+ fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
+ if (!fsl_desc)
+ return NULL;
+ fsl_desc->iscyclic = true;
+
+ dma_buf_next = dma_addr;
+ nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
+ iter = period_len / nbytes;
+
+ for (i = 0; i < sg_len; i++) {
+ if (dma_buf_next >= dma_addr + buf_len)
+ dma_buf_next = dma_addr;
+
+ /* get next sg's physical address */
+ last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
+
+ if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
+ src_addr = dma_buf_next;
+ dst_addr = fsl_chan->fsc.dev_addr;
+ soff = fsl_chan->fsc.addr_width;
+ doff = 0;
+ } else {
+ src_addr = fsl_chan->fsc.dev_addr;
+ dst_addr = dma_buf_next;
+ soff = 0;
+ doff = fsl_chan->fsc.addr_width;
+ }
+
+ fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, src_addr,
+ dst_addr, fsl_chan->fsc.attr, soff, nbytes, 0,
+ iter, iter, doff, last_sg, true, false, true);
+ dma_buf_next += period_len;
+ }
+
+ return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
+}
+
+static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
+ struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ struct fsl_edma_desc *fsl_desc;
+ struct scatterlist *sg;
+ u32 src_addr, dst_addr, last_sg, nbytes;
+ u16 soff, doff, iter;
+ int i;
+
+ if (!is_slave_direction(fsl_chan->fsc.dir))
+ return NULL;
+
+ fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
+ if (!fsl_desc)
+ return NULL;
+ fsl_desc->iscyclic = false;
+
+ nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
+ for_each_sg(sgl, sg, sg_len, i) {
+ /* get next sg's physical address */
+ last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
+
+ if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
+ src_addr = sg_dma_address(sg);
+ dst_addr = fsl_chan->fsc.dev_addr;
+ soff = fsl_chan->fsc.addr_width;
+ doff = 0;
+ } else {
+ src_addr = fsl_chan->fsc.dev_addr;
+ dst_addr = sg_dma_address(sg);
+ soff = 0;
+ doff = fsl_chan->fsc.addr_width;
+ }
+
+ iter = sg_dma_len(sg) / nbytes;
+ if (i < sg_len - 1) {
+ last_sg = fsl_desc->tcd[(i + 1)].ptcd;
+ fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd,
+ src_addr, dst_addr, fsl_chan->fsc.attr,
+ soff, nbytes, 0, iter, iter, doff, last_sg,
+ false, false, true);
+ } else {
+ last_sg = 0;
+ fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd,
+ src_addr, dst_addr, fsl_chan->fsc.attr,
+ soff, nbytes, 0, iter, iter, doff, last_sg,
+ true, true, false);
+ }
+ }
+
+ return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
+}
+
+static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan)
+{
+ struct fsl_edma_hw_tcd *tcd;
+ struct virt_dma_desc *vdesc;
+
+ vdesc = vchan_next_desc(&fsl_chan->vchan);
+ if (!vdesc)
+ return;
+ fsl_chan->edesc = to_fsl_edma_desc(vdesc);
+ tcd = fsl_chan->edesc->tcd[0].vtcd;
+ fsl_edma_set_tcd_params(fsl_chan, tcd->saddr, tcd->daddr, tcd->attr,
+ tcd->soff, tcd->nbytes, tcd->slast, tcd->citer,
+ tcd->biter, tcd->doff, tcd->dlast_sga, tcd->csr);
+ fsl_edma_enable_request(fsl_chan);
+ fsl_chan->status = DMA_IN_PROGRESS;
+}
+
+static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
+{
+ struct fsl_edma_engine *fsl_edma = dev_id;
+ unsigned int intr, ch;
+ void __iomem *base_addr;
+ struct fsl_edma_chan *fsl_chan;
+
+ base_addr = fsl_edma->membase;
+
+ intr = edma_readl(fsl_edma, base_addr + EDMA_INTR);
+ if (!intr)
+ return IRQ_NONE;
+
+ for (ch = 0; ch < fsl_edma->n_chans; ch++) {
+ if (intr & (0x1 << ch)) {
+ edma_writeb(fsl_edma, EDMA_CINT_CINT(ch),
+ base_addr + EDMA_CINT);
+
+ fsl_chan = &fsl_edma->chans[ch];
+
+ spin_lock(&fsl_chan->vchan.lock);
+ if (!fsl_chan->edesc->iscyclic) {
+ list_del(&fsl_chan->edesc->vdesc.node);
+ vchan_cookie_complete(&fsl_chan->edesc->vdesc);
+ fsl_chan->edesc = NULL;
+ fsl_chan->status = DMA_COMPLETE;
+ } else {
+ vchan_cyclic_callback(&fsl_chan->edesc->vdesc);
+ }
+
+ if (!fsl_chan->edesc)
+ fsl_edma_xfer_desc(fsl_chan);
+
+ spin_unlock(&fsl_chan->vchan.lock);
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id)
+{
+ struct fsl_edma_engine *fsl_edma = dev_id;
+ unsigned int err, ch;
+
+ err = edma_readl(fsl_edma, fsl_edma->membase + EDMA_ERR);
+ if (!err)
+ return IRQ_NONE;
+
+ for (ch = 0; ch < fsl_edma->n_chans; ch++) {
+ if (err & (0x1 << ch)) {
+ fsl_edma_disable_request(&fsl_edma->chans[ch]);
+ edma_writeb(fsl_edma, EDMA_CERR_CERR(ch),
+ fsl_edma->membase + EDMA_CERR);
+ fsl_edma->chans[ch].status = DMA_ERROR;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fsl_edma_irq_handler(int irq, void *dev_id)
+{
+ if (fsl_edma_tx_handler(irq, dev_id) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
+ return fsl_edma_err_handler(irq, dev_id);
+}
+
+static void fsl_edma_issue_pending(struct dma_chan *chan)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+
+ if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc)
+ fsl_edma_xfer_desc(fsl_chan);
+
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+}
+
+static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data;
+ struct dma_chan *chan, *_chan;
+
+ if (dma_spec->args_count != 2)
+ return NULL;
+
+ mutex_lock(&fsl_edma->fsl_edma_mutex);
+ list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, device_node) {
+ if (chan->client_count)
+ continue;
+ if ((chan->chan_id / DMAMUX_NR) == dma_spec->args[0]) {
+ chan = dma_get_slave_channel(chan);
+ if (chan) {
+ chan->device->privatecnt++;
+ fsl_edma_chan_mux(to_fsl_edma_chan(chan),
+ dma_spec->args[1], true);
+ mutex_unlock(&fsl_edma->fsl_edma_mutex);
+ return chan;
+ }
+ }
+ }
+ mutex_unlock(&fsl_edma->fsl_edma_mutex);
+ return NULL;
+}
+
+static int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+
+ fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev,
+ sizeof(struct fsl_edma_hw_tcd),
+ 32, 0);
+ return 0;
+}
+
+static void fsl_edma_free_chan_resources(struct dma_chan *chan)
+{
+ struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ fsl_edma_disable_request(fsl_chan);
+ fsl_edma_chan_mux(fsl_chan, 0, false);
+ fsl_chan->edesc = NULL;
+ vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+
+ vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
+ dma_pool_destroy(fsl_chan->tcd_pool);
+ fsl_chan->tcd_pool = NULL;
+}
+
+static int fsl_dma_device_slave_caps(struct dma_chan *dchan,
+ struct dma_slave_caps *caps)
+{
+ caps->src_addr_widths = FSL_EDMA_BUSWIDTHS;
+ caps->dstn_addr_widths = FSL_EDMA_BUSWIDTHS;
+ caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ caps->cmd_pause = true;
+ caps->cmd_terminate = true;
+
+ return 0;
+}
+
+static int
+fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma)
+{
+ int ret;
+
+ fsl_edma->txirq = platform_get_irq_byname(pdev, "edma-tx");
+ if (fsl_edma->txirq < 0) {
+ dev_err(&pdev->dev, "Can't get edma-tx irq.\n");
+ return fsl_edma->txirq;
+ }
+
+ fsl_edma->errirq = platform_get_irq_byname(pdev, "edma-err");
+ if (fsl_edma->errirq < 0) {
+ dev_err(&pdev->dev, "Can't get edma-err irq.\n");
+ return fsl_edma->errirq;
+ }
+
+ if (fsl_edma->txirq == fsl_edma->errirq) {
+ ret = devm_request_irq(&pdev->dev, fsl_edma->txirq,
+ fsl_edma_irq_handler, 0, "eDMA", fsl_edma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register eDMA IRQ.\n");
+ return ret;
+ }
+ } else {
+ ret = devm_request_irq(&pdev->dev, fsl_edma->txirq,
+ fsl_edma_tx_handler, 0, "eDMA tx", fsl_edma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register eDMA tx IRQ.\n");
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev, fsl_edma->errirq,
+ fsl_edma_err_handler, 0, "eDMA err", fsl_edma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register eDMA err IRQ.\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int fsl_edma_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_edma_engine *fsl_edma;
+ struct fsl_edma_chan *fsl_chan;
+ struct resource *res;
+ int len, chans;
+ int ret, i;
+
+ ret = of_property_read_u32(np, "dma-channels", &chans);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get dma-channels.\n");
+ return ret;
+ }
+
+ len = sizeof(*fsl_edma) + sizeof(*fsl_chan) * chans;
+ fsl_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!fsl_edma)
+ return -ENOMEM;
+
+ fsl_edma->n_chans = chans;
+ mutex_init(&fsl_edma->fsl_edma_mutex);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fsl_edma->membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsl_edma->membase))
+ return PTR_ERR(fsl_edma->membase);
+
+ for (i = 0; i < DMAMUX_NR; i++) {
+ char clkname[32];
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i);
+ fsl_edma->muxbase[i] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsl_edma->muxbase[i]))
+ return PTR_ERR(fsl_edma->muxbase[i]);
+
+ sprintf(clkname, "dmamux%d", i);
+ fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname);
+ if (IS_ERR(fsl_edma->muxclk[i])) {
+ dev_err(&pdev->dev, "Missing DMAMUX block clock.\n");
+ return PTR_ERR(fsl_edma->muxclk[i]);
+ }
+
+ ret = clk_prepare_enable(fsl_edma->muxclk[i]);
+ if (ret) {
+ dev_err(&pdev->dev, "DMAMUX clk block failed.\n");
+ return ret;
+ }
+
+ }
+
+ ret = fsl_edma_irq_init(pdev, fsl_edma);
+ if (ret)
+ return ret;
+
+ fsl_edma->big_endian = of_property_read_bool(np, "big-endian");
+
+ INIT_LIST_HEAD(&fsl_edma->dma_dev.channels);
+ for (i = 0; i < fsl_edma->n_chans; i++) {
+ struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i];
+
+ fsl_chan->edma = fsl_edma;
+
+ fsl_chan->vchan.desc_free = fsl_edma_free_desc;
+ vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev);
+
+ edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i));
+ fsl_edma_chan_mux(fsl_chan, 0, false);
+ }
+
+ dma_cap_set(DMA_PRIVATE, fsl_edma->dma_dev.cap_mask);
+ dma_cap_set(DMA_SLAVE, fsl_edma->dma_dev.cap_mask);
+ dma_cap_set(DMA_CYCLIC, fsl_edma->dma_dev.cap_mask);
+
+ fsl_edma->dma_dev.dev = &pdev->dev;
+ fsl_edma->dma_dev.device_alloc_chan_resources
+ = fsl_edma_alloc_chan_resources;
+ fsl_edma->dma_dev.device_free_chan_resources
+ = fsl_edma_free_chan_resources;
+ fsl_edma->dma_dev.device_tx_status = fsl_edma_tx_status;
+ fsl_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg;
+ fsl_edma->dma_dev.device_prep_dma_cyclic = fsl_edma_prep_dma_cyclic;
+ fsl_edma->dma_dev.device_control = fsl_edma_control;
+ fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending;
+ fsl_edma->dma_dev.device_slave_caps = fsl_dma_device_slave_caps;
+
+ platform_set_drvdata(pdev, fsl_edma);
+
+ ret = dma_async_device_register(&fsl_edma->dma_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register Freescale eDMA engine.\n");
+ return ret;
+ }
+
+ ret = of_dma_controller_register(np, fsl_edma_xlate, fsl_edma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register Freescale eDMA of_dma.\n");
+ dma_async_device_unregister(&fsl_edma->dma_dev);
+ return ret;
+ }
+
+ /* enable round robin arbitration */
+ edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, fsl_edma->membase + EDMA_CR);
+
+ return 0;
+}
+
+static int fsl_edma_remove(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev);
+ int i;
+
+ of_dma_controller_free(np);
+ dma_async_device_unregister(&fsl_edma->dma_dev);
+
+ for (i = 0; i < DMAMUX_NR; i++)
+ clk_disable_unprepare(fsl_edma->muxclk[i]);
+
+ return 0;
+}
+
+static const struct of_device_id fsl_edma_dt_ids[] = {
+ { .compatible = "fsl,vf610-edma", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids);
+
+static struct platform_driver fsl_edma_driver = {
+ .driver = {
+ .name = "fsl-edma",
+ .owner = THIS_MODULE,
+ .of_match_table = fsl_edma_dt_ids,
+ },
+ .probe = fsl_edma_probe,
+ .remove = fsl_edma_remove,
+};
+
+module_platform_driver(fsl_edma_driver);
+
+MODULE_ALIAS("platform:fsl-edma");
+MODULE_DESCRIPTION("Freescale eDMA engine driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index 6f9ac2022abd..286660a12cc6 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -422,12 +422,12 @@ static irqreturn_t imxdma_err_handler(int irq, void *dev_id)
/* Tasklet error handler */
tasklet_schedule(&imxdma->channel[i].dma_tasklet);
- printk(KERN_WARNING
- "DMA timeout on channel %d -%s%s%s%s\n", i,
- errcode & IMX_DMA_ERR_BURST ? " burst" : "",
- errcode & IMX_DMA_ERR_REQUEST ? " request" : "",
- errcode & IMX_DMA_ERR_TRANSFER ? " transfer" : "",
- errcode & IMX_DMA_ERR_BUFFER ? " buffer" : "");
+ dev_warn(imxdma->dev,
+ "DMA timeout on channel %d -%s%s%s%s\n", i,
+ errcode & IMX_DMA_ERR_BURST ? " burst" : "",
+ errcode & IMX_DMA_ERR_REQUEST ? " request" : "",
+ errcode & IMX_DMA_ERR_TRANSFER ? " transfer" : "",
+ errcode & IMX_DMA_ERR_BUFFER ? " buffer" : "");
}
return IRQ_HANDLED;
}
@@ -1236,6 +1236,7 @@ static int imxdma_remove(struct platform_device *pdev)
static struct platform_driver imxdma_driver = {
.driver = {
.name = "imx-dma",
+ .owner = THIS_MODULE,
.of_match_table = imx_dma_of_dev_id,
},
.id_table = imx_dma_devtype,
diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c
index b439679f4126..bf02e7beb51a 100644
--- a/drivers/dma/mmp_pdma.c
+++ b/drivers/dma/mmp_pdma.c
@@ -867,8 +867,8 @@ static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev, int idx, int irq)
phy->base = pdev->base;
if (irq) {
- ret = devm_request_irq(pdev->dev, irq, mmp_pdma_chan_handler, 0,
- "pdma", phy);
+ ret = devm_request_irq(pdev->dev, irq, mmp_pdma_chan_handler,
+ IRQF_SHARED, "pdma", phy);
if (ret) {
dev_err(pdev->dev, "channel request irq fail!\n");
return ret;
@@ -957,8 +957,8 @@ static int mmp_pdma_probe(struct platform_device *op)
if (irq_num != dma_channels) {
/* all chan share one irq, demux inside */
irq = platform_get_irq(op, 0);
- ret = devm_request_irq(pdev->dev, irq, mmp_pdma_int_handler, 0,
- "pdma", pdev);
+ ret = devm_request_irq(pdev->dev, irq, mmp_pdma_int_handler,
+ IRQF_SHARED, "pdma", pdev);
if (ret)
return ret;
}
diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c
index 33f96aaa80c7..724f7f4c9720 100644
--- a/drivers/dma/mmp_tdma.c
+++ b/drivers/dma/mmp_tdma.c
@@ -22,6 +22,7 @@
#include <mach/regs-icu.h>
#include <linux/platform_data/dma-mmp_tdma.h>
#include <linux/of_device.h>
+#include <linux/of_dma.h>
#include "dmaengine.h"
@@ -541,6 +542,45 @@ static int mmp_tdma_chan_init(struct mmp_tdma_device *tdev,
return 0;
}
+struct mmp_tdma_filter_param {
+ struct device_node *of_node;
+ unsigned int chan_id;
+};
+
+static bool mmp_tdma_filter_fn(struct dma_chan *chan, void *fn_param)
+{
+ struct mmp_tdma_filter_param *param = fn_param;
+ struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+ struct dma_device *pdma_device = tdmac->chan.device;
+
+ if (pdma_device->dev->of_node != param->of_node)
+ return false;
+
+ if (chan->chan_id != param->chan_id)
+ return false;
+
+ return true;
+}
+
+struct dma_chan *mmp_tdma_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct mmp_tdma_device *tdev = ofdma->of_dma_data;
+ dma_cap_mask_t mask = tdev->device.cap_mask;
+ struct mmp_tdma_filter_param param;
+
+ if (dma_spec->args_count != 1)
+ return NULL;
+
+ param.of_node = ofdma->of_node;
+ param.chan_id = dma_spec->args[0];
+
+ if (param.chan_id >= TDMA_CHANNEL_NUM)
+ return NULL;
+
+ return dma_request_channel(mask, mmp_tdma_filter_fn, &param);
+}
+
static struct of_device_id mmp_tdma_dt_ids[] = {
{ .compatible = "marvell,adma-1.0", .data = (void *)MMP_AUD_TDMA},
{ .compatible = "marvell,pxa910-squ", .data = (void *)PXA910_SQU},
@@ -631,6 +671,16 @@ static int mmp_tdma_probe(struct platform_device *pdev)
return ret;
}
+ if (pdev->dev.of_node) {
+ ret = of_dma_controller_register(pdev->dev.of_node,
+ mmp_tdma_xlate, tdev);
+ if (ret) {
+ dev_err(tdev->device.dev,
+ "failed to register controller\n");
+ dma_async_device_unregister(&tdev->device);
+ }
+ }
+
dev_info(tdev->device.dev, "initialized\n");
return 0;
}
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
index 64ceca2920b8..b19f04f4390b 100644
--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -1088,6 +1088,23 @@ static void omap_dma_free(struct omap_dmadev *od)
}
}
+#define OMAP_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
+
+static int omap_dma_device_slave_caps(struct dma_chan *dchan,
+ struct dma_slave_caps *caps)
+{
+ caps->src_addr_widths = OMAP_DMA_BUSWIDTHS;
+ caps->dstn_addr_widths = OMAP_DMA_BUSWIDTHS;
+ caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ caps->cmd_pause = true;
+ caps->cmd_terminate = true;
+ caps->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+ return 0;
+}
+
static int omap_dma_probe(struct platform_device *pdev)
{
struct omap_dmadev *od;
@@ -1118,6 +1135,7 @@ static int omap_dma_probe(struct platform_device *pdev)
od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic;
od->ddev.device_control = omap_dma_control;
+ od->ddev.device_slave_caps = omap_dma_device_slave_caps;
od->ddev.dev = &pdev->dev;
INIT_LIST_HEAD(&od->ddev.channels);
INIT_LIST_HEAD(&od->pending);
diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c
index 61fdc54a3c88..05fa548bd659 100644
--- a/drivers/dma/pch_dma.c
+++ b/drivers/dma/pch_dma.c
@@ -964,16 +964,16 @@ static void pch_dma_remove(struct pci_dev *pdev)
if (pd) {
dma_async_device_unregister(&pd->dma);
+ free_irq(pdev->irq, pd);
+
list_for_each_entry_safe(chan, _c, &pd->dma.channels,
device_node) {
pd_chan = to_pd_chan(chan);
- tasklet_disable(&pd_chan->tasklet);
tasklet_kill(&pd_chan->tasklet);
}
pci_pool_destroy(pd->pool);
- free_irq(pdev->irq, pd);
pci_iounmap(pdev, pd->membase);
pci_release_regions(pdev);
pci_disable_device(pdev);
diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c
new file mode 100644
index 000000000000..82c923146e49
--- /dev/null
+++ b/drivers/dma/qcom_bam_dma.c
@@ -0,0 +1,1111 @@
+/*
+ * Copyright (c) 2013-2014, 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.
+ *
+ */
+/*
+ * QCOM BAM DMA engine driver
+ *
+ * QCOM BAM DMA blocks are distributed amongst a number of the on-chip
+ * peripherals on the MSM 8x74. The configuration of the channels are dependent
+ * on the way they are hard wired to that specific peripheral. The peripheral
+ * device tree entries specify the configuration of each channel.
+ *
+ * The DMA controller requires the use of external memory for storage of the
+ * hardware descriptors for each channel. The descriptor FIFO is accessed as a
+ * circular buffer and operations are managed according to the offset within the
+ * FIFO. After pipe/channel reset, all of the pipe registers and internal state
+ * are back to defaults.
+ *
+ * During DMA operations, we write descriptors to the FIFO, being careful to
+ * handle wrapping and then write the last FIFO offset to that channel's
+ * P_EVNT_REG register to kick off the transaction. The P_SW_OFSTS register
+ * indicates the current FIFO offset that is being processed, so there is some
+ * indication of where the hardware is currently working.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_dma.h>
+#include <linux/clk.h>
+#include <linux/dmaengine.h>
+
+#include "dmaengine.h"
+#include "virt-dma.h"
+
+struct bam_desc_hw {
+ u32 addr; /* Buffer physical address */
+ u16 size; /* Buffer size in bytes */
+ u16 flags;
+};
+
+#define DESC_FLAG_INT BIT(15)
+#define DESC_FLAG_EOT BIT(14)
+#define DESC_FLAG_EOB BIT(13)
+
+struct bam_async_desc {
+ struct virt_dma_desc vd;
+
+ u32 num_desc;
+ u32 xfer_len;
+ struct bam_desc_hw *curr_desc;
+
+ enum dma_transfer_direction dir;
+ size_t length;
+ struct bam_desc_hw desc[0];
+};
+
+#define BAM_CTRL 0x0000
+#define BAM_REVISION 0x0004
+#define BAM_SW_REVISION 0x0080
+#define BAM_NUM_PIPES 0x003C
+#define BAM_TIMER 0x0040
+#define BAM_TIMER_CTRL 0x0044
+#define BAM_DESC_CNT_TRSHLD 0x0008
+#define BAM_IRQ_SRCS 0x000C
+#define BAM_IRQ_SRCS_MSK 0x0010
+#define BAM_IRQ_SRCS_UNMASKED 0x0030
+#define BAM_IRQ_STTS 0x0014
+#define BAM_IRQ_CLR 0x0018
+#define BAM_IRQ_EN 0x001C
+#define BAM_CNFG_BITS 0x007C
+#define BAM_IRQ_SRCS_EE(ee) (0x0800 + ((ee) * 0x80))
+#define BAM_IRQ_SRCS_MSK_EE(ee) (0x0804 + ((ee) * 0x80))
+#define BAM_P_CTRL(pipe) (0x1000 + ((pipe) * 0x1000))
+#define BAM_P_RST(pipe) (0x1004 + ((pipe) * 0x1000))
+#define BAM_P_HALT(pipe) (0x1008 + ((pipe) * 0x1000))
+#define BAM_P_IRQ_STTS(pipe) (0x1010 + ((pipe) * 0x1000))
+#define BAM_P_IRQ_CLR(pipe) (0x1014 + ((pipe) * 0x1000))
+#define BAM_P_IRQ_EN(pipe) (0x1018 + ((pipe) * 0x1000))
+#define BAM_P_EVNT_DEST_ADDR(pipe) (0x182C + ((pipe) * 0x1000))
+#define BAM_P_EVNT_REG(pipe) (0x1818 + ((pipe) * 0x1000))
+#define BAM_P_SW_OFSTS(pipe) (0x1800 + ((pipe) * 0x1000))
+#define BAM_P_DATA_FIFO_ADDR(pipe) (0x1824 + ((pipe) * 0x1000))
+#define BAM_P_DESC_FIFO_ADDR(pipe) (0x181C + ((pipe) * 0x1000))
+#define BAM_P_EVNT_TRSHLD(pipe) (0x1828 + ((pipe) * 0x1000))
+#define BAM_P_FIFO_SIZES(pipe) (0x1820 + ((pipe) * 0x1000))
+
+/* BAM CTRL */
+#define BAM_SW_RST BIT(0)
+#define BAM_EN BIT(1)
+#define BAM_EN_ACCUM BIT(4)
+#define BAM_TESTBUS_SEL_SHIFT 5
+#define BAM_TESTBUS_SEL_MASK 0x3F
+#define BAM_DESC_CACHE_SEL_SHIFT 13
+#define BAM_DESC_CACHE_SEL_MASK 0x3
+#define BAM_CACHED_DESC_STORE BIT(15)
+#define IBC_DISABLE BIT(16)
+
+/* BAM REVISION */
+#define REVISION_SHIFT 0
+#define REVISION_MASK 0xFF
+#define NUM_EES_SHIFT 8
+#define NUM_EES_MASK 0xF
+#define CE_BUFFER_SIZE BIT(13)
+#define AXI_ACTIVE BIT(14)
+#define USE_VMIDMT BIT(15)
+#define SECURED BIT(16)
+#define BAM_HAS_NO_BYPASS BIT(17)
+#define HIGH_FREQUENCY_BAM BIT(18)
+#define INACTIV_TMRS_EXST BIT(19)
+#define NUM_INACTIV_TMRS BIT(20)
+#define DESC_CACHE_DEPTH_SHIFT 21
+#define DESC_CACHE_DEPTH_1 (0 << DESC_CACHE_DEPTH_SHIFT)
+#define DESC_CACHE_DEPTH_2 (1 << DESC_CACHE_DEPTH_SHIFT)
+#define DESC_CACHE_DEPTH_3 (2 << DESC_CACHE_DEPTH_SHIFT)
+#define DESC_CACHE_DEPTH_4 (3 << DESC_CACHE_DEPTH_SHIFT)
+#define CMD_DESC_EN BIT(23)
+#define INACTIV_TMR_BASE_SHIFT 24
+#define INACTIV_TMR_BASE_MASK 0xFF
+
+/* BAM NUM PIPES */
+#define BAM_NUM_PIPES_SHIFT 0
+#define BAM_NUM_PIPES_MASK 0xFF
+#define PERIPH_NON_PIPE_GRP_SHIFT 16
+#define PERIPH_NON_PIP_GRP_MASK 0xFF
+#define BAM_NON_PIPE_GRP_SHIFT 24
+#define BAM_NON_PIPE_GRP_MASK 0xFF
+
+/* BAM CNFG BITS */
+#define BAM_PIPE_CNFG BIT(2)
+#define BAM_FULL_PIPE BIT(11)
+#define BAM_NO_EXT_P_RST BIT(12)
+#define BAM_IBC_DISABLE BIT(13)
+#define BAM_SB_CLK_REQ BIT(14)
+#define BAM_PSM_CSW_REQ BIT(15)
+#define BAM_PSM_P_RES BIT(16)
+#define BAM_AU_P_RES BIT(17)
+#define BAM_SI_P_RES BIT(18)
+#define BAM_WB_P_RES BIT(19)
+#define BAM_WB_BLK_CSW BIT(20)
+#define BAM_WB_CSW_ACK_IDL BIT(21)
+#define BAM_WB_RETR_SVPNT BIT(22)
+#define BAM_WB_DSC_AVL_P_RST BIT(23)
+#define BAM_REG_P_EN BIT(24)
+#define BAM_PSM_P_HD_DATA BIT(25)
+#define BAM_AU_ACCUMED BIT(26)
+#define BAM_CMD_ENABLE BIT(27)
+
+#define BAM_CNFG_BITS_DEFAULT (BAM_PIPE_CNFG | \
+ BAM_NO_EXT_P_RST | \
+ BAM_IBC_DISABLE | \
+ BAM_SB_CLK_REQ | \
+ BAM_PSM_CSW_REQ | \
+ BAM_PSM_P_RES | \
+ BAM_AU_P_RES | \
+ BAM_SI_P_RES | \
+ BAM_WB_P_RES | \
+ BAM_WB_BLK_CSW | \
+ BAM_WB_CSW_ACK_IDL | \
+ BAM_WB_RETR_SVPNT | \
+ BAM_WB_DSC_AVL_P_RST | \
+ BAM_REG_P_EN | \
+ BAM_PSM_P_HD_DATA | \
+ BAM_AU_ACCUMED | \
+ BAM_CMD_ENABLE)
+
+/* PIPE CTRL */
+#define P_EN BIT(1)
+#define P_DIRECTION BIT(3)
+#define P_SYS_STRM BIT(4)
+#define P_SYS_MODE BIT(5)
+#define P_AUTO_EOB BIT(6)
+#define P_AUTO_EOB_SEL_SHIFT 7
+#define P_AUTO_EOB_SEL_512 (0 << P_AUTO_EOB_SEL_SHIFT)
+#define P_AUTO_EOB_SEL_256 (1 << P_AUTO_EOB_SEL_SHIFT)
+#define P_AUTO_EOB_SEL_128 (2 << P_AUTO_EOB_SEL_SHIFT)
+#define P_AUTO_EOB_SEL_64 (3 << P_AUTO_EOB_SEL_SHIFT)
+#define P_PREFETCH_LIMIT_SHIFT 9
+#define P_PREFETCH_LIMIT_32 (0 << P_PREFETCH_LIMIT_SHIFT)
+#define P_PREFETCH_LIMIT_16 (1 << P_PREFETCH_LIMIT_SHIFT)
+#define P_PREFETCH_LIMIT_4 (2 << P_PREFETCH_LIMIT_SHIFT)
+#define P_WRITE_NWD BIT(11)
+#define P_LOCK_GROUP_SHIFT 16
+#define P_LOCK_GROUP_MASK 0x1F
+
+/* BAM_DESC_CNT_TRSHLD */
+#define CNT_TRSHLD 0xffff
+#define DEFAULT_CNT_THRSHLD 0x4
+
+/* BAM_IRQ_SRCS */
+#define BAM_IRQ BIT(31)
+#define P_IRQ 0x7fffffff
+
+/* BAM_IRQ_SRCS_MSK */
+#define BAM_IRQ_MSK BAM_IRQ
+#define P_IRQ_MSK P_IRQ
+
+/* BAM_IRQ_STTS */
+#define BAM_TIMER_IRQ BIT(4)
+#define BAM_EMPTY_IRQ BIT(3)
+#define BAM_ERROR_IRQ BIT(2)
+#define BAM_HRESP_ERR_IRQ BIT(1)
+
+/* BAM_IRQ_CLR */
+#define BAM_TIMER_CLR BIT(4)
+#define BAM_EMPTY_CLR BIT(3)
+#define BAM_ERROR_CLR BIT(2)
+#define BAM_HRESP_ERR_CLR BIT(1)
+
+/* BAM_IRQ_EN */
+#define BAM_TIMER_EN BIT(4)
+#define BAM_EMPTY_EN BIT(3)
+#define BAM_ERROR_EN BIT(2)
+#define BAM_HRESP_ERR_EN BIT(1)
+
+/* BAM_P_IRQ_EN */
+#define P_PRCSD_DESC_EN BIT(0)
+#define P_TIMER_EN BIT(1)
+#define P_WAKE_EN BIT(2)
+#define P_OUT_OF_DESC_EN BIT(3)
+#define P_ERR_EN BIT(4)
+#define P_TRNSFR_END_EN BIT(5)
+#define P_DEFAULT_IRQS_EN (P_PRCSD_DESC_EN | P_ERR_EN | P_TRNSFR_END_EN)
+
+/* BAM_P_SW_OFSTS */
+#define P_SW_OFSTS_MASK 0xffff
+
+#define BAM_DESC_FIFO_SIZE SZ_32K
+#define MAX_DESCRIPTORS (BAM_DESC_FIFO_SIZE / sizeof(struct bam_desc_hw) - 1)
+#define BAM_MAX_DATA_SIZE (SZ_32K - 8)
+
+struct bam_chan {
+ struct virt_dma_chan vc;
+
+ struct bam_device *bdev;
+
+ /* configuration from device tree */
+ u32 id;
+
+ struct bam_async_desc *curr_txd; /* current running dma */
+
+ /* runtime configuration */
+ struct dma_slave_config slave;
+
+ /* fifo storage */
+ struct bam_desc_hw *fifo_virt;
+ dma_addr_t fifo_phys;
+
+ /* fifo markers */
+ unsigned short head; /* start of active descriptor entries */
+ unsigned short tail; /* end of active descriptor entries */
+
+ unsigned int initialized; /* is the channel hw initialized? */
+ unsigned int paused; /* is the channel paused? */
+ unsigned int reconfigure; /* new slave config? */
+
+ struct list_head node;
+};
+
+static inline struct bam_chan *to_bam_chan(struct dma_chan *common)
+{
+ return container_of(common, struct bam_chan, vc.chan);
+}
+
+struct bam_device {
+ void __iomem *regs;
+ struct device *dev;
+ struct dma_device common;
+ struct device_dma_parameters dma_parms;
+ struct bam_chan *channels;
+ u32 num_channels;
+
+ /* execution environment ID, from DT */
+ u32 ee;
+
+ struct clk *bamclk;
+ int irq;
+
+ /* dma start transaction tasklet */
+ struct tasklet_struct task;
+};
+
+/**
+ * bam_reset_channel - Reset individual BAM DMA channel
+ * @bchan: bam channel
+ *
+ * This function resets a specific BAM channel
+ */
+static void bam_reset_channel(struct bam_chan *bchan)
+{
+ struct bam_device *bdev = bchan->bdev;
+
+ lockdep_assert_held(&bchan->vc.lock);
+
+ /* reset channel */
+ writel_relaxed(1, bdev->regs + BAM_P_RST(bchan->id));
+ writel_relaxed(0, bdev->regs + BAM_P_RST(bchan->id));
+
+ /* don't allow cpu to reorder BAM register accesses done after this */
+ wmb();
+
+ /* make sure hw is initialized when channel is used the first time */
+ bchan->initialized = 0;
+}
+
+/**
+ * bam_chan_init_hw - Initialize channel hardware
+ * @bchan: bam channel
+ *
+ * This function resets and initializes the BAM channel
+ */
+static void bam_chan_init_hw(struct bam_chan *bchan,
+ enum dma_transfer_direction dir)
+{
+ struct bam_device *bdev = bchan->bdev;
+ u32 val;
+
+ /* Reset the channel to clear internal state of the FIFO */
+ bam_reset_channel(bchan);
+
+ /*
+ * write out 8 byte aligned address. We have enough space for this
+ * because we allocated 1 more descriptor (8 bytes) than we can use
+ */
+ writel_relaxed(ALIGN(bchan->fifo_phys, sizeof(struct bam_desc_hw)),
+ bdev->regs + BAM_P_DESC_FIFO_ADDR(bchan->id));
+ writel_relaxed(BAM_DESC_FIFO_SIZE, bdev->regs +
+ BAM_P_FIFO_SIZES(bchan->id));
+
+ /* enable the per pipe interrupts, enable EOT, ERR, and INT irqs */
+ writel_relaxed(P_DEFAULT_IRQS_EN, bdev->regs + BAM_P_IRQ_EN(bchan->id));
+
+ /* unmask the specific pipe and EE combo */
+ val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+ val |= BIT(bchan->id);
+ writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+
+ /* don't allow cpu to reorder the channel enable done below */
+ wmb();
+
+ /* set fixed direction and mode, then enable channel */
+ val = P_EN | P_SYS_MODE;
+ if (dir == DMA_DEV_TO_MEM)
+ val |= P_DIRECTION;
+
+ writel_relaxed(val, bdev->regs + BAM_P_CTRL(bchan->id));
+
+ bchan->initialized = 1;
+
+ /* init FIFO pointers */
+ bchan->head = 0;
+ bchan->tail = 0;
+}
+
+/**
+ * bam_alloc_chan - Allocate channel resources for DMA channel.
+ * @chan: specified channel
+ *
+ * This function allocates the FIFO descriptor memory
+ */
+static int bam_alloc_chan(struct dma_chan *chan)
+{
+ struct bam_chan *bchan = to_bam_chan(chan);
+ struct bam_device *bdev = bchan->bdev;
+
+ if (bchan->fifo_virt)
+ return 0;
+
+ /* allocate FIFO descriptor space, but only if necessary */
+ bchan->fifo_virt = dma_alloc_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE,
+ &bchan->fifo_phys, GFP_KERNEL);
+
+ if (!bchan->fifo_virt) {
+ dev_err(bdev->dev, "Failed to allocate desc fifo\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * bam_free_chan - Frees dma resources associated with specific channel
+ * @chan: specified channel
+ *
+ * Free the allocated fifo descriptor memory and channel resources
+ *
+ */
+static void bam_free_chan(struct dma_chan *chan)
+{
+ struct bam_chan *bchan = to_bam_chan(chan);
+ struct bam_device *bdev = bchan->bdev;
+ u32 val;
+ unsigned long flags;
+
+ vchan_free_chan_resources(to_virt_chan(chan));
+
+ if (bchan->curr_txd) {
+ dev_err(bchan->bdev->dev, "Cannot free busy channel\n");
+ return;
+ }
+
+ spin_lock_irqsave(&bchan->vc.lock, flags);
+ bam_reset_channel(bchan);
+ spin_unlock_irqrestore(&bchan->vc.lock, flags);
+
+ dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, bchan->fifo_virt,
+ bchan->fifo_phys);
+ bchan->fifo_virt = NULL;
+
+ /* mask irq for pipe/channel */
+ val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+ val &= ~BIT(bchan->id);
+ writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+
+ /* disable irq */
+ writel_relaxed(0, bdev->regs + BAM_P_IRQ_EN(bchan->id));
+}
+
+/**
+ * bam_slave_config - set slave configuration for channel
+ * @chan: dma channel
+ * @cfg: slave configuration
+ *
+ * Sets slave configuration for channel
+ *
+ */
+static void bam_slave_config(struct bam_chan *bchan,
+ struct dma_slave_config *cfg)
+{
+ memcpy(&bchan->slave, cfg, sizeof(*cfg));
+ bchan->reconfigure = 1;
+}
+
+/**
+ * bam_prep_slave_sg - Prep slave sg transaction
+ *
+ * @chan: dma channel
+ * @sgl: scatter gather list
+ * @sg_len: length of sg
+ * @direction: DMA transfer direction
+ * @flags: DMA flags
+ * @context: transfer context (unused)
+ */
+static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
+ struct scatterlist *sgl, unsigned int sg_len,
+ enum dma_transfer_direction direction, unsigned long flags,
+ void *context)
+{
+ struct bam_chan *bchan = to_bam_chan(chan);
+ struct bam_device *bdev = bchan->bdev;
+ struct bam_async_desc *async_desc;
+ struct scatterlist *sg;
+ u32 i;
+ struct bam_desc_hw *desc;
+ unsigned int num_alloc = 0;
+
+
+ if (!is_slave_direction(direction)) {
+ dev_err(bdev->dev, "invalid dma direction\n");
+ return NULL;
+ }
+
+ /* calculate number of required entries */
+ for_each_sg(sgl, sg, sg_len, i)
+ num_alloc += DIV_ROUND_UP(sg_dma_len(sg), BAM_MAX_DATA_SIZE);
+
+ /* allocate enough room to accomodate the number of entries */
+ async_desc = kzalloc(sizeof(*async_desc) +
+ (num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT);
+
+ if (!async_desc)
+ goto err_out;
+
+ async_desc->num_desc = num_alloc;
+ async_desc->curr_desc = async_desc->desc;
+ async_desc->dir = direction;
+
+ /* fill in temporary descriptors */
+ desc = async_desc->desc;
+ for_each_sg(sgl, sg, sg_len, i) {
+ unsigned int remainder = sg_dma_len(sg);
+ unsigned int curr_offset = 0;
+
+ do {
+ desc->addr = sg_dma_address(sg) + curr_offset;
+
+ if (remainder > BAM_MAX_DATA_SIZE) {
+ desc->size = BAM_MAX_DATA_SIZE;
+ remainder -= BAM_MAX_DATA_SIZE;
+ curr_offset += BAM_MAX_DATA_SIZE;
+ } else {
+ desc->size = remainder;
+ remainder = 0;
+ }
+
+ async_desc->length += desc->size;
+ desc++;
+ } while (remainder > 0);
+ }
+
+ return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags);
+
+err_out:
+ kfree(async_desc);
+ return NULL;
+}
+
+/**
+ * bam_dma_terminate_all - terminate all transactions on a channel
+ * @bchan: bam dma channel
+ *
+ * Dequeues and frees all transactions
+ * No callbacks are done
+ *
+ */
+static void bam_dma_terminate_all(struct bam_chan *bchan)
+{
+ unsigned long flag;
+ LIST_HEAD(head);
+
+ /* remove all transactions, including active transaction */
+ spin_lock_irqsave(&bchan->vc.lock, flag);
+ if (bchan->curr_txd) {
+ list_add(&bchan->curr_txd->vd.node, &bchan->vc.desc_issued);
+ bchan->curr_txd = NULL;
+ }
+
+ vchan_get_all_descriptors(&bchan->vc, &head);
+ spin_unlock_irqrestore(&bchan->vc.lock, flag);
+
+ vchan_dma_desc_free_list(&bchan->vc, &head);
+}
+
+/**
+ * bam_control - DMA device control
+ * @chan: dma channel
+ * @cmd: control cmd
+ * @arg: cmd argument
+ *
+ * Perform DMA control command
+ *
+ */
+static int bam_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct bam_chan *bchan = to_bam_chan(chan);
+ struct bam_device *bdev = bchan->bdev;
+ int ret = 0;
+ unsigned long flag;
+
+ switch (cmd) {
+ case DMA_PAUSE:
+ spin_lock_irqsave(&bchan->vc.lock, flag);
+ writel_relaxed(1, bdev->regs + BAM_P_HALT(bchan->id));
+ bchan->paused = 1;
+ spin_unlock_irqrestore(&bchan->vc.lock, flag);
+ break;
+
+ case DMA_RESUME:
+ spin_lock_irqsave(&bchan->vc.lock, flag);
+ writel_relaxed(0, bdev->regs + BAM_P_HALT(bchan->id));
+ bchan->paused = 0;
+ spin_unlock_irqrestore(&bchan->vc.lock, flag);
+ break;
+
+ case DMA_TERMINATE_ALL:
+ bam_dma_terminate_all(bchan);
+ break;
+
+ case DMA_SLAVE_CONFIG:
+ spin_lock_irqsave(&bchan->vc.lock, flag);
+ bam_slave_config(bchan, (struct dma_slave_config *)arg);
+ spin_unlock_irqrestore(&bchan->vc.lock, flag);
+ break;
+
+ default:
+ ret = -ENXIO;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * process_channel_irqs - processes the channel interrupts
+ * @bdev: bam controller
+ *
+ * This function processes the channel interrupts
+ *
+ */
+static u32 process_channel_irqs(struct bam_device *bdev)
+{
+ u32 i, srcs, pipe_stts;
+ unsigned long flags;
+ struct bam_async_desc *async_desc;
+
+ srcs = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_EE(bdev->ee));
+
+ /* return early if no pipe/channel interrupts are present */
+ if (!(srcs & P_IRQ))
+ return srcs;
+
+ for (i = 0; i < bdev->num_channels; i++) {
+ struct bam_chan *bchan = &bdev->channels[i];
+
+ if (!(srcs & BIT(i)))
+ continue;
+
+ /* clear pipe irq */
+ pipe_stts = readl_relaxed(bdev->regs +
+ BAM_P_IRQ_STTS(i));
+
+ writel_relaxed(pipe_stts, bdev->regs +
+ BAM_P_IRQ_CLR(i));
+
+ spin_lock_irqsave(&bchan->vc.lock, flags);
+ async_desc = bchan->curr_txd;
+
+ if (async_desc) {
+ async_desc->num_desc -= async_desc->xfer_len;
+ async_desc->curr_desc += async_desc->xfer_len;
+ bchan->curr_txd = NULL;
+
+ /* manage FIFO */
+ bchan->head += async_desc->xfer_len;
+ bchan->head %= MAX_DESCRIPTORS;
+
+ /*
+ * if complete, process cookie. Otherwise
+ * push back to front of desc_issued so that
+ * it gets restarted by the tasklet
+ */
+ if (!async_desc->num_desc)
+ vchan_cookie_complete(&async_desc->vd);
+ else
+ list_add(&async_desc->vd.node,
+ &bchan->vc.desc_issued);
+ }
+
+ spin_unlock_irqrestore(&bchan->vc.lock, flags);
+ }
+
+ return srcs;
+}
+
+/**
+ * bam_dma_irq - irq handler for bam controller
+ * @irq: IRQ of interrupt
+ * @data: callback data
+ *
+ * IRQ handler for the bam controller
+ */
+static irqreturn_t bam_dma_irq(int irq, void *data)
+{
+ struct bam_device *bdev = data;
+ u32 clr_mask = 0, srcs = 0;
+
+ srcs |= process_channel_irqs(bdev);
+
+ /* kick off tasklet to start next dma transfer */
+ if (srcs & P_IRQ)
+ tasklet_schedule(&bdev->task);
+
+ if (srcs & BAM_IRQ)
+ clr_mask = readl_relaxed(bdev->regs + BAM_IRQ_STTS);
+
+ /* don't allow reorder of the various accesses to the BAM registers */
+ mb();
+
+ writel_relaxed(clr_mask, bdev->regs + BAM_IRQ_CLR);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * bam_tx_status - returns status of transaction
+ * @chan: dma channel
+ * @cookie: transaction cookie
+ * @txstate: DMA transaction state
+ *
+ * Return status of dma transaction
+ */
+static enum dma_status bam_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct bam_chan *bchan = to_bam_chan(chan);
+ struct virt_dma_desc *vd;
+ int ret;
+ size_t residue = 0;
+ unsigned int i;
+ unsigned long flags;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret == DMA_COMPLETE)
+ return ret;
+
+ if (!txstate)
+ return bchan->paused ? DMA_PAUSED : ret;
+
+ spin_lock_irqsave(&bchan->vc.lock, flags);
+ vd = vchan_find_desc(&bchan->vc, cookie);
+ if (vd)
+ residue = container_of(vd, struct bam_async_desc, vd)->length;
+ else if (bchan->curr_txd && bchan->curr_txd->vd.tx.cookie == cookie)
+ for (i = 0; i < bchan->curr_txd->num_desc; i++)
+ residue += bchan->curr_txd->curr_desc[i].size;
+
+ spin_unlock_irqrestore(&bchan->vc.lock, flags);
+
+ dma_set_residue(txstate, residue);
+
+ if (ret == DMA_IN_PROGRESS && bchan->paused)
+ ret = DMA_PAUSED;
+
+ return ret;
+}
+
+/**
+ * bam_apply_new_config
+ * @bchan: bam dma channel
+ * @dir: DMA direction
+ */
+static void bam_apply_new_config(struct bam_chan *bchan,
+ enum dma_transfer_direction dir)
+{
+ struct bam_device *bdev = bchan->bdev;
+ u32 maxburst;
+
+ if (dir == DMA_DEV_TO_MEM)
+ maxburst = bchan->slave.src_maxburst;
+ else
+ maxburst = bchan->slave.dst_maxburst;
+
+ writel_relaxed(maxburst, bdev->regs + BAM_DESC_CNT_TRSHLD);
+
+ bchan->reconfigure = 0;
+}
+
+/**
+ * bam_start_dma - start next transaction
+ * @bchan - bam dma channel
+ */
+static void bam_start_dma(struct bam_chan *bchan)
+{
+ struct virt_dma_desc *vd = vchan_next_desc(&bchan->vc);
+ struct bam_device *bdev = bchan->bdev;
+ struct bam_async_desc *async_desc;
+ struct bam_desc_hw *desc;
+ struct bam_desc_hw *fifo = PTR_ALIGN(bchan->fifo_virt,
+ sizeof(struct bam_desc_hw));
+
+ lockdep_assert_held(&bchan->vc.lock);
+
+ if (!vd)
+ return;
+
+ list_del(&vd->node);
+
+ async_desc = container_of(vd, struct bam_async_desc, vd);
+ bchan->curr_txd = async_desc;
+
+ /* on first use, initialize the channel hardware */
+ if (!bchan->initialized)
+ bam_chan_init_hw(bchan, async_desc->dir);
+
+ /* apply new slave config changes, if necessary */
+ if (bchan->reconfigure)
+ bam_apply_new_config(bchan, async_desc->dir);
+
+ desc = bchan->curr_txd->curr_desc;
+
+ if (async_desc->num_desc > MAX_DESCRIPTORS)
+ async_desc->xfer_len = MAX_DESCRIPTORS;
+ else
+ async_desc->xfer_len = async_desc->num_desc;
+
+ /* set INT on last descriptor */
+ desc[async_desc->xfer_len - 1].flags |= DESC_FLAG_INT;
+
+ if (bchan->tail + async_desc->xfer_len > MAX_DESCRIPTORS) {
+ u32 partial = MAX_DESCRIPTORS - bchan->tail;
+
+ memcpy(&fifo[bchan->tail], desc,
+ partial * sizeof(struct bam_desc_hw));
+ memcpy(fifo, &desc[partial], (async_desc->xfer_len - partial) *
+ sizeof(struct bam_desc_hw));
+ } else {
+ memcpy(&fifo[bchan->tail], desc,
+ async_desc->xfer_len * sizeof(struct bam_desc_hw));
+ }
+
+ bchan->tail += async_desc->xfer_len;
+ bchan->tail %= MAX_DESCRIPTORS;
+
+ /* ensure descriptor writes and dma start not reordered */
+ wmb();
+ writel_relaxed(bchan->tail * sizeof(struct bam_desc_hw),
+ bdev->regs + BAM_P_EVNT_REG(bchan->id));
+}
+
+/**
+ * dma_tasklet - DMA IRQ tasklet
+ * @data: tasklet argument (bam controller structure)
+ *
+ * Sets up next DMA operation and then processes all completed transactions
+ */
+static void dma_tasklet(unsigned long data)
+{
+ struct bam_device *bdev = (struct bam_device *)data;
+ struct bam_chan *bchan;
+ unsigned long flags;
+ unsigned int i;
+
+ /* go through the channels and kick off transactions */
+ for (i = 0; i < bdev->num_channels; i++) {
+ bchan = &bdev->channels[i];
+ spin_lock_irqsave(&bchan->vc.lock, flags);
+
+ if (!list_empty(&bchan->vc.desc_issued) && !bchan->curr_txd)
+ bam_start_dma(bchan);
+ spin_unlock_irqrestore(&bchan->vc.lock, flags);
+ }
+}
+
+/**
+ * bam_issue_pending - starts pending transactions
+ * @chan: dma channel
+ *
+ * Calls tasklet directly which in turn starts any pending transactions
+ */
+static void bam_issue_pending(struct dma_chan *chan)
+{
+ struct bam_chan *bchan = to_bam_chan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bchan->vc.lock, flags);
+
+ /* if work pending and idle, start a transaction */
+ if (vchan_issue_pending(&bchan->vc) && !bchan->curr_txd)
+ bam_start_dma(bchan);
+
+ spin_unlock_irqrestore(&bchan->vc.lock, flags);
+}
+
+/**
+ * bam_dma_free_desc - free descriptor memory
+ * @vd: virtual descriptor
+ *
+ */
+static void bam_dma_free_desc(struct virt_dma_desc *vd)
+{
+ struct bam_async_desc *async_desc = container_of(vd,
+ struct bam_async_desc, vd);
+
+ kfree(async_desc);
+}
+
+static struct dma_chan *bam_dma_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *of)
+{
+ struct bam_device *bdev = container_of(of->of_dma_data,
+ struct bam_device, common);
+ unsigned int request;
+
+ if (dma_spec->args_count != 1)
+ return NULL;
+
+ request = dma_spec->args[0];
+ if (request >= bdev->num_channels)
+ return NULL;
+
+ return dma_get_slave_channel(&(bdev->channels[request].vc.chan));
+}
+
+/**
+ * bam_init
+ * @bdev: bam device
+ *
+ * Initialization helper for global bam registers
+ */
+static int bam_init(struct bam_device *bdev)
+{
+ u32 val;
+
+ /* read revision and configuration information */
+ val = readl_relaxed(bdev->regs + BAM_REVISION) >> NUM_EES_SHIFT;
+ val &= NUM_EES_MASK;
+
+ /* check that configured EE is within range */
+ if (bdev->ee >= val)
+ return -EINVAL;
+
+ val = readl_relaxed(bdev->regs + BAM_NUM_PIPES);
+ bdev->num_channels = val & BAM_NUM_PIPES_MASK;
+
+ /* s/w reset bam */
+ /* after reset all pipes are disabled and idle */
+ val = readl_relaxed(bdev->regs + BAM_CTRL);
+ val |= BAM_SW_RST;
+ writel_relaxed(val, bdev->regs + BAM_CTRL);
+ val &= ~BAM_SW_RST;
+ writel_relaxed(val, bdev->regs + BAM_CTRL);
+
+ /* make sure previous stores are visible before enabling BAM */
+ wmb();
+
+ /* enable bam */
+ val |= BAM_EN;
+ writel_relaxed(val, bdev->regs + BAM_CTRL);
+
+ /* set descriptor threshhold, start with 4 bytes */
+ writel_relaxed(DEFAULT_CNT_THRSHLD, bdev->regs + BAM_DESC_CNT_TRSHLD);
+
+ /* Enable default set of h/w workarounds, ie all except BAM_FULL_PIPE */
+ writel_relaxed(BAM_CNFG_BITS_DEFAULT, bdev->regs + BAM_CNFG_BITS);
+
+ /* enable irqs for errors */
+ writel_relaxed(BAM_ERROR_EN | BAM_HRESP_ERR_EN,
+ bdev->regs + BAM_IRQ_EN);
+
+ /* unmask global bam interrupt */
+ writel_relaxed(BAM_IRQ_MSK, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+
+ return 0;
+}
+
+static void bam_channel_init(struct bam_device *bdev, struct bam_chan *bchan,
+ u32 index)
+{
+ bchan->id = index;
+ bchan->bdev = bdev;
+
+ vchan_init(&bchan->vc, &bdev->common);
+ bchan->vc.desc_free = bam_dma_free_desc;
+}
+
+static int bam_dma_probe(struct platform_device *pdev)
+{
+ struct bam_device *bdev;
+ struct resource *iores;
+ int ret, i;
+
+ bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL);
+ if (!bdev)
+ return -ENOMEM;
+
+ bdev->dev = &pdev->dev;
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ bdev->regs = devm_ioremap_resource(&pdev->dev, iores);
+ if (IS_ERR(bdev->regs))
+ return PTR_ERR(bdev->regs);
+
+ bdev->irq = platform_get_irq(pdev, 0);
+ if (bdev->irq < 0)
+ return bdev->irq;
+
+ ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &bdev->ee);
+ if (ret) {
+ dev_err(bdev->dev, "Execution environment unspecified\n");
+ return ret;
+ }
+
+ bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk");
+ if (IS_ERR(bdev->bamclk))
+ return PTR_ERR(bdev->bamclk);
+
+ ret = clk_prepare_enable(bdev->bamclk);
+ if (ret) {
+ dev_err(bdev->dev, "failed to prepare/enable clock\n");
+ return ret;
+ }
+
+ ret = bam_init(bdev);
+ if (ret)
+ goto err_disable_clk;
+
+ tasklet_init(&bdev->task, dma_tasklet, (unsigned long)bdev);
+
+ bdev->channels = devm_kcalloc(bdev->dev, bdev->num_channels,
+ sizeof(*bdev->channels), GFP_KERNEL);
+
+ if (!bdev->channels) {
+ ret = -ENOMEM;
+ goto err_disable_clk;
+ }
+
+ /* allocate and initialize channels */
+ INIT_LIST_HEAD(&bdev->common.channels);
+
+ for (i = 0; i < bdev->num_channels; i++)
+ bam_channel_init(bdev, &bdev->channels[i], i);
+
+ ret = devm_request_irq(bdev->dev, bdev->irq, bam_dma_irq,
+ IRQF_TRIGGER_HIGH, "bam_dma", bdev);
+ if (ret)
+ goto err_disable_clk;
+
+ /* set max dma segment size */
+ bdev->common.dev = bdev->dev;
+ bdev->common.dev->dma_parms = &bdev->dma_parms;
+ ret = dma_set_max_seg_size(bdev->common.dev, BAM_MAX_DATA_SIZE);
+ if (ret) {
+ dev_err(bdev->dev, "cannot set maximum segment size\n");
+ goto err_disable_clk;
+ }
+
+ platform_set_drvdata(pdev, bdev);
+
+ /* set capabilities */
+ dma_cap_zero(bdev->common.cap_mask);
+ dma_cap_set(DMA_SLAVE, bdev->common.cap_mask);
+
+ /* initialize dmaengine apis */
+ bdev->common.device_alloc_chan_resources = bam_alloc_chan;
+ bdev->common.device_free_chan_resources = bam_free_chan;
+ bdev->common.device_prep_slave_sg = bam_prep_slave_sg;
+ bdev->common.device_control = bam_control;
+ bdev->common.device_issue_pending = bam_issue_pending;
+ bdev->common.device_tx_status = bam_tx_status;
+ bdev->common.dev = bdev->dev;
+
+ ret = dma_async_device_register(&bdev->common);
+ if (ret) {
+ dev_err(bdev->dev, "failed to register dma async device\n");
+ goto err_disable_clk;
+ }
+
+ ret = of_dma_controller_register(pdev->dev.of_node, bam_dma_xlate,
+ &bdev->common);
+ if (ret)
+ goto err_unregister_dma;
+
+ return 0;
+
+err_unregister_dma:
+ dma_async_device_unregister(&bdev->common);
+err_disable_clk:
+ clk_disable_unprepare(bdev->bamclk);
+ return ret;
+}
+
+static int bam_dma_remove(struct platform_device *pdev)
+{
+ struct bam_device *bdev = platform_get_drvdata(pdev);
+ u32 i;
+
+ of_dma_controller_free(pdev->dev.of_node);
+ dma_async_device_unregister(&bdev->common);
+
+ /* mask all interrupts for this execution environment */
+ writel_relaxed(0, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee));
+
+ devm_free_irq(bdev->dev, bdev->irq, bdev);
+
+ for (i = 0; i < bdev->num_channels; i++) {
+ bam_dma_terminate_all(&bdev->channels[i]);
+ tasklet_kill(&bdev->channels[i].vc.task);
+
+ dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE,
+ bdev->channels[i].fifo_virt,
+ bdev->channels[i].fifo_phys);
+ }
+
+ tasklet_kill(&bdev->task);
+
+ clk_disable_unprepare(bdev->bamclk);
+
+ return 0;
+}
+
+static const struct of_device_id bam_of_match[] = {
+ { .compatible = "qcom,bam-v1.4.0", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, bam_of_match);
+
+static struct platform_driver bam_dma_driver = {
+ .probe = bam_dma_probe,
+ .remove = bam_dma_remove,
+ .driver = {
+ .name = "bam-dma-engine",
+ .owner = THIS_MODULE,
+ .of_match_table = bam_of_match,
+ },
+};
+
+module_platform_driver(bam_dma_driver);
+
+MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
+MODULE_DESCRIPTION("QCOM BAM DMA engine driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c
index 4eddedb6eb7d..b209a0f17344 100644
--- a/drivers/dma/s3c24xx-dma.c
+++ b/drivers/dma/s3c24xx-dma.c
@@ -192,7 +192,7 @@ struct s3c24xx_dma_phy {
unsigned int id;
bool valid;
void __iomem *base;
- unsigned int irq;
+ int irq;
struct clk *clk;
spinlock_t lock;
struct s3c24xx_dma_chan *serving;
diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig
index dadd9e010c0b..b4c813831006 100644
--- a/drivers/dma/sh/Kconfig
+++ b/drivers/dma/sh/Kconfig
@@ -29,6 +29,12 @@ config RCAR_HPB_DMAE
help
Enable support for the Renesas R-Car series DMA controllers.
+config RCAR_AUDMAC_PP
+ tristate "Renesas R-Car Audio DMAC Peripheral Peripheral support"
+ depends on SH_DMAE_BASE
+ help
+ Enable support for the Renesas R-Car Audio DMAC Peripheral Peripheral controllers.
+
config SHDMA_R8A73A4
def_bool y
depends on ARCH_R8A73A4 && SH_DMAE != n
diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile
index e856af23b789..1ce88b28cfc6 100644
--- a/drivers/dma/sh/Makefile
+++ b/drivers/dma/sh/Makefile
@@ -7,3 +7,4 @@ endif
shdma-objs := $(shdma-y)
obj-$(CONFIG_SUDMAC) += sudmac.o
obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o
+obj-$(CONFIG_RCAR_AUDMAC_PP) += rcar-audmapp.o
diff --git a/drivers/dma/sh/rcar-audmapp.c b/drivers/dma/sh/rcar-audmapp.c
new file mode 100644
index 000000000000..2de77289a2e9
--- /dev/null
+++ b/drivers/dma/sh/rcar-audmapp.c
@@ -0,0 +1,320 @@
+/*
+ * This is for Renesas R-Car Audio-DMAC-peri-peri.
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ * Copyright (C) 2014 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on the drivers/dma/sh/shdma.c
+ *
+ * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * This 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/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/platform_data/dma-rcar-audmapp.h>
+#include <linux/platform_device.h>
+#include <linux/shdma-base.h>
+
+/*
+ * DMA register
+ */
+#define PDMASAR 0x00
+#define PDMADAR 0x04
+#define PDMACHCR 0x0c
+
+/* PDMACHCR */
+#define PDMACHCR_DE (1 << 0)
+
+#define AUDMAPP_MAX_CHANNELS 29
+
+/* Default MEMCPY transfer size = 2^2 = 4 bytes */
+#define LOG2_DEFAULT_XFER_SIZE 2
+#define AUDMAPP_SLAVE_NUMBER 256
+#define AUDMAPP_LEN_MAX (16 * 1024 * 1024)
+
+struct audmapp_chan {
+ struct shdma_chan shdma_chan;
+ struct audmapp_slave_config *config;
+ void __iomem *base;
+};
+
+struct audmapp_device {
+ struct shdma_dev shdma_dev;
+ struct audmapp_pdata *pdata;
+ struct device *dev;
+ void __iomem *chan_reg;
+};
+
+#define to_chan(chan) container_of(chan, struct audmapp_chan, shdma_chan)
+#define to_dev(chan) container_of(chan->shdma_chan.dma_chan.device, \
+ struct audmapp_device, shdma_dev.dma_dev)
+
+static void audmapp_write(struct audmapp_chan *auchan, u32 data, u32 reg)
+{
+ struct audmapp_device *audev = to_dev(auchan);
+ struct device *dev = audev->dev;
+
+ dev_dbg(dev, "w %p : %08x\n", auchan->base + reg, data);
+
+ iowrite32(data, auchan->base + reg);
+}
+
+static u32 audmapp_read(struct audmapp_chan *auchan, u32 reg)
+{
+ return ioread32(auchan->base + reg);
+}
+
+static void audmapp_halt(struct shdma_chan *schan)
+{
+ struct audmapp_chan *auchan = to_chan(schan);
+ int i;
+
+ audmapp_write(auchan, 0, PDMACHCR);
+
+ for (i = 0; i < 1024; i++) {
+ if (0 == audmapp_read(auchan, PDMACHCR))
+ return;
+ udelay(1);
+ }
+}
+
+static void audmapp_start_xfer(struct shdma_chan *schan,
+ struct shdma_desc *sdecs)
+{
+ struct audmapp_chan *auchan = to_chan(schan);
+ struct audmapp_device *audev = to_dev(auchan);
+ struct audmapp_slave_config *cfg = auchan->config;
+ struct device *dev = audev->dev;
+ u32 chcr = cfg->chcr | PDMACHCR_DE;
+
+ dev_dbg(dev, "src/dst/chcr = %pad/%pad/%x\n",
+ &cfg->src, &cfg->dst, cfg->chcr);
+
+ audmapp_write(auchan, cfg->src, PDMASAR);
+ audmapp_write(auchan, cfg->dst, PDMADAR);
+ audmapp_write(auchan, chcr, PDMACHCR);
+}
+
+static struct audmapp_slave_config *
+audmapp_find_slave(struct audmapp_chan *auchan, int slave_id)
+{
+ struct audmapp_device *audev = to_dev(auchan);
+ struct audmapp_pdata *pdata = audev->pdata;
+ struct audmapp_slave_config *cfg;
+ int i;
+
+ if (slave_id >= AUDMAPP_SLAVE_NUMBER)
+ return NULL;
+
+ for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
+ if (cfg->slave_id == slave_id)
+ return cfg;
+
+ return NULL;
+}
+
+static int audmapp_set_slave(struct shdma_chan *schan, int slave_id,
+ dma_addr_t slave_addr, bool try)
+{
+ struct audmapp_chan *auchan = to_chan(schan);
+ struct audmapp_slave_config *cfg =
+ audmapp_find_slave(auchan, slave_id);
+
+ if (!cfg)
+ return -ENODEV;
+ if (try)
+ return 0;
+
+ auchan->config = cfg;
+
+ return 0;
+}
+
+static int audmapp_desc_setup(struct shdma_chan *schan,
+ struct shdma_desc *sdecs,
+ dma_addr_t src, dma_addr_t dst, size_t *len)
+{
+ struct audmapp_chan *auchan = to_chan(schan);
+ struct audmapp_slave_config *cfg = auchan->config;
+
+ if (!cfg)
+ return -ENODEV;
+
+ if (*len > (size_t)AUDMAPP_LEN_MAX)
+ *len = (size_t)AUDMAPP_LEN_MAX;
+
+ return 0;
+}
+
+static void audmapp_setup_xfer(struct shdma_chan *schan,
+ int slave_id)
+{
+}
+
+static dma_addr_t audmapp_slave_addr(struct shdma_chan *schan)
+{
+ return 0; /* always fixed address */
+}
+
+static bool audmapp_channel_busy(struct shdma_chan *schan)
+{
+ struct audmapp_chan *auchan = to_chan(schan);
+ u32 chcr = audmapp_read(auchan, PDMACHCR);
+
+ return chcr & ~PDMACHCR_DE;
+}
+
+static bool audmapp_desc_completed(struct shdma_chan *schan,
+ struct shdma_desc *sdesc)
+{
+ return true;
+}
+
+static struct shdma_desc *audmapp_embedded_desc(void *buf, int i)
+{
+ return &((struct shdma_desc *)buf)[i];
+}
+
+static const struct shdma_ops audmapp_shdma_ops = {
+ .halt_channel = audmapp_halt,
+ .desc_setup = audmapp_desc_setup,
+ .set_slave = audmapp_set_slave,
+ .start_xfer = audmapp_start_xfer,
+ .embedded_desc = audmapp_embedded_desc,
+ .setup_xfer = audmapp_setup_xfer,
+ .slave_addr = audmapp_slave_addr,
+ .channel_busy = audmapp_channel_busy,
+ .desc_completed = audmapp_desc_completed,
+};
+
+static int audmapp_chan_probe(struct platform_device *pdev,
+ struct audmapp_device *audev, int id)
+{
+ struct shdma_dev *sdev = &audev->shdma_dev;
+ struct audmapp_chan *auchan;
+ struct shdma_chan *schan;
+ struct device *dev = audev->dev;
+
+ auchan = devm_kzalloc(dev, sizeof(*auchan), GFP_KERNEL);
+ if (!auchan)
+ return -ENOMEM;
+
+ schan = &auchan->shdma_chan;
+ schan->max_xfer_len = AUDMAPP_LEN_MAX;
+
+ shdma_chan_probe(sdev, schan, id);
+
+ auchan->base = audev->chan_reg + 0x20 + (0x10 * id);
+ dev_dbg(dev, "%02d : %p / %p", id, auchan->base, audev->chan_reg);
+
+ return 0;
+}
+
+static void audmapp_chan_remove(struct audmapp_device *audev)
+{
+ struct dma_device *dma_dev = &audev->shdma_dev.dma_dev;
+ struct shdma_chan *schan;
+ int i;
+
+ shdma_for_each_chan(schan, &audev->shdma_dev, i) {
+ BUG_ON(!schan);
+ shdma_chan_remove(schan);
+ }
+ dma_dev->chancnt = 0;
+}
+
+static int audmapp_probe(struct platform_device *pdev)
+{
+ struct audmapp_pdata *pdata = pdev->dev.platform_data;
+ struct audmapp_device *audev;
+ struct shdma_dev *sdev;
+ struct dma_device *dma_dev;
+ struct resource *res;
+ int err, i;
+
+ if (!pdata)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ audev = devm_kzalloc(&pdev->dev, sizeof(*audev), GFP_KERNEL);
+ if (!audev)
+ return -ENOMEM;
+
+ audev->dev = &pdev->dev;
+ audev->pdata = pdata;
+ audev->chan_reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(audev->chan_reg))
+ return PTR_ERR(audev->chan_reg);
+
+ sdev = &audev->shdma_dev;
+ sdev->ops = &audmapp_shdma_ops;
+ sdev->desc_size = sizeof(struct shdma_desc);
+
+ dma_dev = &sdev->dma_dev;
+ dma_dev->copy_align = LOG2_DEFAULT_XFER_SIZE;
+ dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
+
+ err = shdma_init(&pdev->dev, sdev, AUDMAPP_MAX_CHANNELS);
+ if (err < 0)
+ return err;
+
+ platform_set_drvdata(pdev, audev);
+
+ /* Create DMA Channel */
+ for (i = 0; i < AUDMAPP_MAX_CHANNELS; i++) {
+ err = audmapp_chan_probe(pdev, audev, i);
+ if (err)
+ goto chan_probe_err;
+ }
+
+ err = dma_async_device_register(dma_dev);
+ if (err < 0)
+ goto chan_probe_err;
+
+ return err;
+
+chan_probe_err:
+ audmapp_chan_remove(audev);
+ shdma_cleanup(sdev);
+
+ return err;
+}
+
+static int audmapp_remove(struct platform_device *pdev)
+{
+ struct audmapp_device *audev = platform_get_drvdata(pdev);
+ struct dma_device *dma_dev = &audev->shdma_dev.dma_dev;
+
+ dma_async_device_unregister(dma_dev);
+
+ audmapp_chan_remove(audev);
+ shdma_cleanup(&audev->shdma_dev);
+
+ return 0;
+}
+
+static struct platform_driver audmapp_driver = {
+ .probe = audmapp_probe,
+ .remove = audmapp_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rcar-audmapp-engine",
+ },
+};
+module_platform_driver(audmapp_driver);
+
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_DESCRIPTION("Renesas R-Car Audio DMAC peri-peri driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c
index 2e7b394def80..52396771acbe 100644
--- a/drivers/dma/sh/shdma-base.c
+++ b/drivers/dma/sh/shdma-base.c
@@ -227,7 +227,7 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg)
struct shdma_chan *schan = to_shdma_chan(chan);
struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device);
const struct shdma_ops *ops = sdev->ops;
- int match = (int)arg;
+ int match = (long)arg;
int ret;
if (match < 0)
@@ -491,8 +491,8 @@ static struct shdma_desc *shdma_add_desc(struct shdma_chan *schan,
}
dev_dbg(schan->dev,
- "chaining (%u/%u)@%x -> %x with %p, cookie %d\n",
- copy_size, *len, *src, *dst, &new->async_tx,
+ "chaining (%zu/%zu)@%pad -> %pad with %p, cookie %d\n",
+ copy_size, *len, src, dst, &new->async_tx,
new->async_tx.cookie);
new->mark = DESC_PREPARED;
@@ -555,8 +555,8 @@ static struct dma_async_tx_descriptor *shdma_prep_sg(struct shdma_chan *schan,
goto err_get_desc;
do {
- dev_dbg(schan->dev, "Add SG #%d@%p[%d], dma %llx\n",
- i, sg, len, (unsigned long long)sg_addr);
+ dev_dbg(schan->dev, "Add SG #%d@%p[%zu], dma %pad\n",
+ i, sg, len, &sg_addr);
if (direction == DMA_DEV_TO_MEM)
new = shdma_add_desc(schan, flags,
diff --git a/drivers/dma/sh/shdma-of.c b/drivers/dma/sh/shdma-of.c
index 06473a05fe4e..b4ff9d3e56d1 100644
--- a/drivers/dma/sh/shdma-of.c
+++ b/drivers/dma/sh/shdma-of.c
@@ -33,7 +33,8 @@ static struct dma_chan *shdma_of_xlate(struct of_phandle_args *dma_spec,
/* Only slave DMA channels can be allocated via DT */
dma_cap_set(DMA_SLAVE, mask);
- chan = dma_request_channel(mask, shdma_chan_filter, (void *)id);
+ chan = dma_request_channel(mask, shdma_chan_filter,
+ (void *)(uintptr_t)id);
if (chan)
to_shdma_chan(chan)->hw_req = id;
diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c
index 0d765c0e21ec..dda7e7563f5d 100644
--- a/drivers/dma/sh/shdmac.c
+++ b/drivers/dma/sh/shdmac.c
@@ -443,6 +443,7 @@ static bool sh_dmae_reset(struct sh_dmae_device *shdev)
return ret;
}
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARM)
static irqreturn_t sh_dmae_err(int irq, void *data)
{
struct sh_dmae_device *shdev = data;
@@ -453,6 +454,7 @@ static irqreturn_t sh_dmae_err(int irq, void *data)
sh_dmae_reset(shdev);
return IRQ_HANDLED;
}
+#endif
static bool sh_dmae_desc_completed(struct shdma_chan *schan,
struct shdma_desc *sdesc)
@@ -637,7 +639,7 @@ static int sh_dmae_resume(struct device *dev)
#define sh_dmae_resume NULL
#endif
-const struct dev_pm_ops sh_dmae_pm = {
+static const struct dev_pm_ops sh_dmae_pm = {
.suspend = sh_dmae_suspend,
.resume = sh_dmae_resume,
.runtime_suspend = sh_dmae_runtime_suspend,
@@ -685,9 +687,12 @@ MODULE_DEVICE_TABLE(of, sh_dmae_of_match);
static int sh_dmae_probe(struct platform_device *pdev)
{
const struct sh_dmae_pdata *pdata;
- unsigned long irqflags = 0,
- chan_flag[SH_DMAE_MAX_CHANNELS] = {};
- int errirq, chan_irq[SH_DMAE_MAX_CHANNELS];
+ unsigned long chan_flag[SH_DMAE_MAX_CHANNELS] = {};
+ int chan_irq[SH_DMAE_MAX_CHANNELS];
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARM)
+ unsigned long irqflags = 0;
+ int errirq;
+#endif
int err, i, irq_cnt = 0, irqres = 0, irq_cap = 0;
struct sh_dmae_device *shdev;
struct dma_device *dma_dev;
diff --git a/drivers/dma/sh/sudmac.c b/drivers/dma/sh/sudmac.c
index c7e9cdff0708..4e7df43b50d6 100644
--- a/drivers/dma/sh/sudmac.c
+++ b/drivers/dma/sh/sudmac.c
@@ -178,8 +178,8 @@ static int sudmac_desc_setup(struct shdma_chan *schan,
struct sudmac_chan *sc = to_chan(schan);
struct sudmac_desc *sd = to_desc(sdesc);
- dev_dbg(sc->shdma_chan.dev, "%s: src=%x, dst=%x, len=%d\n",
- __func__, src, dst, *len);
+ dev_dbg(sc->shdma_chan.dev, "%s: src=%pad, dst=%pad, len=%zu\n",
+ __func__, &src, &dst, *len);
if (*len > schan->max_xfer_len)
*len = schan->max_xfer_len;
diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c
index d4d3a3109b16..a1bd8298d55f 100644
--- a/drivers/dma/sirf-dma.c
+++ b/drivers/dma/sirf-dma.c
@@ -18,6 +18,7 @@
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
+#include <linux/of_dma.h>
#include <linux/sirfsoc_dma.h>
#include "dmaengine.h"
@@ -659,6 +660,18 @@ static int sirfsoc_dma_device_slave_caps(struct dma_chan *dchan,
return 0;
}
+static struct dma_chan *of_dma_sirfsoc_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct sirfsoc_dma *sdma = ofdma->of_dma_data;
+ unsigned int request = dma_spec->args[0];
+
+ if (request > SIRFSOC_DMA_CHANNELS)
+ return NULL;
+
+ return dma_get_slave_channel(&sdma->channels[request].chan);
+}
+
static int sirfsoc_dma_probe(struct platform_device *op)
{
struct device_node *dn = op->dev.of_node;
@@ -764,11 +777,20 @@ static int sirfsoc_dma_probe(struct platform_device *op)
if (ret)
goto free_irq;
+ /* Device-tree DMA controller registration */
+ ret = of_dma_controller_register(dn, of_dma_sirfsoc_xlate, sdma);
+ if (ret) {
+ dev_err(dev, "failed to register DMA controller\n");
+ goto unreg_dma_dev;
+ }
+
pm_runtime_enable(&op->dev);
dev_info(dev, "initialized SIRFSOC DMAC driver\n");
return 0;
+unreg_dma_dev:
+ dma_async_device_unregister(dma);
free_irq:
free_irq(sdma->irq, sdma);
irq_dispose:
@@ -781,6 +803,7 @@ static int sirfsoc_dma_remove(struct platform_device *op)
struct device *dev = &op->dev;
struct sirfsoc_dma *sdma = dev_get_drvdata(dev);
+ of_dma_controller_free(op->dev.of_node);
dma_async_device_unregister(&sdma->dma);
free_irq(sdma->irq, sdma);
irq_dispose_mapping(sdma->irq);
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index ff50aeebf0d9..2c41eaece2c1 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -397,7 +397,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
else
chunksize = size;
- status = efi_file_read(fh, files[j].handle,
+ status = efi_file_read(files[j].handle,
&chunksize,
(void *)addr);
if (status != EFI_SUCCESS) {
@@ -408,7 +408,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
size -= chunksize;
}
- efi_file_close(fh, files[j].handle);
+ efi_file_close(files[j].handle);
}
}
@@ -425,7 +425,7 @@ free_file_total:
close_handles:
for (k = j; k < i; k++)
- efi_file_close(fh, files[k].handle);
+ efi_file_close(files[k].handle);
free_files:
efi_call_early(free_pool, files);
fail:
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index b13172cfbeef..bc196f49ec53 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -554,14 +554,6 @@ config SENSORS_IBMPEX
This driver can also be built as a module. If so, the module
will be called ibmpex.
-config SENSORS_IBMPOWERNV
- tristate "IBM PowerNv Platform temperature/power/fan sensor"
- depends on PPC_POWERNV
- default y
- help
- If you say yes here you get support for the temperature/fan/power
- sensors on your platform.
-
config SENSORS_IIO_HWMON
tristate "Hwmon driver that uses channels specified via iio maps"
depends on IIO
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 199c401bf8d9..c48f9873ac73 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -71,7 +71,6 @@ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
-obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o
obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o
obj-$(CONFIG_SENSORS_INA209) += ina209.o
obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c
deleted file mode 100644
index b7b1297a9b02..000000000000
--- a/drivers/hwmon/ibmpowernv.c
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * hwmon driver for temperature/power/fan on IBM PowerNV platform
- * Copyright (C) 2013 IBM
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-
-#include <linux/jiffies.h>
-#include <linux/platform_device.h>
-#include <asm/opal.h>
-#include <linux/err.h>
-
-MODULE_DESCRIPTION("IBM PowerNV Platform power/temp/fan sensor hwmon module");
-MODULE_LICENSE("GPL");
-
-#define MAX_ATTR_LENGTH 32
-
-/* Device tree sensor name prefixes. The device tree has the names in the
- * format "cooling-fan#2-faulted" where the "cooling-fan" is the sensor type,
- * 2 is the sensor count, and "faulted" is the sensor data attribute type.
- */
-#define DT_FAULT_ATTR_SUFFIX "faulted"
-#define DT_DATA_ATTR_SUFFIX "data"
-#define DT_THRESHOLD_ATTR_SUFFIX "thrs"
-
-enum sensors {
- FAN,
- TEMPERATURE,
- POWERSUPPLY,
- POWER,
- MAX_SENSOR_TYPE,
-};
-
-enum attributes {
- INPUT,
- MINIMUM,
- MAXIMUM,
- FAULT,
- MAX_ATTR_TYPES
-};
-
-static struct sensor_name {
- char *name;
- char *compaible;
-} sensor_names[] = {
- {"fan-sensor", "ibm,opal-sensor-cooling-fan"},
- {"amb-temp-sensor", "ibm,opal-sensor-amb-temp"},
- {"power-sensor", "ibm,opal-sensor-power-supply"},
- {"power", "ibm,opal-sensor-power"}
-};
-
-static const char * const attribute_type_table[] = {
- "input",
- "min",
- "max",
- "fault",
- NULL
-};
-
-struct pdev_entry {
- struct list_head list;
- struct platform_device *pdev;
- enum sensors type;
-};
-
-static LIST_HEAD(pdev_list);
-
-/* The sensors are categorised on type.
- *
- * The sensors of same type are categorised under a common platform device.
- * So, The pdev is shared by all sensors of same type.
- * Ex : temp1_input, temp1_max, temp2_input,temp2_max all share same platform
- * device.
- *
- * "sensor_data" is the Platform device specific data.
- * There is one hwmon_device instance for all the sensors of same type.
- * This also holds the list of all sensors with same type but different
- * attribute and index.
- */
-struct sensor_specific_data {
- u32 sensor_id; /* The hex value as in the device tree */
- u32 sensor_index; /* The sensor instance index */
- struct sensor_device_attribute sd_attr;
- enum attributes attr_type;
- char attr_name[64];
-};
-
-struct sensor_data {
- struct device *hwmon_dev;
- struct list_head sensor_list;
- struct device_attribute name_attr;
-};
-
-struct sensor_entry {
- struct list_head list;
- struct sensor_specific_data *sensor_data;
-};
-
-static struct platform_device *powernv_sensor_get_pdev(enum sensors type)
-{
- struct pdev_entry *p;
- list_for_each_entry(p, &pdev_list, list)
- if (p->type == type)
- return p->pdev;
-
- return NULL;
-}
-
-static struct sensor_specific_data *powernv_sensor_get_sensor_data(
- struct sensor_data *pdata,
- int index, enum attributes attr_type)
-{
- struct sensor_entry *p;
- list_for_each_entry(p, &pdata->sensor_list, list)
- if ((p->sensor_data->sensor_index == index) &&
- (attr_type == p->sensor_data->attr_type))
- return p->sensor_data;
-
- return NULL;
-}
-
-static ssize_t show_name(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct platform_device *pdev = to_platform_device(dev);
-
- return sprintf(buf, "%s\n", pdev->name);
-}
-
-/* Note: Data from the sensors for each sensor type needs to be converted to
- * the dimension appropriate.
- */
-static ssize_t show_sensor(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(devattr);
- struct platform_device *pdev = to_platform_device(dev);
- struct sensor_data *pdata = platform_get_drvdata(pdev);
- struct sensor_specific_data *tdata = NULL;
- enum sensors sensor_type = pdev->id;
- u32 x = -1;
- int ret;
-
- if (sd_attr && sd_attr->dev_attr.attr.name) {
- char *pos = strchr(sd_attr->dev_attr.attr.name, '_');
- int i;
-
- for (i = 0; i < MAX_ATTR_TYPES; i++) {
- if (strcmp(pos+1, attribute_type_table[i]) == 0) {
- tdata = powernv_sensor_get_sensor_data(pdata,
- sd_attr->index, i);
- break;
- }
- }
- }
-
- if (tdata) {
- ret = opal_get_sensor_data(tdata->sensor_id, &x);
- if (ret)
- x = -1;
- }
-
- if (sensor_type == TEMPERATURE && x > 0) {
- /* Temperature comes in Degrees and convert it to
- * milli-degrees.
- */
- x = x*1000;
- } else if (sensor_type == POWER && x > 0) {
- /* Power value comes in watts, convert to micro-watts */
- x = x * 1000000;
- }
-
- return sprintf(buf, "%d\n", x);
-}
-
-static u32 get_sensor_index_from_name(const char *name)
-{
- char *hash_position = strchr(name, '#');
- u32 index = 0, copy_length;
- char newbuf[8];
-
- if (hash_position) {
- copy_length = strchr(hash_position, '-') - hash_position - 1;
- if (copy_length < sizeof(newbuf)) {
- strncpy(newbuf, hash_position + 1, copy_length);
- sscanf(newbuf, "%d", &index);
- }
- }
-
- return index;
-}
-
-static inline void get_sensor_suffix_from_name(const char *name, char *suffix)
-{
- char *dash_position = strrchr(name, '-');
- if (dash_position)
- strncpy(suffix, dash_position+1, MAX_ATTR_LENGTH);
- else
- strcpy(suffix,"");
-}
-
-static int get_sensor_attr_properties(const char *sensor_name,
- enum sensors sensor_type, enum attributes *attr_type,
- u32 *sensor_index)
-{
- char suffix[MAX_ATTR_LENGTH];
-
- *attr_type = MAX_ATTR_TYPES;
- *sensor_index = get_sensor_index_from_name(sensor_name);
- if (*sensor_index == 0)
- return -EINVAL;
-
- get_sensor_suffix_from_name(sensor_name, suffix);
- if (strcmp(suffix, "") == 0)
- return -EINVAL;
-
- if (strcmp(suffix, DT_FAULT_ATTR_SUFFIX) == 0)
- *attr_type = FAULT;
- else if (strcmp(suffix, DT_DATA_ATTR_SUFFIX) == 0)
- *attr_type = INPUT;
- else if ((sensor_type == TEMPERATURE) &&
- (strcmp(suffix, DT_THRESHOLD_ATTR_SUFFIX) == 0))
- *attr_type = MAXIMUM;
- else if ((sensor_type == FAN) &&
- (strcmp(suffix, DT_THRESHOLD_ATTR_SUFFIX) == 0))
- *attr_type = MINIMUM;
- else
- return -ENOENT;
-
- if (((sensor_type == FAN) && ((*attr_type == INPUT) ||
- (*attr_type == MINIMUM)))
- || ((sensor_type == TEMPERATURE) && ((*attr_type == INPUT) ||
- (*attr_type == MAXIMUM)))
- || ((sensor_type == POWER) && ((*attr_type == INPUT))))
- return 0;
-
- return -ENOENT;
-}
-
-static int create_sensor_attr(struct sensor_specific_data *tdata,
- struct device *dev, enum sensors sensor_type,
- enum attributes attr_type)
-{
- int err = 0;
- char temp_file_prefix[50];
- static const char *const file_name_format = "%s%d_%s";
-
- tdata->attr_type = attr_type;
-
- if (sensor_type == FAN)
- strcpy(temp_file_prefix, "fan");
- else if (sensor_type == TEMPERATURE)
- strcpy(temp_file_prefix, "temp");
- else if (sensor_type == POWERSUPPLY)
- strcpy(temp_file_prefix, "powersupply");
- else if (sensor_type == POWER)
- strcpy(temp_file_prefix, "power");
-
- snprintf(tdata->attr_name, sizeof(tdata->attr_name), file_name_format,
- temp_file_prefix, tdata->sensor_index,
- attribute_type_table[tdata->attr_type]);
-
- sysfs_attr_init(&tdata->sd_attr.dev_attr.attr);
- tdata->sd_attr.dev_attr.attr.name = tdata->attr_name;
- tdata->sd_attr.dev_attr.attr.mode = S_IRUGO;
- tdata->sd_attr.dev_attr.show = show_sensor;
-
- tdata->sd_attr.index = tdata->sensor_index;
- err = device_create_file(dev, &tdata->sd_attr.dev_attr);
-
- return err;
-}
-
-static int create_name_attr(struct sensor_data *pdata,
- struct device *dev)
-{
- sysfs_attr_init(&pdata->name_attr.attr);
- pdata->name_attr.attr.name = "name";
- pdata->name_attr.attr.mode = S_IRUGO;
- pdata->name_attr.show = show_name;
- return device_create_file(dev, &pdata->name_attr);
-}
-
-static int create_platform_device(enum sensors sensor_type,
- struct platform_device **pdev)
-{
- struct pdev_entry *pdev_entry = NULL;
- int err;
-
- *pdev = platform_device_alloc(sensor_names[sensor_type].name,
- sensor_type);
- if (!*pdev) {
- pr_err("Device allocation failed\n");
- err = -ENOMEM;
- goto exit;
- }
-
- pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
- if (!pdev_entry) {
- pr_err("Device allocation failed\n");
- err = -ENOMEM;
- goto exit_device_put;
- }
-
- err = platform_device_add(*pdev);
- if (err) {
- pr_err("Device addition failed (%d)\n", err);
- goto exit_device_free;
- }
-
- pdev_entry->pdev = *pdev;
- pdev_entry->type = (*pdev)->id;
-
- list_add_tail(&pdev_entry->list, &pdev_list);
-
- return 0;
-exit_device_free:
- kfree(pdev_entry);
-exit_device_put:
- platform_device_put(*pdev);
-exit:
- return err;
-}
-
-static int create_sensor_data(struct platform_device *pdev)
-{
- struct sensor_data *pdata = NULL;
- int err = 0;
-
- pdata = kzalloc(sizeof(struct sensor_data), GFP_KERNEL);
- if (!pdata) {
- err = -ENOMEM;
- goto exit;
- }
-
- err = create_name_attr(pdata, &pdev->dev);
- if (err)
- goto exit_free;
-
- pdata->hwmon_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(pdata->hwmon_dev)) {
- err = PTR_ERR(pdata->hwmon_dev);
- dev_err(&pdev->dev, "Class registration failed (%d)\n",
- err);
- goto exit_name;
- }
-
- INIT_LIST_HEAD(&pdata->sensor_list);
- platform_set_drvdata(pdev, pdata);
-
- return 0;
-
-exit_name:
- device_remove_file(&pdev->dev, &pdata->name_attr);
-exit_free:
- kfree(pdata);
-exit:
- return err;
-}
-
-static void delete_sensor_attr(struct sensor_data *pdata)
-{
- struct sensor_entry *s, *l;
-
- list_for_each_entry_safe(s, l, &pdata->sensor_list, list) {
- struct sensor_specific_data *tdata = s->sensor_data;
- kfree(tdata);
- list_del(&s->list);
- kfree(s);
- }
-}
-
-static int powernv_sensor_init(u32 sensor_id, const struct device_node *np,
- enum sensors sensor_type, enum attributes attr_type,
- u32 sensor_index)
-{
- struct platform_device *pdev = powernv_sensor_get_pdev(sensor_type);
- struct sensor_specific_data *tdata;
- struct sensor_entry *sensor_entry;
- struct sensor_data *pdata;
- int err = 0;
-
- if (!pdev) {
- err = create_platform_device(sensor_type, &pdev);
- if (err)
- goto exit;
-
- err = create_sensor_data(pdev);
- if (err)
- goto exit;
- }
-
- pdata = platform_get_drvdata(pdev);
- if (!pdata) {
- err = -ENOMEM;
- goto exit;
- }
-
- tdata = kzalloc(sizeof(struct sensor_specific_data), GFP_KERNEL);
- if (!tdata) {
- err = -ENOMEM;
- goto exit;
- }
-
- tdata->sensor_id = sensor_id;
- tdata->sensor_index = sensor_index;
-
- err = create_sensor_attr(tdata, &pdev->dev, sensor_type, attr_type);
- if (err)
- goto exit_free;
-
- sensor_entry = kzalloc(sizeof(struct sensor_entry), GFP_KERNEL);
- if (!sensor_entry) {
- err = -ENOMEM;
- goto exit_attr;
- }
-
- sensor_entry->sensor_data = tdata;
-
- list_add_tail(&sensor_entry->list, &pdata->sensor_list);
-
- return 0;
-exit_attr:
- device_remove_file(&pdev->dev, &tdata->sd_attr.dev_attr);
-exit_free:
- kfree(tdata);
-exit:
- return err;
-}
-
-static void delete_unregister_sensors(void)
-{
- struct pdev_entry *p, *n;
-
- list_for_each_entry_safe(p, n, &pdev_list, list) {
- struct sensor_data *pdata = platform_get_drvdata(p->pdev);
- if (pdata) {
- delete_sensor_attr(pdata);
-
- hwmon_device_unregister(pdata->hwmon_dev);
- kfree(pdata);
- }
- platform_device_unregister(p->pdev);
- list_del(&p->list);
- kfree(p);
- }
-}
-
-static int __init powernv_hwmon_init(void)
-{
- struct device_node *opal, *np = NULL;
- enum attributes attr_type;
- enum sensors type;
- const u32 *sensor_id;
- u32 sensor_index;
- int err;
-
- opal = of_find_node_by_path("/ibm,opal/sensors");
- if (!opal) {
- pr_err("%s: Opal 'sensors' node not found\n", __func__);
- return -ENXIO;
- }
-
- for_each_child_of_node(opal, np) {
- if (np->name == NULL)
- continue;
-
- for (type = 0; type < MAX_SENSOR_TYPE; type++)
- if (of_device_is_compatible(np,
- sensor_names[type].compaible))
- break;
-
- if (type == MAX_SENSOR_TYPE)
- continue;
-
- if (get_sensor_attr_properties(np->name, type, &attr_type,
- &sensor_index))
- continue;
-
- sensor_id = of_get_property(np, "sensor-id", NULL);
- if (!sensor_id) {
- pr_info("%s: %s doesn't have sensor-id\n", __func__,
- np->name);
- continue;
- }
-
- err = powernv_sensor_init(*sensor_id, np, type, attr_type,
- sensor_index);
- if (err) {
- of_node_put(opal);
- goto exit;
- }
- }
- of_node_put(opal);
-
- return 0;
-exit:
- delete_unregister_sensors();
- return err;
-
-}
-
-static void powernv_hwmon_exit(void)
-{
- delete_unregister_sensors();
-}
-
-module_init(powernv_hwmon_init);
-module_exit(powernv_hwmon_exit);
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 014afab1d551..c94db1c5e353 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -110,6 +110,7 @@ config I2C_I801
Wellsburg (PCH)
Coleto Creek (PCH)
Wildcat Point-LP (PCH)
+ BayTrail (SOC)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
@@ -375,6 +376,13 @@ config I2C_BLACKFIN_TWI_CLK_KHZ
help
The unit of the TWI clock is kHz.
+config I2C_CADENCE
+ tristate "Cadence I2C Controller"
+ depends on ARCH_ZYNQ
+ help
+ Say yes here to select Cadence I2C Host Controller. This controller is
+ e.g. used by Xilinx Zynq.
+
config I2C_CBUS_GPIO
tristate "CBUS I2C driver"
depends on GPIOLIB
@@ -432,6 +440,13 @@ config I2C_DESIGNWARE_PCI
This driver can also be built as a module. If so, the module
will be called i2c-designware-pci.
+config I2C_EFM32
+ tristate "EFM32 I2C controller"
+ depends on ARCH_EFM32 || COMPILE_TEST
+ help
+ This driver supports the i2c block found in Energy Micro's EFM32
+ SoCs.
+
config I2C_EG20T
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
depends on PCI
@@ -527,7 +542,7 @@ config I2C_MPC
config I2C_MV64XXX
tristate "Marvell mv64xxx I2C Controller"
- depends on (MV64X60 || PLAT_ORION || ARCH_SUNXI)
+ depends on MV64X60 || PLAT_ORION || ARCH_SUNXI
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Marvell 64xxx line of host bridges.
@@ -648,6 +663,16 @@ config I2C_PXA_SLAVE
is necessary for systems where the PXA may be a target on the
I2C bus.
+config I2C_QUP
+ tristate "Qualcomm QUP based I2C controller"
+ depends on ARCH_QCOM
+ help
+ If you say yes to this option, support will be included for the
+ built-in I2C interface on the Qualcomm SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-qup.
+
config I2C_RIIC
tristate "Renesas RIIC adapter"
depends on ARCH_SHMOBILE || COMPILE_TEST
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index a08931fe73e1..18d18ff9db93 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
+obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o
obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
@@ -41,6 +42,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
i2c-designware-platform-objs := i2c-designware-platdrv.o
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
i2c-designware-pci-objs := i2c-designware-pcidrv.o
+obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o
obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
@@ -63,6 +65,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
+obj-$(CONFIG_I2C_QUP) += i2c-qup.o
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index 7d60d3a1f621..451e305f7971 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -494,7 +494,7 @@ static struct i2c_adapter ali1535_adapter = {
.algo = &smbus_algorithm,
};
-static DEFINE_PCI_DEVICE_TABLE(ali1535_ids) = {
+static const struct pci_device_id ali1535_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
{ },
};
diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c
index 4611e4754a67..98a1c97739ba 100644
--- a/drivers/i2c/busses/i2c-ali1563.c
+++ b/drivers/i2c/busses/i2c-ali1563.c
@@ -416,7 +416,7 @@ static void ali1563_remove(struct pci_dev *dev)
ali1563_shutdown(dev);
}
-static DEFINE_PCI_DEVICE_TABLE(ali1563_id_table) = {
+static const struct pci_device_id ali1563_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1563) },
{},
};
diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c
index 4823206a4870..2fa21ce9682b 100644
--- a/drivers/i2c/busses/i2c-ali15x3.c
+++ b/drivers/i2c/busses/i2c-ali15x3.c
@@ -476,7 +476,7 @@ static struct i2c_adapter ali15x3_adapter = {
.algo = &smbus_algorithm,
};
-static DEFINE_PCI_DEVICE_TABLE(ali15x3_ids) = {
+static const struct pci_device_id ali15x3_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c
index 819d3c1062a7..a16f72891358 100644
--- a/drivers/i2c/busses/i2c-amd756.c
+++ b/drivers/i2c/busses/i2c-amd756.c
@@ -307,7 +307,7 @@ static const char* chipname[] = {
"nVidia nForce", "AMD8111",
};
-static DEFINE_PCI_DEVICE_TABLE(amd756_ids) = {
+static const struct pci_device_id amd756_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B),
.driver_data = AMD756 },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413),
diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c
index f3d4d79855b5..95a80a8f81b5 100644
--- a/drivers/i2c/busses/i2c-amd8111.c
+++ b/drivers/i2c/busses/i2c-amd8111.c
@@ -414,7 +414,7 @@ static const struct i2c_algorithm smbus_algorithm = {
};
-static DEFINE_PCI_DEVICE_TABLE(amd8111_ids) = {
+static const struct pci_device_id amd8111_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS2) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index 843d01268ae9..e95f9ba96790 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -32,7 +32,7 @@
#include <linux/slab.h>
#include <linux/platform_data/dma-atmel.h>
-#define TWI_CLK_HZ 100000 /* max 400 Kbits/s */
+#define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */
#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
@@ -711,6 +711,7 @@ static int at91_twi_probe(struct platform_device *pdev)
struct resource *mem;
int rc;
u32 phy_addr;
+ u32 bus_clk_rate;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
@@ -756,13 +757,18 @@ static int at91_twi_probe(struct platform_device *pdev)
dev->use_dma = true;
}
- at91_calc_twi_clock(dev, TWI_CLK_HZ);
+ rc = of_property_read_u32(dev->dev->of_node, "clock-frequency",
+ &bus_clk_rate);
+ if (rc)
+ bus_clk_rate = DEFAULT_TWI_CLK_HZ;
+
+ at91_calc_twi_clock(dev, bus_clk_rate);
at91_init_twi_bus(dev);
snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
i2c_set_adapdata(&dev->adapter, dev);
dev->adapter.owner = THIS_MODULE;
- dev->adapter.class = I2C_CLASS_HWMON;
+ dev->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
dev->adapter.algo = &at91_twi_algorithm;
dev->adapter.dev.parent = dev->dev;
dev->adapter.nr = pdev->id;
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
index 77df97b932af..c60719577fc3 100644
--- a/drivers/i2c/busses/i2c-bcm2835.c
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -219,7 +219,7 @@ static const struct i2c_algorithm bcm2835_i2c_algo = {
static int bcm2835_i2c_probe(struct platform_device *pdev)
{
struct bcm2835_i2c_dev *i2c_dev;
- struct resource *mem, *requested, *irq;
+ struct resource *mem, *irq;
u32 bus_clk_rate, divider;
int ret;
struct i2c_adapter *adap;
@@ -234,25 +234,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
init_completion(&i2c_dev->completion);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "No mem resource\n");
- return -ENODEV;
- }
-
- requested = devm_request_mem_region(&pdev->dev, mem->start,
- resource_size(mem),
- dev_name(&pdev->dev));
- if (!requested) {
- dev_err(&pdev->dev, "Could not claim register region\n");
- return -EBUSY;
- }
-
- i2c_dev->regs = devm_ioremap(&pdev->dev, mem->start,
- resource_size(mem));
- if (!i2c_dev->regs) {
- dev_err(&pdev->dev, "Could not map registers\n");
- return -ENOMEM;
- }
+ i2c_dev->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(i2c_dev->regs))
+ return PTR_ERR(i2c_dev->regs);
i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2c_dev->clk)) {
@@ -295,7 +279,7 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
adap = &i2c_dev->adapter;
i2c_set_adapdata(adap, i2c_dev);
adap->owner = THIS_MODULE;
- adap->class = I2C_CLASS_HWMON;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name));
adap->algo = &bcm2835_i2c_algo;
adap->dev.parent = &pdev->dev;
diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c
index 3b9bd9a3f2b0..e6d5162b6379 100644
--- a/drivers/i2c/busses/i2c-bfin-twi.c
+++ b/drivers/i2c/busses/i2c-bfin-twi.c
@@ -21,10 +21,10 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
+#include <linux/i2c/bfin_twi.h>
-#include <asm/blackfin.h>
-#include <asm/portmux.h>
#include <asm/irq.h>
+#include <asm/portmux.h>
#include <asm/bfin_twi.h>
/* SMBus mode*/
@@ -65,7 +65,6 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface,
/* Transmit next data */
while (iface->writeNum > 0 &&
(read_FIFO_STAT(iface) & XMTSTAT) != XMT_FULL) {
- SSYNC();
write_XMT_DATA8(iface, *(iface->transPtr++));
iface->writeNum--;
}
@@ -248,7 +247,6 @@ static irqreturn_t bfin_twi_interrupt_entry(int irq, void *dev_id)
/* Clear interrupt status */
write_INT_STAT(iface, twi_int_status);
bfin_twi_handle_interrupt(iface, twi_int_status);
- SSYNC();
}
spin_unlock_irqrestore(&iface->lock, flags);
return IRQ_HANDLED;
@@ -294,9 +292,7 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
* discarded before start a new operation.
*/
write_FIFO_CTL(iface, 0x3);
- SSYNC();
write_FIFO_CTL(iface, 0);
- SSYNC();
if (pmsg->flags & I2C_M_RD)
iface->read_write = I2C_SMBUS_READ;
@@ -306,7 +302,6 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
if (iface->writeNum > 0) {
write_XMT_DATA8(iface, *(iface->transPtr++));
iface->writeNum--;
- SSYNC();
}
}
@@ -315,7 +310,6 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
/* Interrupt mask . Enable XMT, RCV interrupt */
write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
- SSYNC();
if (pmsg->len <= 255)
write_MASTER_CTL(iface, pmsg->len << 6);
@@ -329,7 +323,6 @@ static int bfin_twi_do_master_xfer(struct i2c_adapter *adap,
(iface->msg_num > 1 ? RSTART : 0) |
((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
- SSYNC();
while (!iface->result) {
if (!wait_for_completion_timeout(&iface->complete,
@@ -453,7 +446,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
* start a new operation.
*/
write_FIFO_CTL(iface, 0x3);
- SSYNC();
write_FIFO_CTL(iface, 0);
/* clear int stat */
@@ -461,7 +453,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
/* Set Transmit device address */
write_MASTER_ADDR(iface, addr);
- SSYNC();
switch (iface->cur_mode) {
case TWI_I2C_MODE_STANDARDSUB:
@@ -469,7 +460,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
write_INT_MASK(iface, MCOMP | MERR |
((iface->read_write == I2C_SMBUS_READ) ?
RCVSERV : XMTSERV));
- SSYNC();
if (iface->writeNum + 1 <= 255)
write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
@@ -484,7 +474,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
case TWI_I2C_MODE_COMBINED:
write_XMT_DATA8(iface, iface->command);
write_INT_MASK(iface, MCOMP | MERR | RCVSERV | XMTSERV);
- SSYNC();
if (iface->writeNum > 0)
write_MASTER_CTL(iface, (iface->writeNum + 1) << 6);
@@ -531,7 +520,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
write_INT_MASK(iface, MCOMP | MERR |
((iface->read_write == I2C_SMBUS_READ) ?
RCVSERV : XMTSERV));
- SSYNC();
/* Master enable */
write_MASTER_CTL(iface, read_MASTER_CTL(iface) | MEN |
@@ -539,7 +527,6 @@ int bfin_twi_do_smbus_xfer(struct i2c_adapter *adap, u16 addr,
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
break;
}
- SSYNC();
while (!iface->result) {
if (!wait_for_completion_timeout(&iface->complete,
@@ -669,7 +656,7 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name));
p_adap->algo = &bfin_twi_algorithm;
p_adap->algo_data = iface;
- p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ p_adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED;
p_adap->dev.parent = &pdev->dev;
p_adap->timeout = 5 * HZ;
p_adap->retries = 3;
@@ -704,7 +691,6 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev)
/* Enable TWI */
write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA);
- SSYNC();
rc = i2c_add_numbered_adapter(p_adap);
if (rc < 0) {
diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
new file mode 100644
index 000000000000..63f3f03ecc9b
--- /dev/null
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -0,0 +1,905 @@
+/*
+ * I2C bus driver for the Cadence I2C controller.
+ *
+ * Copyright (C) 2009 - 2014 Xilinx, Inc.
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2 of the License, or (at your option) any
+ * later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+/* Register offsets for the I2C device. */
+#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */
+#define CDNS_I2C_SR_OFFSET 0x04 /* Status Register, RO */
+#define CDNS_I2C_ADDR_OFFSET 0x08 /* I2C Address Register, RW */
+#define CDNS_I2C_DATA_OFFSET 0x0C /* I2C Data Register, RW */
+#define CDNS_I2C_ISR_OFFSET 0x10 /* IRQ Status Register, RW */
+#define CDNS_I2C_XFER_SIZE_OFFSET 0x14 /* Transfer Size Register, RW */
+#define CDNS_I2C_TIME_OUT_OFFSET 0x1C /* Time Out Register, RW */
+#define CDNS_I2C_IER_OFFSET 0x24 /* IRQ Enable Register, WO */
+#define CDNS_I2C_IDR_OFFSET 0x28 /* IRQ Disable Register, WO */
+
+/* Control Register Bit mask definitions */
+#define CDNS_I2C_CR_HOLD BIT(4) /* Hold Bus bit */
+#define CDNS_I2C_CR_ACK_EN BIT(3)
+#define CDNS_I2C_CR_NEA BIT(2)
+#define CDNS_I2C_CR_MS BIT(1)
+/* Read or Write Master transfer 0 = Transmitter, 1 = Receiver */
+#define CDNS_I2C_CR_RW BIT(0)
+/* 1 = Auto init FIFO to zeroes */
+#define CDNS_I2C_CR_CLR_FIFO BIT(6)
+#define CDNS_I2C_CR_DIVA_SHIFT 14
+#define CDNS_I2C_CR_DIVA_MASK (3 << CDNS_I2C_CR_DIVA_SHIFT)
+#define CDNS_I2C_CR_DIVB_SHIFT 8
+#define CDNS_I2C_CR_DIVB_MASK (0x3f << CDNS_I2C_CR_DIVB_SHIFT)
+
+/* Status Register Bit mask definitions */
+#define CDNS_I2C_SR_BA BIT(8)
+#define CDNS_I2C_SR_RXDV BIT(5)
+
+/*
+ * I2C Address Register Bit mask definitions
+ * Normal addressing mode uses [6:0] bits. Extended addressing mode uses [9:0]
+ * bits. A write access to this register always initiates a transfer if the I2C
+ * is in master mode.
+ */
+#define CDNS_I2C_ADDR_MASK 0x000003FF /* I2C Address Mask */
+
+/*
+ * I2C Interrupt Registers Bit mask definitions
+ * All the four interrupt registers (Status/Mask/Enable/Disable) have the same
+ * bit definitions.
+ */
+#define CDNS_I2C_IXR_ARB_LOST BIT(9)
+#define CDNS_I2C_IXR_RX_UNF BIT(7)
+#define CDNS_I2C_IXR_TX_OVF BIT(6)
+#define CDNS_I2C_IXR_RX_OVF BIT(5)
+#define CDNS_I2C_IXR_SLV_RDY BIT(4)
+#define CDNS_I2C_IXR_TO BIT(3)
+#define CDNS_I2C_IXR_NACK BIT(2)
+#define CDNS_I2C_IXR_DATA BIT(1)
+#define CDNS_I2C_IXR_COMP BIT(0)
+
+#define CDNS_I2C_IXR_ALL_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \
+ CDNS_I2C_IXR_RX_UNF | \
+ CDNS_I2C_IXR_TX_OVF | \
+ CDNS_I2C_IXR_RX_OVF | \
+ CDNS_I2C_IXR_SLV_RDY | \
+ CDNS_I2C_IXR_TO | \
+ CDNS_I2C_IXR_NACK | \
+ CDNS_I2C_IXR_DATA | \
+ CDNS_I2C_IXR_COMP)
+
+#define CDNS_I2C_IXR_ERR_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \
+ CDNS_I2C_IXR_RX_UNF | \
+ CDNS_I2C_IXR_TX_OVF | \
+ CDNS_I2C_IXR_RX_OVF | \
+ CDNS_I2C_IXR_NACK)
+
+#define CDNS_I2C_ENABLED_INTR_MASK (CDNS_I2C_IXR_ARB_LOST | \
+ CDNS_I2C_IXR_RX_UNF | \
+ CDNS_I2C_IXR_TX_OVF | \
+ CDNS_I2C_IXR_RX_OVF | \
+ CDNS_I2C_IXR_NACK | \
+ CDNS_I2C_IXR_DATA | \
+ CDNS_I2C_IXR_COMP)
+
+#define CDNS_I2C_TIMEOUT msecs_to_jiffies(1000)
+
+#define CDNS_I2C_FIFO_DEPTH 16
+/* FIFO depth at which the DATA interrupt occurs */
+#define CDNS_I2C_DATA_INTR_DEPTH (CDNS_I2C_FIFO_DEPTH - 2)
+#define CDNS_I2C_MAX_TRANSFER_SIZE 255
+/* Transfer size in multiples of data interrupt depth */
+#define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_MAX_TRANSFER_SIZE - 3)
+
+#define DRIVER_NAME "cdns-i2c"
+
+#define CDNS_I2C_SPEED_MAX 400000
+#define CDNS_I2C_SPEED_DEFAULT 100000
+
+#define CDNS_I2C_DIVA_MAX 4
+#define CDNS_I2C_DIVB_MAX 64
+
+#define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset)
+#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
+
+/**
+ * struct cdns_i2c - I2C device private data structure
+ * @membase: Base address of the I2C device
+ * @adap: I2C adapter instance
+ * @p_msg: Message pointer
+ * @err_status: Error status in Interrupt Status Register
+ * @xfer_done: Transfer complete status
+ * @p_send_buf: Pointer to transmit buffer
+ * @p_recv_buf: Pointer to receive buffer
+ * @suspended: Flag holding the device's PM status
+ * @send_count: Number of bytes still expected to send
+ * @recv_count: Number of bytes still expected to receive
+ * @irq: IRQ number
+ * @input_clk: Input clock to I2C controller
+ * @i2c_clk: Maximum I2C clock speed
+ * @bus_hold_flag: Flag used in repeated start for clearing HOLD bit
+ * @clk: Pointer to struct clk
+ * @clk_rate_change_nb: Notifier block for clock rate changes
+ */
+struct cdns_i2c {
+ void __iomem *membase;
+ struct i2c_adapter adap;
+ struct i2c_msg *p_msg;
+ int err_status;
+ struct completion xfer_done;
+ unsigned char *p_send_buf;
+ unsigned char *p_recv_buf;
+ u8 suspended;
+ unsigned int send_count;
+ unsigned int recv_count;
+ int irq;
+ unsigned long input_clk;
+ unsigned int i2c_clk;
+ unsigned int bus_hold_flag;
+ struct clk *clk;
+ struct notifier_block clk_rate_change_nb;
+};
+
+#define to_cdns_i2c(_nb) container_of(_nb, struct cdns_i2c, \
+ clk_rate_change_nb)
+
+/**
+ * cdns_i2c_clear_bus_hold() - Clear bus hold bit
+ * @id: Pointer to driver data struct
+ *
+ * Helper to clear the controller's bus hold bit.
+ */
+static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id)
+{
+ u32 reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+ if (reg & CDNS_I2C_CR_HOLD)
+ cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET);
+}
+
+/**
+ * cdns_i2c_isr - Interrupt handler for the I2C device
+ * @irq: irq number for the I2C device
+ * @ptr: void pointer to cdns_i2c structure
+ *
+ * This function handles the data interrupt, transfer complete interrupt and
+ * the error interrupts of the I2C device.
+ *
+ * Return: IRQ_HANDLED always
+ */
+static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
+{
+ unsigned int isr_status, avail_bytes;
+ unsigned int bytes_to_recv, bytes_to_send;
+ struct cdns_i2c *id = ptr;
+ /* Signal completion only after everything is updated */
+ int done_flag = 0;
+ irqreturn_t status = IRQ_NONE;
+
+ isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
+
+ /* Handling nack and arbitration lost interrupt */
+ if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) {
+ done_flag = 1;
+ status = IRQ_HANDLED;
+ }
+
+ /* Handling Data interrupt */
+ if ((isr_status & CDNS_I2C_IXR_DATA) &&
+ (id->recv_count >= CDNS_I2C_DATA_INTR_DEPTH)) {
+ /* Always read data interrupt threshold bytes */
+ bytes_to_recv = CDNS_I2C_DATA_INTR_DEPTH;
+ id->recv_count -= CDNS_I2C_DATA_INTR_DEPTH;
+ avail_bytes = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
+
+ /*
+ * if the tranfer size register value is zero, then
+ * check for the remaining bytes and update the
+ * transfer size register.
+ */
+ if (!avail_bytes) {
+ if (id->recv_count > CDNS_I2C_TRANSFER_SIZE)
+ cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
+ CDNS_I2C_XFER_SIZE_OFFSET);
+ else
+ cdns_i2c_writereg(id->recv_count,
+ CDNS_I2C_XFER_SIZE_OFFSET);
+ }
+
+ /* Process the data received */
+ while (bytes_to_recv--)
+ *(id->p_recv_buf)++ =
+ cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
+
+ if (!id->bus_hold_flag &&
+ (id->recv_count <= CDNS_I2C_FIFO_DEPTH))
+ cdns_i2c_clear_bus_hold(id);
+
+ status = IRQ_HANDLED;
+ }
+
+ /* Handling Transfer Complete interrupt */
+ if (isr_status & CDNS_I2C_IXR_COMP) {
+ if (!id->p_recv_buf) {
+ /*
+ * If the device is sending data If there is further
+ * data to be sent. Calculate the available space
+ * in FIFO and fill the FIFO with that many bytes.
+ */
+ if (id->send_count) {
+ avail_bytes = CDNS_I2C_FIFO_DEPTH -
+ cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
+ if (id->send_count > avail_bytes)
+ bytes_to_send = avail_bytes;
+ else
+ bytes_to_send = id->send_count;
+
+ while (bytes_to_send--) {
+ cdns_i2c_writereg(
+ (*(id->p_send_buf)++),
+ CDNS_I2C_DATA_OFFSET);
+ id->send_count--;
+ }
+ } else {
+ /*
+ * Signal the completion of transaction and
+ * clear the hold bus bit if there are no
+ * further messages to be processed.
+ */
+ done_flag = 1;
+ }
+ if (!id->send_count && !id->bus_hold_flag)
+ cdns_i2c_clear_bus_hold(id);
+ } else {
+ if (!id->bus_hold_flag)
+ cdns_i2c_clear_bus_hold(id);
+ /*
+ * If the device is receiving data, then signal
+ * the completion of transaction and read the data
+ * present in the FIFO. Signal the completion of
+ * transaction.
+ */
+ while (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) &
+ CDNS_I2C_SR_RXDV) {
+ *(id->p_recv_buf)++ =
+ cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
+ id->recv_count--;
+ }
+ done_flag = 1;
+ }
+
+ status = IRQ_HANDLED;
+ }
+
+ /* Update the status for errors */
+ id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;
+ if (id->err_status)
+ status = IRQ_HANDLED;
+
+ cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
+
+ if (done_flag)
+ complete(&id->xfer_done);
+
+ return status;
+}
+
+/**
+ * cdns_i2c_mrecv - Prepare and start a master receive operation
+ * @id: pointer to the i2c device structure
+ */
+static void cdns_i2c_mrecv(struct cdns_i2c *id)
+{
+ unsigned int ctrl_reg;
+ unsigned int isr_status;
+
+ id->p_recv_buf = id->p_msg->buf;
+ id->recv_count = id->p_msg->len;
+
+ /* Put the controller in master receive mode and clear the FIFO */
+ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+ ctrl_reg |= CDNS_I2C_CR_RW | CDNS_I2C_CR_CLR_FIFO;
+
+ if (id->p_msg->flags & I2C_M_RECV_LEN)
+ id->recv_count = I2C_SMBUS_BLOCK_MAX + 1;
+
+ /*
+ * Check for the message size against FIFO depth and set the
+ * 'hold bus' bit if it is greater than FIFO depth.
+ */
+ if (id->recv_count > CDNS_I2C_FIFO_DEPTH)
+ ctrl_reg |= CDNS_I2C_CR_HOLD;
+
+ cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
+
+ /* Clear the interrupts in interrupt status register */
+ isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
+ cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
+
+ /*
+ * The no. of bytes to receive is checked against the limit of
+ * max transfer size. Set transfer size register with no of bytes
+ * receive if it is less than transfer size and transfer size if
+ * it is more. Enable the interrupts.
+ */
+ if (id->recv_count > CDNS_I2C_TRANSFER_SIZE)
+ cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
+ CDNS_I2C_XFER_SIZE_OFFSET);
+ else
+ cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
+ /* Clear the bus hold flag if bytes to receive is less than FIFO size */
+ if (!id->bus_hold_flag &&
+ ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
+ (id->recv_count <= CDNS_I2C_FIFO_DEPTH))
+ cdns_i2c_clear_bus_hold(id);
+ /* Set the slave address in address register - triggers operation */
+ cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
+ CDNS_I2C_ADDR_OFFSET);
+ cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
+}
+
+/**
+ * cdns_i2c_msend - Prepare and start a master send operation
+ * @id: pointer to the i2c device
+ */
+static void cdns_i2c_msend(struct cdns_i2c *id)
+{
+ unsigned int avail_bytes;
+ unsigned int bytes_to_send;
+ unsigned int ctrl_reg;
+ unsigned int isr_status;
+
+ id->p_recv_buf = NULL;
+ id->p_send_buf = id->p_msg->buf;
+ id->send_count = id->p_msg->len;
+
+ /* Set the controller in Master transmit mode and clear the FIFO. */
+ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+ ctrl_reg &= ~CDNS_I2C_CR_RW;
+ ctrl_reg |= CDNS_I2C_CR_CLR_FIFO;
+
+ /*
+ * Check for the message size against FIFO depth and set the
+ * 'hold bus' bit if it is greater than FIFO depth.
+ */
+ if (id->send_count > CDNS_I2C_FIFO_DEPTH)
+ ctrl_reg |= CDNS_I2C_CR_HOLD;
+ cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
+
+ /* Clear the interrupts in interrupt status register. */
+ isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
+ cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
+
+ /*
+ * Calculate the space available in FIFO. Check the message length
+ * against the space available, and fill the FIFO accordingly.
+ * Enable the interrupts.
+ */
+ avail_bytes = CDNS_I2C_FIFO_DEPTH -
+ cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
+
+ if (id->send_count > avail_bytes)
+ bytes_to_send = avail_bytes;
+ else
+ bytes_to_send = id->send_count;
+
+ while (bytes_to_send--) {
+ cdns_i2c_writereg((*(id->p_send_buf)++), CDNS_I2C_DATA_OFFSET);
+ id->send_count--;
+ }
+
+ /*
+ * Clear the bus hold flag if there is no more data
+ * and if it is the last message.
+ */
+ if (!id->bus_hold_flag && !id->send_count)
+ cdns_i2c_clear_bus_hold(id);
+ /* Set the slave address in address register - triggers operation. */
+ cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
+ CDNS_I2C_ADDR_OFFSET);
+
+ cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
+}
+
+/**
+ * cdns_i2c_master_reset - Reset the interface
+ * @adap: pointer to the i2c adapter driver instance
+ *
+ * This function cleanup the fifos, clear the hold bit and status
+ * and disable the interrupts.
+ */
+static void cdns_i2c_master_reset(struct i2c_adapter *adap)
+{
+ struct cdns_i2c *id = adap->algo_data;
+ u32 regval;
+
+ /* Disable the interrupts */
+ cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET);
+ /* Clear the hold bit and fifos */
+ regval = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+ regval &= ~CDNS_I2C_CR_HOLD;
+ regval |= CDNS_I2C_CR_CLR_FIFO;
+ cdns_i2c_writereg(regval, CDNS_I2C_CR_OFFSET);
+ /* Update the transfercount register to zero */
+ cdns_i2c_writereg(0, CDNS_I2C_XFER_SIZE_OFFSET);
+ /* Clear the interupt status register */
+ regval = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
+ cdns_i2c_writereg(regval, CDNS_I2C_ISR_OFFSET);
+ /* Clear the status register */
+ regval = cdns_i2c_readreg(CDNS_I2C_SR_OFFSET);
+ cdns_i2c_writereg(regval, CDNS_I2C_SR_OFFSET);
+}
+
+static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
+ struct i2c_adapter *adap)
+{
+ int ret;
+ u32 reg;
+
+ id->p_msg = msg;
+ id->err_status = 0;
+ reinit_completion(&id->xfer_done);
+
+ /* Check for the TEN Bit mode on each msg */
+ reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+ if (msg->flags & I2C_M_TEN) {
+ if (reg & CDNS_I2C_CR_NEA)
+ cdns_i2c_writereg(reg & ~CDNS_I2C_CR_NEA,
+ CDNS_I2C_CR_OFFSET);
+ } else {
+ if (!(reg & CDNS_I2C_CR_NEA))
+ cdns_i2c_writereg(reg | CDNS_I2C_CR_NEA,
+ CDNS_I2C_CR_OFFSET);
+ }
+
+ /* Check for the R/W flag on each msg */
+ if (msg->flags & I2C_M_RD)
+ cdns_i2c_mrecv(id);
+ else
+ cdns_i2c_msend(id);
+
+ /* Wait for the signal of completion */
+ ret = wait_for_completion_timeout(&id->xfer_done, adap->timeout);
+ if (!ret) {
+ cdns_i2c_master_reset(adap);
+ dev_err(id->adap.dev.parent,
+ "timeout waiting on completion\n");
+ return -ETIMEDOUT;
+ }
+
+ cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK,
+ CDNS_I2C_IDR_OFFSET);
+
+ /* If it is bus arbitration error, try again */
+ if (id->err_status & CDNS_I2C_IXR_ARB_LOST)
+ return -EAGAIN;
+
+ return 0;
+}
+
+/**
+ * cdns_i2c_master_xfer - The main i2c transfer function
+ * @adap: pointer to the i2c adapter driver instance
+ * @msgs: pointer to the i2c message structure
+ * @num: the number of messages to transfer
+ *
+ * Initiates the send/recv activity based on the transfer message received.
+ *
+ * Return: number of msgs processed on success, negative error otherwise
+ */
+static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ int ret, count;
+ u32 reg;
+ struct cdns_i2c *id = adap->algo_data;
+
+ /* Check if the bus is free */
+ if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
+ return -EAGAIN;
+
+ /*
+ * Set the flag to one when multiple messages are to be
+ * processed with a repeated start.
+ */
+ if (num > 1) {
+ id->bus_hold_flag = 1;
+ reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+ reg |= CDNS_I2C_CR_HOLD;
+ cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET);
+ } else {
+ id->bus_hold_flag = 0;
+ }
+
+ /* Process the msg one by one */
+ for (count = 0; count < num; count++, msgs++) {
+ if (count == (num - 1))
+ id->bus_hold_flag = 0;
+
+ ret = cdns_i2c_process_msg(id, msgs, adap);
+ if (ret)
+ return ret;
+
+ /* Report the other error interrupts to application */
+ if (id->err_status) {
+ cdns_i2c_master_reset(adap);
+
+ if (id->err_status & CDNS_I2C_IXR_NACK)
+ return -ENXIO;
+
+ return -EIO;
+ }
+ }
+
+ return num;
+}
+
+/**
+ * cdns_i2c_func - Returns the supported features of the I2C driver
+ * @adap: pointer to the i2c adapter structure
+ *
+ * Return: 32 bit value, each bit corresponding to a feature
+ */
+static u32 cdns_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
+ (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
+ I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm cdns_i2c_algo = {
+ .master_xfer = cdns_i2c_master_xfer,
+ .functionality = cdns_i2c_func,
+};
+
+/**
+ * cdns_i2c_calc_divs - Calculate clock dividers
+ * @f: I2C clock frequency
+ * @input_clk: Input clock frequency
+ * @a: First divider (return value)
+ * @b: Second divider (return value)
+ *
+ * f is used as input and output variable. As input it is used as target I2C
+ * frequency. On function exit f holds the actually resulting I2C frequency.
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+static int cdns_i2c_calc_divs(unsigned long *f, unsigned long input_clk,
+ unsigned int *a, unsigned int *b)
+{
+ unsigned long fscl = *f, best_fscl = *f, actual_fscl, temp;
+ unsigned int div_a, div_b, calc_div_a = 0, calc_div_b = 0;
+ unsigned int last_error, current_error;
+
+ /* calculate (divisor_a+1) x (divisor_b+1) */
+ temp = input_clk / (22 * fscl);
+
+ /*
+ * If the calculated value is negative or 0, the fscl input is out of
+ * range. Return error.
+ */
+ if (!temp || (temp > (CDNS_I2C_DIVA_MAX * CDNS_I2C_DIVB_MAX)))
+ return -EINVAL;
+
+ last_error = -1;
+ for (div_a = 0; div_a < CDNS_I2C_DIVA_MAX; div_a++) {
+ div_b = DIV_ROUND_UP(input_clk, 22 * fscl * (div_a + 1));
+
+ if ((div_b < 1) || (div_b > CDNS_I2C_DIVB_MAX))
+ continue;
+ div_b--;
+
+ actual_fscl = input_clk / (22 * (div_a + 1) * (div_b + 1));
+
+ if (actual_fscl > fscl)
+ continue;
+
+ current_error = ((actual_fscl > fscl) ? (actual_fscl - fscl) :
+ (fscl - actual_fscl));
+
+ if (last_error > current_error) {
+ calc_div_a = div_a;
+ calc_div_b = div_b;
+ best_fscl = actual_fscl;
+ last_error = current_error;
+ }
+ }
+
+ *a = calc_div_a;
+ *b = calc_div_b;
+ *f = best_fscl;
+
+ return 0;
+}
+
+/**
+ * cdns_i2c_setclk - This function sets the serial clock rate for the I2C device
+ * @clk_in: I2C clock input frequency in Hz
+ * @id: Pointer to the I2C device structure
+ *
+ * The device must be idle rather than busy transferring data before setting
+ * these device options.
+ * The data rate is set by values in the control register.
+ * The formula for determining the correct register values is
+ * Fscl = Fpclk/(22 x (divisor_a+1) x (divisor_b+1))
+ * See the hardware data sheet for a full explanation of setting the serial
+ * clock rate. The clock can not be faster than the input clock divide by 22.
+ * The two most common clock rates are 100KHz and 400KHz.
+ *
+ * Return: 0 on success, negative error otherwise
+ */
+static int cdns_i2c_setclk(unsigned long clk_in, struct cdns_i2c *id)
+{
+ unsigned int div_a, div_b;
+ unsigned int ctrl_reg;
+ int ret = 0;
+ unsigned long fscl = id->i2c_clk;
+
+ ret = cdns_i2c_calc_divs(&fscl, clk_in, &div_a, &div_b);
+ if (ret)
+ return ret;
+
+ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+ ctrl_reg &= ~(CDNS_I2C_CR_DIVA_MASK | CDNS_I2C_CR_DIVB_MASK);
+ ctrl_reg |= ((div_a << CDNS_I2C_CR_DIVA_SHIFT) |
+ (div_b << CDNS_I2C_CR_DIVB_SHIFT));
+ cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
+
+ return 0;
+}
+
+/**
+ * cdns_i2c_clk_notifier_cb - Clock rate change callback
+ * @nb: Pointer to notifier block
+ * @event: Notification reason
+ * @data: Pointer to notification data object
+ *
+ * This function is called when the cdns_i2c input clock frequency changes.
+ * The callback checks whether a valid bus frequency can be generated after the
+ * change. If so, the change is acknowledged, otherwise the change is aborted.
+ * New dividers are written to the HW in the pre- or post change notification
+ * depending on the scaling direction.
+ *
+ * Return: NOTIFY_STOP if the rate change should be aborted, NOTIFY_OK
+ * to acknowedge the change, NOTIFY_DONE if the notification is
+ * considered irrelevant.
+ */
+static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
+ event, void *data)
+{
+ struct clk_notifier_data *ndata = data;
+ struct cdns_i2c *id = to_cdns_i2c(nb);
+
+ if (id->suspended)
+ return NOTIFY_OK;
+
+ switch (event) {
+ case PRE_RATE_CHANGE:
+ {
+ unsigned long input_clk = ndata->new_rate;
+ unsigned long fscl = id->i2c_clk;
+ unsigned int div_a, div_b;
+ int ret;
+
+ ret = cdns_i2c_calc_divs(&fscl, input_clk, &div_a, &div_b);
+ if (ret) {
+ dev_warn(id->adap.dev.parent,
+ "clock rate change rejected\n");
+ return NOTIFY_STOP;
+ }
+
+ /* scale up */
+ if (ndata->new_rate > ndata->old_rate)
+ cdns_i2c_setclk(ndata->new_rate, id);
+
+ return NOTIFY_OK;
+ }
+ case POST_RATE_CHANGE:
+ id->input_clk = ndata->new_rate;
+ /* scale down */
+ if (ndata->new_rate < ndata->old_rate)
+ cdns_i2c_setclk(ndata->new_rate, id);
+ return NOTIFY_OK;
+ case ABORT_RATE_CHANGE:
+ /* scale up */
+ if (ndata->new_rate > ndata->old_rate)
+ cdns_i2c_setclk(ndata->old_rate, id);
+ return NOTIFY_OK;
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+/**
+ * cdns_i2c_suspend - Suspend method for the driver
+ * @_dev: Address of the platform_device structure
+ *
+ * Put the driver into low power mode.
+ *
+ * Return: 0 always
+ */
+static int __maybe_unused cdns_i2c_suspend(struct device *_dev)
+{
+ struct platform_device *pdev = container_of(_dev,
+ struct platform_device, dev);
+ struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
+
+ clk_disable(xi2c->clk);
+ xi2c->suspended = 1;
+
+ return 0;
+}
+
+/**
+ * cdns_i2c_resume - Resume from suspend
+ * @_dev: Address of the platform_device structure
+ *
+ * Resume operation after suspend.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int __maybe_unused cdns_i2c_resume(struct device *_dev)
+{
+ struct platform_device *pdev = container_of(_dev,
+ struct platform_device, dev);
+ struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = clk_enable(xi2c->clk);
+ if (ret) {
+ dev_err(_dev, "Cannot enable clock.\n");
+ return ret;
+ }
+
+ xi2c->suspended = 0;
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(cdns_i2c_dev_pm_ops, cdns_i2c_suspend,
+ cdns_i2c_resume);
+
+/**
+ * cdns_i2c_probe - Platform registration call
+ * @pdev: Handle to the platform device structure
+ *
+ * This function does all the memory allocation and registration for the i2c
+ * device. User can modify the address mode to 10 bit address mode using the
+ * ioctl call with option I2C_TENBIT.
+ *
+ * Return: 0 on success, negative error otherwise
+ */
+static int cdns_i2c_probe(struct platform_device *pdev)
+{
+ struct resource *r_mem;
+ struct cdns_i2c *id;
+ int ret;
+
+ id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL);
+ if (!id)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, id);
+
+ r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ id->membase = devm_ioremap_resource(&pdev->dev, r_mem);
+ if (IS_ERR(id->membase))
+ return PTR_ERR(id->membase);
+
+ id->irq = platform_get_irq(pdev, 0);
+
+ id->adap.dev.of_node = pdev->dev.of_node;
+ id->adap.algo = &cdns_i2c_algo;
+ id->adap.timeout = CDNS_I2C_TIMEOUT;
+ id->adap.retries = 3; /* Default retry value. */
+ id->adap.algo_data = id;
+ id->adap.dev.parent = &pdev->dev;
+ init_completion(&id->xfer_done);
+ snprintf(id->adap.name, sizeof(id->adap.name),
+ "Cadence I2C at %08lx", (unsigned long)r_mem->start);
+
+ id->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(id->clk)) {
+ dev_err(&pdev->dev, "input clock not found.\n");
+ return PTR_ERR(id->clk);
+ }
+ ret = clk_prepare_enable(id->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to enable clock.\n");
+ return ret;
+ }
+ id->clk_rate_change_nb.notifier_call = cdns_i2c_clk_notifier_cb;
+ if (clk_notifier_register(id->clk, &id->clk_rate_change_nb))
+ dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
+ id->input_clk = clk_get_rate(id->clk);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+ &id->i2c_clk);
+ if (ret || (id->i2c_clk > CDNS_I2C_SPEED_MAX))
+ id->i2c_clk = CDNS_I2C_SPEED_DEFAULT;
+
+ cdns_i2c_writereg(CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS,
+ CDNS_I2C_CR_OFFSET);
+
+ ret = cdns_i2c_setclk(id->input_clk, id);
+ if (ret) {
+ dev_err(&pdev->dev, "invalid SCL clock: %u Hz\n", id->i2c_clk);
+ ret = -EINVAL;
+ goto err_clk_dis;
+ }
+
+ ret = devm_request_irq(&pdev->dev, id->irq, cdns_i2c_isr, 0,
+ DRIVER_NAME, id);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot get irq %d\n", id->irq);
+ goto err_clk_dis;
+ }
+
+ ret = i2c_add_adapter(&id->adap);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "reg adap failed: %d\n", ret);
+ goto err_clk_dis;
+ }
+
+ dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
+ id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);
+
+ return 0;
+
+err_clk_dis:
+ clk_disable_unprepare(id->clk);
+ return ret;
+}
+
+/**
+ * cdns_i2c_remove - Unregister the device after releasing the resources
+ * @pdev: Handle to the platform device structure
+ *
+ * This function frees all the resources allocated to the device.
+ *
+ * Return: 0 always
+ */
+static int cdns_i2c_remove(struct platform_device *pdev)
+{
+ struct cdns_i2c *id = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&id->adap);
+ clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
+ clk_disable_unprepare(id->clk);
+
+ return 0;
+}
+
+static const struct of_device_id cdns_i2c_of_match[] = {
+ { .compatible = "cdns,i2c-r1p10", },
+ { /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
+
+static struct platform_driver cdns_i2c_drv = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = cdns_i2c_of_match,
+ .pm = &cdns_i2c_dev_pm_ops,
+ },
+ .probe = cdns_i2c_probe,
+ .remove = cdns_i2c_remove,
+};
+
+module_platform_driver(cdns_i2c_drv);
+
+MODULE_AUTHOR("Xilinx Inc.");
+MODULE_DESCRIPTION("Cadence I2C bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index af0b5830303d..389bc68c55ad 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -712,7 +712,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
- adap->class = I2C_CLASS_HWMON;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
strlcpy(adap->name, "DaVinci I2C adapter", sizeof(adap->name));
adap->algo = &i2c_davinci_algo;
adap->dev.parent = &pdev->dev;
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 14c4b30d4ccc..22e92c3d3d07 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -218,7 +218,7 @@ i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
*
* If your hardware is free from tHD;STA issue, try this one.
*/
- return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset;
+ return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset;
else
/*
* Conditional expression:
@@ -234,7 +234,8 @@ i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
* The reason why we need to take into account "tf" here,
* is the same as described in i2c_dw_scl_lcnt().
*/
- return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset;
+ return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000
+ - 3 + offset;
}
static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
@@ -250,7 +251,7 @@ static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
* account the fall time of SCL signal (tf). Default tf value
* should be 0.3 us, for safety.
*/
- return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset;
+ return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
}
static void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable)
@@ -287,6 +288,7 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
u32 input_clock_khz;
u32 hcnt, lcnt;
u32 reg;
+ u32 sda_falling_time, scl_falling_time;
input_clock_khz = dev->get_clk_rate_khz(dev);
@@ -308,15 +310,18 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
/* set standard and fast speed deviders for high/low periods */
+ sda_falling_time = dev->sda_falling_time ?: 300; /* ns */
+ scl_falling_time = dev->scl_falling_time ?: 300; /* ns */
+
/* Standard-mode */
hcnt = i2c_dw_scl_hcnt(input_clock_khz,
- 40, /* tHD;STA = tHIGH = 4.0 us */
- 3, /* tf = 0.3 us */
+ 4000, /* tHD;STA = tHIGH = 4.0 us */
+ sda_falling_time,
0, /* 0: DW default, 1: Ideal */
0); /* No offset */
lcnt = i2c_dw_scl_lcnt(input_clock_khz,
- 47, /* tLOW = 4.7 us */
- 3, /* tf = 0.3 us */
+ 4700, /* tLOW = 4.7 us */
+ scl_falling_time,
0); /* No offset */
/* Allow platforms to specify the ideal HCNT and LCNT values */
@@ -330,13 +335,13 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
/* Fast-mode */
hcnt = i2c_dw_scl_hcnt(input_clock_khz,
- 6, /* tHD;STA = tHIGH = 0.6 us */
- 3, /* tf = 0.3 us */
+ 600, /* tHD;STA = tHIGH = 0.6 us */
+ sda_falling_time,
0, /* 0: DW default, 1: Ideal */
0); /* No offset */
lcnt = i2c_dw_scl_lcnt(input_clock_khz,
- 13, /* tLOW = 1.3 us */
- 3, /* tf = 0.3 us */
+ 1300, /* tLOW = 1.3 us */
+ scl_falling_time,
0); /* No offset */
if (dev->fs_hcnt && dev->fs_lcnt) {
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index e8a756537ed0..d66b6cbc9edc 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -99,6 +99,8 @@ struct dw_i2c_dev {
unsigned int rx_fifo_depth;
int rx_outstanding;
u32 sda_hold_time;
+ u32 sda_falling_time;
+ u32 scl_falling_time;
u16 ss_hcnt;
u16 ss_lcnt;
u16 fs_hcnt;
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index f6ed06c966ee..85056c22d21e 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -54,6 +54,16 @@ enum dw_pci_ctl_id_t {
medfield_3,
medfield_4,
medfield_5,
+
+ baytrail,
+};
+
+struct dw_scl_sda_cfg {
+ u32 ss_hcnt;
+ u32 fs_hcnt;
+ u32 ss_lcnt;
+ u32 fs_lcnt;
+ u32 sda_hold;
};
struct dw_pci_controller {
@@ -62,12 +72,29 @@ struct dw_pci_controller {
u32 tx_fifo_depth;
u32 rx_fifo_depth;
u32 clk_khz;
+ u32 functionality;
+ struct dw_scl_sda_cfg *scl_sda_cfg;
};
#define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \
DW_IC_CON_SLAVE_DISABLE | \
DW_IC_CON_RESTART_EN)
+#define DW_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
+ I2C_FUNC_SMBUS_BYTE | \
+ I2C_FUNC_SMBUS_BYTE_DATA | \
+ I2C_FUNC_SMBUS_WORD_DATA | \
+ I2C_FUNC_SMBUS_I2C_BLOCK)
+
+/* BayTrail HCNT/LCNT/SDA hold time */
+static struct dw_scl_sda_cfg byt_config = {
+ .ss_hcnt = 0x200,
+ .fs_hcnt = 0x55,
+ .ss_lcnt = 0x200,
+ .fs_lcnt = 0x99,
+ .sda_hold = 0x6,
+};
+
static struct dw_pci_controller dw_pci_controllers[] = {
[moorestown_0] = {
.bus_num = 0,
@@ -132,75 +159,40 @@ static struct dw_pci_controller dw_pci_controllers[] = {
.rx_fifo_depth = 32,
.clk_khz = 25000,
},
+ [baytrail] = {
+ .bus_num = -1,
+ .bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+ .tx_fifo_depth = 32,
+ .rx_fifo_depth = 32,
+ .clk_khz = 100000,
+ .functionality = I2C_FUNC_10BIT_ADDR,
+ .scl_sda_cfg = &byt_config,
+ },
};
static struct i2c_algorithm i2c_dw_algo = {
.master_xfer = i2c_dw_xfer,
.functionality = i2c_dw_func,
};
+#ifdef CONFIG_PM
static int i2c_dw_pci_suspend(struct device *dev)
{
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
- struct dw_i2c_dev *i2c = pci_get_drvdata(pdev);
- int err;
-
-
- i2c_dw_disable(i2c);
-
- err = pci_save_state(pdev);
- if (err) {
- dev_err(&pdev->dev, "pci_save_state failed\n");
- return err;
- }
-
- err = pci_set_power_state(pdev, PCI_D3hot);
- if (err) {
- dev_err(&pdev->dev, "pci_set_power_state failed\n");
- return err;
- }
+ i2c_dw_disable(pci_get_drvdata(pdev));
return 0;
}
static int i2c_dw_pci_resume(struct device *dev)
{
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
- struct dw_i2c_dev *i2c = pci_get_drvdata(pdev);
- int err;
- u32 enabled;
-
- enabled = i2c_dw_is_enabled(i2c);
- if (enabled)
- return 0;
-
- err = pci_set_power_state(pdev, PCI_D0);
- if (err) {
- dev_err(&pdev->dev, "pci_set_power_state() failed\n");
- return err;
- }
-
- pci_restore_state(pdev);
-
- i2c_dw_init(i2c);
- return 0;
-}
-static int i2c_dw_pci_runtime_idle(struct device *dev)
-{
- int err = pm_schedule_suspend(dev, 500);
- dev_dbg(dev, "runtime_idle called\n");
-
- if (err != 0)
- return 0;
- return -EBUSY;
+ return i2c_dw_init(pci_get_drvdata(pdev));
}
+#endif
-static const struct dev_pm_ops i2c_dw_pm_ops = {
- .resume = i2c_dw_pci_resume,
- .suspend = i2c_dw_pci_suspend,
- SET_RUNTIME_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume,
- i2c_dw_pci_runtime_idle)
-};
+static UNIVERSAL_DEV_PM_OPS(i2c_dw_pm_ops, i2c_dw_pci_suspend,
+ i2c_dw_pci_resume, NULL);
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
{
@@ -214,6 +206,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
struct i2c_adapter *adap;
int r;
struct dw_pci_controller *controller;
+ struct dw_scl_sda_cfg *cfg;
if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) {
dev_err(&pdev->dev, "%s: invalid driver data %ld\n", __func__,
@@ -247,13 +240,18 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
dev->base = pcim_iomap_table(pdev)[0];
dev->dev = &pdev->dev;
- dev->functionality =
- I2C_FUNC_I2C |
- I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_I2C_BLOCK;
+ dev->functionality = controller->functionality |
+ DW_DEFAULT_FUNCTIONALITY;
+
dev->master_cfg = controller->bus_cfg;
+ if (controller->scl_sda_cfg) {
+ cfg = controller->scl_sda_cfg;
+ dev->ss_hcnt = cfg->ss_hcnt;
+ dev->fs_hcnt = cfg->fs_hcnt;
+ dev->ss_lcnt = cfg->ss_lcnt;
+ dev->fs_lcnt = cfg->fs_lcnt;
+ dev->sda_hold_time = cfg->sda_hold;
+ }
pci_set_drvdata(pdev, dev);
@@ -270,8 +268,8 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
adap->algo = &i2c_dw_algo;
adap->dev.parent = &pdev->dev;
adap->nr = controller->bus_num;
- snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci-%d",
- adap->nr);
+
+ snprintf(adap->name, sizeof(adap->name), "i2c-designware-pci");
r = devm_request_irq(&pdev->dev, pdev->irq, i2c_dw_isr, IRQF_SHARED,
adap->name, dev);
@@ -290,6 +288,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
pm_runtime_allow(&pdev->dev);
return 0;
@@ -309,7 +308,7 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev)
/* work with hotplug and coldplug */
MODULE_ALIAS("i2c_designware-pci");
-static DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = {
+static const struct pci_device_id i2_designware_pci_ids[] = {
/* Moorestown */
{ PCI_VDEVICE(INTEL, 0x0802), moorestown_0 },
{ PCI_VDEVICE(INTEL, 0x0803), moorestown_1 },
@@ -321,6 +320,14 @@ static DEFINE_PCI_DEVICE_TABLE(i2_designware_pci_ids) = {
{ PCI_VDEVICE(INTEL, 0x082C), medfield_0 },
{ PCI_VDEVICE(INTEL, 0x082D), medfield_1 },
{ PCI_VDEVICE(INTEL, 0x082E), medfield_2 },
+ /* Baytrail */
+ { PCI_VDEVICE(INTEL, 0x0F41), baytrail },
+ { PCI_VDEVICE(INTEL, 0x0F42), baytrail },
+ { PCI_VDEVICE(INTEL, 0x0F43), baytrail },
+ { PCI_VDEVICE(INTEL, 0x0F44), baytrail },
+ { PCI_VDEVICE(INTEL, 0x0F45), baytrail },
+ { PCI_VDEVICE(INTEL, 0x0F46), baytrail },
+ { PCI_VDEVICE(INTEL, 0x0F47), baytrail },
{ 0,}
};
MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index d0bdac0498ce..9c7802614342 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -159,6 +159,13 @@ static int dw_i2c_probe(struct platform_device *pdev)
"i2c-sda-hold-time-ns", &ht);
dev->sda_hold_time = div_u64((u64)ic_clk * ht + 500000,
1000000);
+
+ of_property_read_u32(pdev->dev.of_node,
+ "i2c-sda-falling-time-ns",
+ &dev->sda_falling_time);
+ of_property_read_u32(pdev->dev.of_node,
+ "i2c-scl-falling-time-ns",
+ &dev->scl_falling_time);
}
dev->functionality =
@@ -195,7 +202,7 @@ static int dw_i2c_probe(struct platform_device *pdev)
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
- adap->class = I2C_CLASS_HWMON;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
strlcpy(adap->name, "Synopsys DesignWare I2C adapter",
sizeof(adap->name));
adap->algo = &i2c_dw_algo;
diff --git a/drivers/i2c/busses/i2c-efm32.c b/drivers/i2c/busses/i2c-efm32.c
new file mode 100644
index 000000000000..777ed409a24a
--- /dev/null
+++ b/drivers/i2c/busses/i2c-efm32.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2014 Uwe Kleine-Koenig for Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#define DRIVER_NAME "efm32-i2c"
+
+#define MASK_VAL(mask, val) ((val << __ffs(mask)) & mask)
+
+#define REG_CTRL 0x00
+#define REG_CTRL_EN 0x00001
+#define REG_CTRL_SLAVE 0x00002
+#define REG_CTRL_AUTOACK 0x00004
+#define REG_CTRL_AUTOSE 0x00008
+#define REG_CTRL_AUTOSN 0x00010
+#define REG_CTRL_ARBDIS 0x00020
+#define REG_CTRL_GCAMEN 0x00040
+#define REG_CTRL_CLHR__MASK 0x00300
+#define REG_CTRL_BITO__MASK 0x03000
+#define REG_CTRL_BITO_OFF 0x00000
+#define REG_CTRL_BITO_40PCC 0x01000
+#define REG_CTRL_BITO_80PCC 0x02000
+#define REG_CTRL_BITO_160PCC 0x03000
+#define REG_CTRL_GIBITO 0x08000
+#define REG_CTRL_CLTO__MASK 0x70000
+#define REG_CTRL_CLTO_OFF 0x00000
+
+#define REG_CMD 0x04
+#define REG_CMD_START 0x00001
+#define REG_CMD_STOP 0x00002
+#define REG_CMD_ACK 0x00004
+#define REG_CMD_NACK 0x00008
+#define REG_CMD_CONT 0x00010
+#define REG_CMD_ABORT 0x00020
+#define REG_CMD_CLEARTX 0x00040
+#define REG_CMD_CLEARPC 0x00080
+
+#define REG_STATE 0x08
+#define REG_STATE_BUSY 0x00001
+#define REG_STATE_MASTER 0x00002
+#define REG_STATE_TRANSMITTER 0x00004
+#define REG_STATE_NACKED 0x00008
+#define REG_STATE_BUSHOLD 0x00010
+#define REG_STATE_STATE__MASK 0x000e0
+#define REG_STATE_STATE_IDLE 0x00000
+#define REG_STATE_STATE_WAIT 0x00020
+#define REG_STATE_STATE_START 0x00040
+#define REG_STATE_STATE_ADDR 0x00060
+#define REG_STATE_STATE_ADDRACK 0x00080
+#define REG_STATE_STATE_DATA 0x000a0
+#define REG_STATE_STATE_DATAACK 0x000c0
+
+#define REG_STATUS 0x0c
+#define REG_STATUS_PSTART 0x00001
+#define REG_STATUS_PSTOP 0x00002
+#define REG_STATUS_PACK 0x00004
+#define REG_STATUS_PNACK 0x00008
+#define REG_STATUS_PCONT 0x00010
+#define REG_STATUS_PABORT 0x00020
+#define REG_STATUS_TXC 0x00040
+#define REG_STATUS_TXBL 0x00080
+#define REG_STATUS_RXDATAV 0x00100
+
+#define REG_CLKDIV 0x10
+#define REG_CLKDIV_DIV__MASK 0x001ff
+#define REG_CLKDIV_DIV(div) MASK_VAL(REG_CLKDIV_DIV__MASK, (div))
+
+#define REG_SADDR 0x14
+#define REG_SADDRMASK 0x18
+#define REG_RXDATA 0x1c
+#define REG_RXDATAP 0x20
+#define REG_TXDATA 0x24
+#define REG_IF 0x28
+#define REG_IF_START 0x00001
+#define REG_IF_RSTART 0x00002
+#define REG_IF_ADDR 0x00004
+#define REG_IF_TXC 0x00008
+#define REG_IF_TXBL 0x00010
+#define REG_IF_RXDATAV 0x00020
+#define REG_IF_ACK 0x00040
+#define REG_IF_NACK 0x00080
+#define REG_IF_MSTOP 0x00100
+#define REG_IF_ARBLOST 0x00200
+#define REG_IF_BUSERR 0x00400
+#define REG_IF_BUSHOLD 0x00800
+#define REG_IF_TXOF 0x01000
+#define REG_IF_RXUF 0x02000
+#define REG_IF_BITO 0x04000
+#define REG_IF_CLTO 0x08000
+#define REG_IF_SSTOP 0x10000
+
+#define REG_IFS 0x2c
+#define REG_IFC 0x30
+#define REG_IFC__MASK 0x1ffcf
+
+#define REG_IEN 0x34
+
+#define REG_ROUTE 0x38
+#define REG_ROUTE_SDAPEN 0x00001
+#define REG_ROUTE_SCLPEN 0x00002
+#define REG_ROUTE_LOCATION__MASK 0x00700
+#define REG_ROUTE_LOCATION(n) MASK_VAL(REG_ROUTE_LOCATION__MASK, (n))
+
+struct efm32_i2c_ddata {
+ struct i2c_adapter adapter;
+
+ struct clk *clk;
+ void __iomem *base;
+ unsigned int irq;
+ u8 location;
+ unsigned long frequency;
+
+ /* transfer data */
+ struct completion done;
+ struct i2c_msg *msgs;
+ size_t num_msgs;
+ size_t current_word, current_msg;
+ int retval;
+};
+
+static u32 efm32_i2c_read32(struct efm32_i2c_ddata *ddata, unsigned offset)
+{
+ return readl(ddata->base + offset);
+}
+
+static void efm32_i2c_write32(struct efm32_i2c_ddata *ddata,
+ unsigned offset, u32 value)
+{
+ writel(value, ddata->base + offset);
+}
+
+static void efm32_i2c_send_next_msg(struct efm32_i2c_ddata *ddata)
+{
+ struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
+
+ efm32_i2c_write32(ddata, REG_CMD, REG_CMD_START);
+ efm32_i2c_write32(ddata, REG_TXDATA, cur_msg->addr << 1 |
+ (cur_msg->flags & I2C_M_RD ? 1 : 0));
+}
+
+static void efm32_i2c_send_next_byte(struct efm32_i2c_ddata *ddata)
+{
+ struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
+
+ if (ddata->current_word >= cur_msg->len) {
+ /* cur_msg completely transferred */
+ ddata->current_word = 0;
+ ddata->current_msg += 1;
+
+ if (ddata->current_msg >= ddata->num_msgs) {
+ efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
+ complete(&ddata->done);
+ } else {
+ efm32_i2c_send_next_msg(ddata);
+ }
+ } else {
+ efm32_i2c_write32(ddata, REG_TXDATA,
+ cur_msg->buf[ddata->current_word++]);
+ }
+}
+
+static void efm32_i2c_recv_next_byte(struct efm32_i2c_ddata *ddata)
+{
+ struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
+
+ cur_msg->buf[ddata->current_word] = efm32_i2c_read32(ddata, REG_RXDATA);
+ ddata->current_word += 1;
+ if (ddata->current_word >= cur_msg->len) {
+ /* cur_msg completely transferred */
+ ddata->current_word = 0;
+ ddata->current_msg += 1;
+
+ efm32_i2c_write32(ddata, REG_CMD, REG_CMD_NACK);
+
+ if (ddata->current_msg >= ddata->num_msgs) {
+ efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
+ complete(&ddata->done);
+ } else {
+ efm32_i2c_send_next_msg(ddata);
+ }
+ } else {
+ efm32_i2c_write32(ddata, REG_CMD, REG_CMD_ACK);
+ }
+}
+
+static irqreturn_t efm32_i2c_irq(int irq, void *dev_id)
+{
+ struct efm32_i2c_ddata *ddata = dev_id;
+ struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
+ u32 irqflag = efm32_i2c_read32(ddata, REG_IF);
+ u32 state = efm32_i2c_read32(ddata, REG_STATE);
+
+ efm32_i2c_write32(ddata, REG_IFC, irqflag & REG_IFC__MASK);
+
+ switch (state & REG_STATE_STATE__MASK) {
+ case REG_STATE_STATE_IDLE:
+ /* arbitration lost? */
+ ddata->retval = -EAGAIN;
+ complete(&ddata->done);
+ break;
+ case REG_STATE_STATE_WAIT:
+ /*
+ * huh, this shouldn't happen.
+ * Reset hardware state and get out
+ */
+ ddata->retval = -EIO;
+ efm32_i2c_write32(ddata, REG_CMD,
+ REG_CMD_STOP | REG_CMD_ABORT |
+ REG_CMD_CLEARTX | REG_CMD_CLEARPC);
+ complete(&ddata->done);
+ break;
+ case REG_STATE_STATE_START:
+ /* "caller" is expected to send an address */
+ break;
+ case REG_STATE_STATE_ADDR:
+ /* wait for Ack or NAck of slave */
+ break;
+ case REG_STATE_STATE_ADDRACK:
+ if (state & REG_STATE_NACKED) {
+ efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
+ ddata->retval = -ENXIO;
+ complete(&ddata->done);
+ } else if (cur_msg->flags & I2C_M_RD) {
+ /* wait for slave to send first data byte */
+ } else {
+ efm32_i2c_send_next_byte(ddata);
+ }
+ break;
+ case REG_STATE_STATE_DATA:
+ if (cur_msg->flags & I2C_M_RD) {
+ efm32_i2c_recv_next_byte(ddata);
+ } else {
+ /* wait for Ack or Nack of slave */
+ }
+ break;
+ case REG_STATE_STATE_DATAACK:
+ if (state & REG_STATE_NACKED) {
+ efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
+ complete(&ddata->done);
+ } else {
+ efm32_i2c_send_next_byte(ddata);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int efm32_i2c_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct efm32_i2c_ddata *ddata = i2c_get_adapdata(adap);
+ int ret;
+
+ if (ddata->msgs)
+ return -EBUSY;
+
+ ddata->msgs = msgs;
+ ddata->num_msgs = num;
+ ddata->current_word = 0;
+ ddata->current_msg = 0;
+ ddata->retval = -EIO;
+
+ reinit_completion(&ddata->done);
+
+ dev_dbg(&ddata->adapter.dev, "state: %08x, status: %08x\n",
+ efm32_i2c_read32(ddata, REG_STATE),
+ efm32_i2c_read32(ddata, REG_STATUS));
+
+ efm32_i2c_send_next_msg(ddata);
+
+ wait_for_completion(&ddata->done);
+
+ if (ddata->current_msg >= ddata->num_msgs)
+ ret = ddata->num_msgs;
+ else
+ ret = ddata->retval;
+
+ return ret;
+}
+
+static u32 efm32_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm efm32_i2c_algo = {
+ .master_xfer = efm32_i2c_master_xfer,
+ .functionality = efm32_i2c_functionality,
+};
+
+static u32 efm32_i2c_get_configured_location(struct efm32_i2c_ddata *ddata)
+{
+ u32 reg = efm32_i2c_read32(ddata, REG_ROUTE);
+
+ return (reg & REG_ROUTE_LOCATION__MASK) >>
+ __ffs(REG_ROUTE_LOCATION__MASK);
+}
+
+static int efm32_i2c_probe(struct platform_device *pdev)
+{
+ struct efm32_i2c_ddata *ddata;
+ struct resource *res;
+ unsigned long rate;
+ struct device_node *np = pdev->dev.of_node;
+ u32 location, frequency;
+ int ret;
+ u32 clkdiv;
+
+ if (!np)
+ return -EINVAL;
+
+ ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata) {
+ dev_dbg(&pdev->dev, "failed to allocate private data\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, ddata);
+
+ init_completion(&ddata->done);
+ strlcpy(ddata->adapter.name, pdev->name, sizeof(ddata->adapter.name));
+ ddata->adapter.owner = THIS_MODULE;
+ ddata->adapter.algo = &efm32_i2c_algo;
+ ddata->adapter.dev.parent = &pdev->dev;
+ ddata->adapter.dev.of_node = pdev->dev.of_node;
+ i2c_set_adapdata(&ddata->adapter, ddata);
+
+ ddata->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(ddata->clk)) {
+ ret = PTR_ERR(ddata->clk);
+ dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to determine base address\n");
+ return -ENODEV;
+ }
+
+ if (resource_size(res) < 0x42) {
+ dev_err(&pdev->dev, "memory resource too small\n");
+ return -EINVAL;
+ }
+
+ ddata->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ddata->base))
+ return PTR_ERR(ddata->base);
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret <= 0) {
+ dev_err(&pdev->dev, "failed to get irq (%d)\n", ret);
+ if (!ret)
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ddata->irq = ret;
+
+ ret = clk_prepare_enable(ddata->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret);
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "efm32,location", &location);
+ if (!ret) {
+ dev_dbg(&pdev->dev, "using location %u\n", location);
+ } else {
+ /* default to location configured in hardware */
+ location = efm32_i2c_get_configured_location(ddata);
+
+ dev_info(&pdev->dev, "fall back to location %u\n", location);
+ }
+
+ ddata->location = location;
+
+ ret = of_property_read_u32(np, "clock-frequency", &frequency);
+ if (!ret) {
+ dev_dbg(&pdev->dev, "using frequency %u\n", frequency);
+ } else {
+ frequency = 100000;
+ dev_info(&pdev->dev, "defaulting to 100 kHz\n");
+ }
+ ddata->frequency = frequency;
+
+ rate = clk_get_rate(ddata->clk);
+ if (!rate) {
+ dev_err(&pdev->dev, "there is no input clock available\n");
+ ret = -EINVAL;
+ goto err_disable_clk;
+ }
+ clkdiv = DIV_ROUND_UP(rate, 8 * ddata->frequency) - 1;
+ if (clkdiv >= 0x200) {
+ dev_err(&pdev->dev,
+ "input clock too fast (%lu) to divide down to bus freq (%lu)",
+ rate, ddata->frequency);
+ ret = -EINVAL;
+ goto err_disable_clk;
+ }
+
+ dev_dbg(&pdev->dev, "input clock = %lu, bus freq = %lu, clkdiv = %lu\n",
+ rate, ddata->frequency, (unsigned long)clkdiv);
+ efm32_i2c_write32(ddata, REG_CLKDIV, REG_CLKDIV_DIV(clkdiv));
+
+ efm32_i2c_write32(ddata, REG_ROUTE, REG_ROUTE_SDAPEN |
+ REG_ROUTE_SCLPEN |
+ REG_ROUTE_LOCATION(ddata->location));
+
+ efm32_i2c_write32(ddata, REG_CTRL, REG_CTRL_EN |
+ REG_CTRL_BITO_160PCC | 0 * REG_CTRL_GIBITO);
+
+ efm32_i2c_write32(ddata, REG_IFC, REG_IFC__MASK);
+ efm32_i2c_write32(ddata, REG_IEN, REG_IF_TXC | REG_IF_ACK | REG_IF_NACK
+ | REG_IF_ARBLOST | REG_IF_BUSERR | REG_IF_RXDATAV);
+
+ /* to make bus idle */
+ efm32_i2c_write32(ddata, REG_CMD, REG_CMD_ABORT);
+
+ ret = request_irq(ddata->irq, efm32_i2c_irq, 0, DRIVER_NAME, ddata);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request irq (%d)\n", ret);
+ return ret;
+ }
+
+ ret = i2c_add_adapter(&ddata->adapter);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add i2c adapter (%d)\n", ret);
+ free_irq(ddata->irq, ddata);
+
+err_disable_clk:
+ clk_disable_unprepare(ddata->clk);
+ }
+ return ret;
+}
+
+static int efm32_i2c_remove(struct platform_device *pdev)
+{
+ struct efm32_i2c_ddata *ddata = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&ddata->adapter);
+ free_irq(ddata->irq, ddata);
+ clk_disable_unprepare(ddata->clk);
+
+ return 0;
+}
+
+static const struct of_device_id efm32_i2c_dt_ids[] = {
+ {
+ .compatible = "energymicro,efm32-i2c",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, efm32_i2c_dt_ids);
+
+static struct platform_driver efm32_i2c_driver = {
+ .probe = efm32_i2c_probe,
+ .remove = efm32_i2c_remove,
+
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = efm32_i2c_dt_ids,
+ },
+};
+module_platform_driver(efm32_i2c_driver);
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_DESCRIPTION("EFM32 i2c driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index e08e458bab02..ff775ac29e49 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -186,7 +186,7 @@ static DEFINE_MUTEX(pch_mutex);
#define PCI_DEVICE_ID_ML7223_I2C 0x8010
#define PCI_DEVICE_ID_ML7831_I2C 0x8817
-static DEFINE_PCI_DEVICE_TABLE(pch_pcidev_id) = {
+static const struct pci_device_id pch_pcidev_id[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, },
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index 9fd711c03dd2..00af0a0a3361 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -566,7 +566,7 @@ static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
static int exynos5_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
- struct exynos5_i2c *i2c = (struct exynos5_i2c *)adap->algo_data;
+ struct exynos5_i2c *i2c = adap->algo_data;
int i = 0, ret = 0, stop = 0;
if (i2c->suspended) {
@@ -715,6 +715,7 @@ static int exynos5_i2c_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int exynos5_i2c_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -745,6 +746,7 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(exynos5_i2c_dev_pm_ops, exynos5_i2c_suspend_noirq,
exynos5_i2c_resume_noirq);
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index d9f7e186a4c7..02d2d4abb9dd 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -94,6 +94,9 @@ static int of_i2c_gpio_get_pins(struct device_node *np,
*sda_pin = of_get_gpio(np, 0);
*scl_pin = of_get_gpio(np, 1);
+ if (*sda_pin == -EPROBE_DEFER || *scl_pin == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
if (!gpio_is_valid(*sda_pin) || !gpio_is_valid(*scl_pin)) {
pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n",
np->full_name, *sda_pin, *scl_pin);
diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c
index e248257fe517..14d2b76de25f 100644
--- a/drivers/i2c/busses/i2c-hydra.c
+++ b/drivers/i2c/busses/i2c-hydra.c
@@ -104,7 +104,7 @@ static struct i2c_adapter hydra_adap = {
.algo_data = &hydra_bit_data,
};
-static DEFINE_PCI_DEVICE_TABLE(hydra_ids) = {
+static const struct pci_device_id hydra_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_HYDRA) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 349c2d35e792..6777cd6f8776 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -60,6 +60,7 @@
Wellsburg (PCH) MS 0x8d7f 32 hard yes yes yes
Coleto Creek (PCH) 0x23b0 32 hard yes yes yes
Wildcat Point-LP (PCH) 0x9ca2 32 hard yes yes yes
+ BayTrail (SOC) 0x0f12 32 hard yes yes yes
Features supported by this driver:
Software PEC no
@@ -161,6 +162,7 @@
STATUS_ERROR_FLAGS)
/* Older devices have their ID defined in <linux/pci_ids.h> */
+#define PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS 0x0f12
#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22
/* Patsburg also has three 'Integrated Device Function' SMBus controllers */
@@ -789,7 +791,7 @@ static const struct i2c_algorithm smbus_algorithm = {
.functionality = i801_func,
};
-static DEFINE_PCI_DEVICE_TABLE(i801_ids) = {
+static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_2) },
@@ -822,6 +824,7 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c
index 8ce4f517fc56..984492553e95 100644
--- a/drivers/i2c/busses/i2c-ismt.c
+++ b/drivers/i2c/busses/i2c-ismt.c
@@ -182,7 +182,7 @@ struct ismt_priv {
/**
* ismt_ids - PCI device IDs supported by this driver
*/
-static DEFINE_PCI_DEVICE_TABLE(ismt_ids) = {
+static const struct pci_device_id ismt_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMT) },
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index d52d84937ad3..540ea692bf79 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include <linux/mv643xx_i2c.h>
#include <linux/platform_device.h>
+#include <linux/reset.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -97,7 +98,6 @@ enum {
enum {
MV64XXX_I2C_ACTION_INVALID,
MV64XXX_I2C_ACTION_CONTINUE,
- MV64XXX_I2C_ACTION_SEND_START,
MV64XXX_I2C_ACTION_SEND_RESTART,
MV64XXX_I2C_ACTION_OFFLOAD_RESTART,
MV64XXX_I2C_ACTION_SEND_ADDR_1,
@@ -148,6 +148,8 @@ struct mv64xxx_i2c_data {
bool offload_enabled;
/* 5us delay in order to avoid repeated start timing violation */
bool errata_delay;
+ struct reset_control *rstc;
+ bool irq_clear_inverted;
};
static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = {
@@ -176,11 +178,6 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
{
u32 dir = 0;
- drv_data->msg = msg;
- drv_data->byte_posn = 0;
- drv_data->bytes_left = msg->len;
- drv_data->aborting = 0;
- drv_data->rc = 0;
drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
@@ -206,11 +203,6 @@ static int mv64xxx_i2c_offload_msg(struct mv64xxx_i2c_data *drv_data)
if (!drv_data->offload_enabled)
return -EOPNOTSUPP;
- drv_data->msg = msg;
- drv_data->byte_posn = 0;
- drv_data->bytes_left = msg->len;
- drv_data->aborting = 0;
- drv_data->rc = 0;
/* Only regular transactions can be offloaded */
if ((msg->flags & ~(I2C_M_TEN | I2C_M_RD)) != 0)
return -EINVAL;
@@ -419,6 +411,23 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
}
}
+static void mv64xxx_i2c_send_start(struct mv64xxx_i2c_data *drv_data)
+{
+ drv_data->msg = drv_data->msgs;
+ drv_data->byte_posn = 0;
+ drv_data->bytes_left = drv_data->msg->len;
+ drv_data->aborting = 0;
+ drv_data->rc = 0;
+
+ /* Can we offload this msg ? */
+ if (mv64xxx_i2c_offload_msg(drv_data) < 0) {
+ /* No, switch to standard path */
+ mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs);
+ writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+ drv_data->reg_base + drv_data->reg_offsets.control);
+ }
+}
+
static void
mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
{
@@ -435,14 +444,8 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
drv_data->msgs++;
drv_data->num_msgs--;
- if (mv64xxx_i2c_offload_msg(drv_data) < 0) {
- drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_START;
- writel(drv_data->cntl_bits,
- drv_data->reg_base + drv_data->reg_offsets.control);
+ mv64xxx_i2c_send_start(drv_data);
- /* Setup for the next message */
- mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs);
- }
if (drv_data->errata_delay)
udelay(5);
@@ -459,16 +462,6 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
drv_data->reg_base + drv_data->reg_offsets.control);
break;
- case MV64XXX_I2C_ACTION_SEND_START:
- /* Can we offload this msg ? */
- if (mv64xxx_i2c_offload_msg(drv_data) < 0) {
- /* No, switch to standard path */
- mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs);
- writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
- drv_data->reg_base + drv_data->reg_offsets.control);
- }
- break;
-
case MV64XXX_I2C_ACTION_SEND_ADDR_1:
writel(drv_data->addr1,
drv_data->reg_base + drv_data->reg_offsets.data);
@@ -566,6 +559,11 @@ mv64xxx_i2c_intr(int irq, void *dev_id)
status = readl(drv_data->reg_base + drv_data->reg_offsets.status);
mv64xxx_i2c_fsm(drv_data, status);
mv64xxx_i2c_do_action(drv_data);
+
+ if (drv_data->irq_clear_inverted)
+ writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_IFLG,
+ drv_data->reg_base + drv_data->reg_offsets.control);
+
rc = IRQ_HANDLED;
}
spin_unlock_irqrestore(&drv_data->lock, flags);
@@ -626,12 +624,11 @@ mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg,
spin_lock_irqsave(&drv_data->lock, flags);
- drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
drv_data->send_stop = is_last;
drv_data->block = 1;
- mv64xxx_i2c_do_action(drv_data);
+ mv64xxx_i2c_send_start(drv_data);
spin_unlock_irqrestore(&drv_data->lock, flags);
mv64xxx_i2c_wait_for_completion(drv_data);
@@ -685,6 +682,7 @@ static const struct i2c_algorithm mv64xxx_i2c_algo = {
*/
static const struct of_device_id mv64xxx_i2c_of_match_table[] = {
{ .compatible = "allwinner,sun4i-i2c", .data = &mv64xxx_i2c_regs_sun4i},
+ { .compatible = "allwinner,sun6i-a31-i2c", .data = &mv64xxx_i2c_regs_sun4i},
{ .compatible = "marvell,mv64xxx-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
{ .compatible = "marvell,mv78230-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
{ .compatible = "marvell,mv78230-a0-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
@@ -759,6 +757,16 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
}
drv_data->irq = irq_of_parse_and_map(np, 0);
+ drv_data->rstc = devm_reset_control_get_optional(dev, NULL);
+ if (IS_ERR(drv_data->rstc)) {
+ if (PTR_ERR(drv_data->rstc) == -EPROBE_DEFER) {
+ rc = -EPROBE_DEFER;
+ goto out;
+ }
+ } else {
+ reset_control_deassert(drv_data->rstc);
+ }
+
/* Its not yet defined how timeouts will be specified in device tree.
* So hard code the value to 1 second.
*/
@@ -783,6 +791,10 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
drv_data->offload_enabled = false;
drv_data->errata_delay = true;
}
+
+ if (of_device_is_compatible(np, "allwinner,sun6i-a31-i2c"))
+ drv_data->irq_clear_inverted = true;
+
out:
return rc;
#endif
@@ -845,13 +857,13 @@ mv64xxx_i2c_probe(struct platform_device *pd)
}
if (drv_data->irq < 0) {
rc = -ENXIO;
- goto exit_clk;
+ goto exit_reset;
}
drv_data->adapter.dev.parent = &pd->dev;
drv_data->adapter.algo = &mv64xxx_i2c_algo;
drv_data->adapter.owner = THIS_MODULE;
- drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED;
drv_data->adapter.nr = pd->id;
drv_data->adapter.dev.of_node = pd->dev.of_node;
platform_set_drvdata(pd, drv_data);
@@ -865,7 +877,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
dev_err(&drv_data->adapter.dev,
"mv64xxx: Can't register intr handler irq%d: %d\n",
drv_data->irq, rc);
- goto exit_clk;
+ goto exit_reset;
} else if ((rc = i2c_add_numbered_adapter(&drv_data->adapter)) != 0) {
dev_err(&drv_data->adapter.dev,
"mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
@@ -876,6 +888,9 @@ mv64xxx_i2c_probe(struct platform_device *pd)
exit_free_irq:
free_irq(drv_data->irq, drv_data);
+exit_reset:
+ if (!IS_ERR_OR_NULL(drv_data->rstc))
+ reset_control_assert(drv_data->rstc);
exit_clk:
#if defined(CONFIG_HAVE_CLK)
/* Not all platforms have a clk */
@@ -894,6 +909,8 @@ mv64xxx_i2c_remove(struct platform_device *dev)
i2c_del_adapter(&drv_data->adapter);
free_irq(drv_data->irq, drv_data);
+ if (!IS_ERR_OR_NULL(drv_data->rstc))
+ reset_control_assert(drv_data->rstc);
#if defined(CONFIG_HAVE_CLK)
/* Not all platforms have a clk */
if (!IS_ERR(drv_data->clk)) {
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index 0cde4e6ab2b2..7170fc892829 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -806,7 +806,6 @@ static int mxs_i2c_probe(struct platform_device *pdev)
struct mxs_i2c_dev *i2c;
struct i2c_adapter *adap;
struct resource *res;
- resource_size_t res_size;
int err, irq;
i2c = devm_kzalloc(dev, sizeof(struct mxs_i2c_dev), GFP_KERNEL);
@@ -819,18 +818,13 @@ static int mxs_i2c_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
-
- if (!res || irq < 0)
- return -ENOENT;
+ i2c->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(i2c->regs))
+ return PTR_ERR(i2c->regs);
- res_size = resource_size(res);
- if (!devm_request_mem_region(dev, res->start, res_size, res->name))
- return -EBUSY;
-
- i2c->regs = devm_ioremap_nocache(dev, res->start, res_size);
- if (!i2c->regs)
- return -EBUSY;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
err = devm_request_irq(dev, irq, mxs_i2c_isr, 0, dev_name(dev), i2c);
if (err)
diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c
index 0038c451095c..ee3a76c7ae97 100644
--- a/drivers/i2c/busses/i2c-nforce2.c
+++ b/drivers/i2c/busses/i2c-nforce2.c
@@ -306,7 +306,7 @@ static struct i2c_algorithm smbus_algorithm = {
};
-static DEFINE_PCI_DEVICE_TABLE(nforce2_ids) = {
+static const struct pci_device_id nforce2_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS) },
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 4443613514ee..28cbe1b2a2ec 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -111,22 +111,6 @@ enum i2c_freq_mode {
};
/**
- * struct nmk_i2c_controller - client specific controller configuration
- * @clk_freq: clock frequency for the operation mode
- * @tft: Tx FIFO Threshold in bytes
- * @rft: Rx FIFO Threshold in bytes
- * @timeout Slave response timeout(ms)
- * @sm: speed mode
- */
-struct nmk_i2c_controller {
- u32 clk_freq;
- unsigned char tft;
- unsigned char rft;
- int timeout;
- enum i2c_freq_mode sm;
-};
-
-/**
* struct i2c_vendor_data - per-vendor variations
* @has_mtdws: variant has the MTDWS bit
* @fifodepth: variant FIFO depth
@@ -174,12 +158,15 @@ struct i2c_nmk_client {
* @irq: interrupt line for the controller.
* @virtbase: virtual io memory area.
* @clk: hardware i2c block clock.
- * @cfg: machine provided controller configuration.
* @cli: holder of client specific data.
+ * @clk_freq: clock frequency for the operation mode
+ * @tft: Tx FIFO Threshold in bytes
+ * @rft: Rx FIFO Threshold in bytes
+ * @timeout Slave response timeout (ms)
+ * @sm: speed mode
* @stop: stop condition.
* @xfer_complete: acknowledge completion for a I2C message.
* @result: controller propogated result.
- * @busy: Busy doing transfer.
*/
struct nmk_i2c_dev {
struct i2c_vendor_data *vendor;
@@ -188,12 +175,15 @@ struct nmk_i2c_dev {
int irq;
void __iomem *virtbase;
struct clk *clk;
- struct nmk_i2c_controller cfg;
struct i2c_nmk_client cli;
+ u32 clk_freq;
+ unsigned char tft;
+ unsigned char rft;
+ int timeout;
+ enum i2c_freq_mode sm;
int stop;
struct completion xfer_complete;
int result;
- bool busy;
};
/* controller's abort causes */
@@ -386,7 +376,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
* slsu = cycles / (1000000000 / f) + 1
*/
ns = DIV_ROUND_UP_ULL(1000000000ULL, i2c_clk);
- switch (dev->cfg.sm) {
+ switch (dev->sm) {
case I2C_FREQ_MODE_FAST:
case I2C_FREQ_MODE_FAST_PLUS:
slsu = DIV_ROUND_UP(100, ns); /* Fast */
@@ -409,7 +399,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
* 2 whereas it is 3 for fast and fastplus mode of
* operation. TODO - high speed support.
*/
- div = (dev->cfg.clk_freq > 100000) ? 3 : 2;
+ div = (dev->clk_freq > 100000) ? 3 : 2;
/*
* generate the mask for baud rate counters. The controller
@@ -419,7 +409,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
* so set brcr1 to 0.
*/
brcr1 = 0 << 16;
- brcr2 = (i2c_clk/(dev->cfg.clk_freq * div)) & 0xffff;
+ brcr2 = (i2c_clk/(dev->clk_freq * div)) & 0xffff;
/* set the baud rate counter register */
writel((brcr1 | brcr2), dev->virtbase + I2C_BRCR);
@@ -430,7 +420,7 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
* TODO - support for fast mode plus (up to 1Mb/s)
* and high speed (up to 3.4 Mb/s)
*/
- if (dev->cfg.sm > I2C_FREQ_MODE_FAST) {
+ if (dev->sm > I2C_FREQ_MODE_FAST) {
dev_err(&dev->adev->dev,
"do not support this mode defaulting to std. mode\n");
brcr2 = i2c_clk/(100000 * 2) & 0xffff;
@@ -438,11 +428,11 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
writel(I2C_FREQ_MODE_STANDARD << 4,
dev->virtbase + I2C_CR);
}
- writel(dev->cfg.sm << 4, dev->virtbase + I2C_CR);
+ writel(dev->sm << 4, dev->virtbase + I2C_CR);
/* set the Tx and Rx FIFO threshold */
- writel(dev->cfg.tft, dev->virtbase + I2C_TFTR);
- writel(dev->cfg.rft, dev->virtbase + I2C_RFTR);
+ writel(dev->tft, dev->virtbase + I2C_TFTR);
+ writel(dev->rft, dev->virtbase + I2C_RFTR);
}
/**
@@ -674,28 +664,13 @@ static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num_msgs)
{
- int status;
+ int status = 0;
int i;
struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap);
int j;
- dev->busy = true;
-
pm_runtime_get_sync(&dev->adev->dev);
- status = clk_prepare_enable(dev->clk);
- if (status) {
- dev_err(&dev->adev->dev, "can't prepare_enable clock\n");
- goto out_clk;
- }
-
- /* Optionaly enable pins to be muxed in and configured */
- pinctrl_pm_select_default_state(&dev->adev->dev);
-
- status = init_hw(dev);
- if (status)
- goto out;
-
/* Attempt three times to send the message queue */
for (j = 0; j < 3; j++) {
/* setup the i2c controller */
@@ -716,16 +691,8 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
break;
}
-out:
- clk_disable_unprepare(dev->clk);
-out_clk:
- /* Optionally let pins go into idle state */
- pinctrl_pm_select_idle_state(&dev->adev->dev);
-
pm_runtime_put_sync(&dev->adev->dev);
- dev->busy = false;
-
/* return the no. messages processed */
if (status)
return status;
@@ -909,22 +876,15 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
return IRQ_HANDLED;
}
-
-#ifdef CONFIG_PM
-static int nmk_i2c_suspend(struct device *dev)
+#ifdef CONFIG_PM_SLEEP
+static int nmk_i2c_suspend_late(struct device *dev)
{
- struct amba_device *adev = to_amba_device(dev);
- struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev);
-
- if (nmk_i2c->busy)
- return -EBUSY;
-
pinctrl_pm_select_sleep_state(dev);
return 0;
}
-static int nmk_i2c_resume(struct device *dev)
+static int nmk_i2c_resume_early(struct device *dev)
{
/* First go to the default state */
pinctrl_pm_select_default_state(dev);
@@ -933,19 +893,48 @@ static int nmk_i2c_resume(struct device *dev)
return 0;
}
-#else
-#define nmk_i2c_suspend NULL
-#define nmk_i2c_resume NULL
#endif
-/*
- * We use noirq so that we suspend late and resume before the wakeup interrupt
- * to ensure that we do the !pm_runtime_suspended() check in resume before
- * there has been a regular pm runtime resume (via pm_runtime_get_sync()).
- */
+#ifdef CONFIG_PM
+static int nmk_i2c_runtime_suspend(struct device *dev)
+{
+ struct amba_device *adev = to_amba_device(dev);
+ struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev);
+
+ clk_disable_unprepare(nmk_i2c->clk);
+ pinctrl_pm_select_idle_state(dev);
+ return 0;
+}
+
+static int nmk_i2c_runtime_resume(struct device *dev)
+{
+ struct amba_device *adev = to_amba_device(dev);
+ struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev);
+ int ret;
+
+ ret = clk_prepare_enable(nmk_i2c->clk);
+ if (ret) {
+ dev_err(dev, "can't prepare_enable clock\n");
+ return ret;
+ }
+
+ pinctrl_pm_select_default_state(dev);
+
+ ret = init_hw(nmk_i2c);
+ if (ret) {
+ clk_disable_unprepare(nmk_i2c->clk);
+ pinctrl_pm_select_idle_state(dev);
+ }
+
+ return ret;
+}
+#endif
+
static const struct dev_pm_ops nmk_i2c_pm = {
- .suspend_noirq = nmk_i2c_suspend,
- .resume_noirq = nmk_i2c_resume,
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(nmk_i2c_suspend_late, nmk_i2c_resume_early)
+ SET_PM_RUNTIME_PM_OPS(nmk_i2c_runtime_suspend,
+ nmk_i2c_runtime_resume,
+ NULL)
};
static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
@@ -958,118 +947,98 @@ static const struct i2c_algorithm nmk_i2c_algo = {
.functionality = nmk_i2c_functionality
};
-static struct nmk_i2c_controller u8500_i2c = {
- .tft = 1, /* Tx FIFO threshold */
- .rft = 8, /* Rx FIFO threshold */
- .clk_freq = 400000, /* fast mode operation */
- .timeout = 200, /* Slave response timeout(ms) */
- .sm = I2C_FREQ_MODE_FAST,
-};
-
static void nmk_i2c_of_probe(struct device_node *np,
- struct nmk_i2c_controller *pdata)
+ struct nmk_i2c_dev *nmk)
{
- of_property_read_u32(np, "clock-frequency", &pdata->clk_freq);
+ /* Default to 100 kHz if no frequency is given in the node */
+ if (of_property_read_u32(np, "clock-frequency", &nmk->clk_freq))
+ nmk->clk_freq = 100000;
/* This driver only supports 'standard' and 'fast' modes of operation. */
- if (pdata->clk_freq <= 100000)
- pdata->sm = I2C_FREQ_MODE_STANDARD;
+ if (nmk->clk_freq <= 100000)
+ nmk->sm = I2C_FREQ_MODE_STANDARD;
else
- pdata->sm = I2C_FREQ_MODE_FAST;
+ nmk->sm = I2C_FREQ_MODE_FAST;
+ nmk->tft = 1; /* Tx FIFO threshold */
+ nmk->rft = 8; /* Rx FIFO threshold */
+ nmk->timeout = 200; /* Slave response timeout(ms) */
}
static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret = 0;
- struct nmk_i2c_controller *pdata = dev_get_platdata(&adev->dev);
struct device_node *np = adev->dev.of_node;
struct nmk_i2c_dev *dev;
struct i2c_adapter *adap;
struct i2c_vendor_data *vendor = id->data;
u32 max_fifo_threshold = (vendor->fifodepth / 2) - 1;
- if (!pdata) {
- if (np) {
- pdata = devm_kzalloc(&adev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- ret = -ENOMEM;
- goto err_no_mem;
- }
- /* Provide the default configuration as a base. */
- memcpy(pdata, &u8500_i2c, sizeof(struct nmk_i2c_controller));
- nmk_i2c_of_probe(np, pdata);
- } else
- /* No i2c configuration found, using the default. */
- pdata = &u8500_i2c;
+ dev = devm_kzalloc(&adev->dev, sizeof(struct nmk_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&adev->dev, "cannot allocate memory\n");
+ ret = -ENOMEM;
+ goto err_no_mem;
}
+ dev->vendor = vendor;
+ dev->adev = adev;
+ nmk_i2c_of_probe(np, dev);
- if (pdata->tft > max_fifo_threshold) {
+ if (dev->tft > max_fifo_threshold) {
dev_warn(&adev->dev, "requested TX FIFO threshold %u, adjusted down to %u\n",
- pdata->tft, max_fifo_threshold);
- pdata->tft = max_fifo_threshold;
+ dev->tft, max_fifo_threshold);
+ dev->tft = max_fifo_threshold;
}
- if (pdata->rft > max_fifo_threshold) {
+ if (dev->rft > max_fifo_threshold) {
dev_warn(&adev->dev, "requested RX FIFO threshold %u, adjusted down to %u\n",
- pdata->rft, max_fifo_threshold);
- pdata->rft = max_fifo_threshold;
+ dev->rft, max_fifo_threshold);
+ dev->rft = max_fifo_threshold;
}
- dev = kzalloc(sizeof(struct nmk_i2c_dev), GFP_KERNEL);
- if (!dev) {
- dev_err(&adev->dev, "cannot allocate memory\n");
- ret = -ENOMEM;
- goto err_no_mem;
- }
- dev->vendor = vendor;
- dev->busy = false;
- dev->adev = adev;
amba_set_drvdata(adev, dev);
- /* Select default pin state */
- pinctrl_pm_select_default_state(&adev->dev);
- /* If possible, let's go to idle until the first transfer */
- pinctrl_pm_select_idle_state(&adev->dev);
-
- dev->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
- if (!dev->virtbase) {
+ dev->virtbase = devm_ioremap(&adev->dev, adev->res.start,
+ resource_size(&adev->res));
+ if (IS_ERR(dev->virtbase)) {
ret = -ENOMEM;
- goto err_no_ioremap;
+ goto err_no_mem;
}
dev->irq = adev->irq[0];
- ret = request_irq(dev->irq, i2c_irq_handler, 0,
+ ret = devm_request_irq(&adev->dev, dev->irq, i2c_irq_handler, 0,
DRIVER_NAME, dev);
if (ret) {
dev_err(&adev->dev, "cannot claim the irq %d\n", dev->irq);
- goto err_irq;
+ goto err_no_mem;
}
pm_suspend_ignore_children(&adev->dev, true);
- dev->clk = clk_get(&adev->dev, NULL);
+ dev->clk = devm_clk_get(&adev->dev, NULL);
if (IS_ERR(dev->clk)) {
dev_err(&adev->dev, "could not get i2c clock\n");
ret = PTR_ERR(dev->clk);
- goto err_no_clk;
+ goto err_no_mem;
+ }
+
+ ret = clk_prepare_enable(dev->clk);
+ if (ret) {
+ dev_err(&adev->dev, "can't prepare_enable clock\n");
+ goto err_no_mem;
}
+ init_hw(dev);
+
adap = &dev->adap;
adap->dev.of_node = np;
adap->dev.parent = &adev->dev;
adap->owner = THIS_MODULE;
- adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED;
adap->algo = &nmk_i2c_algo;
- adap->timeout = msecs_to_jiffies(pdata->timeout);
+ adap->timeout = msecs_to_jiffies(dev->timeout);
snprintf(adap->name, sizeof(adap->name),
"Nomadik I2C at %pR", &adev->res);
- /* fetch the controller configuration from machine */
- dev->cfg.clk_freq = pdata->clk_freq;
- dev->cfg.tft = pdata->tft;
- dev->cfg.rft = pdata->rft;
- dev->cfg.sm = pdata->sm;
-
i2c_set_adapdata(adap, dev);
dev_info(&adev->dev,
@@ -1079,21 +1048,15 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
ret = i2c_add_adapter(adap);
if (ret) {
dev_err(&adev->dev, "failed to add adapter\n");
- goto err_add_adap;
+ goto err_no_adap;
}
pm_runtime_put(&adev->dev);
return 0;
- err_add_adap:
- clk_put(dev->clk);
- err_no_clk:
- free_irq(dev->irq, dev);
- err_irq:
- iounmap(dev->virtbase);
- err_no_ioremap:
- kfree(dev);
+ err_no_adap:
+ clk_disable_unprepare(dev->clk);
err_no_mem:
return ret;
@@ -1110,13 +1073,9 @@ static int nmk_i2c_remove(struct amba_device *adev)
clear_all_interrupts(dev);
/* disable the controller */
i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
- free_irq(dev->irq, dev);
- iounmap(dev->virtbase);
+ clk_disable_unprepare(dev->clk);
if (res)
release_mem_region(res->start, resource_size(res));
- clk_put(dev->clk);
- pm_runtime_disable(&adev->dev);
- kfree(dev);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index 80e06fa45720..1f6369f14fb6 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -246,7 +246,7 @@ static const struct i2c_algorithm ocores_algorithm = {
static struct i2c_adapter ocores_adapter = {
.owner = THIS_MODULE,
.name = "i2c-ocores",
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED,
.algo = &ocores_algorithm,
};
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 90dcc2eaac5f..85f8eac9ba18 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -636,7 +636,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
int r;
r = pm_runtime_get_sync(dev->dev);
- if (IS_ERR_VALUE(r))
+ if (r < 0)
goto out;
r = omap_i2c_wait_for_bb(dev);
@@ -1155,7 +1155,7 @@ omap_i2c_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(dev->dev);
r = pm_runtime_get_sync(dev->dev);
- if (IS_ERR_VALUE(r))
+ if (r < 0)
goto err_free_mem;
/*
@@ -1238,7 +1238,7 @@ omap_i2c_probe(struct platform_device *pdev)
adap = &dev->adapter;
i2c_set_adapdata(adap, dev);
adap->owner = THIS_MODULE;
- adap->class = I2C_CLASS_HWMON;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));
adap->algo = &omap_i2c_algo;
adap->dev.parent = &pdev->dev;
@@ -1276,7 +1276,7 @@ static int omap_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&dev->adapter);
ret = pm_runtime_get_sync(&pdev->dev);
- if (IS_ERR_VALUE(ret))
+ if (ret < 0)
return ret;
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c
index 615f632c846f..7a9dce43e115 100644
--- a/drivers/i2c/busses/i2c-pasemi.c
+++ b/drivers/i2c/busses/i2c-pasemi.c
@@ -401,7 +401,7 @@ static void pasemi_smb_remove(struct pci_dev *dev)
kfree(smbus);
}
-static DEFINE_PCI_DEVICE_TABLE(pasemi_smb_ids) = {
+static const struct pci_device_id pasemi_smb_ids[] = {
{ PCI_DEVICE(0x1959, 0xa003) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 39dd8ec60dfd..a6f54ba27e2a 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -540,7 +540,7 @@ static const struct i2c_algorithm smbus_algorithm = {
.functionality = piix4_func,
};
-static DEFINE_PCI_DEVICE_TABLE(piix4_ids) = {
+static const struct pci_device_id piix4_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) },
{ PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3) },
diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c
index 9639be86e53f..417464e9ea2a 100644
--- a/drivers/i2c/busses/i2c-pxa-pci.c
+++ b/drivers/i2c/busses/i2c-pxa-pci.c
@@ -148,7 +148,7 @@ static void ce4100_i2c_remove(struct pci_dev *dev)
kfree(sds);
}
-static DEFINE_PCI_DEVICE_TABLE(ce4100_i2c_devices) = {
+static const struct pci_device_id ce4100_i2c_devices[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e68)},
{ },
};
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
new file mode 100644
index 000000000000..1b4cf14f1106
--- /dev/null
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, Sony Mobile Communications AB.
+ *
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+/* QUP Registers */
+#define QUP_CONFIG 0x000
+#define QUP_STATE 0x004
+#define QUP_IO_MODE 0x008
+#define QUP_SW_RESET 0x00c
+#define QUP_OPERATIONAL 0x018
+#define QUP_ERROR_FLAGS 0x01c
+#define QUP_ERROR_FLAGS_EN 0x020
+#define QUP_HW_VERSION 0x030
+#define QUP_MX_OUTPUT_CNT 0x100
+#define QUP_OUT_FIFO_BASE 0x110
+#define QUP_MX_WRITE_CNT 0x150
+#define QUP_MX_INPUT_CNT 0x200
+#define QUP_MX_READ_CNT 0x208
+#define QUP_IN_FIFO_BASE 0x218
+#define QUP_I2C_CLK_CTL 0x400
+#define QUP_I2C_STATUS 0x404
+
+/* QUP States and reset values */
+#define QUP_RESET_STATE 0
+#define QUP_RUN_STATE 1
+#define QUP_PAUSE_STATE 3
+#define QUP_STATE_MASK 3
+
+#define QUP_STATE_VALID BIT(2)
+#define QUP_I2C_MAST_GEN BIT(4)
+
+#define QUP_OPERATIONAL_RESET 0x000ff0
+#define QUP_I2C_STATUS_RESET 0xfffffc
+
+/* QUP OPERATIONAL FLAGS */
+#define QUP_I2C_NACK_FLAG BIT(3)
+#define QUP_OUT_NOT_EMPTY BIT(4)
+#define QUP_IN_NOT_EMPTY BIT(5)
+#define QUP_OUT_FULL BIT(6)
+#define QUP_OUT_SVC_FLAG BIT(8)
+#define QUP_IN_SVC_FLAG BIT(9)
+#define QUP_MX_OUTPUT_DONE BIT(10)
+#define QUP_MX_INPUT_DONE BIT(11)
+
+/* I2C mini core related values */
+#define QUP_CLOCK_AUTO_GATE BIT(13)
+#define I2C_MINI_CORE (2 << 8)
+#define I2C_N_VAL 15
+/* Most significant word offset in FIFO port */
+#define QUP_MSW_SHIFT (I2C_N_VAL + 1)
+
+/* Packing/Unpacking words in FIFOs, and IO modes */
+#define QUP_OUTPUT_BLK_MODE (1 << 10)
+#define QUP_INPUT_BLK_MODE (1 << 12)
+#define QUP_UNPACK_EN BIT(14)
+#define QUP_PACK_EN BIT(15)
+
+#define QUP_REPACK_EN (QUP_UNPACK_EN | QUP_PACK_EN)
+
+#define QUP_OUTPUT_BLOCK_SIZE(x)(((x) >> 0) & 0x03)
+#define QUP_OUTPUT_FIFO_SIZE(x) (((x) >> 2) & 0x07)
+#define QUP_INPUT_BLOCK_SIZE(x) (((x) >> 5) & 0x03)
+#define QUP_INPUT_FIFO_SIZE(x) (((x) >> 7) & 0x07)
+
+/* QUP tags */
+#define QUP_TAG_START (1 << 8)
+#define QUP_TAG_DATA (2 << 8)
+#define QUP_TAG_STOP (3 << 8)
+#define QUP_TAG_REC (4 << 8)
+
+/* Status, Error flags */
+#define I2C_STATUS_WR_BUFFER_FULL BIT(0)
+#define I2C_STATUS_BUS_ACTIVE BIT(8)
+#define I2C_STATUS_ERROR_MASK 0x38000fc
+#define QUP_STATUS_ERROR_FLAGS 0x7c
+
+#define QUP_READ_LIMIT 256
+
+struct qup_i2c_dev {
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ struct clk *clk;
+ struct clk *pclk;
+ struct i2c_adapter adap;
+
+ int clk_ctl;
+ int out_fifo_sz;
+ int in_fifo_sz;
+ int out_blk_sz;
+ int in_blk_sz;
+
+ unsigned long one_byte_t;
+
+ struct i2c_msg *msg;
+ /* Current posion in user message buffer */
+ int pos;
+ /* I2C protocol errors */
+ u32 bus_err;
+ /* QUP core errors */
+ u32 qup_err;
+
+ struct completion xfer;
+};
+
+static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
+{
+ struct qup_i2c_dev *qup = dev;
+ u32 bus_err;
+ u32 qup_err;
+ u32 opflags;
+
+ bus_err = readl(qup->base + QUP_I2C_STATUS);
+ qup_err = readl(qup->base + QUP_ERROR_FLAGS);
+ opflags = readl(qup->base + QUP_OPERATIONAL);
+
+ if (!qup->msg) {
+ /* Clear Error interrupt */
+ writel(QUP_RESET_STATE, qup->base + QUP_STATE);
+ return IRQ_HANDLED;
+ }
+
+ bus_err &= I2C_STATUS_ERROR_MASK;
+ qup_err &= QUP_STATUS_ERROR_FLAGS;
+
+ if (qup_err) {
+ /* Clear Error interrupt */
+ writel(qup_err, qup->base + QUP_ERROR_FLAGS);
+ goto done;
+ }
+
+ if (bus_err) {
+ /* Clear Error interrupt */
+ writel(QUP_RESET_STATE, qup->base + QUP_STATE);
+ goto done;
+ }
+
+ if (opflags & QUP_IN_SVC_FLAG)
+ writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+
+ if (opflags & QUP_OUT_SVC_FLAG)
+ writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL);
+
+done:
+ qup->qup_err = qup_err;
+ qup->bus_err = bus_err;
+ complete(&qup->xfer);
+ return IRQ_HANDLED;
+}
+
+static int qup_i2c_poll_state_mask(struct qup_i2c_dev *qup,
+ u32 req_state, u32 req_mask)
+{
+ int retries = 1;
+ u32 state;
+
+ /*
+ * State transition takes 3 AHB clocks cycles + 3 I2C master clock
+ * cycles. So retry once after a 1uS delay.
+ */
+ do {
+ state = readl(qup->base + QUP_STATE);
+
+ if (state & QUP_STATE_VALID &&
+ (state & req_mask) == req_state)
+ return 0;
+
+ udelay(1);
+ } while (retries--);
+
+ return -ETIMEDOUT;
+}
+
+static int qup_i2c_poll_state(struct qup_i2c_dev *qup, u32 req_state)
+{
+ return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK);
+}
+
+static int qup_i2c_poll_state_valid(struct qup_i2c_dev *qup)
+{
+ return qup_i2c_poll_state_mask(qup, 0, 0);
+}
+
+static int qup_i2c_poll_state_i2c_master(struct qup_i2c_dev *qup)
+{
+ return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN);
+}
+
+static int qup_i2c_change_state(struct qup_i2c_dev *qup, u32 state)
+{
+ if (qup_i2c_poll_state_valid(qup) != 0)
+ return -EIO;
+
+ writel(state, qup->base + QUP_STATE);
+
+ if (qup_i2c_poll_state(qup, state) != 0)
+ return -EIO;
+ return 0;
+}
+
+static int qup_i2c_wait_writeready(struct qup_i2c_dev *qup)
+{
+ unsigned long timeout;
+ u32 opflags;
+ u32 status;
+
+ timeout = jiffies + HZ;
+
+ for (;;) {
+ opflags = readl(qup->base + QUP_OPERATIONAL);
+ status = readl(qup->base + QUP_I2C_STATUS);
+
+ if (!(opflags & QUP_OUT_NOT_EMPTY) &&
+ !(status & I2C_STATUS_BUS_ACTIVE))
+ return 0;
+
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ usleep_range(qup->one_byte_t, qup->one_byte_t * 2);
+ }
+}
+
+static void qup_i2c_set_write_mode(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ /* Number of entries to shift out, including the start */
+ int total = msg->len + 1;
+
+ if (total < qup->out_fifo_sz) {
+ /* FIFO mode */
+ writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
+ writel(total, qup->base + QUP_MX_WRITE_CNT);
+ } else {
+ /* BLOCK mode (transfer data on chunks) */
+ writel(QUP_OUTPUT_BLK_MODE | QUP_REPACK_EN,
+ qup->base + QUP_IO_MODE);
+ writel(total, qup->base + QUP_MX_OUTPUT_CNT);
+ }
+}
+
+static void qup_i2c_issue_write(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ u32 addr = msg->addr << 1;
+ u32 qup_tag;
+ u32 opflags;
+ int idx;
+ u32 val;
+
+ if (qup->pos == 0) {
+ val = QUP_TAG_START | addr;
+ idx = 1;
+ } else {
+ val = 0;
+ idx = 0;
+ }
+
+ while (qup->pos < msg->len) {
+ /* Check that there's space in the FIFO for our pair */
+ opflags = readl(qup->base + QUP_OPERATIONAL);
+ if (opflags & QUP_OUT_FULL)
+ break;
+
+ if (qup->pos == msg->len - 1)
+ qup_tag = QUP_TAG_STOP;
+ else
+ qup_tag = QUP_TAG_DATA;
+
+ if (idx & 1)
+ val |= (qup_tag | msg->buf[qup->pos]) << QUP_MSW_SHIFT;
+ else
+ val = qup_tag | msg->buf[qup->pos];
+
+ /* Write out the pair and the last odd value */
+ if (idx & 1 || qup->pos == msg->len - 1)
+ writel(val, qup->base + QUP_OUT_FIFO_BASE);
+
+ qup->pos++;
+ idx++;
+ }
+}
+
+static int qup_i2c_write_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ unsigned long left;
+ int ret;
+
+ qup->msg = msg;
+ qup->pos = 0;
+
+ enable_irq(qup->irq);
+
+ qup_i2c_set_write_mode(qup, msg);
+
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ goto err;
+
+ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+
+ do {
+ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+ if (ret)
+ goto err;
+
+ qup_i2c_issue_write(qup, msg);
+
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ goto err;
+
+ left = wait_for_completion_timeout(&qup->xfer, HZ);
+ if (!left) {
+ writel(1, qup->base + QUP_SW_RESET);
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (qup->bus_err || qup->qup_err) {
+ if (qup->bus_err & QUP_I2C_NACK_FLAG)
+ dev_err(qup->dev, "NACK from %x\n", msg->addr);
+ ret = -EIO;
+ goto err;
+ }
+ } while (qup->pos < msg->len);
+
+ /* Wait for the outstanding data in the fifo to drain */
+ ret = qup_i2c_wait_writeready(qup);
+
+err:
+ disable_irq(qup->irq);
+ qup->msg = NULL;
+
+ return ret;
+}
+
+static void qup_i2c_set_read_mode(struct qup_i2c_dev *qup, int len)
+{
+ if (len < qup->in_fifo_sz) {
+ /* FIFO mode */
+ writel(QUP_REPACK_EN, qup->base + QUP_IO_MODE);
+ writel(len, qup->base + QUP_MX_READ_CNT);
+ } else {
+ /* BLOCK mode (transfer data on chunks) */
+ writel(QUP_INPUT_BLK_MODE | QUP_REPACK_EN,
+ qup->base + QUP_IO_MODE);
+ writel(len, qup->base + QUP_MX_INPUT_CNT);
+ }
+}
+
+static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ u32 addr, len, val;
+
+ addr = (msg->addr << 1) | 1;
+
+ /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
+ len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
+
+ val = ((QUP_TAG_REC | len) << QUP_MSW_SHIFT) | QUP_TAG_START | addr;
+ writel(val, qup->base + QUP_OUT_FIFO_BASE);
+}
+
+
+static void qup_i2c_read_fifo(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ u32 opflags;
+ u32 val = 0;
+ int idx;
+
+ for (idx = 0; qup->pos < msg->len; idx++) {
+ if ((idx & 1) == 0) {
+ /* Check that FIFO have data */
+ opflags = readl(qup->base + QUP_OPERATIONAL);
+ if (!(opflags & QUP_IN_NOT_EMPTY))
+ break;
+
+ /* Reading 2 words at time */
+ val = readl(qup->base + QUP_IN_FIFO_BASE);
+
+ msg->buf[qup->pos++] = val & 0xFF;
+ } else {
+ msg->buf[qup->pos++] = val >> QUP_MSW_SHIFT;
+ }
+ }
+}
+
+static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg)
+{
+ unsigned long left;
+ int ret;
+
+ /*
+ * The QUP block will issue a NACK and STOP on the bus when reaching
+ * the end of the read, the length of the read is specified as one byte
+ * which limits the possible read to 256 (QUP_READ_LIMIT) bytes.
+ */
+ if (msg->len > QUP_READ_LIMIT) {
+ dev_err(qup->dev, "HW not capable of reads over %d bytes\n",
+ QUP_READ_LIMIT);
+ return -EINVAL;
+ }
+
+ qup->msg = msg;
+ qup->pos = 0;
+
+ enable_irq(qup->irq);
+
+ qup_i2c_set_read_mode(qup, msg->len);
+
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ goto err;
+
+ writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL);
+
+ ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE);
+ if (ret)
+ goto err;
+
+ qup_i2c_issue_read(qup, msg);
+
+ ret = qup_i2c_change_state(qup, QUP_RUN_STATE);
+ if (ret)
+ goto err;
+
+ do {
+ left = wait_for_completion_timeout(&qup->xfer, HZ);
+ if (!left) {
+ writel(1, qup->base + QUP_SW_RESET);
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (qup->bus_err || qup->qup_err) {
+ if (qup->bus_err & QUP_I2C_NACK_FLAG)
+ dev_err(qup->dev, "NACK from %x\n", msg->addr);
+ ret = -EIO;
+ goto err;
+ }
+
+ qup_i2c_read_fifo(qup, msg);
+ } while (qup->pos < msg->len);
+
+err:
+ disable_irq(qup->irq);
+ qup->msg = NULL;
+
+ return ret;
+}
+
+static int qup_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[],
+ int num)
+{
+ struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
+ int ret, idx;
+
+ ret = pm_runtime_get_sync(qup->dev);
+ if (ret)
+ goto out;
+
+ writel(1, qup->base + QUP_SW_RESET);
+ ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
+ if (ret)
+ goto out;
+
+ /* Configure QUP as I2C mini core */
+ writel(I2C_MINI_CORE | I2C_N_VAL, qup->base + QUP_CONFIG);
+
+ for (idx = 0; idx < num; idx++) {
+ if (msgs[idx].len == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (qup_i2c_poll_state_i2c_master(qup)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (msgs[idx].flags & I2C_M_RD)
+ ret = qup_i2c_read_one(qup, &msgs[idx]);
+ else
+ ret = qup_i2c_write_one(qup, &msgs[idx]);
+
+ if (ret)
+ break;
+
+ ret = qup_i2c_change_state(qup, QUP_RESET_STATE);
+ if (ret)
+ break;
+ }
+
+ if (ret == 0)
+ ret = num;
+out:
+
+ pm_runtime_mark_last_busy(qup->dev);
+ pm_runtime_put_autosuspend(qup->dev);
+
+ return ret;
+}
+
+static u32 qup_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm qup_i2c_algo = {
+ .master_xfer = qup_i2c_xfer,
+ .functionality = qup_i2c_func,
+};
+
+static void qup_i2c_enable_clocks(struct qup_i2c_dev *qup)
+{
+ clk_prepare_enable(qup->clk);
+ clk_prepare_enable(qup->pclk);
+}
+
+static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup)
+{
+ u32 config;
+
+ qup_i2c_change_state(qup, QUP_RESET_STATE);
+ clk_disable_unprepare(qup->clk);
+ config = readl(qup->base + QUP_CONFIG);
+ config |= QUP_CLOCK_AUTO_GATE;
+ writel(config, qup->base + QUP_CONFIG);
+ clk_disable_unprepare(qup->pclk);
+}
+
+static int qup_i2c_probe(struct platform_device *pdev)
+{
+ static const int blk_sizes[] = {4, 16, 32};
+ struct device_node *node = pdev->dev.of_node;
+ struct qup_i2c_dev *qup;
+ unsigned long one_bit_t;
+ struct resource *res;
+ u32 io_mode, hw_ver, size;
+ int ret, fs_div, hs_div;
+ int src_clk_freq;
+ u32 clk_freq = 100000;
+
+ qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
+ if (!qup)
+ return -ENOMEM;
+
+ qup->dev = &pdev->dev;
+ init_completion(&qup->xfer);
+ platform_set_drvdata(pdev, qup);
+
+ of_property_read_u32(node, "clock-frequency", &clk_freq);
+
+ /* We support frequencies up to FAST Mode (400KHz) */
+ if (!clk_freq || clk_freq > 400000) {
+ dev_err(qup->dev, "clock frequency not supported %d\n",
+ clk_freq);
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ qup->base = devm_ioremap_resource(qup->dev, res);
+ if (IS_ERR(qup->base))
+ return PTR_ERR(qup->base);
+
+ qup->irq = platform_get_irq(pdev, 0);
+ if (qup->irq < 0) {
+ dev_err(qup->dev, "No IRQ defined\n");
+ return qup->irq;
+ }
+
+ qup->clk = devm_clk_get(qup->dev, "core");
+ if (IS_ERR(qup->clk)) {
+ dev_err(qup->dev, "Could not get core clock\n");
+ return PTR_ERR(qup->clk);
+ }
+
+ qup->pclk = devm_clk_get(qup->dev, "iface");
+ if (IS_ERR(qup->pclk)) {
+ dev_err(qup->dev, "Could not get iface clock\n");
+ return PTR_ERR(qup->pclk);
+ }
+
+ qup_i2c_enable_clocks(qup);
+
+ /*
+ * Bootloaders might leave a pending interrupt on certain QUP's,
+ * so we reset the core before registering for interrupts.
+ */
+ writel(1, qup->base + QUP_SW_RESET);
+ ret = qup_i2c_poll_state_valid(qup);
+ if (ret)
+ goto fail;
+
+ ret = devm_request_irq(qup->dev, qup->irq, qup_i2c_interrupt,
+ IRQF_TRIGGER_HIGH, "i2c_qup", qup);
+ if (ret) {
+ dev_err(qup->dev, "Request %d IRQ failed\n", qup->irq);
+ goto fail;
+ }
+ disable_irq(qup->irq);
+
+ hw_ver = readl(qup->base + QUP_HW_VERSION);
+ dev_dbg(qup->dev, "Revision %x\n", hw_ver);
+
+ io_mode = readl(qup->base + QUP_IO_MODE);
+
+ /*
+ * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
+ * associated with each byte written/received
+ */
+ size = QUP_OUTPUT_BLOCK_SIZE(io_mode);
+ if (size >= ARRAY_SIZE(blk_sizes))
+ return -EIO;
+ qup->out_blk_sz = blk_sizes[size] / 2;
+
+ size = QUP_INPUT_BLOCK_SIZE(io_mode);
+ if (size >= ARRAY_SIZE(blk_sizes))
+ return -EIO;
+ qup->in_blk_sz = blk_sizes[size] / 2;
+
+ size = QUP_OUTPUT_FIFO_SIZE(io_mode);
+ qup->out_fifo_sz = qup->out_blk_sz * (2 << size);
+
+ size = QUP_INPUT_FIFO_SIZE(io_mode);
+ qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
+
+ src_clk_freq = clk_get_rate(qup->clk);
+ fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
+ hs_div = 3;
+ qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
+
+ /*
+ * Time it takes for a byte to be clocked out on the bus.
+ * Each byte takes 9 clock cycles (8 bits + 1 ack).
+ */
+ one_bit_t = (USEC_PER_SEC / clk_freq) + 1;
+ qup->one_byte_t = one_bit_t * 9;
+
+ dev_dbg(qup->dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
+ qup->in_blk_sz, qup->in_fifo_sz,
+ qup->out_blk_sz, qup->out_fifo_sz);
+
+ i2c_set_adapdata(&qup->adap, qup);
+ qup->adap.algo = &qup_i2c_algo;
+ qup->adap.dev.parent = qup->dev;
+ qup->adap.dev.of_node = pdev->dev.of_node;
+ strlcpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name));
+
+ ret = i2c_add_adapter(&qup->adap);
+ if (ret)
+ goto fail;
+
+ pm_runtime_set_autosuspend_delay(qup->dev, MSEC_PER_SEC);
+ pm_runtime_use_autosuspend(qup->dev);
+ pm_runtime_set_active(qup->dev);
+ pm_runtime_enable(qup->dev);
+ return 0;
+
+fail:
+ qup_i2c_disable_clocks(qup);
+ return ret;
+}
+
+static int qup_i2c_remove(struct platform_device *pdev)
+{
+ struct qup_i2c_dev *qup = platform_get_drvdata(pdev);
+
+ disable_irq(qup->irq);
+ qup_i2c_disable_clocks(qup);
+ i2c_del_adapter(&qup->adap);
+ pm_runtime_disable(qup->dev);
+ pm_runtime_set_suspended(qup->dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int qup_i2c_pm_suspend_runtime(struct device *device)
+{
+ struct qup_i2c_dev *qup = dev_get_drvdata(device);
+
+ dev_dbg(device, "pm_runtime: suspending...\n");
+ qup_i2c_disable_clocks(qup);
+ return 0;
+}
+
+static int qup_i2c_pm_resume_runtime(struct device *device)
+{
+ struct qup_i2c_dev *qup = dev_get_drvdata(device);
+
+ dev_dbg(device, "pm_runtime: resuming...\n");
+ qup_i2c_enable_clocks(qup);
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int qup_i2c_suspend(struct device *device)
+{
+ qup_i2c_pm_suspend_runtime(device);
+ return 0;
+}
+
+static int qup_i2c_resume(struct device *device)
+{
+ qup_i2c_pm_resume_runtime(device);
+ pm_runtime_mark_last_busy(device);
+ pm_request_autosuspend(device);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops qup_i2c_qup_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(
+ qup_i2c_suspend,
+ qup_i2c_resume)
+ SET_RUNTIME_PM_OPS(
+ qup_i2c_pm_suspend_runtime,
+ qup_i2c_pm_resume_runtime,
+ NULL)
+};
+
+static const struct of_device_id qup_i2c_dt_match[] = {
+ { .compatible = "qcom,i2c-qup-v1.1.1" },
+ { .compatible = "qcom,i2c-qup-v2.1.1" },
+ { .compatible = "qcom,i2c-qup-v2.2.1" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qup_i2c_dt_match);
+
+static struct platform_driver qup_i2c_driver = {
+ .probe = qup_i2c_probe,
+ .remove = qup_i2c_remove,
+ .driver = {
+ .name = "i2c_qup",
+ .owner = THIS_MODULE,
+ .pm = &qup_i2c_qup_pm_ops,
+ .of_match_table = qup_i2c_dt_match,
+ },
+};
+
+module_platform_driver(qup_i2c_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:i2c_qup");
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index 0282d4d42805..d4fa8eba6e9d 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -638,6 +638,7 @@ static const struct of_device_id rcar_i2c_dt_ids[] = {
{ .compatible = "renesas,i2c-r8a7778", .data = (void *)I2C_RCAR_GEN1 },
{ .compatible = "renesas,i2c-r8a7779", .data = (void *)I2C_RCAR_GEN1 },
{ .compatible = "renesas,i2c-r8a7790", .data = (void *)I2C_RCAR_GEN2 },
+ { .compatible = "renesas,i2c-r8a7791", .data = (void *)I2C_RCAR_GEN2 },
{},
};
MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);
@@ -691,7 +692,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
adap = &priv->adap;
adap->nr = pdev->id;
adap->algo = &rcar_i2c_algo;
- adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED;
adap->retries = 3;
adap->dev.parent = dev;
adap->dev.of_node = dev->of_node;
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 684d21e71e4a..ae4491062e41 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -601,6 +601,31 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
return IRQ_HANDLED;
}
+/*
+ * Disable the bus so that we won't get any interrupts from now on, or try
+ * to drive any lines. This is the default state when we don't have
+ * anything to send/receive.
+ *
+ * If there is an event on the bus, or we have a pre-existing event at
+ * kernel boot time, we may not notice the event and the I2C controller
+ * will lock the bus with the I2C clock line low indefinitely.
+ */
+static inline void s3c24xx_i2c_disable_bus(struct s3c24xx_i2c *i2c)
+{
+ unsigned long tmp;
+
+ /* Stop driving the I2C pins */
+ tmp = readl(i2c->regs + S3C2410_IICSTAT);
+ tmp &= ~S3C2410_IICSTAT_TXRXEN;
+ writel(tmp, i2c->regs + S3C2410_IICSTAT);
+
+ /* We don't expect any interrupts now, and don't want send acks */
+ tmp = readl(i2c->regs + S3C2410_IICCON);
+ tmp &= ~(S3C2410_IICCON_IRQEN | S3C2410_IICCON_IRQPEND |
+ S3C2410_IICCON_ACKEN);
+ writel(tmp, i2c->regs + S3C2410_IICCON);
+}
+
/* s3c24xx_i2c_set_master
*
@@ -735,7 +760,11 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
s3c24xx_i2c_wait_idle(i2c);
+ s3c24xx_i2c_disable_bus(i2c);
+
out:
+ i2c->state = STATE_IDLE;
+
return ret;
}
@@ -1004,7 +1033,6 @@ static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
{
- unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
struct s3c2410_platform_i2c *pdata;
unsigned int freq;
@@ -1018,12 +1046,12 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
- writel(iicon, i2c->regs + S3C2410_IICCON);
+ writel(0, i2c->regs + S3C2410_IICCON);
+ writel(0, i2c->regs + S3C2410_IICSTAT);
/* we need to work out the divisors for the clock... */
if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
- writel(0, i2c->regs + S3C2410_IICCON);
dev_err(i2c->dev, "cannot meet bus frequency required\n");
return -EINVAL;
}
@@ -1031,7 +1059,8 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
/* todo - check that the i2c lines aren't being dragged anywhere */
dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
- dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
+ dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02x\n",
+ readl(i2c->regs + S3C2410_IICCON));
return 0;
}
@@ -1106,7 +1135,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = 2;
- i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED;
i2c->tx_setup = 50;
init_waitqueue_head(&i2c->wait);
diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c
index 6784f7f527a4..8e3be7ed0586 100644
--- a/drivers/i2c/busses/i2c-sirf.c
+++ b/drivers/i2c/busses/i2c-sirf.c
@@ -312,7 +312,7 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev)
goto out;
}
adap = &siic->adapter;
- adap->class = I2C_CLASS_HWMON;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
siic->base = devm_ioremap_resource(&pdev->dev, mem_res);
diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c
index 79fd96a04386..ac9bc33acef4 100644
--- a/drivers/i2c/busses/i2c-sis5595.c
+++ b/drivers/i2c/busses/i2c-sis5595.c
@@ -369,7 +369,7 @@ static struct i2c_adapter sis5595_adapter = {
.algo = &smbus_algorithm,
};
-static DEFINE_PCI_DEVICE_TABLE(sis5595_ids) = {
+static const struct pci_device_id sis5595_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
index 19b8505d0cdd..c6366733008d 100644
--- a/drivers/i2c/busses/i2c-sis630.c
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -510,7 +510,7 @@ static struct i2c_adapter sis630_adapter = {
.retries = 3
};
-static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = {
+static const struct pci_device_id sis630_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) },
diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c
index f8aa0c29f02b..8dc2fc5f74ff 100644
--- a/drivers/i2c/busses/i2c-sis96x.c
+++ b/drivers/i2c/busses/i2c-sis96x.c
@@ -244,7 +244,7 @@ static struct i2c_adapter sis96x_adapter = {
.algo = &smbus_algorithm,
};
-static DEFINE_PCI_DEVICE_TABLE(sis96x_ids) = {
+static const struct pci_device_id sis96x_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c
index 9cf715d69551..872016196ef3 100644
--- a/drivers/i2c/busses/i2c-st.c
+++ b/drivers/i2c/busses/i2c-st.c
@@ -574,7 +574,7 @@ static irqreturn_t st_i2c_isr_thread(int irq, void *data)
writel_relaxed(it, i2c_dev->base + SSC_IEN);
st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_STOPG);
- c->result = -EIO;
+ c->result = -EAGAIN;
break;
default:
diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c
index 5b80ef310841..29b1fb778943 100644
--- a/drivers/i2c/busses/i2c-stu300.c
+++ b/drivers/i2c/busses/i2c-stu300.c
@@ -911,7 +911,7 @@ static int stu300_probe(struct platform_device *pdev)
adap = &dev->adapter;
adap->owner = THIS_MODULE;
/* DDC class but actually often used for more generic I2C */
- adap->class = I2C_CLASS_DDC;
+ adap->class = I2C_CLASS_DDC | I2C_CLASS_DEPRECATED;
strlcpy(adap->name, "ST Microelectronics DDC I2C adapter",
sizeof(adap->name));
adap->nr = bus_nr;
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 9704537aee3c..00f04cb5b4eb 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -794,7 +794,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
i2c_dev->adapter.owner = THIS_MODULE;
- i2c_dev->adapter.class = I2C_CLASS_HWMON;
+ i2c_dev->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_DEPRECATED;
strlcpy(i2c_dev->adapter.name, "Tegra I2C adapter",
sizeof(i2c_dev->adapter.name));
i2c_dev->adapter.algo = &tegra_i2c_algo;
diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c
index 49d7f14b9d27..f4a1ed757612 100644
--- a/drivers/i2c/busses/i2c-via.c
+++ b/drivers/i2c/busses/i2c-via.c
@@ -88,7 +88,7 @@ static struct i2c_adapter vt586b_adapter = {
};
-static DEFINE_PCI_DEVICE_TABLE(vt586b_ids) = {
+static const struct pci_device_id vt586b_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c
index 40d36df678de..6841200b6e50 100644
--- a/drivers/i2c/busses/i2c-viapro.c
+++ b/drivers/i2c/busses/i2c-viapro.c
@@ -442,7 +442,7 @@ release_region:
return error;
}
-static DEFINE_PCI_DEVICE_TABLE(vt596_ids) = {
+static const struct pci_device_id vt596_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596_3),
.driver_data = SMBBA1 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596B_3),
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index 28107502517f..7731f1795869 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -684,7 +684,7 @@ static const struct i2c_algorithm xiic_algorithm = {
static struct i2c_adapter xiic_adapter = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
- .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLASS_DEPRECATED,
.algo = &xiic_algorithm,
};
diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c
index 2d1d2c5653fb..cb66f9586f76 100644
--- a/drivers/i2c/busses/scx200_acb.c
+++ b/drivers/i2c/busses/scx200_acb.c
@@ -556,7 +556,7 @@ static struct platform_driver scx200_pci_driver = {
.remove = scx200_remove,
};
-static DEFINE_PCI_DEVICE_TABLE(scx200_isa) = {
+static const struct pci_device_id scx200_isa[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) },
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) },
{ 0, }
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 5fb80b8962a2..7c7f4b856bad 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -48,10 +48,13 @@
#include <linux/rwsem.h>
#include <linux/pm_runtime.h>
#include <linux/acpi.h>
+#include <linux/jump_label.h>
#include <asm/uaccess.h>
#include "i2c-core.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/i2c.h>
/* core_lock protects i2c_adapter_idr, and guarantees
that device detection, deletion of detected devices, and attach_adapter
@@ -62,6 +65,18 @@ static DEFINE_IDR(i2c_adapter_idr);
static struct device_type i2c_client_type;
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
+static struct static_key i2c_trace_msg = STATIC_KEY_INIT_FALSE;
+
+void i2c_transfer_trace_reg(void)
+{
+ static_key_slow_inc(&i2c_trace_msg);
+}
+
+void i2c_transfer_trace_unreg(void)
+{
+ static_key_slow_dec(&i2c_trace_msg);
+}
+
/* ------------------------------------------------------------------------- */
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
@@ -1686,6 +1701,7 @@ static void __exit i2c_exit(void)
class_compat_unregister(i2c_adapter_compat_class);
#endif
bus_unregister(&i2c_bus_type);
+ tracepoint_synchronize_unregister();
}
/* We must initialize early, because some subsystems register i2c drivers
@@ -1716,6 +1732,19 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
unsigned long orig_jiffies;
int ret, try;
+ /* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets
+ * enabled. This is an efficient way of keeping the for-loop from
+ * being executed when not needed.
+ */
+ if (static_key_false(&i2c_trace_msg)) {
+ int i;
+ for (i = 0; i < num; i++)
+ if (msgs[i].flags & I2C_M_RD)
+ trace_i2c_read(adap, &msgs[i], i);
+ else
+ trace_i2c_write(adap, &msgs[i], i);
+ }
+
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
@@ -1726,6 +1755,14 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
break;
}
+ if (static_key_false(&i2c_trace_msg)) {
+ int i;
+ for (i = 0; i < ret; i++)
+ if (msgs[i].flags & I2C_M_RD)
+ trace_i2c_reply(adap, &msgs[i], i);
+ trace_i2c_result(adap, i, ret);
+ }
+
return ret;
}
EXPORT_SYMBOL(__i2c_transfer);
@@ -1941,6 +1978,13 @@ static int i2c_detect_address(struct i2c_client *temp_client,
struct i2c_client *client;
/* Detection succeeded, instantiate the device */
+ if (adapter->class & I2C_CLASS_DEPRECATED)
+ dev_warn(&adapter->dev,
+ "This adapter will soon drop class based instantiation of devices. "
+ "Please make sure client 0x%02x gets instantiated by other means. "
+ "Check 'Documentation/i2c/instantiating-devices' for details.\n",
+ info.addr);
+
dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
info.type, info.addr);
client = i2c_new_device(adapter, &info);
@@ -2521,6 +2565,14 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
int try;
s32 res;
+ /* If enabled, the following two tracepoints are conditional on
+ * read_write and protocol.
+ */
+ trace_smbus_write(adapter, addr, flags, read_write,
+ command, protocol, data);
+ trace_smbus_read(adapter, addr, flags, read_write,
+ command, protocol);
+
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
if (adapter->algo->smbus_xfer) {
@@ -2541,15 +2593,24 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
i2c_unlock_adapter(adapter);
if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
- return res;
+ goto trace;
/*
* Fall back to i2c_smbus_xfer_emulated if the adapter doesn't
* implement native support for the SMBus operation.
*/
}
- return i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
- command, protocol, data);
+ res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
+ command, protocol, data);
+
+trace:
+ /* If enabled, the reply tracepoint is conditional on read_write. */
+ trace_smbus_reply(adapter, addr, flags, read_write,
+ command, protocol, data);
+ trace_smbus_result(adapter, addr, flags, read_write,
+ command, protocol, res);
+
+ return res;
}
EXPORT_SYMBOL(i2c_smbus_xfer);
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index 51493ed4643b..a43220c2e3d9 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -196,6 +196,53 @@ static struct cpuidle_state snb_cstates[] = {
.enter = NULL }
};
+static struct cpuidle_state byt_cstates[] = {
+ {
+ .name = "C1-BYT",
+ .desc = "MWAIT 0x00",
+ .flags = MWAIT2flg(0x00) | CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 1,
+ .target_residency = 1,
+ .enter = &intel_idle },
+ {
+ .name = "C1E-BYT",
+ .desc = "MWAIT 0x01",
+ .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 15,
+ .target_residency = 30,
+ .enter = &intel_idle },
+ {
+ .name = "C6N-BYT",
+ .desc = "MWAIT 0x58",
+ .flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 40,
+ .target_residency = 275,
+ .enter = &intel_idle },
+ {
+ .name = "C6S-BYT",
+ .desc = "MWAIT 0x52",
+ .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 140,
+ .target_residency = 560,
+ .enter = &intel_idle },
+ {
+ .name = "C7-BYT",
+ .desc = "MWAIT 0x60",
+ .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 1200,
+ .target_residency = 1500,
+ .enter = &intel_idle },
+ {
+ .name = "C7S-BYT",
+ .desc = "MWAIT 0x64",
+ .flags = MWAIT2flg(0x64) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 10000,
+ .target_residency = 20000,
+ .enter = &intel_idle },
+ {
+ .enter = NULL }
+};
+
static struct cpuidle_state ivb_cstates[] = {
{
.name = "C1-IVB",
@@ -236,6 +283,105 @@ static struct cpuidle_state ivb_cstates[] = {
.enter = NULL }
};
+static struct cpuidle_state ivt_cstates[] = {
+ {
+ .name = "C1-IVT",
+ .desc = "MWAIT 0x00",
+ .flags = MWAIT2flg(0x00) | CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 1,
+ .target_residency = 1,
+ .enter = &intel_idle },
+ {
+ .name = "C1E-IVT",
+ .desc = "MWAIT 0x01",
+ .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 10,
+ .target_residency = 80,
+ .enter = &intel_idle },
+ {
+ .name = "C3-IVT",
+ .desc = "MWAIT 0x10",
+ .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 59,
+ .target_residency = 156,
+ .enter = &intel_idle },
+ {
+ .name = "C6-IVT",
+ .desc = "MWAIT 0x20",
+ .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 82,
+ .target_residency = 300,
+ .enter = &intel_idle },
+ {
+ .enter = NULL }
+};
+
+static struct cpuidle_state ivt_cstates_4s[] = {
+ {
+ .name = "C1-IVT-4S",
+ .desc = "MWAIT 0x00",
+ .flags = MWAIT2flg(0x00) | CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 1,
+ .target_residency = 1,
+ .enter = &intel_idle },
+ {
+ .name = "C1E-IVT-4S",
+ .desc = "MWAIT 0x01",
+ .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 10,
+ .target_residency = 250,
+ .enter = &intel_idle },
+ {
+ .name = "C3-IVT-4S",
+ .desc = "MWAIT 0x10",
+ .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 59,
+ .target_residency = 300,
+ .enter = &intel_idle },
+ {
+ .name = "C6-IVT-4S",
+ .desc = "MWAIT 0x20",
+ .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 84,
+ .target_residency = 400,
+ .enter = &intel_idle },
+ {
+ .enter = NULL }
+};
+
+static struct cpuidle_state ivt_cstates_8s[] = {
+ {
+ .name = "C1-IVT-8S",
+ .desc = "MWAIT 0x00",
+ .flags = MWAIT2flg(0x00) | CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 1,
+ .target_residency = 1,
+ .enter = &intel_idle },
+ {
+ .name = "C1E-IVT-8S",
+ .desc = "MWAIT 0x01",
+ .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 10,
+ .target_residency = 500,
+ .enter = &intel_idle },
+ {
+ .name = "C3-IVT-8S",
+ .desc = "MWAIT 0x10",
+ .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 59,
+ .target_residency = 600,
+ .enter = &intel_idle },
+ {
+ .name = "C6-IVT-8S",
+ .desc = "MWAIT 0x20",
+ .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 88,
+ .target_residency = 700,
+ .enter = &intel_idle },
+ {
+ .enter = NULL }
+};
+
static struct cpuidle_state hsw_cstates[] = {
{
.name = "C1-HSW",
@@ -464,11 +610,21 @@ static const struct idle_cpu idle_cpu_snb = {
.disable_promotion_to_c1e = true,
};
+static const struct idle_cpu idle_cpu_byt = {
+ .state_table = byt_cstates,
+ .disable_promotion_to_c1e = true,
+};
+
static const struct idle_cpu idle_cpu_ivb = {
.state_table = ivb_cstates,
.disable_promotion_to_c1e = true,
};
+static const struct idle_cpu idle_cpu_ivt = {
+ .state_table = ivt_cstates,
+ .disable_promotion_to_c1e = true,
+};
+
static const struct idle_cpu idle_cpu_hsw = {
.state_table = hsw_cstates,
.disable_promotion_to_c1e = true,
@@ -494,8 +650,10 @@ static const struct x86_cpu_id intel_idle_ids[] = {
ICPU(0x2f, idle_cpu_nehalem),
ICPU(0x2a, idle_cpu_snb),
ICPU(0x2d, idle_cpu_snb),
+ ICPU(0x36, idle_cpu_atom),
+ ICPU(0x37, idle_cpu_byt),
ICPU(0x3a, idle_cpu_ivb),
- ICPU(0x3e, idle_cpu_ivb),
+ ICPU(0x3e, idle_cpu_ivt),
ICPU(0x3c, idle_cpu_hsw),
ICPU(0x3f, idle_cpu_hsw),
ICPU(0x45, idle_cpu_hsw),
@@ -572,6 +730,39 @@ static void intel_idle_cpuidle_devices_uninit(void)
free_percpu(intel_idle_cpuidle_devices);
return;
}
+
+/*
+ * intel_idle_state_table_update()
+ *
+ * Update the default state_table for this CPU-id
+ *
+ * Currently used to access tuned IVT multi-socket targets
+ * Assumption: num_sockets == (max_package_num + 1)
+ */
+void intel_idle_state_table_update(void)
+{
+ /* IVT uses a different table for 1-2, 3-4, and > 4 sockets */
+ if (boot_cpu_data.x86_model == 0x3e) { /* IVT */
+ int cpu, package_num, num_sockets = 1;
+
+ for_each_online_cpu(cpu) {
+ package_num = topology_physical_package_id(cpu);
+ if (package_num + 1 > num_sockets) {
+ num_sockets = package_num + 1;
+
+ if (num_sockets > 4)
+ cpuidle_state_table = ivt_cstates_8s;
+ return;
+ }
+ }
+
+ if (num_sockets > 2)
+ cpuidle_state_table = ivt_cstates_4s;
+ /* else, 1 and 2 socket systems use default ivt_cstates */
+ }
+ return;
+}
+
/*
* intel_idle_cpuidle_driver_init()
* allocate, initialize cpuidle_states
@@ -581,10 +772,12 @@ static int __init intel_idle_cpuidle_driver_init(void)
int cstate;
struct cpuidle_driver *drv = &intel_idle_driver;
+ intel_idle_state_table_update();
+
drv->state_count = 1;
for (cstate = 0; cstate < CPUIDLE_STATE_MAX; ++cstate) {
- int num_substates, mwait_hint, mwait_cstate, mwait_substate;
+ int num_substates, mwait_hint, mwait_cstate;
if (cpuidle_state_table[cstate].enter == NULL)
break;
@@ -597,14 +790,13 @@ static int __init intel_idle_cpuidle_driver_init(void)
mwait_hint = flg2MWAIT(cpuidle_state_table[cstate].flags);
mwait_cstate = MWAIT_HINT2CSTATE(mwait_hint);
- mwait_substate = MWAIT_HINT2SUBSTATE(mwait_hint);
- /* does the state exist in CPUID.MWAIT? */
+ /* number of sub-states for this state in CPUID.MWAIT */
num_substates = (mwait_substates >> ((mwait_cstate + 1) * 4))
& MWAIT_SUBSTATE_MASK;
- /* if sub-state in table is not enumerated by CPUID */
- if ((mwait_substate + 1) > num_substates)
+ /* if NO sub-states for this state in CPUID, skip it */
+ if (num_substates == 0)
continue;
if (((mwait_cstate + 1) > 2) &&
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index 8ee228e9ab5a..c98fdb185931 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -51,6 +51,8 @@ isert_unreg_rdma(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn);
static int
isert_reg_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
struct isert_rdma_wr *wr);
+static int
+isert_put_response(struct iscsi_conn *conn, struct iscsi_cmd *cmd);
static void
isert_qp_event_callback(struct ib_event *e, void *context)
@@ -87,7 +89,8 @@ isert_query_device(struct ib_device *ib_dev, struct ib_device_attr *devattr)
}
static int
-isert_conn_setup_qp(struct isert_conn *isert_conn, struct rdma_cm_id *cma_id)
+isert_conn_setup_qp(struct isert_conn *isert_conn, struct rdma_cm_id *cma_id,
+ u8 protection)
{
struct isert_device *device = isert_conn->conn_device;
struct ib_qp_init_attr attr;
@@ -119,6 +122,8 @@ isert_conn_setup_qp(struct isert_conn *isert_conn, struct rdma_cm_id *cma_id)
attr.cap.max_recv_sge = 1;
attr.sq_sig_type = IB_SIGNAL_REQ_WR;
attr.qp_type = IB_QPT_RC;
+ if (protection)
+ attr.create_flags |= IB_QP_CREATE_SIGNATURE_EN;
pr_debug("isert_conn_setup_qp cma_id->device: %p\n",
cma_id->device);
@@ -226,7 +231,8 @@ isert_create_device_ib_res(struct isert_device *device)
return ret;
/* asign function handlers */
- if (dev_attr->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS) {
+ if (dev_attr->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS &&
+ dev_attr->device_cap_flags & IB_DEVICE_SIGNATURE_HANDOVER) {
device->use_fastreg = 1;
device->reg_rdma_mem = isert_reg_rdma;
device->unreg_rdma_mem = isert_unreg_rdma;
@@ -236,13 +242,18 @@ isert_create_device_ib_res(struct isert_device *device)
device->unreg_rdma_mem = isert_unmap_cmd;
}
+ /* Check signature cap */
+ device->pi_capable = dev_attr->device_cap_flags &
+ IB_DEVICE_SIGNATURE_HANDOVER ? true : false;
+
device->cqs_used = min_t(int, num_online_cpus(),
device->ib_device->num_comp_vectors);
device->cqs_used = min(ISERT_MAX_CQ, device->cqs_used);
pr_debug("Using %d CQs, device %s supports %d vectors support "
- "Fast registration %d\n",
+ "Fast registration %d pi_capable %d\n",
device->cqs_used, device->ib_device->name,
- device->ib_device->num_comp_vectors, device->use_fastreg);
+ device->ib_device->num_comp_vectors, device->use_fastreg,
+ device->pi_capable);
device->cq_desc = kzalloc(sizeof(struct isert_cq_desc) *
device->cqs_used, GFP_KERNEL);
if (!device->cq_desc) {
@@ -395,6 +406,12 @@ isert_conn_free_fastreg_pool(struct isert_conn *isert_conn)
list_del(&fr_desc->list);
ib_free_fast_reg_page_list(fr_desc->data_frpl);
ib_dereg_mr(fr_desc->data_mr);
+ if (fr_desc->pi_ctx) {
+ ib_free_fast_reg_page_list(fr_desc->pi_ctx->prot_frpl);
+ ib_dereg_mr(fr_desc->pi_ctx->prot_mr);
+ ib_destroy_mr(fr_desc->pi_ctx->sig_mr);
+ kfree(fr_desc->pi_ctx);
+ }
kfree(fr_desc);
++i;
}
@@ -406,8 +423,10 @@ isert_conn_free_fastreg_pool(struct isert_conn *isert_conn)
static int
isert_create_fr_desc(struct ib_device *ib_device, struct ib_pd *pd,
- struct fast_reg_descriptor *fr_desc)
+ struct fast_reg_descriptor *fr_desc, u8 protection)
{
+ int ret;
+
fr_desc->data_frpl = ib_alloc_fast_reg_page_list(ib_device,
ISCSI_ISER_SG_TABLESIZE);
if (IS_ERR(fr_desc->data_frpl)) {
@@ -420,27 +439,88 @@ isert_create_fr_desc(struct ib_device *ib_device, struct ib_pd *pd,
if (IS_ERR(fr_desc->data_mr)) {
pr_err("Failed to allocate data frmr err=%ld\n",
PTR_ERR(fr_desc->data_mr));
- ib_free_fast_reg_page_list(fr_desc->data_frpl);
- return PTR_ERR(fr_desc->data_mr);
+ ret = PTR_ERR(fr_desc->data_mr);
+ goto err_data_frpl;
}
pr_debug("Create fr_desc %p page_list %p\n",
fr_desc, fr_desc->data_frpl->page_list);
+ fr_desc->ind |= ISERT_DATA_KEY_VALID;
+
+ if (protection) {
+ struct ib_mr_init_attr mr_init_attr = {0};
+ struct pi_context *pi_ctx;
+
+ fr_desc->pi_ctx = kzalloc(sizeof(*fr_desc->pi_ctx), GFP_KERNEL);
+ if (!fr_desc->pi_ctx) {
+ pr_err("Failed to allocate pi context\n");
+ ret = -ENOMEM;
+ goto err_data_mr;
+ }
+ pi_ctx = fr_desc->pi_ctx;
+
+ pi_ctx->prot_frpl = ib_alloc_fast_reg_page_list(ib_device,
+ ISCSI_ISER_SG_TABLESIZE);
+ if (IS_ERR(pi_ctx->prot_frpl)) {
+ pr_err("Failed to allocate prot frpl err=%ld\n",
+ PTR_ERR(pi_ctx->prot_frpl));
+ ret = PTR_ERR(pi_ctx->prot_frpl);
+ goto err_pi_ctx;
+ }
- fr_desc->valid = true;
+ pi_ctx->prot_mr = ib_alloc_fast_reg_mr(pd, ISCSI_ISER_SG_TABLESIZE);
+ if (IS_ERR(pi_ctx->prot_mr)) {
+ pr_err("Failed to allocate prot frmr err=%ld\n",
+ PTR_ERR(pi_ctx->prot_mr));
+ ret = PTR_ERR(pi_ctx->prot_mr);
+ goto err_prot_frpl;
+ }
+ fr_desc->ind |= ISERT_PROT_KEY_VALID;
+
+ mr_init_attr.max_reg_descriptors = 2;
+ mr_init_attr.flags |= IB_MR_SIGNATURE_EN;
+ pi_ctx->sig_mr = ib_create_mr(pd, &mr_init_attr);
+ if (IS_ERR(pi_ctx->sig_mr)) {
+ pr_err("Failed to allocate signature enabled mr err=%ld\n",
+ PTR_ERR(pi_ctx->sig_mr));
+ ret = PTR_ERR(pi_ctx->sig_mr);
+ goto err_prot_mr;
+ }
+ fr_desc->ind |= ISERT_SIG_KEY_VALID;
+ }
+ fr_desc->ind &= ~ISERT_PROTECTED;
return 0;
+err_prot_mr:
+ ib_dereg_mr(fr_desc->pi_ctx->prot_mr);
+err_prot_frpl:
+ ib_free_fast_reg_page_list(fr_desc->pi_ctx->prot_frpl);
+err_pi_ctx:
+ kfree(fr_desc->pi_ctx);
+err_data_mr:
+ ib_dereg_mr(fr_desc->data_mr);
+err_data_frpl:
+ ib_free_fast_reg_page_list(fr_desc->data_frpl);
+
+ return ret;
}
static int
-isert_conn_create_fastreg_pool(struct isert_conn *isert_conn)
+isert_conn_create_fastreg_pool(struct isert_conn *isert_conn, u8 pi_support)
{
struct fast_reg_descriptor *fr_desc;
struct isert_device *device = isert_conn->conn_device;
- int i, ret;
+ struct se_session *se_sess = isert_conn->conn->sess->se_sess;
+ struct se_node_acl *se_nacl = se_sess->se_node_acl;
+ int i, ret, tag_num;
+ /*
+ * Setup the number of FRMRs based upon the number of tags
+ * available to session in iscsi_target_locate_portal().
+ */
+ tag_num = max_t(u32, ISCSIT_MIN_TAGS, se_nacl->queue_depth);
+ tag_num = (tag_num * 2) + ISCSIT_EXTRA_TAGS;
- INIT_LIST_HEAD(&isert_conn->conn_fr_pool);
isert_conn->conn_fr_pool_size = 0;
- for (i = 0; i < ISCSI_DEF_XMIT_CMDS_MAX; i++) {
+ for (i = 0; i < tag_num; i++) {
fr_desc = kzalloc(sizeof(*fr_desc), GFP_KERNEL);
if (!fr_desc) {
pr_err("Failed to allocate fast_reg descriptor\n");
@@ -449,7 +529,8 @@ isert_conn_create_fastreg_pool(struct isert_conn *isert_conn)
}
ret = isert_create_fr_desc(device->ib_device,
- isert_conn->conn_pd, fr_desc);
+ isert_conn->conn_pd, fr_desc,
+ pi_support);
if (ret) {
pr_err("Failed to create fastreg descriptor err=%d\n",
ret);
@@ -480,6 +561,7 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
struct isert_device *device;
struct ib_device *ib_dev = cma_id->device;
int ret = 0;
+ u8 pi_support = np->tpg_np->tpg->tpg_attrib.t10_pi;
pr_debug("Entering isert_connect_request cma_id: %p, context: %p\n",
cma_id, cma_id->context);
@@ -498,6 +580,7 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
kref_get(&isert_conn->conn_kref);
mutex_init(&isert_conn->conn_mutex);
spin_lock_init(&isert_conn->conn_lock);
+ INIT_LIST_HEAD(&isert_conn->conn_fr_pool);
cma_id->context = isert_conn;
isert_conn->conn_cm_id = cma_id;
@@ -569,16 +652,13 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
goto out_mr;
}
- if (device->use_fastreg) {
- ret = isert_conn_create_fastreg_pool(isert_conn);
- if (ret) {
- pr_err("Conn: %p failed to create fastreg pool\n",
- isert_conn);
- goto out_fastreg;
- }
+ if (pi_support && !device->pi_capable) {
+ pr_err("Protection information requested but not supported\n");
+ ret = -EINVAL;
+ goto out_mr;
}
- ret = isert_conn_setup_qp(isert_conn, cma_id);
+ ret = isert_conn_setup_qp(isert_conn, cma_id, pi_support);
if (ret)
goto out_conn_dev;
@@ -591,9 +671,6 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
return 0;
out_conn_dev:
- if (device->use_fastreg)
- isert_conn_free_fastreg_pool(isert_conn);
-out_fastreg:
ib_dereg_mr(isert_conn->conn_mr);
out_mr:
ib_dealloc_pd(isert_conn->conn_pd);
@@ -967,6 +1044,18 @@ isert_put_login_tx(struct iscsi_conn *conn, struct iscsi_login *login,
}
if (!login->login_failed) {
if (login->login_complete) {
+ if (isert_conn->conn_device->use_fastreg) {
+ u8 pi_support = login->np->tpg_np->tpg->tpg_attrib.t10_pi;
+
+ ret = isert_conn_create_fastreg_pool(isert_conn,
+ pi_support);
+ if (ret) {
+ pr_err("Conn: %p failed to create"
+ " fastreg pool\n", isert_conn);
+ return ret;
+ }
+ }
+
ret = isert_alloc_rx_descriptors(isert_conn);
if (ret)
return ret;
@@ -1392,19 +1481,60 @@ isert_rx_completion(struct iser_rx_desc *desc, struct isert_conn *isert_conn,
}
}
+static int
+isert_map_data_buf(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd,
+ struct scatterlist *sg, u32 nents, u32 length, u32 offset,
+ enum iser_ib_op_code op, struct isert_data_buf *data)
+{
+ struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
+
+ data->dma_dir = op == ISER_IB_RDMA_WRITE ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+ data->len = length - offset;
+ data->offset = offset;
+ data->sg_off = data->offset / PAGE_SIZE;
+
+ data->sg = &sg[data->sg_off];
+ data->nents = min_t(unsigned int, nents - data->sg_off,
+ ISCSI_ISER_SG_TABLESIZE);
+ data->len = min_t(unsigned int, data->len, ISCSI_ISER_SG_TABLESIZE *
+ PAGE_SIZE);
+
+ data->dma_nents = ib_dma_map_sg(ib_dev, data->sg, data->nents,
+ data->dma_dir);
+ if (unlikely(!data->dma_nents)) {
+ pr_err("Cmd: unable to dma map SGs %p\n", sg);
+ return -EINVAL;
+ }
+
+ pr_debug("Mapped cmd: %p count: %u sg: %p sg_nents: %u rdma_len %d\n",
+ isert_cmd, data->dma_nents, data->sg, data->nents, data->len);
+
+ return 0;
+}
+
+static void
+isert_unmap_data_buf(struct isert_conn *isert_conn, struct isert_data_buf *data)
+{
+ struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
+
+ ib_dma_unmap_sg(ib_dev, data->sg, data->nents, data->dma_dir);
+ memset(data, 0, sizeof(*data));
+}
+
+
+
static void
isert_unmap_cmd(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn)
{
struct isert_rdma_wr *wr = &isert_cmd->rdma_wr;
- struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
pr_debug("isert_unmap_cmd: %p\n", isert_cmd);
- if (wr->sge) {
+
+ if (wr->data.sg) {
pr_debug("isert_unmap_cmd: %p unmap_sg op\n", isert_cmd);
- ib_dma_unmap_sg(ib_dev, wr->sge, wr->num_sge,
- (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
- wr->sge = NULL;
+ isert_unmap_data_buf(isert_conn, &wr->data);
}
if (wr->send_wr) {
@@ -1424,7 +1554,6 @@ static void
isert_unreg_rdma(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn)
{
struct isert_rdma_wr *wr = &isert_cmd->rdma_wr;
- struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
LIST_HEAD(unmap_list);
pr_debug("unreg_fastreg_cmd: %p\n", isert_cmd);
@@ -1432,18 +1561,19 @@ isert_unreg_rdma(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn)
if (wr->fr_desc) {
pr_debug("unreg_fastreg_cmd: %p free fr_desc %p\n",
isert_cmd, wr->fr_desc);
+ if (wr->fr_desc->ind & ISERT_PROTECTED) {
+ isert_unmap_data_buf(isert_conn, &wr->prot);
+ wr->fr_desc->ind &= ~ISERT_PROTECTED;
+ }
spin_lock_bh(&isert_conn->conn_lock);
list_add_tail(&wr->fr_desc->list, &isert_conn->conn_fr_pool);
spin_unlock_bh(&isert_conn->conn_lock);
wr->fr_desc = NULL;
}
- if (wr->sge) {
+ if (wr->data.sg) {
pr_debug("unreg_fastreg_cmd: %p unmap_sg op\n", isert_cmd);
- ib_dma_unmap_sg(ib_dev, wr->sge, wr->num_sge,
- (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
- wr->sge = NULL;
+ isert_unmap_data_buf(isert_conn, &wr->data);
}
wr->ib_sge = NULL;
@@ -1451,7 +1581,7 @@ isert_unreg_rdma(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn)
}
static void
-isert_put_cmd(struct isert_cmd *isert_cmd)
+isert_put_cmd(struct isert_cmd *isert_cmd, bool comp_err)
{
struct iscsi_cmd *cmd = isert_cmd->iscsi_cmd;
struct isert_conn *isert_conn = isert_cmd->conn;
@@ -1467,8 +1597,21 @@ isert_put_cmd(struct isert_cmd *isert_cmd)
list_del_init(&cmd->i_conn_node);
spin_unlock_bh(&conn->cmd_lock);
- if (cmd->data_direction == DMA_TO_DEVICE)
+ if (cmd->data_direction == DMA_TO_DEVICE) {
iscsit_stop_dataout_timer(cmd);
+ /*
+ * Check for special case during comp_err where
+ * WRITE_PENDING has been handed off from core,
+ * but requires an extra target_put_sess_cmd()
+ * before transport_generic_free_cmd() below.
+ */
+ if (comp_err &&
+ cmd->se_cmd.t_state == TRANSPORT_WRITE_PENDING) {
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+
+ target_put_sess_cmd(se_cmd->se_sess, se_cmd);
+ }
+ }
device->unreg_rdma_mem(isert_cmd, isert_conn);
transport_generic_free_cmd(&cmd->se_cmd, 0);
@@ -1523,7 +1666,7 @@ isert_unmap_tx_desc(struct iser_tx_desc *tx_desc, struct ib_device *ib_dev)
static void
isert_completion_put(struct iser_tx_desc *tx_desc, struct isert_cmd *isert_cmd,
- struct ib_device *ib_dev)
+ struct ib_device *ib_dev, bool comp_err)
{
if (isert_cmd->pdu_buf_dma != 0) {
pr_debug("Calling ib_dma_unmap_single for isert_cmd->pdu_buf_dma\n");
@@ -1533,7 +1676,77 @@ isert_completion_put(struct iser_tx_desc *tx_desc, struct isert_cmd *isert_cmd,
}
isert_unmap_tx_desc(tx_desc, ib_dev);
- isert_put_cmd(isert_cmd);
+ isert_put_cmd(isert_cmd, comp_err);
+}
+
+static int
+isert_check_pi_status(struct se_cmd *se_cmd, struct ib_mr *sig_mr)
+{
+ struct ib_mr_status mr_status;
+ int ret;
+
+ ret = ib_check_mr_status(sig_mr, IB_MR_CHECK_SIG_STATUS, &mr_status);
+ if (ret) {
+ pr_err("ib_check_mr_status failed, ret %d\n", ret);
+ goto fail_mr_status;
+ }
+
+ if (mr_status.fail_status & IB_MR_CHECK_SIG_STATUS) {
+ u64 sec_offset_err;
+ u32 block_size = se_cmd->se_dev->dev_attrib.block_size + 8;
+
+ switch (mr_status.sig_err.err_type) {
+ case IB_SIG_BAD_GUARD:
+ se_cmd->pi_err = TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED;
+ break;
+ case IB_SIG_BAD_REFTAG:
+ se_cmd->pi_err = TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED;
+ break;
+ case IB_SIG_BAD_APPTAG:
+ se_cmd->pi_err = TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED;
+ break;
+ }
+ sec_offset_err = mr_status.sig_err.sig_err_offset;
+ do_div(sec_offset_err, block_size);
+ se_cmd->bad_sector = sec_offset_err + se_cmd->t_task_lba;
+
+ pr_err("isert: PI error found type %d at sector 0x%llx "
+ "expected 0x%x vs actual 0x%x\n",
+ mr_status.sig_err.err_type,
+ (unsigned long long)se_cmd->bad_sector,
+ mr_status.sig_err.expected,
+ mr_status.sig_err.actual);
+ ret = 1;
+ }
+
+fail_mr_status:
+ return ret;
+}
+
+static void
+isert_completion_rdma_write(struct iser_tx_desc *tx_desc,
+ struct isert_cmd *isert_cmd)
+{
+ struct isert_rdma_wr *wr = &isert_cmd->rdma_wr;
+ struct iscsi_cmd *cmd = isert_cmd->iscsi_cmd;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct isert_conn *isert_conn = isert_cmd->conn;
+ struct isert_device *device = isert_conn->conn_device;
+ int ret = 0;
+
+ if (wr->fr_desc && wr->fr_desc->ind & ISERT_PROTECTED) {
+ ret = isert_check_pi_status(se_cmd,
+ wr->fr_desc->pi_ctx->sig_mr);
+ wr->fr_desc->ind &= ~ISERT_PROTECTED;
+ }
+
+ device->unreg_rdma_mem(isert_cmd, isert_conn);
+ wr->send_wr_num = 0;
+ if (ret)
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->pi_err, 0);
+ else
+ isert_put_response(isert_conn->conn, cmd);
}
static void
@@ -1545,10 +1758,17 @@ isert_completion_rdma_read(struct iser_tx_desc *tx_desc,
struct se_cmd *se_cmd = &cmd->se_cmd;
struct isert_conn *isert_conn = isert_cmd->conn;
struct isert_device *device = isert_conn->conn_device;
+ int ret = 0;
+
+ if (wr->fr_desc && wr->fr_desc->ind & ISERT_PROTECTED) {
+ ret = isert_check_pi_status(se_cmd,
+ wr->fr_desc->pi_ctx->sig_mr);
+ wr->fr_desc->ind &= ~ISERT_PROTECTED;
+ }
iscsit_stop_dataout_timer(cmd);
device->unreg_rdma_mem(isert_cmd, isert_conn);
- cmd->write_data_done = wr->cur_rdma_length;
+ cmd->write_data_done = wr->data.len;
wr->send_wr_num = 0;
pr_debug("Cmd: %p RDMA_READ comp calling execute_cmd\n", isert_cmd);
@@ -1557,7 +1777,11 @@ isert_completion_rdma_read(struct iser_tx_desc *tx_desc,
cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT;
spin_unlock_bh(&cmd->istate_lock);
- target_execute_cmd(se_cmd);
+ if (ret)
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->pi_err, 0);
+ else
+ target_execute_cmd(se_cmd);
}
static void
@@ -1577,14 +1801,14 @@ isert_do_control_comp(struct work_struct *work)
iscsit_tmr_post_handler(cmd, cmd->conn);
cmd->i_state = ISTATE_SENT_STATUS;
- isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev);
+ isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev, false);
break;
case ISTATE_SEND_REJECT:
pr_debug("Got isert_do_control_comp ISTATE_SEND_REJECT: >>>\n");
atomic_dec(&isert_conn->post_send_buf_count);
cmd->i_state = ISTATE_SENT_STATUS;
- isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev);
+ isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev, false);
break;
case ISTATE_SEND_LOGOUTRSP:
pr_debug("Calling iscsit_logout_post_handler >>>>>>>>>>>>>>\n");
@@ -1598,7 +1822,7 @@ isert_do_control_comp(struct work_struct *work)
case ISTATE_SEND_TEXTRSP:
atomic_dec(&isert_conn->post_send_buf_count);
cmd->i_state = ISTATE_SENT_STATUS;
- isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev);
+ isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev, false);
break;
default:
pr_err("Unknown do_control_comp i_state %d\n", cmd->i_state);
@@ -1626,10 +1850,21 @@ isert_response_completion(struct iser_tx_desc *tx_desc,
queue_work(isert_comp_wq, &isert_cmd->comp_work);
return;
}
- atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count);
+
+ /**
+ * If send_wr_num is 0 this means that we got
+ * RDMA completion and we cleared it and we should
+ * simply decrement the response post. else the
+ * response is incorporated in send_wr_num, just
+ * sub it.
+ **/
+ if (wr->send_wr_num)
+ atomic_sub(wr->send_wr_num, &isert_conn->post_send_buf_count);
+ else
+ atomic_dec(&isert_conn->post_send_buf_count);
cmd->i_state = ISTATE_SENT_STATUS;
- isert_completion_put(tx_desc, isert_cmd, ib_dev);
+ isert_completion_put(tx_desc, isert_cmd, ib_dev, false);
}
static void
@@ -1658,8 +1893,9 @@ __isert_send_completion(struct iser_tx_desc *tx_desc,
isert_conn, ib_dev);
break;
case ISER_IB_RDMA_WRITE:
- pr_err("isert_send_completion: Got ISER_IB_RDMA_WRITE\n");
- dump_stack();
+ pr_debug("isert_send_completion: Got ISER_IB_RDMA_WRITE\n");
+ atomic_sub(wr->send_wr_num, &isert_conn->post_send_buf_count);
+ isert_completion_rdma_write(tx_desc, isert_cmd);
break;
case ISER_IB_RDMA_READ:
pr_debug("isert_send_completion: Got ISER_IB_RDMA_READ:\n");
@@ -1709,8 +1945,20 @@ isert_cq_drain_comp_llist(struct isert_conn *isert_conn, struct ib_device *ib_de
llnode = llist_next(llnode);
wr = &t->isert_cmd->rdma_wr;
- atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count);
- isert_completion_put(t, t->isert_cmd, ib_dev);
+ /**
+ * If send_wr_num is 0 this means that we got
+ * RDMA completion and we cleared it and we should
+ * simply decrement the response post. else the
+ * response is incorporated in send_wr_num, just
+ * sub it.
+ **/
+ if (wr->send_wr_num)
+ atomic_sub(wr->send_wr_num,
+ &isert_conn->post_send_buf_count);
+ else
+ atomic_dec(&isert_conn->post_send_buf_count);
+
+ isert_completion_put(t, t->isert_cmd, ib_dev, true);
}
}
@@ -1728,15 +1976,27 @@ isert_cq_tx_comp_err(struct iser_tx_desc *tx_desc, struct isert_conn *isert_conn
llnode = llist_next(llnode);
wr = &t->isert_cmd->rdma_wr;
- atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count);
- isert_completion_put(t, t->isert_cmd, ib_dev);
+ /**
+ * If send_wr_num is 0 this means that we got
+ * RDMA completion and we cleared it and we should
+ * simply decrement the response post. else the
+ * response is incorporated in send_wr_num, just
+ * sub it.
+ **/
+ if (wr->send_wr_num)
+ atomic_sub(wr->send_wr_num,
+ &isert_conn->post_send_buf_count);
+ else
+ atomic_dec(&isert_conn->post_send_buf_count);
+
+ isert_completion_put(t, t->isert_cmd, ib_dev, true);
}
tx_desc->comp_llnode_batch = NULL;
if (!isert_cmd)
isert_unmap_tx_desc(tx_desc, ib_dev);
else
- isert_completion_put(tx_desc, isert_cmd, ib_dev);
+ isert_completion_put(tx_desc, isert_cmd, ib_dev, true);
}
static void
@@ -1918,6 +2178,36 @@ isert_put_response(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
return isert_post_response(isert_conn, isert_cmd);
}
+static void
+isert_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
+{
+ struct isert_cmd *isert_cmd = iscsit_priv_cmd(cmd);
+ struct isert_conn *isert_conn = (struct isert_conn *)conn->context;
+ struct isert_device *device = isert_conn->conn_device;
+
+ spin_lock_bh(&conn->cmd_lock);
+ if (!list_empty(&cmd->i_conn_node))
+ list_del_init(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ if (cmd->data_direction == DMA_TO_DEVICE)
+ iscsit_stop_dataout_timer(cmd);
+
+ device->unreg_rdma_mem(isert_cmd, isert_conn);
+}
+
+static enum target_prot_op
+isert_get_sup_prot_ops(struct iscsi_conn *conn)
+{
+ struct isert_conn *isert_conn = (struct isert_conn *)conn->context;
+ struct isert_device *device = isert_conn->conn_device;
+
+ if (device->pi_capable)
+ return TARGET_PROT_ALL;
+
+ return TARGET_PROT_NORMAL;
+}
+
static int
isert_put_nopin(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
bool nopout_response)
@@ -2099,54 +2389,39 @@ isert_map_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
struct se_cmd *se_cmd = &cmd->se_cmd;
struct isert_cmd *isert_cmd = iscsit_priv_cmd(cmd);
struct isert_conn *isert_conn = (struct isert_conn *)conn->context;
- struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
+ struct isert_data_buf *data = &wr->data;
struct ib_send_wr *send_wr;
struct ib_sge *ib_sge;
- struct scatterlist *sg_start;
- u32 sg_off = 0, sg_nents;
- u32 offset = 0, data_len, data_left, rdma_write_max, va_offset = 0;
- int ret = 0, count, i, ib_sge_cnt;
+ u32 offset, data_len, data_left, rdma_write_max, va_offset = 0;
+ int ret = 0, i, ib_sge_cnt;
- if (wr->iser_ib_op == ISER_IB_RDMA_WRITE) {
- data_left = se_cmd->data_length;
- } else {
- sg_off = cmd->write_data_done / PAGE_SIZE;
- data_left = se_cmd->data_length - cmd->write_data_done;
- offset = cmd->write_data_done;
- isert_cmd->tx_desc.isert_cmd = isert_cmd;
- }
+ isert_cmd->tx_desc.isert_cmd = isert_cmd;
- sg_start = &cmd->se_cmd.t_data_sg[sg_off];
- sg_nents = se_cmd->t_data_nents - sg_off;
+ offset = wr->iser_ib_op == ISER_IB_RDMA_READ ? cmd->write_data_done : 0;
+ ret = isert_map_data_buf(isert_conn, isert_cmd, se_cmd->t_data_sg,
+ se_cmd->t_data_nents, se_cmd->data_length,
+ offset, wr->iser_ib_op, &wr->data);
+ if (ret)
+ return ret;
- count = ib_dma_map_sg(ib_dev, sg_start, sg_nents,
- (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (unlikely(!count)) {
- pr_err("Cmd: %p unrable to map SGs\n", isert_cmd);
- return -EINVAL;
- }
- wr->sge = sg_start;
- wr->num_sge = sg_nents;
- wr->cur_rdma_length = data_left;
- pr_debug("Mapped cmd: %p count: %u sg: %p sg_nents: %u rdma_len %d\n",
- isert_cmd, count, sg_start, sg_nents, data_left);
+ data_left = data->len;
+ offset = data->offset;
- ib_sge = kzalloc(sizeof(struct ib_sge) * sg_nents, GFP_KERNEL);
+ ib_sge = kzalloc(sizeof(struct ib_sge) * data->nents, GFP_KERNEL);
if (!ib_sge) {
pr_warn("Unable to allocate ib_sge\n");
ret = -ENOMEM;
- goto unmap_sg;
+ goto unmap_cmd;
}
wr->ib_sge = ib_sge;
- wr->send_wr_num = DIV_ROUND_UP(sg_nents, isert_conn->max_sge);
+ wr->send_wr_num = DIV_ROUND_UP(data->nents, isert_conn->max_sge);
wr->send_wr = kzalloc(sizeof(struct ib_send_wr) * wr->send_wr_num,
GFP_KERNEL);
if (!wr->send_wr) {
pr_debug("Unable to allocate wr->send_wr\n");
ret = -ENOMEM;
- goto unmap_sg;
+ goto unmap_cmd;
}
wr->isert_cmd = isert_cmd;
@@ -2185,10 +2460,9 @@ isert_map_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
}
return 0;
-unmap_sg:
- ib_dma_unmap_sg(ib_dev, sg_start, sg_nents,
- (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
+unmap_cmd:
+ isert_unmap_data_buf(isert_conn, data);
+
return ret;
}
@@ -2232,49 +2506,70 @@ isert_map_fr_pagelist(struct ib_device *ib_dev,
}
static int
-isert_fast_reg_mr(struct fast_reg_descriptor *fr_desc,
- struct isert_conn *isert_conn, struct scatterlist *sg_start,
- struct ib_sge *ib_sge, u32 sg_nents, u32 offset,
- unsigned int data_len)
+isert_fast_reg_mr(struct isert_conn *isert_conn,
+ struct fast_reg_descriptor *fr_desc,
+ struct isert_data_buf *mem,
+ enum isert_indicator ind,
+ struct ib_sge *sge)
{
struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
+ struct ib_mr *mr;
+ struct ib_fast_reg_page_list *frpl;
struct ib_send_wr fr_wr, inv_wr;
struct ib_send_wr *bad_wr, *wr = NULL;
int ret, pagelist_len;
u32 page_off;
u8 key;
- sg_nents = min_t(unsigned int, sg_nents, ISCSI_ISER_SG_TABLESIZE);
- page_off = offset % PAGE_SIZE;
+ if (mem->dma_nents == 1) {
+ sge->lkey = isert_conn->conn_mr->lkey;
+ sge->addr = ib_sg_dma_address(ib_dev, &mem->sg[0]);
+ sge->length = ib_sg_dma_len(ib_dev, &mem->sg[0]);
+ pr_debug("%s:%d sge: addr: 0x%llx length: %u lkey: %x\n",
+ __func__, __LINE__, sge->addr, sge->length,
+ sge->lkey);
+ return 0;
+ }
+
+ if (ind == ISERT_DATA_KEY_VALID) {
+ /* Registering data buffer */
+ mr = fr_desc->data_mr;
+ frpl = fr_desc->data_frpl;
+ } else {
+ /* Registering protection buffer */
+ mr = fr_desc->pi_ctx->prot_mr;
+ frpl = fr_desc->pi_ctx->prot_frpl;
+ }
+
+ page_off = mem->offset % PAGE_SIZE;
pr_debug("Use fr_desc %p sg_nents %d offset %u\n",
- fr_desc, sg_nents, offset);
+ fr_desc, mem->nents, mem->offset);
- pagelist_len = isert_map_fr_pagelist(ib_dev, sg_start, sg_nents,
- &fr_desc->data_frpl->page_list[0]);
+ pagelist_len = isert_map_fr_pagelist(ib_dev, mem->sg, mem->nents,
+ &frpl->page_list[0]);
- if (!fr_desc->valid) {
+ if (!(fr_desc->ind & ISERT_DATA_KEY_VALID)) {
memset(&inv_wr, 0, sizeof(inv_wr));
inv_wr.wr_id = ISER_FASTREG_LI_WRID;
inv_wr.opcode = IB_WR_LOCAL_INV;
- inv_wr.ex.invalidate_rkey = fr_desc->data_mr->rkey;
+ inv_wr.ex.invalidate_rkey = mr->rkey;
wr = &inv_wr;
/* Bump the key */
- key = (u8)(fr_desc->data_mr->rkey & 0x000000FF);
- ib_update_fast_reg_key(fr_desc->data_mr, ++key);
+ key = (u8)(mr->rkey & 0x000000FF);
+ ib_update_fast_reg_key(mr, ++key);
}
/* Prepare FASTREG WR */
memset(&fr_wr, 0, sizeof(fr_wr));
fr_wr.wr_id = ISER_FASTREG_LI_WRID;
fr_wr.opcode = IB_WR_FAST_REG_MR;
- fr_wr.wr.fast_reg.iova_start =
- fr_desc->data_frpl->page_list[0] + page_off;
- fr_wr.wr.fast_reg.page_list = fr_desc->data_frpl;
+ fr_wr.wr.fast_reg.iova_start = frpl->page_list[0] + page_off;
+ fr_wr.wr.fast_reg.page_list = frpl;
fr_wr.wr.fast_reg.page_list_len = pagelist_len;
fr_wr.wr.fast_reg.page_shift = PAGE_SHIFT;
- fr_wr.wr.fast_reg.length = data_len;
- fr_wr.wr.fast_reg.rkey = fr_desc->data_mr->rkey;
+ fr_wr.wr.fast_reg.length = mem->len;
+ fr_wr.wr.fast_reg.rkey = mr->rkey;
fr_wr.wr.fast_reg.access_flags = IB_ACCESS_LOCAL_WRITE;
if (!wr)
@@ -2287,15 +2582,157 @@ isert_fast_reg_mr(struct fast_reg_descriptor *fr_desc,
pr_err("fast registration failed, ret:%d\n", ret);
return ret;
}
- fr_desc->valid = false;
+ fr_desc->ind &= ~ind;
+
+ sge->lkey = mr->lkey;
+ sge->addr = frpl->page_list[0] + page_off;
+ sge->length = mem->len;
+
+ pr_debug("%s:%d sge: addr: 0x%llx length: %u lkey: %x\n",
+ __func__, __LINE__, sge->addr, sge->length,
+ sge->lkey);
+
+ return ret;
+}
+
+static inline enum ib_t10_dif_type
+se2ib_prot_type(enum target_prot_type prot_type)
+{
+ switch (prot_type) {
+ case TARGET_DIF_TYPE0_PROT:
+ return IB_T10DIF_NONE;
+ case TARGET_DIF_TYPE1_PROT:
+ return IB_T10DIF_TYPE1;
+ case TARGET_DIF_TYPE2_PROT:
+ return IB_T10DIF_TYPE2;
+ case TARGET_DIF_TYPE3_PROT:
+ return IB_T10DIF_TYPE3;
+ default:
+ return IB_T10DIF_NONE;
+ }
+}
- ib_sge->lkey = fr_desc->data_mr->lkey;
- ib_sge->addr = fr_desc->data_frpl->page_list[0] + page_off;
- ib_sge->length = data_len;
+static int
+isert_set_sig_attrs(struct se_cmd *se_cmd, struct ib_sig_attrs *sig_attrs)
+{
+ enum ib_t10_dif_type ib_prot_type = se2ib_prot_type(se_cmd->prot_type);
+
+ sig_attrs->mem.sig_type = IB_SIG_TYPE_T10_DIF;
+ sig_attrs->wire.sig_type = IB_SIG_TYPE_T10_DIF;
+ sig_attrs->mem.sig.dif.pi_interval =
+ se_cmd->se_dev->dev_attrib.block_size;
+ sig_attrs->wire.sig.dif.pi_interval =
+ se_cmd->se_dev->dev_attrib.block_size;
+
+ switch (se_cmd->prot_op) {
+ case TARGET_PROT_DIN_INSERT:
+ case TARGET_PROT_DOUT_STRIP:
+ sig_attrs->mem.sig.dif.type = IB_T10DIF_NONE;
+ sig_attrs->wire.sig.dif.type = ib_prot_type;
+ sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC;
+ sig_attrs->wire.sig.dif.ref_tag = se_cmd->reftag_seed;
+ break;
+ case TARGET_PROT_DOUT_INSERT:
+ case TARGET_PROT_DIN_STRIP:
+ sig_attrs->mem.sig.dif.type = ib_prot_type;
+ sig_attrs->mem.sig.dif.bg_type = IB_T10DIF_CRC;
+ sig_attrs->mem.sig.dif.ref_tag = se_cmd->reftag_seed;
+ sig_attrs->wire.sig.dif.type = IB_T10DIF_NONE;
+ break;
+ case TARGET_PROT_DIN_PASS:
+ case TARGET_PROT_DOUT_PASS:
+ sig_attrs->mem.sig.dif.type = ib_prot_type;
+ sig_attrs->mem.sig.dif.bg_type = IB_T10DIF_CRC;
+ sig_attrs->mem.sig.dif.ref_tag = se_cmd->reftag_seed;
+ sig_attrs->wire.sig.dif.type = ib_prot_type;
+ sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC;
+ sig_attrs->wire.sig.dif.ref_tag = se_cmd->reftag_seed;
+ break;
+ default:
+ pr_err("Unsupported PI operation %d\n", se_cmd->prot_op);
+ return -EINVAL;
+ }
- pr_debug("RDMA ib_sge: addr: 0x%16llx length: %u lkey: %08x\n",
- ib_sge->addr, ib_sge->length, ib_sge->lkey);
+ return 0;
+}
+
+static inline u8
+isert_set_prot_checks(u8 prot_checks)
+{
+ return (prot_checks & TARGET_DIF_CHECK_GUARD ? 0xc0 : 0) |
+ (prot_checks & TARGET_DIF_CHECK_REFTAG ? 0x30 : 0) |
+ (prot_checks & TARGET_DIF_CHECK_REFTAG ? 0x0f : 0);
+}
+
+static int
+isert_reg_sig_mr(struct isert_conn *isert_conn, struct se_cmd *se_cmd,
+ struct fast_reg_descriptor *fr_desc,
+ struct ib_sge *data_sge, struct ib_sge *prot_sge,
+ struct ib_sge *sig_sge)
+{
+ struct ib_send_wr sig_wr, inv_wr;
+ struct ib_send_wr *bad_wr, *wr = NULL;
+ struct pi_context *pi_ctx = fr_desc->pi_ctx;
+ struct ib_sig_attrs sig_attrs;
+ int ret;
+ u32 key;
+
+ memset(&sig_attrs, 0, sizeof(sig_attrs));
+ ret = isert_set_sig_attrs(se_cmd, &sig_attrs);
+ if (ret)
+ goto err;
+
+ sig_attrs.check_mask = isert_set_prot_checks(se_cmd->prot_checks);
+
+ if (!(fr_desc->ind & ISERT_SIG_KEY_VALID)) {
+ memset(&inv_wr, 0, sizeof(inv_wr));
+ inv_wr.opcode = IB_WR_LOCAL_INV;
+ inv_wr.wr_id = ISER_FASTREG_LI_WRID;
+ inv_wr.ex.invalidate_rkey = pi_ctx->sig_mr->rkey;
+ wr = &inv_wr;
+ /* Bump the key */
+ key = (u8)(pi_ctx->sig_mr->rkey & 0x000000FF);
+ ib_update_fast_reg_key(pi_ctx->sig_mr, ++key);
+ }
+
+ memset(&sig_wr, 0, sizeof(sig_wr));
+ sig_wr.opcode = IB_WR_REG_SIG_MR;
+ sig_wr.wr_id = ISER_FASTREG_LI_WRID;
+ sig_wr.sg_list = data_sge;
+ sig_wr.num_sge = 1;
+ sig_wr.wr.sig_handover.access_flags = IB_ACCESS_LOCAL_WRITE;
+ sig_wr.wr.sig_handover.sig_attrs = &sig_attrs;
+ sig_wr.wr.sig_handover.sig_mr = pi_ctx->sig_mr;
+ if (se_cmd->t_prot_sg)
+ sig_wr.wr.sig_handover.prot = prot_sge;
+
+ if (!wr)
+ wr = &sig_wr;
+ else
+ wr->next = &sig_wr;
+
+ ret = ib_post_send(isert_conn->conn_qp, wr, &bad_wr);
+ if (ret) {
+ pr_err("fast registration failed, ret:%d\n", ret);
+ goto err;
+ }
+ fr_desc->ind &= ~ISERT_SIG_KEY_VALID;
+
+ sig_sge->lkey = pi_ctx->sig_mr->lkey;
+ sig_sge->addr = 0;
+ sig_sge->length = se_cmd->data_length;
+ if (se_cmd->prot_op != TARGET_PROT_DIN_STRIP &&
+ se_cmd->prot_op != TARGET_PROT_DOUT_INSERT)
+ /*
+ * We have protection guards on the wire
+ * so we need to set a larget transfer
+ */
+ sig_sge->length += se_cmd->prot_length;
+ pr_debug("sig_sge: addr: 0x%llx length: %u lkey: %x\n",
+ sig_sge->addr, sig_sge->length,
+ sig_sge->lkey);
+err:
return ret;
}
@@ -2305,62 +2742,82 @@ isert_reg_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
{
struct se_cmd *se_cmd = &cmd->se_cmd;
struct isert_cmd *isert_cmd = iscsit_priv_cmd(cmd);
- struct isert_conn *isert_conn = (struct isert_conn *)conn->context;
- struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
+ struct isert_conn *isert_conn = conn->context;
+ struct ib_sge data_sge;
struct ib_send_wr *send_wr;
- struct ib_sge *ib_sge;
- struct scatterlist *sg_start;
- struct fast_reg_descriptor *fr_desc;
- u32 sg_off = 0, sg_nents;
- u32 offset = 0, data_len, data_left, rdma_write_max;
- int ret = 0, count;
+ struct fast_reg_descriptor *fr_desc = NULL;
+ u32 offset;
+ int ret = 0;
unsigned long flags;
- if (wr->iser_ib_op == ISER_IB_RDMA_WRITE) {
- data_left = se_cmd->data_length;
- } else {
- offset = cmd->write_data_done;
- sg_off = offset / PAGE_SIZE;
- data_left = se_cmd->data_length - cmd->write_data_done;
- isert_cmd->tx_desc.isert_cmd = isert_cmd;
- }
+ isert_cmd->tx_desc.isert_cmd = isert_cmd;
- sg_start = &cmd->se_cmd.t_data_sg[sg_off];
- sg_nents = se_cmd->t_data_nents - sg_off;
+ offset = wr->iser_ib_op == ISER_IB_RDMA_READ ? cmd->write_data_done : 0;
+ ret = isert_map_data_buf(isert_conn, isert_cmd, se_cmd->t_data_sg,
+ se_cmd->t_data_nents, se_cmd->data_length,
+ offset, wr->iser_ib_op, &wr->data);
+ if (ret)
+ return ret;
- count = ib_dma_map_sg(ib_dev, sg_start, sg_nents,
- (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (unlikely(!count)) {
- pr_err("Cmd: %p unrable to map SGs\n", isert_cmd);
- return -EINVAL;
+ if (wr->data.dma_nents != 1 ||
+ se_cmd->prot_op != TARGET_PROT_NORMAL) {
+ spin_lock_irqsave(&isert_conn->conn_lock, flags);
+ fr_desc = list_first_entry(&isert_conn->conn_fr_pool,
+ struct fast_reg_descriptor, list);
+ list_del(&fr_desc->list);
+ spin_unlock_irqrestore(&isert_conn->conn_lock, flags);
+ wr->fr_desc = fr_desc;
}
- wr->sge = sg_start;
- wr->num_sge = sg_nents;
- pr_debug("Mapped cmd: %p count: %u sg: %p sg_nents: %u rdma_len %d\n",
- isert_cmd, count, sg_start, sg_nents, data_left);
- memset(&wr->s_ib_sge, 0, sizeof(*ib_sge));
- ib_sge = &wr->s_ib_sge;
- wr->ib_sge = ib_sge;
+ ret = isert_fast_reg_mr(isert_conn, fr_desc, &wr->data,
+ ISERT_DATA_KEY_VALID, &data_sge);
+ if (ret)
+ goto unmap_cmd;
+
+ if (se_cmd->prot_op != TARGET_PROT_NORMAL) {
+ struct ib_sge prot_sge, sig_sge;
+
+ if (se_cmd->t_prot_sg) {
+ ret = isert_map_data_buf(isert_conn, isert_cmd,
+ se_cmd->t_prot_sg,
+ se_cmd->t_prot_nents,
+ se_cmd->prot_length,
+ 0, wr->iser_ib_op, &wr->prot);
+ if (ret)
+ goto unmap_cmd;
+
+ ret = isert_fast_reg_mr(isert_conn, fr_desc, &wr->prot,
+ ISERT_PROT_KEY_VALID, &prot_sge);
+ if (ret)
+ goto unmap_prot_cmd;
+ }
+
+ ret = isert_reg_sig_mr(isert_conn, se_cmd, fr_desc,
+ &data_sge, &prot_sge, &sig_sge);
+ if (ret)
+ goto unmap_prot_cmd;
+ fr_desc->ind |= ISERT_PROTECTED;
+ memcpy(&wr->s_ib_sge, &sig_sge, sizeof(sig_sge));
+ } else
+ memcpy(&wr->s_ib_sge, &data_sge, sizeof(data_sge));
+
+ wr->ib_sge = &wr->s_ib_sge;
wr->send_wr_num = 1;
memset(&wr->s_send_wr, 0, sizeof(*send_wr));
wr->send_wr = &wr->s_send_wr;
-
wr->isert_cmd = isert_cmd;
- rdma_write_max = ISCSI_ISER_SG_TABLESIZE * PAGE_SIZE;
send_wr = &isert_cmd->rdma_wr.s_send_wr;
- send_wr->sg_list = ib_sge;
+ send_wr->sg_list = &wr->s_ib_sge;
send_wr->num_sge = 1;
send_wr->wr_id = (unsigned long)&isert_cmd->tx_desc;
if (wr->iser_ib_op == ISER_IB_RDMA_WRITE) {
send_wr->opcode = IB_WR_RDMA_WRITE;
send_wr->wr.rdma.remote_addr = isert_cmd->read_va;
send_wr->wr.rdma.rkey = isert_cmd->read_stag;
- send_wr->send_flags = 0;
- send_wr->next = &isert_cmd->tx_desc.send_wr;
+ send_wr->send_flags = se_cmd->prot_op == TARGET_PROT_NORMAL ?
+ 0 : IB_SEND_SIGNALED;
} else {
send_wr->opcode = IB_WR_RDMA_READ;
send_wr->wr.rdma.remote_addr = isert_cmd->write_va;
@@ -2368,37 +2825,18 @@ isert_reg_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
send_wr->send_flags = IB_SEND_SIGNALED;
}
- data_len = min(data_left, rdma_write_max);
- wr->cur_rdma_length = data_len;
-
- /* if there is a single dma entry, dma mr is sufficient */
- if (count == 1) {
- ib_sge->addr = ib_sg_dma_address(ib_dev, &sg_start[0]);
- ib_sge->length = ib_sg_dma_len(ib_dev, &sg_start[0]);
- ib_sge->lkey = isert_conn->conn_mr->lkey;
- wr->fr_desc = NULL;
- } else {
+ return 0;
+unmap_prot_cmd:
+ if (se_cmd->t_prot_sg)
+ isert_unmap_data_buf(isert_conn, &wr->prot);
+unmap_cmd:
+ if (fr_desc) {
spin_lock_irqsave(&isert_conn->conn_lock, flags);
- fr_desc = list_first_entry(&isert_conn->conn_fr_pool,
- struct fast_reg_descriptor, list);
- list_del(&fr_desc->list);
+ list_add_tail(&fr_desc->list, &isert_conn->conn_fr_pool);
spin_unlock_irqrestore(&isert_conn->conn_lock, flags);
- wr->fr_desc = fr_desc;
-
- ret = isert_fast_reg_mr(fr_desc, isert_conn, sg_start,
- ib_sge, sg_nents, offset, data_len);
- if (ret) {
- list_add_tail(&fr_desc->list, &isert_conn->conn_fr_pool);
- goto unmap_sg;
- }
}
+ isert_unmap_data_buf(isert_conn, &wr->data);
- return 0;
-
-unmap_sg:
- ib_dma_unmap_sg(ib_dev, sg_start, sg_nents,
- (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
return ret;
}
@@ -2422,25 +2860,35 @@ isert_put_datain(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
return rc;
}
- /*
- * Build isert_conn->tx_desc for iSCSI response PDU and attach
- */
- isert_create_send_desc(isert_conn, isert_cmd, &isert_cmd->tx_desc);
- iscsit_build_rsp_pdu(cmd, conn, true, (struct iscsi_scsi_rsp *)
- &isert_cmd->tx_desc.iscsi_header);
- isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc);
- isert_init_send_wr(isert_conn, isert_cmd,
- &isert_cmd->tx_desc.send_wr, true);
+ if (se_cmd->prot_op == TARGET_PROT_NORMAL) {
+ /*
+ * Build isert_conn->tx_desc for iSCSI response PDU and attach
+ */
+ isert_create_send_desc(isert_conn, isert_cmd,
+ &isert_cmd->tx_desc);
+ iscsit_build_rsp_pdu(cmd, conn, true, (struct iscsi_scsi_rsp *)
+ &isert_cmd->tx_desc.iscsi_header);
+ isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc);
+ isert_init_send_wr(isert_conn, isert_cmd,
+ &isert_cmd->tx_desc.send_wr, true);
+ isert_cmd->rdma_wr.s_send_wr.next = &isert_cmd->tx_desc.send_wr;
+ wr->send_wr_num += 1;
+ }
- atomic_add(wr->send_wr_num + 1, &isert_conn->post_send_buf_count);
+ atomic_add(wr->send_wr_num, &isert_conn->post_send_buf_count);
rc = ib_post_send(isert_conn->conn_qp, wr->send_wr, &wr_failed);
if (rc) {
pr_warn("ib_post_send() failed for IB_WR_RDMA_WRITE\n");
- atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count);
+ atomic_sub(wr->send_wr_num, &isert_conn->post_send_buf_count);
}
- pr_debug("Cmd: %p posted RDMA_WRITE + Response for iSER Data READ\n",
- isert_cmd);
+
+ if (se_cmd->prot_op == TARGET_PROT_NORMAL)
+ pr_debug("Cmd: %p posted RDMA_WRITE + Response for iSER Data "
+ "READ\n", isert_cmd);
+ else
+ pr_debug("Cmd: %p posted RDMA_WRITE for iSER Data READ\n",
+ isert_cmd);
return 1;
}
@@ -2815,6 +3263,8 @@ static struct iscsit_transport iser_target_transport = {
.iscsit_get_dataout = isert_get_dataout,
.iscsit_queue_data_in = isert_put_datain,
.iscsit_queue_status = isert_put_response,
+ .iscsit_aborted_task = isert_aborted_task,
+ .iscsit_get_sup_prot_ops = isert_get_sup_prot_ops,
};
static int __init isert_init(void)
diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h
index f6ae7f5dd408..4c072ae34c01 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.h
+++ b/drivers/infiniband/ulp/isert/ib_isert.h
@@ -50,11 +50,35 @@ struct iser_tx_desc {
struct ib_send_wr send_wr;
} __packed;
+enum isert_indicator {
+ ISERT_PROTECTED = 1 << 0,
+ ISERT_DATA_KEY_VALID = 1 << 1,
+ ISERT_PROT_KEY_VALID = 1 << 2,
+ ISERT_SIG_KEY_VALID = 1 << 3,
+};
+
+struct pi_context {
+ struct ib_mr *prot_mr;
+ struct ib_fast_reg_page_list *prot_frpl;
+ struct ib_mr *sig_mr;
+};
+
struct fast_reg_descriptor {
- struct list_head list;
- struct ib_mr *data_mr;
- struct ib_fast_reg_page_list *data_frpl;
- bool valid;
+ struct list_head list;
+ struct ib_mr *data_mr;
+ struct ib_fast_reg_page_list *data_frpl;
+ u8 ind;
+ struct pi_context *pi_ctx;
+};
+
+struct isert_data_buf {
+ struct scatterlist *sg;
+ int nents;
+ u32 sg_off;
+ u32 len; /* cur_rdma_length */
+ u32 offset;
+ unsigned int dma_nents;
+ enum dma_data_direction dma_dir;
};
struct isert_rdma_wr {
@@ -63,12 +87,11 @@ struct isert_rdma_wr {
enum iser_ib_op_code iser_ib_op;
struct ib_sge *ib_sge;
struct ib_sge s_ib_sge;
- int num_sge;
- struct scatterlist *sge;
int send_wr_num;
struct ib_send_wr *send_wr;
struct ib_send_wr s_send_wr;
- u32 cur_rdma_length;
+ struct isert_data_buf data;
+ struct isert_data_buf prot;
struct fast_reg_descriptor *fr_desc;
};
@@ -141,6 +164,7 @@ struct isert_cq_desc {
struct isert_device {
int use_fastreg;
+ bool pi_capable;
int cqs_used;
int refcount;
int cq_active_qps[ISERT_MAX_CQ];
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 0e537d8d0e47..fe09f2788b15 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1078,6 +1078,7 @@ static void srpt_unmap_sg_to_ib_sge(struct srpt_rdma_ch *ch,
static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch,
struct srpt_send_ioctx *ioctx)
{
+ struct ib_device *dev = ch->sport->sdev->device;
struct se_cmd *cmd;
struct scatterlist *sg, *sg_orig;
int sg_cnt;
@@ -1124,7 +1125,7 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch,
db = ioctx->rbufs;
tsize = cmd->data_length;
- dma_len = sg_dma_len(&sg[0]);
+ dma_len = ib_sg_dma_len(dev, &sg[0]);
riu = ioctx->rdma_ius;
/*
@@ -1155,7 +1156,8 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch,
++j;
if (j < count) {
sg = sg_next(sg);
- dma_len = sg_dma_len(sg);
+ dma_len = ib_sg_dma_len(
+ dev, sg);
}
}
} else {
@@ -1192,8 +1194,8 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch,
tsize = cmd->data_length;
riu = ioctx->rdma_ius;
sg = sg_orig;
- dma_len = sg_dma_len(&sg[0]);
- dma_addr = sg_dma_address(&sg[0]);
+ dma_len = ib_sg_dma_len(dev, &sg[0]);
+ dma_addr = ib_sg_dma_address(dev, &sg[0]);
/* this second loop is really mapped sg_addres to rdma_iu->ib_sge */
for (i = 0, j = 0;
@@ -1216,8 +1218,10 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch,
++j;
if (j < count) {
sg = sg_next(sg);
- dma_len = sg_dma_len(sg);
- dma_addr = sg_dma_address(sg);
+ dma_len = ib_sg_dma_len(
+ dev, sg);
+ dma_addr = ib_sg_dma_address(
+ dev, sg);
}
}
} else {
@@ -2580,7 +2584,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id,
goto destroy_ib;
}
- ch->sess = transport_init_session();
+ ch->sess = transport_init_session(TARGET_PROT_NORMAL);
if (IS_ERR(ch->sess)) {
rej->reason = __constant_cpu_to_be32(
SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES);
@@ -3081,6 +3085,14 @@ static void srpt_queue_tm_rsp(struct se_cmd *cmd)
srpt_queue_response(cmd);
}
+static void srpt_aborted_task(struct se_cmd *cmd)
+{
+ struct srpt_send_ioctx *ioctx = container_of(cmd,
+ struct srpt_send_ioctx, cmd);
+
+ srpt_unmap_sg_to_ib_sge(ioctx->ch, ioctx);
+}
+
static int srpt_queue_status(struct se_cmd *cmd)
{
struct srpt_send_ioctx *ioctx;
@@ -3928,6 +3940,7 @@ static struct target_core_fabric_ops srpt_template = {
.queue_data_in = srpt_queue_data_in,
.queue_status = srpt_queue_status,
.queue_tm_rsp = srpt_queue_tm_rsp,
+ .aborted_task = srpt_aborted_task,
/*
* Setup function pointers for generic logic in
* target_core_fabric_configfs.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 44c358ecf5a1..6de9dfbf61c1 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -416,7 +416,7 @@ config LEDS_MC13783
depends on MFD_MC13XXX
help
This option enable support for on-chip LED drivers found
- on Freescale Semiconductor MC13783/MC13892 PMIC.
+ on Freescale Semiconductor MC13783/MC13892/MC34708 PMIC.
config LEDS_NS2
tristate "LED support for Network Space v2 GPIO LEDs"
@@ -474,7 +474,7 @@ config LEDS_LM355x
config LEDS_OT200
tristate "LED support for the Bachmann OT200"
- depends on LEDS_CLASS && HAS_IOMEM
+ depends on LEDS_CLASS && HAS_IOMEM && (X86_32 || COMPILE_TEST)
help
This option enables support for the LEDs on the Bachmann OT200.
Say Y to enable LEDs on the Bachmann OT200.
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index ce8921a753a3..71b40d3bf776 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -39,9 +39,11 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
led_cdev->blink_delay_on = delay_on;
led_cdev->blink_delay_off = delay_off;
- /* never on - don't blink */
- if (!delay_on)
+ /* never on - just set to off */
+ if (!delay_on) {
+ __led_set_brightness(led_cdev, LED_OFF);
return;
+ }
/* never off - just set to brightness */
if (!delay_off) {
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index e387f41a9cb7..c3734f10fdd5 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/device.h>
@@ -220,9 +219,12 @@ void led_trigger_unregister(struct led_trigger *trig)
{
struct led_classdev *led_cdev;
+ if (list_empty_careful(&trig->next_trig))
+ return;
+
/* Remove from the list of led triggers */
down_write(&triggers_list_lock);
- list_del(&trig->next_trig);
+ list_del_init(&trig->next_trig);
up_write(&triggers_list_lock);
/* Remove anyone actively using this trigger */
diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c
index 5f588c0a376e..d1e1bca90d11 100644
--- a/drivers/leds/leds-88pm860x.c
+++ b/drivers/leds/leds-88pm860x.c
@@ -11,7 +11,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c
index 7e311a120b11..86b5bdb0c773 100644
--- a/drivers/leds/leds-adp5520.c
+++ b/drivers/leds/leds-adp5520.c
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c
index 6de216a89a0c..70c74a7f0dfe 100644
--- a/drivers/leds/leds-asic3.c
+++ b/drivers/leds/leds-asic3.c
@@ -7,7 +7,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/slab.h>
diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c
index 66d0a57db221..d0452b099aee 100644
--- a/drivers/leds/leds-blinkm.c
+++ b/drivers/leds/leds-blinkm.c
@@ -18,7 +18,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
@@ -444,7 +443,7 @@ static void led_work(struct work_struct *work)
{
int ret;
struct blinkm_led *led;
- struct blinkm_data *data ;
+ struct blinkm_data *data;
struct blinkm_work *blm_work = work_to_blmwork(work);
led = blm_work->blinkm_led;
diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c
index d93e2455da5c..f58a354428e3 100644
--- a/drivers/leds/leds-clevo-mail.c
+++ b/drivers/leds/leds-clevo-mail.c
@@ -19,7 +19,7 @@ MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");
MODULE_DESCRIPTION("Clevo mail LED driver");
MODULE_LICENSE("GPL");
-static bool __initdata nodetect;
+static bool nodetect;
module_param_named(nodetect, nodetect, bool, 0);
MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection");
@@ -153,7 +153,7 @@ static struct led_classdev clevo_mail_led = {
.flags = LED_CORE_SUSPENDRESUME,
};
-static int clevo_mail_led_probe(struct platform_device *pdev)
+static int __init clevo_mail_led_probe(struct platform_device *pdev)
{
return led_classdev_register(&pdev->dev, &clevo_mail_led);
}
@@ -165,7 +165,6 @@ static int clevo_mail_led_remove(struct platform_device *pdev)
}
static struct platform_driver clevo_mail_led_driver = {
- .probe = clevo_mail_led_probe,
.remove = clevo_mail_led_remove,
.driver = {
.name = KBUILD_MODNAME,
diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c
index 8abcb66db01c..910339d86edf 100644
--- a/drivers/leds/leds-cobalt-qube.c
+++ b/drivers/leds/leds-cobalt-qube.c
@@ -3,7 +3,6 @@
*
* Control the Cobalt Qube/RaQ front LED
*/
-#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/leds.h>
diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c
index 2a4b87f8091a..35dffb100388 100644
--- a/drivers/leds/leds-da903x.c
+++ b/drivers/leds/leds-da903x.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c
index 865d4faf874a..01486adc7f8b 100644
--- a/drivers/leds/leds-da9052.c
+++ b/drivers/leds/leds-da9052.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c
index b4d5a44cc41b..2b4dc738dcd6 100644
--- a/drivers/leds/leds-fsg.c
+++ b/drivers/leds/leds-fsg.c
@@ -16,7 +16,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/module.h>
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 78b0e273a903..57ff20fecf57 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -11,7 +11,6 @@
*
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/leds.h>
@@ -204,6 +203,9 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
led.default_state = LEDS_GPIO_DEFSTATE_OFF;
}
+ if (of_get_property(child, "retain-state-suspended", NULL))
+ led.retain_state_suspended = 1;
+
ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
&pdev->dev, NULL);
if (ret < 0) {
@@ -224,6 +226,8 @@ static const struct of_device_id of_gpio_leds_match[] = {
{ .compatible = "gpio-leds", },
{},
};
+
+MODULE_DEVICE_TABLE(of, of_gpio_leds_match);
#else /* CONFIG_OF_GPIO */
static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
{
diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c
index 366b6055e330..d61a98896c71 100644
--- a/drivers/leds/leds-hp6xx.c
+++ b/drivers/leds/leds-hp6xx.c
@@ -12,7 +12,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <asm/hd64461.h>
diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c
index 027ede73b80d..e2c642c1169b 100644
--- a/drivers/leds/leds-lm3533.c
+++ b/drivers/leds/leds-lm3533.c
@@ -12,7 +12,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/leds.h>
#include <linux/mfd/core.h>
#include <linux/mutex.h>
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 2ec34cfcedce..8ca197af2864 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -25,7 +25,6 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c
index 4ade66a2d9d4..cb5ed82994ba 100644
--- a/drivers/leds/leds-lp5523.c
+++ b/drivers/leds/leds-lp5523.c
@@ -25,7 +25,6 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c
index bf006f4e44a0..ca85724ab138 100644
--- a/drivers/leds/leds-lp5562.c
+++ b/drivers/leds/leds-lp5562.c
@@ -13,7 +13,6 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -347,9 +346,9 @@ static void lp5562_write_program_memory(struct lp55xx_chip *chip,
/* check the size of program count */
static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn)
{
- return (ptn->size_r >= LP5562_PROGRAM_LENGTH ||
- ptn->size_g >= LP5562_PROGRAM_LENGTH ||
- ptn->size_b >= LP5562_PROGRAM_LENGTH);
+ return ptn->size_r >= LP5562_PROGRAM_LENGTH ||
+ ptn->size_g >= LP5562_PROGRAM_LENGTH ||
+ ptn->size_b >= LP5562_PROGRAM_LENGTH;
}
static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c
index 3417e5be7b57..059f5b1f3553 100644
--- a/drivers/leds/leds-lt3593.c
+++ b/drivers/leds/leds-lt3593.c
@@ -17,7 +17,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c
index ca87a1b4a0db..f1db88e25138 100644
--- a/drivers/leds/leds-mc13783.c
+++ b/drivers/leds/leds-mc13783.c
@@ -1,5 +1,5 @@
/*
- * LEDs driver for Freescale MC13783/MC13892
+ * LEDs driver for Freescale MC13783/MC13892/MC34708
*
* Copyright (C) 2010 Philippe Rétornaz
*
@@ -17,57 +17,56 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
+#include <linux/of.h>
#include <linux/workqueue.h>
#include <linux/mfd/mc13xxx.h>
-#define MC13XXX_REG_LED_CONTROL(x) (51 + (x))
-
struct mc13xxx_led_devtype {
int led_min;
int led_max;
int num_regs;
+ u32 ledctrl_base;
};
struct mc13xxx_led {
struct led_classdev cdev;
struct work_struct work;
- struct mc13xxx *master;
enum led_brightness new_brightness;
int id;
+ struct mc13xxx_leds *leds;
};
struct mc13xxx_leds {
+ struct mc13xxx *master;
struct mc13xxx_led_devtype *devtype;
int num_leds;
- struct mc13xxx_led led[0];
+ struct mc13xxx_led *led;
};
+static unsigned int mc13xxx_max_brightness(int id)
+{
+ if (id >= MC13783_LED_MD && id <= MC13783_LED_KP)
+ return 0x0f;
+ else if (id >= MC13783_LED_R1 && id <= MC13783_LED_B3)
+ return 0x1f;
+
+ return 0x3f;
+}
+
static void mc13xxx_led_work(struct work_struct *work)
{
struct mc13xxx_led *led = container_of(work, struct mc13xxx_led, work);
- int reg, mask, value, bank, off, shift;
+ struct mc13xxx_leds *leds = led->leds;
+ unsigned int reg, bank, off, shift;
switch (led->id) {
case MC13783_LED_MD:
- reg = MC13XXX_REG_LED_CONTROL(2);
- shift = 9;
- mask = 0x0f;
- value = led->new_brightness >> 4;
- break;
case MC13783_LED_AD:
- reg = MC13XXX_REG_LED_CONTROL(2);
- shift = 13;
- mask = 0x0f;
- value = led->new_brightness >> 4;
- break;
case MC13783_LED_KP:
- reg = MC13XXX_REG_LED_CONTROL(2);
- shift = 17;
- mask = 0x0f;
- value = led->new_brightness >> 4;
+ reg = 2;
+ shift = 9 + (led->id - MC13783_LED_MD) * 4;
break;
case MC13783_LED_R1:
case MC13783_LED_G1:
@@ -80,44 +79,35 @@ static void mc13xxx_led_work(struct work_struct *work)
case MC13783_LED_B3:
off = led->id - MC13783_LED_R1;
bank = off / 3;
- reg = MC13XXX_REG_LED_CONTROL(3) + bank;
+ reg = 3 + bank;
shift = (off - bank * 3) * 5 + 6;
- value = led->new_brightness >> 3;
- mask = 0x1f;
break;
case MC13892_LED_MD:
- reg = MC13XXX_REG_LED_CONTROL(0);
- shift = 3;
- mask = 0x3f;
- value = led->new_brightness >> 2;
- break;
case MC13892_LED_AD:
- reg = MC13XXX_REG_LED_CONTROL(0);
- shift = 15;
- mask = 0x3f;
- value = led->new_brightness >> 2;
- break;
case MC13892_LED_KP:
- reg = MC13XXX_REG_LED_CONTROL(1);
- shift = 3;
- mask = 0x3f;
- value = led->new_brightness >> 2;
+ reg = (led->id - MC13892_LED_MD) / 2;
+ shift = 3 + (led->id - MC13892_LED_MD) * 12;
break;
case MC13892_LED_R:
case MC13892_LED_G:
case MC13892_LED_B:
off = led->id - MC13892_LED_R;
bank = off / 2;
- reg = MC13XXX_REG_LED_CONTROL(2) + bank;
+ reg = 2 + bank;
shift = (off - bank * 2) * 12 + 3;
- value = led->new_brightness >> 2;
- mask = 0x3f;
+ break;
+ case MC34708_LED_R:
+ case MC34708_LED_G:
+ reg = 0;
+ shift = 3 + (led->id - MC34708_LED_R) * 12;
break;
default:
BUG();
}
- mc13xxx_reg_rmw(led->master, reg, mask << shift, value << shift);
+ mc13xxx_reg_rmw(leds->master, leds->devtype->ledctrl_base + reg,
+ mc13xxx_max_brightness(led->id) << shift,
+ led->new_brightness << shift);
}
static void mc13xxx_led_set(struct led_classdev *led_cdev,
@@ -130,47 +120,121 @@ static void mc13xxx_led_set(struct led_classdev *led_cdev,
schedule_work(&led->work);
}
-static int __init mc13xxx_led_probe(struct platform_device *pdev)
+#ifdef CONFIG_OF
+static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
+ struct platform_device *pdev)
{
- struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct mc13xxx *mcdev = dev_get_drvdata(pdev->dev.parent);
- struct mc13xxx_led_devtype *devtype =
- (struct mc13xxx_led_devtype *)pdev->id_entry->driver_data;
- struct mc13xxx_leds *leds;
- int i, id, num_leds, ret = -ENODATA;
- u32 reg, init_led = 0;
+ struct mc13xxx_leds *leds = platform_get_drvdata(pdev);
+ struct mc13xxx_leds_platform_data *pdata;
+ struct device_node *parent, *child;
+ struct device *dev = &pdev->dev;
+ int i = 0, ret = -ENODATA;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ of_node_get(dev->parent->of_node);
+
+ parent = of_find_node_by_name(dev->parent->of_node, "leds");
+ if (!parent)
+ goto out_node_put;
- if (!pdata) {
- dev_err(&pdev->dev, "Missing platform data\n");
- return -ENODEV;
+ ret = of_property_read_u32_array(parent, "led-control",
+ pdata->led_control,
+ leds->devtype->num_regs);
+ if (ret)
+ goto out_node_put;
+
+ pdata->num_leds = of_get_child_count(parent);
+
+ pdata->led = devm_kzalloc(dev, pdata->num_leds * sizeof(*pdata->led),
+ GFP_KERNEL);
+ if (!pdata->led) {
+ ret = -ENOMEM;
+ goto out_node_put;
}
- num_leds = pdata->num_leds;
+ for_each_child_of_node(parent, child) {
+ const char *str;
+ u32 tmp;
- if ((num_leds < 1) ||
- (num_leds > (devtype->led_max - devtype->led_min + 1))) {
- dev_err(&pdev->dev, "Invalid LED count %d\n", num_leds);
- return -EINVAL;
+ if (of_property_read_u32(child, "reg", &tmp))
+ continue;
+ pdata->led[i].id = leds->devtype->led_min + tmp;
+
+ if (!of_property_read_string(child, "label", &str))
+ pdata->led[i].name = str;
+ if (!of_property_read_string(child, "linux,default-trigger",
+ &str))
+ pdata->led[i].default_trigger = str;
+
+ i++;
}
- leds = devm_kzalloc(&pdev->dev, num_leds * sizeof(struct mc13xxx_led) +
- sizeof(struct mc13xxx_leds), GFP_KERNEL);
+ pdata->num_leds = i;
+ ret = i > 0 ? 0 : -ENODATA;
+
+out_node_put:
+ of_node_put(parent);
+
+ return ret ? ERR_PTR(ret) : pdata;
+}
+#else
+static inline struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
+ struct platform_device *pdev)
+{
+ return ERR_PTR(-ENOSYS);
+}
+#endif
+
+static int __init mc13xxx_led_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(dev);
+ struct mc13xxx *mcdev = dev_get_drvdata(dev->parent);
+ struct mc13xxx_led_devtype *devtype =
+ (struct mc13xxx_led_devtype *)pdev->id_entry->driver_data;
+ struct mc13xxx_leds *leds;
+ int i, id, ret = -ENODATA;
+ u32 init_led = 0;
+
+ leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL);
if (!leds)
return -ENOMEM;
leds->devtype = devtype;
- leds->num_leds = num_leds;
+ leds->master = mcdev;
platform_set_drvdata(pdev, leds);
+ if (dev->parent->of_node) {
+ pdata = mc13xxx_led_probe_dt(pdev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ } else if (!pdata)
+ return -ENODATA;
+
+ leds->num_leds = pdata->num_leds;
+
+ if ((leds->num_leds < 1) ||
+ (leds->num_leds > (devtype->led_max - devtype->led_min + 1))) {
+ dev_err(dev, "Invalid LED count %d\n", leds->num_leds);
+ return -EINVAL;
+ }
+
+ leds->led = devm_kzalloc(dev, leds->num_leds * sizeof(*leds->led),
+ GFP_KERNEL);
+ if (!leds->led)
+ return -ENOMEM;
+
for (i = 0; i < devtype->num_regs; i++) {
- reg = pdata->led_control[i];
- WARN_ON(reg >= (1 << 24));
- ret = mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), reg);
+ ret = mc13xxx_reg_write(mcdev, leds->devtype->ledctrl_base + i,
+ pdata->led_control[i]);
if (ret)
return ret;
}
- for (i = 0; i < num_leds; i++) {
+ for (i = 0; i < leds->num_leds; i++) {
const char *name, *trig;
ret = -EINVAL;
@@ -180,30 +244,29 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev)
trig = pdata->led[i].default_trigger;
if ((id > devtype->led_max) || (id < devtype->led_min)) {
- dev_err(&pdev->dev, "Invalid ID %i\n", id);
+ dev_err(dev, "Invalid ID %i\n", id);
break;
}
if (init_led & (1 << id)) {
- dev_warn(&pdev->dev,
- "LED %i already initialized\n", id);
+ dev_warn(dev, "LED %i already initialized\n", id);
break;
}
init_led |= 1 << id;
leds->led[i].id = id;
- leds->led[i].master = mcdev;
+ leds->led[i].leds = leds;
leds->led[i].cdev.name = name;
leds->led[i].cdev.default_trigger = trig;
+ leds->led[i].cdev.flags = LED_CORE_SUSPENDRESUME;
leds->led[i].cdev.brightness_set = mc13xxx_led_set;
- leds->led[i].cdev.brightness = LED_OFF;
+ leds->led[i].cdev.max_brightness = mc13xxx_max_brightness(id);
INIT_WORK(&leds->led[i].work, mc13xxx_led_work);
- ret = led_classdev_register(pdev->dev.parent,
- &leds->led[i].cdev);
+ ret = led_classdev_register(dev->parent, &leds->led[i].cdev);
if (ret) {
- dev_err(&pdev->dev, "Failed to register LED %i\n", id);
+ dev_err(dev, "Failed to register LED %i\n", id);
break;
}
}
@@ -219,7 +282,6 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev)
static int mc13xxx_led_remove(struct platform_device *pdev)
{
- struct mc13xxx *mcdev = dev_get_drvdata(pdev->dev.parent);
struct mc13xxx_leds *leds = platform_get_drvdata(pdev);
int i;
@@ -228,9 +290,6 @@ static int mc13xxx_led_remove(struct platform_device *pdev)
cancel_work_sync(&leds->led[i].work);
}
- for (i = 0; i < leds->devtype->num_regs; i++)
- mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), 0);
-
return 0;
}
@@ -238,17 +297,27 @@ static const struct mc13xxx_led_devtype mc13783_led_devtype = {
.led_min = MC13783_LED_MD,
.led_max = MC13783_LED_B3,
.num_regs = 6,
+ .ledctrl_base = 51,
};
static const struct mc13xxx_led_devtype mc13892_led_devtype = {
.led_min = MC13892_LED_MD,
.led_max = MC13892_LED_B,
.num_regs = 4,
+ .ledctrl_base = 51,
+};
+
+static const struct mc13xxx_led_devtype mc34708_led_devtype = {
+ .led_min = MC34708_LED_R,
+ .led_max = MC34708_LED_G,
+ .num_regs = 1,
+ .ledctrl_base = 54,
};
static const struct platform_device_id mc13xxx_led_id_table[] = {
{ "mc13783-led", (kernel_ulong_t)&mc13783_led_devtype, },
{ "mc13892-led", (kernel_ulong_t)&mc13892_led_devtype, },
+ { "mc34708-led", (kernel_ulong_t)&mc34708_led_devtype, },
{ }
};
MODULE_DEVICE_TABLE(platform, mc13xxx_led_id_table);
diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c
index 2f9f141084ba..e97f443a6e07 100644
--- a/drivers/leds/leds-netxbig.c
+++ b/drivers/leds/leds-netxbig.c
@@ -21,7 +21,6 @@
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c
index c7a4230233ea..efa625883c83 100644
--- a/drivers/leds/leds-ns2.c
+++ b/drivers/leds/leds-ns2.c
@@ -23,7 +23,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
diff --git a/drivers/leds/leds-ot200.c b/drivers/leds/leds-ot200.c
index 98cae529373f..c9d906098466 100644
--- a/drivers/leds/leds-ot200.c
+++ b/drivers/leds/leds-ot200.c
@@ -8,7 +8,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/leds.h>
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index 605047428b5a..7d0aaed1e23a 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/fb.h>
@@ -84,6 +83,15 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds)
(sizeof(struct led_pwm_data) * num_leds);
}
+static void led_pwm_cleanup(struct led_pwm_priv *priv)
+{
+ while (priv->num_leds--) {
+ led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
+ if (priv->leds[priv->num_leds].can_sleep)
+ cancel_work_sync(&priv->leds[priv->num_leds].work);
+ }
+}
+
static int led_pwm_create_of(struct platform_device *pdev,
struct led_pwm_priv *priv)
{
@@ -131,8 +139,7 @@ static int led_pwm_create_of(struct platform_device *pdev,
return 0;
err:
- while (priv->num_leds--)
- led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
+ led_pwm_cleanup(priv);
return ret;
}
@@ -200,8 +207,8 @@ static int led_pwm_probe(struct platform_device *pdev)
return 0;
err:
- while (i--)
- led_classdev_unregister(&priv->leds[i].cdev);
+ priv->num_leds = i;
+ led_pwm_cleanup(priv);
return ret;
}
@@ -209,13 +216,8 @@ err:
static int led_pwm_remove(struct platform_device *pdev)
{
struct led_pwm_priv *priv = platform_get_drvdata(pdev);
- int i;
- for (i = 0; i < priv->num_leds; i++) {
- led_classdev_unregister(&priv->leds[i].cdev);
- if (priv->leds[i].can_sleep)
- cancel_work_sync(&priv->leds[i].work);
- }
+ led_pwm_cleanup(priv);
return 0;
}
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c
index 98174e7240ee..28988b7b4fab 100644
--- a/drivers/leds/leds-s3c24xx.c
+++ b/drivers/leds/leds-s3c24xx.c
@@ -12,7 +12,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio.h>
diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c
index 5b8f938a8d73..2eb3ef62962b 100644
--- a/drivers/leds/leds-ss4200.c
+++ b/drivers/leds/leds-ss4200.c
@@ -63,7 +63,7 @@ MODULE_LICENSE("GPL");
/*
* PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives.
*/
-static DEFINE_PCI_DEVICE_TABLE(ich7_lpc_pci_id) = {
+static const struct pci_device_id ich7_lpc_pci_id[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) },
@@ -78,7 +78,7 @@ static int __init ss4200_led_dmi_callback(const struct dmi_system_id *id)
return 1;
}
-static bool __initdata nodetect;
+static bool nodetect;
module_param_named(nodetect, nodetect, bool, 0);
MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection");
diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c
index 0a1a13f3a6a5..e72c974142d0 100644
--- a/drivers/leds/leds-wm831x-status.c
+++ b/drivers/leds/leds-wm831x-status.c
@@ -10,7 +10,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/leds.h>
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
index 3f75fd22fd49..4133ffe29015 100644
--- a/drivers/leds/leds-wm8350.c
+++ b/drivers/leds/leds-wm8350.c
@@ -10,7 +10,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/err.h>
diff --git a/drivers/leds/trigger/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c
index 118335eccc56..1c3ee9fcaf34 100644
--- a/drivers/leds/trigger/ledtrig-cpu.c
+++ b/drivers/leds/trigger/ledtrig-cpu.c
@@ -26,6 +26,7 @@
#include <linux/percpu.h>
#include <linux/syscore_ops.h>
#include <linux/rwsem.h>
+#include <linux/cpu.h>
#include "../leds.h"
#define MAX_NAME_LEN 8
@@ -92,6 +93,26 @@ static struct syscore_ops ledtrig_cpu_syscore_ops = {
.resume = ledtrig_cpu_syscore_resume,
};
+static int ledtrig_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_STARTING:
+ ledtrig_cpu(CPU_LED_START);
+ break;
+ case CPU_DYING:
+ ledtrig_cpu(CPU_LED_STOP);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+
+static struct notifier_block ledtrig_cpu_nb = {
+ .notifier_call = ledtrig_cpu_notify,
+};
+
static int __init ledtrig_cpu_init(void)
{
int cpu;
@@ -113,6 +134,7 @@ static int __init ledtrig_cpu_init(void)
}
register_syscore_ops(&ledtrig_cpu_syscore_ops);
+ register_cpu_notifier(&ledtrig_cpu_nb);
pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n");
@@ -124,6 +146,8 @@ static void __exit ledtrig_cpu_exit(void)
{
int cpu;
+ unregister_cpu_notifier(&ledtrig_cpu_nb);
+
for_each_possible_cpu(cpu) {
struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 4195a01b1535..9a8e66ae04f5 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -1988,7 +1988,6 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
if (mddev->bitmap_info.file) {
struct file *f = mddev->bitmap_info.file;
mddev->bitmap_info.file = NULL;
- restore_bitmap_write_access(f);
fput(f);
}
} else {
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 4ad5cc4e63e8..8fda38d23e38 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -5181,32 +5181,6 @@ static int restart_array(struct mddev *mddev)
return 0;
}
-/* similar to deny_write_access, but accounts for our holding a reference
- * to the file ourselves */
-static int deny_bitmap_write_access(struct file * file)
-{
- struct inode *inode = file->f_mapping->host;
-
- spin_lock(&inode->i_lock);
- if (atomic_read(&inode->i_writecount) > 1) {
- spin_unlock(&inode->i_lock);
- return -ETXTBSY;
- }
- atomic_set(&inode->i_writecount, -1);
- spin_unlock(&inode->i_lock);
-
- return 0;
-}
-
-void restore_bitmap_write_access(struct file *file)
-{
- struct inode *inode = file->f_mapping->host;
-
- spin_lock(&inode->i_lock);
- atomic_set(&inode->i_writecount, 1);
- spin_unlock(&inode->i_lock);
-}
-
static void md_clean(struct mddev *mddev)
{
mddev->array_sectors = 0;
@@ -5427,7 +5401,6 @@ static int do_md_stop(struct mddev * mddev, int mode,
bitmap_destroy(mddev);
if (mddev->bitmap_info.file) {
- restore_bitmap_write_access(mddev->bitmap_info.file);
fput(mddev->bitmap_info.file);
mddev->bitmap_info.file = NULL;
}
@@ -5979,7 +5952,7 @@ abort_export:
static int set_bitmap_file(struct mddev *mddev, int fd)
{
- int err;
+ int err = 0;
if (mddev->pers) {
if (!mddev->pers->quiesce)
@@ -5991,6 +5964,7 @@ static int set_bitmap_file(struct mddev *mddev, int fd)
if (fd >= 0) {
+ struct inode *inode;
if (mddev->bitmap)
return -EEXIST; /* cannot add when bitmap is present */
mddev->bitmap_info.file = fget(fd);
@@ -6001,10 +5975,21 @@ static int set_bitmap_file(struct mddev *mddev, int fd)
return -EBADF;
}
- err = deny_bitmap_write_access(mddev->bitmap_info.file);
- if (err) {
+ inode = mddev->bitmap_info.file->f_mapping->host;
+ if (!S_ISREG(inode->i_mode)) {
+ printk(KERN_ERR "%s: error: bitmap file must be a regular file\n",
+ mdname(mddev));
+ err = -EBADF;
+ } else if (!(mddev->bitmap_info.file->f_mode & FMODE_WRITE)) {
+ printk(KERN_ERR "%s: error: bitmap file must open for write\n",
+ mdname(mddev));
+ err = -EBADF;
+ } else if (atomic_read(&inode->i_writecount) != 1) {
printk(KERN_ERR "%s: error: bitmap file is already in use\n",
mdname(mddev));
+ err = -EBUSY;
+ }
+ if (err) {
fput(mddev->bitmap_info.file);
mddev->bitmap_info.file = NULL;
return err;
@@ -6027,10 +6012,8 @@ static int set_bitmap_file(struct mddev *mddev, int fd)
mddev->pers->quiesce(mddev, 0);
}
if (fd < 0) {
- if (mddev->bitmap_info.file) {
- restore_bitmap_write_access(mddev->bitmap_info.file);
+ if (mddev->bitmap_info.file)
fput(mddev->bitmap_info.file);
- }
mddev->bitmap_info.file = NULL;
}
@@ -7182,11 +7165,14 @@ static int md_seq_open(struct inode *inode, struct file *file)
return error;
}
+static int md_unloading;
static unsigned int mdstat_poll(struct file *filp, poll_table *wait)
{
struct seq_file *seq = filp->private_data;
int mask;
+ if (md_unloading)
+ return POLLIN|POLLRDNORM|POLLERR|POLLPRI;;
poll_wait(filp, &md_event_waiters, wait);
/* always allow read */
@@ -8672,6 +8658,7 @@ static __exit void md_exit(void)
{
struct mddev *mddev;
struct list_head *tmp;
+ int delay = 1;
blk_unregister_region(MKDEV(MD_MAJOR,0), 1U << MINORBITS);
blk_unregister_region(MKDEV(mdp_major,0), 1U << MINORBITS);
@@ -8680,7 +8667,19 @@ static __exit void md_exit(void)
unregister_blkdev(mdp_major, "mdp");
unregister_reboot_notifier(&md_notifier);
unregister_sysctl_table(raid_table_header);
+
+ /* We cannot unload the modules while some process is
+ * waiting for us in select() or poll() - wake them up
+ */
+ md_unloading = 1;
+ while (waitqueue_active(&md_event_waiters)) {
+ /* not safe to leave yet */
+ wake_up(&md_event_waiters);
+ msleep(delay);
+ delay += delay;
+ }
remove_proc_entry("mdstat", NULL);
+
for_each_mddev(mddev, tmp) {
export_array(mddev);
mddev->hold_active = 0;
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 07bba96de260..a49d991f3fe1 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -605,7 +605,6 @@ extern int md_check_no_bitmap(struct mddev *mddev);
extern int md_integrity_register(struct mddev *mddev);
extern void md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev);
extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale);
-extern void restore_bitmap_write_access(struct file *file);
extern void mddev_init(struct mddev *mddev);
extern int md_run(struct mddev *mddev);
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 4a6ca1cb2e78..56e24c072b62 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -97,6 +97,7 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
struct pool_info *pi = data;
struct r1bio *r1_bio;
struct bio *bio;
+ int need_pages;
int i, j;
r1_bio = r1bio_pool_alloc(gfp_flags, pi);
@@ -119,15 +120,15 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
* RESYNC_PAGES for each bio.
*/
if (test_bit(MD_RECOVERY_REQUESTED, &pi->mddev->recovery))
- j = pi->raid_disks;
+ need_pages = pi->raid_disks;
else
- j = 1;
- while(j--) {
+ need_pages = 1;
+ for (j = 0; j < need_pages; j++) {
bio = r1_bio->bios[j];
bio->bi_vcnt = RESYNC_PAGES;
if (bio_alloc_pages(bio, gfp_flags))
- goto out_free_bio;
+ goto out_free_pages;
}
/* If not user-requests, copy the page pointers to all bios */
if (!test_bit(MD_RECOVERY_REQUESTED, &pi->mddev->recovery)) {
@@ -141,6 +142,14 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data)
return r1_bio;
+out_free_pages:
+ while (--j >= 0) {
+ struct bio_vec *bv;
+
+ bio_for_each_segment_all(bv, r1_bio->bios[j], i)
+ __free_page(bv->bv_page);
+ }
+
out_free_bio:
while (++j < pi->raid_disks)
bio_put(r1_bio->bios[j]);
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 16f5c21963db..25247a852912 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -679,14 +679,9 @@ get_active_stripe(struct r5conf *conf, sector_t sector,
init_stripe(sh, sector, previous);
atomic_inc(&sh->count);
}
- } else {
+ } else if (!atomic_inc_not_zero(&sh->count)) {
spin_lock(&conf->device_lock);
- if (atomic_read(&sh->count)) {
- BUG_ON(!list_empty(&sh->lru)
- && !test_bit(STRIPE_EXPANDING, &sh->state)
- && !test_bit(STRIPE_ON_UNPLUG_LIST, &sh->state)
- );
- } else {
+ if (!atomic_read(&sh->count)) {
if (!test_bit(STRIPE_HANDLE, &sh->state))
atomic_inc(&conf->active_stripes);
BUG_ON(list_empty(&sh->lru) &&
@@ -4552,6 +4547,8 @@ static void make_request(struct mddev *mddev, struct bio * bi)
struct stripe_head *sh;
const int rw = bio_data_dir(bi);
int remaining;
+ DEFINE_WAIT(w);
+ bool do_prepare;
if (unlikely(bi->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bi);
@@ -4575,15 +4572,18 @@ static void make_request(struct mddev *mddev, struct bio * bi)
bi->bi_next = NULL;
bi->bi_phys_segments = 1; /* over-loaded to count active stripes */
+ prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE);
for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) {
- DEFINE_WAIT(w);
int previous;
int seq;
+ do_prepare = false;
retry:
seq = read_seqcount_begin(&conf->gen_lock);
previous = 0;
- prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE);
+ if (do_prepare)
+ prepare_to_wait(&conf->wait_for_overlap, &w,
+ TASK_UNINTERRUPTIBLE);
if (unlikely(conf->reshape_progress != MaxSector)) {
/* spinlock is needed as reshape_progress may be
* 64bit on a 32bit platform, and so it might be
@@ -4604,6 +4604,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
: logical_sector >= conf->reshape_safe) {
spin_unlock_irq(&conf->device_lock);
schedule();
+ do_prepare = true;
goto retry;
}
}
@@ -4640,6 +4641,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
if (must_retry) {
release_stripe(sh);
schedule();
+ do_prepare = true;
goto retry;
}
}
@@ -4663,8 +4665,10 @@ static void make_request(struct mddev *mddev, struct bio * bi)
prepare_to_wait(&conf->wait_for_overlap,
&w, TASK_INTERRUPTIBLE);
if (logical_sector >= mddev->suspend_lo &&
- logical_sector < mddev->suspend_hi)
+ logical_sector < mddev->suspend_hi) {
schedule();
+ do_prepare = true;
+ }
goto retry;
}
@@ -4677,9 +4681,9 @@ static void make_request(struct mddev *mddev, struct bio * bi)
md_wakeup_thread(mddev->thread);
release_stripe(sh);
schedule();
+ do_prepare = true;
goto retry;
}
- finish_wait(&conf->wait_for_overlap, &w);
set_bit(STRIPE_HANDLE, &sh->state);
clear_bit(STRIPE_DELAYED, &sh->state);
if ((bi->bi_rw & REQ_SYNC) &&
@@ -4689,10 +4693,10 @@ static void make_request(struct mddev *mddev, struct bio * bi)
} else {
/* cannot get stripe for read-ahead, just give-up */
clear_bit(BIO_UPTODATE, &bi->bi_flags);
- finish_wait(&conf->wait_for_overlap, &w);
break;
}
}
+ finish_wait(&conf->wait_for_overlap, &w);
remaining = raid5_dec_bi_active_stripes(bi);
if (remaining == 0) {
diff --git a/drivers/media/dvb-frontends/drx39xyj/Kconfig b/drivers/media/dvb-frontends/drx39xyj/Kconfig
index 15628eb5cf0c..6c2ccb6a506b 100644
--- a/drivers/media/dvb-frontends/drx39xyj/Kconfig
+++ b/drivers/media/dvb-frontends/drx39xyj/Kconfig
@@ -1,7 +1,7 @@
config DVB_DRX39XYJ
tristate "Micronas DRX-J demodulator"
depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
+ default m if !MEDIA_SUBDRV_AUTOSELECT
help
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
to support this frontend.
diff --git a/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c
index 1d2c47378cf8..92c891a571ab 100644
--- a/drivers/media/dvb-frontends/lgdt3305.c
+++ b/drivers/media/dvb-frontends/lgdt3305.c
@@ -1176,6 +1176,7 @@ static struct dvb_frontend_ops lgdt3304_ops = {
},
.i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl,
.init = lgdt3305_init,
+ .sleep = lgdt3305_sleep,
.set_frontend = lgdt3304_set_parameters,
.get_frontend = lgdt3305_get_frontend,
.get_tune_settings = lgdt3305_get_tune_settings,
diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c
index 32cffca14d0b..d63bc9c13dce 100644
--- a/drivers/media/dvb-frontends/m88rs2000.c
+++ b/drivers/media/dvb-frontends/m88rs2000.c
@@ -297,7 +297,7 @@ struct inittab {
u8 val;
};
-struct inittab m88rs2000_setup[] = {
+static struct inittab m88rs2000_setup[] = {
{DEMOD_WRITE, 0x9a, 0x30},
{DEMOD_WRITE, 0x00, 0x01},
{WRITE_DELAY, 0x19, 0x00},
@@ -315,7 +315,7 @@ struct inittab m88rs2000_setup[] = {
{0xff, 0xaa, 0xff}
};
-struct inittab m88rs2000_shutdown[] = {
+static struct inittab m88rs2000_shutdown[] = {
{DEMOD_WRITE, 0x9a, 0x30},
{DEMOD_WRITE, 0xb0, 0x00},
{DEMOD_WRITE, 0xf1, 0x89},
@@ -325,7 +325,7 @@ struct inittab m88rs2000_shutdown[] = {
{0xff, 0xaa, 0xff}
};
-struct inittab fe_reset[] = {
+static struct inittab fe_reset[] = {
{DEMOD_WRITE, 0x00, 0x01},
{DEMOD_WRITE, 0x20, 0x81},
{DEMOD_WRITE, 0x21, 0x80},
@@ -363,7 +363,7 @@ struct inittab fe_reset[] = {
{0xff, 0xaa, 0xff}
};
-struct inittab fe_trigger[] = {
+static struct inittab fe_trigger[] = {
{DEMOD_WRITE, 0x97, 0x04},
{DEMOD_WRITE, 0x99, 0x77},
{DEMOD_WRITE, 0x9b, 0x64},
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index 7a77a5b7a075..5c421886d97c 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -49,8 +49,8 @@
#define VPE_MODULE_NAME "vpe"
/* minimum and maximum frame sizes */
-#define MIN_W 128
-#define MIN_H 128
+#define MIN_W 32
+#define MIN_H 32
#define MAX_W 1920
#define MAX_H 1080
@@ -887,6 +887,9 @@ static int job_ready(void *priv)
if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < needed)
return 0;
+ if (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < needed)
+ return 0;
+
return 1;
}
@@ -1277,18 +1280,17 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
s_buf = &s_vb->v4l2_buf;
d_buf = &d_vb->v4l2_buf;
+ d_buf->flags = s_buf->flags;
+
d_buf->timestamp = s_buf->timestamp;
- d_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
- d_buf->flags |= s_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
- if (s_buf->flags & V4L2_BUF_FLAG_TIMECODE) {
- d_buf->flags |= V4L2_BUF_FLAG_TIMECODE;
+ if (s_buf->flags & V4L2_BUF_FLAG_TIMECODE)
d_buf->timecode = s_buf->timecode;
- }
+
d_buf->sequence = ctx->sequence;
- d_buf->field = ctx->field;
d_q_data = &ctx->q_data[Q_DATA_DST];
if (d_q_data->flags & Q_DATA_INTERLACED) {
+ d_buf->field = ctx->field;
if (ctx->field == V4L2_FIELD_BOTTOM) {
ctx->sequence++;
ctx->field = V4L2_FIELD_TOP;
@@ -1297,6 +1299,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
ctx->field = V4L2_FIELD_BOTTOM;
}
} else {
+ d_buf->field = V4L2_FIELD_NONE;
ctx->sequence++;
}
@@ -1335,8 +1338,9 @@ static int vpe_querycap(struct file *file, void *priv,
{
strncpy(cap->driver, VPE_MODULE_NAME, sizeof(cap->driver) - 1);
strncpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card) - 1);
- strlcpy(cap->bus_info, VPE_MODULE_NAME, sizeof(cap->bus_info));
- cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ VPE_MODULE_NAME);
+ cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -1476,6 +1480,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
}
}
+ memset(pix->reserved, 0, sizeof(pix->reserved));
for (i = 0; i < pix->num_planes; i++) {
plane_fmt = &pix->plane_fmt[i];
depth = fmt->vpdma_fmt[i]->depth;
@@ -1487,6 +1492,8 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
plane_fmt->sizeimage =
(pix->height * pix->width * depth) >> 3;
+
+ memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved));
}
return 0;
@@ -1717,6 +1724,16 @@ static int vpe_buf_prepare(struct vb2_buffer *vb)
q_data = get_q_data(ctx, vb->vb2_queue->type);
num_planes = q_data->fmt->coplanar ? 2 : 1;
+ if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (!(q_data->flags & Q_DATA_INTERLACED)) {
+ vb->v4l2_buf.field = V4L2_FIELD_NONE;
+ } else {
+ if (vb->v4l2_buf.field != V4L2_FIELD_TOP &&
+ vb->v4l2_buf.field != V4L2_FIELD_BOTTOM)
+ return -EINVAL;
+ }
+ }
+
for (i = 0; i < num_planes; i++) {
if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) {
vpe_err(ctx->dev,
@@ -1866,9 +1883,11 @@ static int vpe_open(struct file *file)
s_q_data->fmt = &vpe_formats[2];
s_q_data->width = 1920;
s_q_data->height = 1080;
- s_q_data->sizeimage[VPE_LUMA] = (s_q_data->width * s_q_data->height *
+ s_q_data->bytesperline[VPE_LUMA] = (s_q_data->width *
s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3;
- s_q_data->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ s_q_data->sizeimage[VPE_LUMA] = (s_q_data->bytesperline[VPE_LUMA] *
+ s_q_data->height);
+ s_q_data->colorspace = V4L2_COLORSPACE_REC709;
s_q_data->field = V4L2_FIELD_NONE;
s_q_data->c_rect.left = 0;
s_q_data->c_rect.top = 0;
@@ -2002,7 +2021,7 @@ static struct video_device vpe_videodev = {
.fops = &vpe_fops,
.ioctl_ops = &vpe_ioctl_ops,
.minor = -1,
- .release = video_device_release,
+ .release = video_device_release_empty,
.vfl_dir = VFL_DIR_M2M,
};
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
index 579a52b3edce..0127dd257a57 100644
--- a/drivers/media/rc/img-ir/img-ir-hw.c
+++ b/drivers/media/rc/img-ir/img-ir-hw.c
@@ -504,6 +504,18 @@ unlock:
return ret;
}
+static int img_ir_set_normal_filter(struct rc_dev *dev,
+ struct rc_scancode_filter *sc_filter)
+{
+ return img_ir_set_filter(dev, RC_FILTER_NORMAL, sc_filter);
+}
+
+static int img_ir_set_wakeup_filter(struct rc_dev *dev,
+ struct rc_scancode_filter *sc_filter)
+{
+ return img_ir_set_filter(dev, RC_FILTER_WAKEUP, sc_filter);
+}
+
/**
* img_ir_set_decoder() - Set the current decoder.
* @priv: IR private data.
@@ -986,7 +998,8 @@ int img_ir_probe_hw(struct img_ir_priv *priv)
rdev->map_name = RC_MAP_EMPTY;
rc_set_allowed_protocols(rdev, img_ir_allowed_protos(priv));
rdev->input_name = "IMG Infrared Decoder";
- rdev->s_filter = img_ir_set_filter;
+ rdev->s_filter = img_ir_set_normal_filter;
+ rdev->s_wakeup_filter = img_ir_set_wakeup_filter;
/* Register hardware decoder */
error = rc_register_device(rdev);
diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c
index e7a731bc3a9b..751d9d945269 100644
--- a/drivers/media/rc/img-ir/img-ir-nec.c
+++ b/drivers/media/rc/img-ir/img-ir-nec.c
@@ -5,6 +5,7 @@
*/
#include "img-ir-hw.h"
+#include <linux/bitrev.h>
/* Convert NEC data to a scancode */
static int img_ir_nec_scancode(int len, u64 raw, int *scancode, u64 protocols)
@@ -22,11 +23,11 @@ static int img_ir_nec_scancode(int len, u64 raw, int *scancode, u64 protocols)
data_inv = (raw >> 24) & 0xff;
if ((data_inv ^ data) != 0xff) {
/* 32-bit NEC (used by Apple and TiVo remotes) */
- /* scan encoding: aaAAddDD */
- *scancode = addr_inv << 24 |
- addr << 16 |
- data_inv << 8 |
- data;
+ /* scan encoding: as transmitted, MSBit = first received bit */
+ *scancode = bitrev8(addr) << 24 |
+ bitrev8(addr_inv) << 16 |
+ bitrev8(data) << 8 |
+ bitrev8(data_inv);
} else if ((addr_inv ^ addr) != 0xff) {
/* Extended NEC */
/* scan encoding: AAaaDD */
@@ -54,13 +55,15 @@ static int img_ir_nec_filter(const struct rc_scancode_filter *in,
if ((in->data | in->mask) & 0xff000000) {
/* 32-bit NEC (used by Apple and TiVo remotes) */
- /* scan encoding: aaAAddDD */
- addr_inv = (in->data >> 24) & 0xff;
- addr_inv_m = (in->mask >> 24) & 0xff;
- addr = (in->data >> 16) & 0xff;
- addr_m = (in->mask >> 16) & 0xff;
- data_inv = (in->data >> 8) & 0xff;
- data_inv_m = (in->mask >> 8) & 0xff;
+ /* scan encoding: as transmitted, MSBit = first received bit */
+ addr = bitrev8(in->data >> 24);
+ addr_m = bitrev8(in->mask >> 24);
+ addr_inv = bitrev8(in->data >> 16);
+ addr_inv_m = bitrev8(in->mask >> 16);
+ data = bitrev8(in->data >> 8);
+ data_m = bitrev8(in->mask >> 8);
+ data_inv = bitrev8(in->data >> 0);
+ data_inv_m = bitrev8(in->mask >> 0);
} else if ((in->data | in->mask) & 0x00ff0000) {
/* Extended NEC */
/* scan encoding AAaaDD */
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 9de1791d2494..35c42e5e270b 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -172,10 +172,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (send_32bits) {
/* NEC transport, but modified protocol, used by at
* least Apple and TiVo remotes */
- scancode = not_address << 24 |
- address << 16 |
- not_command << 8 |
- command;
+ scancode = data->bits;
IR_dprintk(1, "NEC (modified) scancode 0x%08x\n", scancode);
} else if ((address ^ not_address) != 0xff) {
/* Extended NEC */
diff --git a/drivers/media/rc/keymaps/rc-tivo.c b/drivers/media/rc/keymaps/rc-tivo.c
index 5cc1b456e329..454e06295692 100644
--- a/drivers/media/rc/keymaps/rc-tivo.c
+++ b/drivers/media/rc/keymaps/rc-tivo.c
@@ -15,62 +15,62 @@
* Initial mapping is for the TiVo remote included in the Nero LiquidTV bundle,
* which also ships with a TiVo-branded IR transceiver, supported by the mceusb
* driver. Note that the remote uses an NEC-ish protocol, but instead of having
- * a command/not_command pair, it has a vendor ID of 0x3085, but some keys, the
+ * a command/not_command pair, it has a vendor ID of 0xa10c, but some keys, the
* NEC extended checksums do pass, so the table presently has the intended
* values and the checksum-passed versions for those keys.
*/
static struct rc_map_table tivo[] = {
- { 0x3085f009, KEY_MEDIA }, /* TiVo Button */
- { 0x3085e010, KEY_POWER2 }, /* TV Power */
- { 0x3085e011, KEY_TV }, /* Live TV/Swap */
- { 0x3085c034, KEY_VIDEO_NEXT }, /* TV Input */
- { 0x3085e013, KEY_INFO },
- { 0x3085a05f, KEY_CYCLEWINDOWS }, /* Window */
+ { 0xa10c900f, KEY_MEDIA }, /* TiVo Button */
+ { 0xa10c0807, KEY_POWER2 }, /* TV Power */
+ { 0xa10c8807, KEY_TV }, /* Live TV/Swap */
+ { 0xa10c2c03, KEY_VIDEO_NEXT }, /* TV Input */
+ { 0xa10cc807, KEY_INFO },
+ { 0xa10cfa05, KEY_CYCLEWINDOWS }, /* Window */
{ 0x0085305f, KEY_CYCLEWINDOWS },
- { 0x3085c036, KEY_EPG }, /* Guide */
+ { 0xa10c6c03, KEY_EPG }, /* Guide */
- { 0x3085e014, KEY_UP },
- { 0x3085e016, KEY_DOWN },
- { 0x3085e017, KEY_LEFT },
- { 0x3085e015, KEY_RIGHT },
+ { 0xa10c2807, KEY_UP },
+ { 0xa10c6807, KEY_DOWN },
+ { 0xa10ce807, KEY_LEFT },
+ { 0xa10ca807, KEY_RIGHT },
- { 0x3085e018, KEY_SCROLLDOWN }, /* Red Thumbs Down */
- { 0x3085e019, KEY_SELECT },
- { 0x3085e01a, KEY_SCROLLUP }, /* Green Thumbs Up */
+ { 0xa10c1807, KEY_SCROLLDOWN }, /* Red Thumbs Down */
+ { 0xa10c9807, KEY_SELECT },
+ { 0xa10c5807, KEY_SCROLLUP }, /* Green Thumbs Up */
- { 0x3085e01c, KEY_VOLUMEUP },
- { 0x3085e01d, KEY_VOLUMEDOWN },
- { 0x3085e01b, KEY_MUTE },
- { 0x3085d020, KEY_RECORD },
- { 0x3085e01e, KEY_CHANNELUP },
- { 0x3085e01f, KEY_CHANNELDOWN },
+ { 0xa10c3807, KEY_VOLUMEUP },
+ { 0xa10cb807, KEY_VOLUMEDOWN },
+ { 0xa10cd807, KEY_MUTE },
+ { 0xa10c040b, KEY_RECORD },
+ { 0xa10c7807, KEY_CHANNELUP },
+ { 0xa10cf807, KEY_CHANNELDOWN },
{ 0x0085301f, KEY_CHANNELDOWN },
- { 0x3085d021, KEY_PLAY },
- { 0x3085d023, KEY_PAUSE },
- { 0x3085d025, KEY_SLOW },
- { 0x3085d022, KEY_REWIND },
- { 0x3085d024, KEY_FASTFORWARD },
- { 0x3085d026, KEY_PREVIOUS },
- { 0x3085d027, KEY_NEXT }, /* ->| */
+ { 0xa10c840b, KEY_PLAY },
+ { 0xa10cc40b, KEY_PAUSE },
+ { 0xa10ca40b, KEY_SLOW },
+ { 0xa10c440b, KEY_REWIND },
+ { 0xa10c240b, KEY_FASTFORWARD },
+ { 0xa10c640b, KEY_PREVIOUS },
+ { 0xa10ce40b, KEY_NEXT }, /* ->| */
- { 0x3085b044, KEY_ZOOM }, /* Aspect */
- { 0x3085b048, KEY_STOP },
- { 0x3085b04a, KEY_DVD }, /* DVD Menu */
+ { 0xa10c220d, KEY_ZOOM }, /* Aspect */
+ { 0xa10c120d, KEY_STOP },
+ { 0xa10c520d, KEY_DVD }, /* DVD Menu */
- { 0x3085d028, KEY_NUMERIC_1 },
- { 0x3085d029, KEY_NUMERIC_2 },
- { 0x3085d02a, KEY_NUMERIC_3 },
- { 0x3085d02b, KEY_NUMERIC_4 },
- { 0x3085d02c, KEY_NUMERIC_5 },
- { 0x3085d02d, KEY_NUMERIC_6 },
- { 0x3085d02e, KEY_NUMERIC_7 },
- { 0x3085d02f, KEY_NUMERIC_8 },
+ { 0xa10c140b, KEY_NUMERIC_1 },
+ { 0xa10c940b, KEY_NUMERIC_2 },
+ { 0xa10c540b, KEY_NUMERIC_3 },
+ { 0xa10cd40b, KEY_NUMERIC_4 },
+ { 0xa10c340b, KEY_NUMERIC_5 },
+ { 0xa10cb40b, KEY_NUMERIC_6 },
+ { 0xa10c740b, KEY_NUMERIC_7 },
+ { 0xa10cf40b, KEY_NUMERIC_8 },
{ 0x0085302f, KEY_NUMERIC_8 },
- { 0x3085c030, KEY_NUMERIC_9 },
- { 0x3085c031, KEY_NUMERIC_0 },
- { 0x3085c033, KEY_ENTER },
- { 0x3085c032, KEY_CLEAR },
+ { 0xa10c0c03, KEY_NUMERIC_9 },
+ { 0xa10c8c03, KEY_NUMERIC_0 },
+ { 0xa10ccc03, KEY_ENTER },
+ { 0xa10c4c03, KEY_CLEAR },
};
static struct rc_map_list tivo_map = {
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 99697aae92ff..970b93d6f399 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -633,19 +633,13 @@ EXPORT_SYMBOL_GPL(rc_repeat);
static void ir_do_keydown(struct rc_dev *dev, int scancode,
u32 keycode, u8 toggle)
{
- struct rc_scancode_filter *filter;
- bool new_event = !dev->keypressed ||
- dev->last_scancode != scancode ||
- dev->last_toggle != toggle;
+ bool new_event = (!dev->keypressed ||
+ dev->last_scancode != scancode ||
+ dev->last_toggle != toggle);
if (new_event && dev->keypressed)
ir_do_keyup(dev, false);
- /* Generic scancode filtering */
- filter = &dev->scancode_filters[RC_FILTER_NORMAL];
- if (filter->mask && ((scancode ^ filter->data) & filter->mask))
- return;
-
input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode);
if (new_event && keycode != KEY_RESERVED) {
@@ -923,6 +917,7 @@ static ssize_t store_protocols(struct device *device,
int rc, i, count = 0;
ssize_t ret;
int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
+ int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
struct rc_scancode_filter local_filter, *filter;
/* Device is being removed */
@@ -1007,24 +1002,23 @@ static ssize_t store_protocols(struct device *device,
* Fall back to clearing the filter.
*/
filter = &dev->scancode_filters[fattr->type];
- if (old_type != type && filter->mask) {
+ set_filter = (fattr->type == RC_FILTER_NORMAL)
+ ? dev->s_filter : dev->s_wakeup_filter;
+
+ if (set_filter && old_type != type && filter->mask) {
local_filter = *filter;
if (!type) {
/* no protocol => clear filter */
ret = -1;
- } else if (!dev->s_filter) {
- /* generic filtering => accept any filter */
- ret = 0;
} else {
/* hardware filtering => try setting, otherwise clear */
- ret = dev->s_filter(dev, fattr->type, &local_filter);
+ ret = set_filter(dev, &local_filter);
}
if (ret < 0) {
/* clear the filter */
local_filter.data = 0;
local_filter.mask = 0;
- if (dev->s_filter)
- dev->s_filter(dev, fattr->type, &local_filter);
+ set_filter(dev, &local_filter);
}
/* commit the new filter */
@@ -1068,7 +1062,10 @@ static ssize_t show_filter(struct device *device,
return -EINVAL;
mutex_lock(&dev->lock);
- if (fattr->mask)
+ if ((fattr->type == RC_FILTER_NORMAL && !dev->s_filter) ||
+ (fattr->type == RC_FILTER_WAKEUP && !dev->s_wakeup_filter))
+ val = 0;
+ else if (fattr->mask)
val = dev->scancode_filters[fattr->type].mask;
else
val = dev->scancode_filters[fattr->type].data;
@@ -1106,6 +1103,7 @@ static ssize_t store_filter(struct device *device,
struct rc_scancode_filter local_filter, *filter;
int ret;
unsigned long val;
+ int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
/* Device is being removed */
if (!dev)
@@ -1115,9 +1113,11 @@ static ssize_t store_filter(struct device *device,
if (ret < 0)
return ret;
- /* Scancode filter not supported (but still accept 0) */
- if (!dev->s_filter && fattr->type != RC_FILTER_NORMAL)
- return val ? -EINVAL : count;
+ /* Can the scancode filter be set? */
+ set_filter = (fattr->type == RC_FILTER_NORMAL) ? dev->s_filter :
+ dev->s_wakeup_filter;
+ if (!set_filter)
+ return -EINVAL;
mutex_lock(&dev->lock);
@@ -1128,16 +1128,16 @@ static ssize_t store_filter(struct device *device,
local_filter.mask = val;
else
local_filter.data = val;
+
if (!dev->enabled_protocols[fattr->type] && local_filter.mask) {
/* refuse to set a filter unless a protocol is enabled */
ret = -EINVAL;
goto unlock;
}
- if (dev->s_filter) {
- ret = dev->s_filter(dev, fattr->type, &local_filter);
- if (ret < 0)
- goto unlock;
- }
+
+ ret = set_filter(dev, &local_filter);
+ if (ret < 0)
+ goto unlock;
/* Success, commit the new filter */
*filter = local_filter;
@@ -1189,27 +1189,45 @@ static RC_FILTER_ATTR(wakeup_filter, S_IRUGO|S_IWUSR,
static RC_FILTER_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR,
show_filter, store_filter, RC_FILTER_WAKEUP, true);
-static struct attribute *rc_dev_attrs[] = {
+static struct attribute *rc_dev_protocol_attrs[] = {
&dev_attr_protocols.attr.attr,
+ NULL,
+};
+
+static struct attribute_group rc_dev_protocol_attr_grp = {
+ .attrs = rc_dev_protocol_attrs,
+};
+
+static struct attribute *rc_dev_wakeup_protocol_attrs[] = {
&dev_attr_wakeup_protocols.attr.attr,
+ NULL,
+};
+
+static struct attribute_group rc_dev_wakeup_protocol_attr_grp = {
+ .attrs = rc_dev_wakeup_protocol_attrs,
+};
+
+static struct attribute *rc_dev_filter_attrs[] = {
&dev_attr_filter.attr.attr,
&dev_attr_filter_mask.attr.attr,
- &dev_attr_wakeup_filter.attr.attr,
- &dev_attr_wakeup_filter_mask.attr.attr,
NULL,
};
-static struct attribute_group rc_dev_attr_grp = {
- .attrs = rc_dev_attrs,
+static struct attribute_group rc_dev_filter_attr_grp = {
+ .attrs = rc_dev_filter_attrs,
};
-static const struct attribute_group *rc_dev_attr_groups[] = {
- &rc_dev_attr_grp,
- NULL
+static struct attribute *rc_dev_wakeup_filter_attrs[] = {
+ &dev_attr_wakeup_filter.attr.attr,
+ &dev_attr_wakeup_filter_mask.attr.attr,
+ NULL,
+};
+
+static struct attribute_group rc_dev_wakeup_filter_attr_grp = {
+ .attrs = rc_dev_wakeup_filter_attrs,
};
static struct device_type rc_dev_type = {
- .groups = rc_dev_attr_groups,
.release = rc_dev_release,
.uevent = rc_dev_uevent,
};
@@ -1266,7 +1284,7 @@ int rc_register_device(struct rc_dev *dev)
static bool raw_init = false; /* raw decoders loaded? */
struct rc_map *rc_map;
const char *path;
- int rc, devno;
+ int rc, devno, attr = 0;
if (!dev || !dev->map_name)
return -EINVAL;
@@ -1294,6 +1312,16 @@ int rc_register_device(struct rc_dev *dev)
return -ENOMEM;
} while (test_and_set_bit(devno, ir_core_dev_number));
+ dev->dev.groups = dev->sysfs_groups;
+ dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp;
+ if (dev->s_filter)
+ dev->sysfs_groups[attr++] = &rc_dev_filter_attr_grp;
+ if (dev->s_wakeup_filter)
+ dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp;
+ if (dev->change_wakeup_protocol)
+ dev->sysfs_groups[attr++] = &rc_dev_wakeup_protocol_attr_grp;
+ dev->sysfs_groups[attr++] = NULL;
+
/*
* Take the lock here, as the device sysfs node will appear
* when device_add() is called, which may trigger an ir-keytable udev
diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c
index 319adc4f0561..96ccfebce7ca 100644
--- a/drivers/media/tuners/r820t.c
+++ b/drivers/media/tuners/r820t.c
@@ -1468,7 +1468,8 @@ static int r820t_imr_prepare(struct r820t_priv *priv)
static int r820t_multi_read(struct r820t_priv *priv)
{
int rc, i;
- u8 data[2], min = 0, max = 255, sum = 0;
+ u16 sum = 0;
+ u8 data[2], min = 255, max = 0;
usleep_range(5000, 6000);
diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c
index 76a816511f2f..6ef93ee1fdcb 100644
--- a/drivers/media/tuners/tuner-xc2028.c
+++ b/drivers/media/tuners/tuner-xc2028.c
@@ -1107,6 +1107,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
offset += 200000;
}
#endif
+ break;
default:
tuner_err("Unsupported tuner type %d.\n", new_type);
break;
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index c83c16cece01..61d196e8b3ab 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -1503,8 +1503,6 @@ static const struct usb_device_id rtl28xxu_id_table[] = {
/* RTL2832P devices: */
{ DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131,
&rtl2832u_props, "Astrometa DVB-T2", NULL) },
- { DVB_USB_DEVICE(USB_VID_KYE, 0x707f,
- &rtl2832u_props, "Genius TVGo DVB-T03", NULL) },
{ }
};
MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table);
diff --git a/drivers/media/usb/gspca/jpeg.h b/drivers/media/usb/gspca/jpeg.h
index ab54910418b4..0aa2b671faa4 100644
--- a/drivers/media/usb/gspca/jpeg.h
+++ b/drivers/media/usb/gspca/jpeg.h
@@ -154,7 +154,9 @@ static void jpeg_set_qual(u8 *jpeg_hdr,
{
int i, sc;
- if (quality < 50)
+ if (quality <= 0)
+ sc = 5000;
+ else if (quality < 50)
sc = 5000 / quality;
else
sc = 200 - quality * 2;
diff --git a/drivers/media/usb/stk1160/stk1160-ac97.c b/drivers/media/usb/stk1160/stk1160-ac97.c
index c46c8be89602..2dd308f9541f 100644
--- a/drivers/media/usb/stk1160/stk1160-ac97.c
+++ b/drivers/media/usb/stk1160/stk1160-ac97.c
@@ -108,7 +108,7 @@ int stk1160_ac97_register(struct stk1160 *dev)
"stk1160-mixer");
snprintf(card->longname, sizeof(card->longname),
"stk1160 ac97 codec mixer control");
- strncpy(card->driver, dev->dev->driver->name, sizeof(card->driver));
+ strlcpy(card->driver, dev->dev->driver->name, sizeof(card->driver));
rc = snd_ac97_bus(card, 0, &stk1160_ac97_ops, NULL, &ac97_bus);
if (rc)
diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c
index 1d15735f9ef9..c9de3d598ea5 100644
--- a/drivers/mfd/rtsx_pcr.c
+++ b/drivers/mfd/rtsx_pcr.c
@@ -338,58 +338,28 @@ int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist,
int num_sg, bool read, int timeout)
{
struct completion trans_done;
- u8 dir;
- int err = 0, i, count;
+ int err = 0, count;
long timeleft;
unsigned long flags;
- struct scatterlist *sg;
- enum dma_data_direction dma_dir;
- u32 val;
- dma_addr_t addr;
- unsigned int len;
-
- dev_dbg(&(pcr->pci->dev), "--> %s: num_sg = %d\n", __func__, num_sg);
-
- /* don't transfer data during abort processing */
- if (pcr->remove_pci)
- return -EINVAL;
-
- if ((sglist == NULL) || (num_sg <= 0))
- return -EINVAL;
- if (read) {
- dir = DEVICE_TO_HOST;
- dma_dir = DMA_FROM_DEVICE;
- } else {
- dir = HOST_TO_DEVICE;
- dma_dir = DMA_TO_DEVICE;
- }
-
- count = dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
+ count = rtsx_pci_dma_map_sg(pcr, sglist, num_sg, read);
if (count < 1) {
dev_err(&(pcr->pci->dev), "scatterlist map failed\n");
return -EINVAL;
}
dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count);
- val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE;
- pcr->sgi = 0;
- for_each_sg(sglist, sg, count, i) {
- addr = sg_dma_address(sg);
- len = sg_dma_len(sg);
- rtsx_pci_add_sg_tbl(pcr, addr, len, i == count - 1);
- }
spin_lock_irqsave(&pcr->lock, flags);
pcr->done = &trans_done;
pcr->trans_result = TRANS_NOT_READY;
init_completion(&trans_done);
- rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr);
- rtsx_pci_writel(pcr, RTSX_HDBCTLR, val);
spin_unlock_irqrestore(&pcr->lock, flags);
+ rtsx_pci_dma_transfer(pcr, sglist, count, read);
+
timeleft = wait_for_completion_interruptible_timeout(
&trans_done, msecs_to_jiffies(timeout));
if (timeleft <= 0) {
@@ -413,7 +383,7 @@ out:
pcr->done = NULL;
spin_unlock_irqrestore(&pcr->lock, flags);
- dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
+ rtsx_pci_dma_unmap_sg(pcr, sglist, num_sg, read);
if ((err < 0) && (err != -ENODEV))
rtsx_pci_stop_cmd(pcr);
@@ -425,6 +395,73 @@ out:
}
EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data);
+int rtsx_pci_dma_map_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist,
+ int num_sg, bool read)
+{
+ enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (pcr->remove_pci)
+ return -EINVAL;
+
+ if ((sglist == NULL) || num_sg < 1)
+ return -EINVAL;
+
+ return dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dir);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_dma_map_sg);
+
+int rtsx_pci_dma_unmap_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist,
+ int num_sg, bool read)
+{
+ enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (pcr->remove_pci)
+ return -EINVAL;
+
+ if (sglist == NULL || num_sg < 1)
+ return -EINVAL;
+
+ dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dir);
+ return num_sg;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_dma_unmap_sg);
+
+int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist,
+ int sg_count, bool read)
+{
+ struct scatterlist *sg;
+ dma_addr_t addr;
+ unsigned int len;
+ int i;
+ u32 val;
+ u8 dir = read ? DEVICE_TO_HOST : HOST_TO_DEVICE;
+ unsigned long flags;
+
+ if (pcr->remove_pci)
+ return -EINVAL;
+
+ if ((sglist == NULL) || (sg_count < 1))
+ return -EINVAL;
+
+ val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE;
+ pcr->sgi = 0;
+ for_each_sg(sglist, sg, sg_count, i) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+ rtsx_pci_add_sg_tbl(pcr, addr, len, i == sg_count - 1);
+ }
+
+ spin_lock_irqsave(&pcr->lock, flags);
+
+ rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr);
+ rtsx_pci_writel(pcr, RTSX_HDBCTLR, val);
+
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_dma_transfer);
+
int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len)
{
int err;
@@ -836,6 +873,8 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
int_reg = rtsx_pci_readl(pcr, RTSX_BIPR);
/* Clear interrupt flag */
rtsx_pci_writel(pcr, RTSX_BIPR, int_reg);
+ dev_dbg(&pcr->pci->dev, "=========== BIPR 0x%8x ==========\n", int_reg);
+
if ((int_reg & pcr->bier) == 0) {
spin_unlock(&pcr->lock);
return IRQ_NONE;
@@ -866,17 +905,28 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
}
if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) {
- if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) {
+ if (int_reg & (TRANS_FAIL_INT | DELINK_INT))
pcr->trans_result = TRANS_RESULT_FAIL;
- if (pcr->done)
- complete(pcr->done);
- } else if (int_reg & TRANS_OK_INT) {
+ else if (int_reg & TRANS_OK_INT)
pcr->trans_result = TRANS_RESULT_OK;
- if (pcr->done)
- complete(pcr->done);
+
+ if (pcr->done)
+ complete(pcr->done);
+
+ if (int_reg & SD_EXIST) {
+ struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD];
+ if (slot && slot->done_transfer)
+ slot->done_transfer(slot->p_dev);
+ }
+
+ if (int_reg & MS_EXIST) {
+ struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD];
+ if (slot && slot->done_transfer)
+ slot->done_transfer(slot->p_dev);
}
}
+
if (pcr->card_inserted || pcr->card_removed)
schedule_delayed_work(&pcr->carddet_work,
msecs_to_jiffies(200));
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 7b5424f398ac..452782bffebc 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -415,8 +415,7 @@ static int ioctl_do_sanitize(struct mmc_card *card)
{
int err;
- if (!(mmc_can_sanitize(card) &&
- (card->host->caps2 & MMC_CAP2_SANITIZE))) {
+ if (!mmc_can_sanitize(card)) {
pr_warn("%s: %s - SANITIZE is not supported\n",
mmc_hostname(card->host), __func__);
err = -EOPNOTSUPP;
@@ -722,19 +721,6 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
return result;
}
-static int send_stop(struct mmc_card *card, u32 *status)
-{
- struct mmc_command cmd = {0};
- int err;
-
- cmd.opcode = MMC_STOP_TRANSMISSION;
- cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
- err = mmc_wait_for_cmd(card->host, &cmd, 5);
- if (err == 0)
- *status = cmd.resp[0];
- return err;
-}
-
static int get_card_status(struct mmc_card *card, u32 *status, int retries)
{
struct mmc_command cmd = {0};
@@ -750,6 +736,99 @@ static int get_card_status(struct mmc_card *card, u32 *status, int retries)
return err;
}
+static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
+ bool hw_busy_detect, struct request *req, int *gen_err)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
+ int err = 0;
+ u32 status;
+
+ do {
+ err = get_card_status(card, &status, 5);
+ if (err) {
+ pr_err("%s: error %d requesting status\n",
+ req->rq_disk->disk_name, err);
+ return err;
+ }
+
+ if (status & R1_ERROR) {
+ pr_err("%s: %s: error sending status cmd, status %#x\n",
+ req->rq_disk->disk_name, __func__, status);
+ *gen_err = 1;
+ }
+
+ /* We may rely on the host hw to handle busy detection.*/
+ if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) &&
+ hw_busy_detect)
+ break;
+
+ /*
+ * Timeout if the device never becomes ready for data and never
+ * leaves the program state.
+ */
+ if (time_after(jiffies, timeout)) {
+ pr_err("%s: Card stuck in programming state! %s %s\n",
+ mmc_hostname(card->host),
+ req->rq_disk->disk_name, __func__);
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * Some cards mishandle the status bits,
+ * so make sure to check both the busy
+ * indication and the card state.
+ */
+ } while (!(status & R1_READY_FOR_DATA) ||
+ (R1_CURRENT_STATE(status) == R1_STATE_PRG));
+
+ return err;
+}
+
+static int send_stop(struct mmc_card *card, unsigned int timeout_ms,
+ struct request *req, int *gen_err, u32 *stop_status)
+{
+ struct mmc_host *host = card->host;
+ struct mmc_command cmd = {0};
+ int err;
+ bool use_r1b_resp = rq_data_dir(req) == WRITE;
+
+ /*
+ * Normally we use R1B responses for WRITE, but in cases where the host
+ * has specified a max_busy_timeout we need to validate it. A failure
+ * means we need to prevent the host from doing hw busy detection, which
+ * is done by converting to a R1 response instead.
+ */
+ if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout))
+ use_r1b_resp = false;
+
+ cmd.opcode = MMC_STOP_TRANSMISSION;
+ if (use_r1b_resp) {
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ cmd.busy_timeout = timeout_ms;
+ } else {
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ }
+
+ err = mmc_wait_for_cmd(host, &cmd, 5);
+ if (err)
+ return err;
+
+ *stop_status = cmd.resp[0];
+
+ /* No need to check card status in case of READ. */
+ if (rq_data_dir(req) == READ)
+ return 0;
+
+ if (!mmc_host_is_spi(host) &&
+ (*stop_status & R1_ERROR)) {
+ pr_err("%s: %s: general error sending stop command, resp %#x\n",
+ req->rq_disk->disk_name, __func__, *stop_status);
+ *gen_err = 1;
+ }
+
+ return card_busy_detect(card, timeout_ms, use_r1b_resp, req, gen_err);
+}
+
#define ERR_NOMEDIUM 3
#define ERR_RETRY 2
#define ERR_ABORT 1
@@ -866,26 +945,21 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
*/
if (R1_CURRENT_STATE(status) == R1_STATE_DATA ||
R1_CURRENT_STATE(status) == R1_STATE_RCV) {
- err = send_stop(card, &stop_status);
- if (err)
+ err = send_stop(card,
+ DIV_ROUND_UP(brq->data.timeout_ns, 1000000),
+ req, gen_err, &stop_status);
+ if (err) {
pr_err("%s: error %d sending stop command\n",
req->rq_disk->disk_name, err);
-
- /*
- * If the stop cmd also timed out, the card is probably
- * not present, so abort. Other errors are bad news too.
- */
- if (err)
+ /*
+ * If the stop cmd also timed out, the card is probably
+ * not present, so abort. Other errors are bad news too.
+ */
return ERR_ABORT;
+ }
+
if (stop_status & R1_CARD_ECC_FAILED)
*ecc_err = 1;
- if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ)
- if (stop_status & R1_ERROR) {
- pr_err("%s: %s: general error sending stop command, stop cmd response %#x\n",
- req->rq_disk->disk_name, __func__,
- stop_status);
- *gen_err = 1;
- }
}
/* Check for set block count errors */
@@ -1157,8 +1231,7 @@ static int mmc_blk_err_check(struct mmc_card *card,
* program mode, which we have to wait for it to complete.
*/
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
- u32 status;
- unsigned long timeout;
+ int err;
/* Check stop command response */
if (brq->stop.resp[0] & R1_ERROR) {
@@ -1168,39 +1241,10 @@ static int mmc_blk_err_check(struct mmc_card *card,
gen_err = 1;
}
- timeout = jiffies + msecs_to_jiffies(MMC_BLK_TIMEOUT_MS);
- do {
- int err = get_card_status(card, &status, 5);
- if (err) {
- pr_err("%s: error %d requesting status\n",
- req->rq_disk->disk_name, err);
- return MMC_BLK_CMD_ERR;
- }
-
- if (status & R1_ERROR) {
- pr_err("%s: %s: general error sending status command, card status %#x\n",
- req->rq_disk->disk_name, __func__,
- status);
- gen_err = 1;
- }
-
- /* Timeout if the device never becomes ready for data
- * and never leaves the program state.
- */
- if (time_after(jiffies, timeout)) {
- pr_err("%s: Card stuck in programming state!"\
- " %s %s\n", mmc_hostname(card->host),
- req->rq_disk->disk_name, __func__);
-
- return MMC_BLK_CMD_ERR;
- }
- /*
- * Some cards mishandle the status bits,
- * so make sure to check both the busy
- * indication and the card state.
- */
- } while (!(status & R1_READY_FOR_DATA) ||
- (R1_CURRENT_STATE(status) == R1_STATE_PRG));
+ err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, false, req,
+ &gen_err);
+ if (err)
+ return MMC_BLK_CMD_ERR;
}
/* if general error occurs, retry the write operation. */
@@ -1335,7 +1379,6 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
brq->data.blksz = 512;
brq->stop.opcode = MMC_STOP_TRANSMISSION;
brq->stop.arg = 0;
- brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
brq->data.blocks = blk_rq_sectors(req);
/*
@@ -1378,9 +1421,15 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
if (rq_data_dir(req) == READ) {
brq->cmd.opcode = readcmd;
brq->data.flags |= MMC_DATA_READ;
+ if (brq->mrq.stop)
+ brq->stop.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 |
+ MMC_CMD_AC;
} else {
brq->cmd.opcode = writecmd;
brq->data.flags |= MMC_DATA_WRITE;
+ if (brq->mrq.stop)
+ brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B |
+ MMC_CMD_AC;
}
if (do_rel_wr)
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index 269d072ef55e..9ebee72d9c3f 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -2,21 +2,6 @@
# MMC core configuration
#
-config MMC_UNSAFE_RESUME
- bool "Assume MMC/SD cards are non-removable (DANGEROUS)"
- help
- If you say Y here, the MMC layer will assume that all cards
- stayed in their respective slots during the suspend. The
- normal behaviour is to remove them at suspend and
- redetecting them at resume. Breaking this assumption will
- in most cases result in data corruption.
-
- This option is usually just for embedded systems which use
- a MMC/SD card for rootfs. Most people should say N here.
-
- This option sets a default which can be overridden by the
- module parameter "removable=0" or "removable=1".
-
config MMC_CLKGATE
bool "MMC host clock gating"
help
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 64145a32b917..824644875d41 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -185,24 +185,16 @@ static int mmc_runtime_suspend(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
- int ret = 0;
- if (host->bus_ops->runtime_suspend)
- ret = host->bus_ops->runtime_suspend(host);
-
- return ret;
+ return host->bus_ops->runtime_suspend(host);
}
static int mmc_runtime_resume(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
- int ret = 0;
- if (host->bus_ops->runtime_resume)
- ret = host->bus_ops->runtime_resume(host);
-
- return ret;
+ return host->bus_ops->runtime_resume(host);
}
static int mmc_runtime_idle(struct device *dev)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 098374b1ab2b..acbc3f2aaaf9 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -34,6 +34,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
+#include <linux/mmc/slot-gpio.h>
#include "core.h"
#include "bus.h"
@@ -65,23 +66,6 @@ bool use_spi_crc = 1;
module_param(use_spi_crc, bool, 0);
/*
- * We normally treat cards as removed during suspend if they are not
- * known to be on a non-removable bus, to avoid the risk of writing
- * back data to a different card after resume. Allow this to be
- * overridden if necessary.
- */
-#ifdef CONFIG_MMC_UNSAFE_RESUME
-bool mmc_assume_removable;
-#else
-bool mmc_assume_removable = 1;
-#endif
-EXPORT_SYMBOL(mmc_assume_removable);
-module_param_named(removable, mmc_assume_removable, bool, 0644);
-MODULE_PARM_DESC(
- removable,
- "MMC/SD cards are removable and may be removed during suspend");
-
-/*
* Internal function. Schedule delayed work in the MMC work queue.
*/
static int mmc_schedule_delayed_work(struct delayed_work *work,
@@ -302,7 +286,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
}
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true);
+ 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);
@@ -1950,7 +1935,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
cmd.opcode = MMC_ERASE;
cmd.arg = arg;
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
- cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty);
+ cmd.busy_timeout = mmc_erase_timeout(card, arg, qty);
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) {
pr_err("mmc_erase: erase error %d, status %#x\n",
@@ -2137,7 +2122,7 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
y = 0;
for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
timeout = mmc_erase_timeout(card, arg, qty + x);
- if (timeout > host->max_discard_to)
+ if (timeout > host->max_busy_timeout)
break;
if (timeout < last_timeout)
break;
@@ -2169,7 +2154,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
struct mmc_host *host = card->host;
unsigned int max_discard, max_trim;
- if (!host->max_discard_to)
+ if (!host->max_busy_timeout)
return UINT_MAX;
/*
@@ -2189,7 +2174,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
max_discard = 0;
}
pr_debug("%s: calculated max. discard sectors %u for timeout %u ms\n",
- mmc_hostname(host), max_discard, host->max_discard_to);
+ mmc_hostname(host), max_discard, host->max_busy_timeout);
return max_discard;
}
EXPORT_SYMBOL(mmc_calc_max_discard);
@@ -2248,9 +2233,6 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check)
{
struct mmc_card *card = host->card;
- if (!host->bus_ops->power_restore)
- return -EOPNOTSUPP;
-
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
return -EOPNOTSUPP;
@@ -2352,7 +2334,7 @@ int _mmc_detect_card_removed(struct mmc_host *host)
{
int ret;
- if ((host->caps & MMC_CAP_NONREMOVABLE) || !host->bus_ops->alive)
+ if (host->caps & MMC_CAP_NONREMOVABLE)
return 0;
if (!host->card || mmc_card_removed(host->card))
@@ -2435,7 +2417,7 @@ void mmc_rescan(struct work_struct *work)
* if there is a _removable_ card registered, check whether it is
* still present
*/
- if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
+ if (host->bus_ops && !host->bus_dead
&& !(host->caps & MMC_CAP_NONREMOVABLE))
host->bus_ops->detect(host);
@@ -2490,6 +2472,7 @@ void mmc_start_host(struct mmc_host *host)
mmc_power_off(host);
else
mmc_power_up(host, host->ocr_avail);
+ mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false);
}
@@ -2501,6 +2484,8 @@ void mmc_stop_host(struct mmc_host *host)
host->removed = 1;
spin_unlock_irqrestore(&host->lock, flags);
#endif
+ if (host->slot.cd_irq >= 0)
+ disable_irq(host->slot.cd_irq);
host->rescan_disable = 1;
cancel_delayed_work_sync(&host->detect);
@@ -2537,7 +2522,7 @@ int mmc_power_save_host(struct mmc_host *host)
mmc_bus_get(host);
- if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
+ if (!host->bus_ops || host->bus_dead) {
mmc_bus_put(host);
return -EINVAL;
}
@@ -2563,7 +2548,7 @@ int mmc_power_restore_host(struct mmc_host *host)
mmc_bus_get(host);
- if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
+ if (!host->bus_ops || host->bus_dead) {
mmc_bus_put(host);
return -EINVAL;
}
@@ -2582,12 +2567,8 @@ EXPORT_SYMBOL(mmc_power_restore_host);
*/
int mmc_flush_cache(struct mmc_card *card)
{
- struct mmc_host *host = card->host;
int err = 0;
- if (!(host->caps2 & MMC_CAP2_CACHE_CTRL))
- return err;
-
if (mmc_card_mmc(card) &&
(card->ext_csd.cache_size > 0) &&
(card->ext_csd.cache_ctrl & 1)) {
@@ -2602,44 +2583,6 @@ int mmc_flush_cache(struct mmc_card *card)
}
EXPORT_SYMBOL(mmc_flush_cache);
-/*
- * Turn the cache ON/OFF.
- * Turning the cache OFF shall trigger flushing of the data
- * to the non-volatile storage.
- * This function should be called with host claimed
- */
-int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
-{
- struct mmc_card *card = host->card;
- unsigned int timeout;
- int err = 0;
-
- if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) ||
- mmc_card_is_removable(host))
- return err;
-
- if (card && mmc_card_mmc(card) &&
- (card->ext_csd.cache_size > 0)) {
- enable = !!enable;
-
- if (card->ext_csd.cache_ctrl ^ enable) {
- timeout = enable ? card->ext_csd.generic_cmd6_time : 0;
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_CACHE_CTRL, enable, timeout);
- if (err)
- pr_err("%s: cache %s error %d\n",
- mmc_hostname(card->host),
- enable ? "on" : "off",
- err);
- else
- card->ext_csd.cache_ctrl = enable;
- }
- }
-
- return err;
-}
-EXPORT_SYMBOL(mmc_cache_ctrl);
-
#ifdef CONFIG_PM
/* Do the card removal on suspend if card is assumed removeable
@@ -2668,7 +2611,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
/* Validate prerequisites for suspend */
if (host->bus_ops->pre_suspend)
err = host->bus_ops->pre_suspend(host);
- if (!err && host->bus_ops->suspend)
+ if (!err)
break;
/* Calling bus_ops->remove() with a claimed host can deadlock */
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 114f6bdfbef3..fdea825dbb24 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -419,6 +419,16 @@ int mmc_of_parse(struct mmc_host *host)
host->caps |= MMC_CAP_SD_HIGHSPEED;
if (of_find_property(np, "cap-mmc-highspeed", &len))
host->caps |= MMC_CAP_MMC_HIGHSPEED;
+ if (of_find_property(np, "sd-uhs-sdr12", &len))
+ host->caps |= MMC_CAP_UHS_SDR12;
+ if (of_find_property(np, "sd-uhs-sdr25", &len))
+ host->caps |= MMC_CAP_UHS_SDR25;
+ if (of_find_property(np, "sd-uhs-sdr50", &len))
+ host->caps |= MMC_CAP_UHS_SDR50;
+ if (of_find_property(np, "sd-uhs-sdr104", &len))
+ host->caps |= MMC_CAP_UHS_SDR104;
+ if (of_find_property(np, "sd-uhs-ddr50", &len))
+ host->caps |= MMC_CAP_UHS_DDR50;
if (of_find_property(np, "cap-power-off-card", &len))
host->caps |= MMC_CAP_POWER_OFF_CARD;
if (of_find_property(np, "cap-sdio-irq", &len))
@@ -429,6 +439,14 @@ int mmc_of_parse(struct mmc_host *host)
host->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", &len))
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+ if (of_find_property(np, "mmc-ddr-1_8v", &len))
+ host->caps |= MMC_CAP_1_8V_DDR;
+ if (of_find_property(np, "mmc-ddr-1_2v", &len))
+ host->caps |= MMC_CAP_1_2V_DDR;
+ if (of_find_property(np, "mmc-hs200-1_8v", &len))
+ host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+ if (of_find_property(np, "mmc-hs200-1_2v", &len))
+ host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
return 0;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 98e9eb0f6643..1ab5f3a0af5b 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -856,8 +856,10 @@ static int mmc_select_hs200(struct mmc_card *card)
/* switch to HS200 mode if bus width set successfully */
if (!err)
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, 2, 0);
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, 2,
+ card->ext_csd.generic_cmd6_time,
+ true, true, true);
err:
return err;
}
@@ -1074,9 +1076,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
host->caps2 & MMC_CAP2_HS200)
err = mmc_select_hs200(card);
else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, 1,
- card->ext_csd.generic_cmd6_time);
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, 1,
+ card->ext_csd.generic_cmd6_time,
+ true, true, true);
if (err && err != -EBADMSG)
goto free_card;
@@ -1287,8 +1290,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* If cache size is higher than 0, this indicates
* the existence of cache and it can be turned on.
*/
- if ((host->caps2 & MMC_CAP2_CACHE_CTRL) &&
- card->ext_csd.cache_size > 0) {
+ if (card->ext_csd.cache_size > 0) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_CACHE_CTRL, 1,
card->ext_csd.generic_cmd6_time);
@@ -1356,11 +1358,9 @@ static int mmc_sleep(struct mmc_host *host)
{
struct mmc_command cmd = {0};
struct mmc_card *card = host->card;
+ unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
int err;
- if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
- return 0;
-
err = mmc_deselect_cards(host);
if (err)
return err;
@@ -1369,7 +1369,19 @@ static int mmc_sleep(struct mmc_host *host)
cmd.arg = card->rca << 16;
cmd.arg |= 1 << 15;
- cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ /*
+ * If the max_busy_timeout of the host is specified, validate it against
+ * the sleep cmd timeout. A failure means we need to prevent the host
+ * from doing hw busy detection, which is done by converting to a R1
+ * response instead of a R1B.
+ */
+ if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout)) {
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ } else {
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ cmd.busy_timeout = timeout_ms;
+ }
+
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
return err;
@@ -1380,8 +1392,8 @@ static int mmc_sleep(struct mmc_host *host)
* SEND_STATUS command to poll the status because that command (and most
* others) is invalid while the card sleeps.
*/
- if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
- mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));
+ if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
+ mmc_delay(timeout_ms);
return err;
}
@@ -1404,7 +1416,7 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
- notify_type, timeout, true, false);
+ notify_type, timeout, true, false, false);
if (err)
pr_err("%s: Power Off Notification timed out, %u\n",
mmc_hostname(card->host), timeout);
@@ -1484,7 +1496,7 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
goto out;
}
- err = mmc_cache_ctrl(host, 0);
+ err = mmc_flush_cache(host->card);
if (err)
goto out;
@@ -1636,16 +1648,6 @@ static int mmc_power_restore(struct mmc_host *host)
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
- .suspend = NULL,
- .resume = NULL,
- .power_restore = mmc_power_restore,
- .alive = mmc_alive,
- .shutdown = mmc_shutdown,
-};
-
-static const struct mmc_bus_ops mmc_ops_unsafe = {
- .remove = mmc_remove,
- .detect = mmc_detect,
.suspend = mmc_suspend,
.resume = mmc_resume,
.runtime_suspend = mmc_runtime_suspend,
@@ -1655,17 +1657,6 @@ static const struct mmc_bus_ops mmc_ops_unsafe = {
.shutdown = mmc_shutdown,
};
-static void mmc_attach_bus_ops(struct mmc_host *host)
-{
- const struct mmc_bus_ops *bus_ops;
-
- if (!mmc_card_is_removable(host))
- bus_ops = &mmc_ops_unsafe;
- else
- bus_ops = &mmc_ops;
- mmc_attach_bus(host, bus_ops);
-}
-
/*
* Starting point for MMC card init.
*/
@@ -1685,7 +1676,7 @@ int mmc_attach_mmc(struct mmc_host *host)
if (err)
return err;
- mmc_attach_bus_ops(host);
+ mmc_attach_bus(host, &mmc_ops);
if (host->ocr_avail_mmc)
host->ocr_avail = host->ocr_avail_mmc;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index e5b5eeb548d1..f51b5ba3bbea 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -405,20 +405,30 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
* timeout of zero implies maximum possible timeout
* @use_busy_signal: use the busy signal as response type
* @send_status: send status cmd to poll for busy
+ * @ignore_crc: ignore CRC errors when sending status cmd to poll for busy
*
* Modifies the EXT_CSD register for selected card.
*/
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
- unsigned int timeout_ms, bool use_busy_signal, bool send_status)
+ unsigned int timeout_ms, bool use_busy_signal, bool send_status,
+ bool ignore_crc)
{
+ struct mmc_host *host = card->host;
int err;
struct mmc_command cmd = {0};
unsigned long timeout;
u32 status = 0;
- bool ignore_crc = false;
+ bool use_r1b_resp = use_busy_signal;
- BUG_ON(!card);
- BUG_ON(!card->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
+ * the host from doing hw busy detection, which is done by converting
+ * to a R1 response instead of a R1B.
+ */
+ if (timeout_ms && host->max_busy_timeout &&
+ (timeout_ms > host->max_busy_timeout))
+ use_r1b_resp = false;
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
@@ -426,17 +436,21 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
(value << 8) |
set;
cmd.flags = MMC_CMD_AC;
- if (use_busy_signal)
+ if (use_r1b_resp) {
cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
- else
+ /*
+ * A busy_timeout of zero means the host can decide to use
+ * whatever value it finds suitable.
+ */
+ cmd.busy_timeout = timeout_ms;
+ } else {
cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;
+ }
-
- cmd.cmd_timeout_ms = timeout_ms;
if (index == EXT_CSD_SANITIZE_START)
cmd.sanitize_busy = true;
- err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
@@ -445,24 +459,27 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
return 0;
/*
- * Must check status to be sure of no errors
- * If CMD13 is to check the busy completion of the timing change,
- * disable the check of CRC error.
+ * CRC errors shall only be ignored in cases were CMD13 is used to poll
+ * to detect busy completion.
*/
- if (index == EXT_CSD_HS_TIMING &&
- !(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY))
- ignore_crc = true;
+ if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
+ ignore_crc = false;
+
+ /* We have an unspecified cmd timeout, use the fallback value. */
+ if (!timeout_ms)
+ timeout_ms = MMC_OPS_TIMEOUT_MS;
- timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
+ /* Must check status to be sure of no errors. */
+ timeout = jiffies + msecs_to_jiffies(timeout_ms);
do {
if (send_status) {
err = __mmc_send_status(card, &status, ignore_crc);
if (err)
return err;
}
- if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
break;
- if (mmc_host_is_spi(card->host))
+ if (mmc_host_is_spi(host))
break;
/*
@@ -478,18 +495,18 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
/* 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(card->host), __func__);
+ mmc_hostname(host), __func__);
return -ETIMEDOUT;
}
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
- if (mmc_host_is_spi(card->host)) {
+ if (mmc_host_is_spi(host)) {
if (status & R1_SPI_ILLEGAL_COMMAND)
return -EBADMSG;
} else {
if (status & 0xFDFFA000)
- pr_warning("%s: unexpected status %#x after "
- "switch", mmc_hostname(card->host), status);
+ pr_warn("%s: unexpected status %#x after switch\n",
+ mmc_hostname(host), status);
if (status & R1_SWITCH_ERROR)
return -EBADMSG;
}
@@ -501,7 +518,8 @@ EXPORT_SYMBOL_GPL(__mmc_switch);
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms)
{
- return __mmc_switch(card, set, index, value, timeout_ms, true, true);
+ return __mmc_switch(card, set, index, value, timeout_ms, true, true,
+ false);
}
EXPORT_SYMBOL_GPL(mmc_switch);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 692fdb177294..2dd359d2242f 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1209,16 +1209,6 @@ static int mmc_sd_power_restore(struct mmc_host *host)
static const struct mmc_bus_ops mmc_sd_ops = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
- .suspend = NULL,
- .resume = NULL,
- .power_restore = mmc_sd_power_restore,
- .alive = mmc_sd_alive,
- .shutdown = mmc_sd_suspend,
-};
-
-static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
- .remove = mmc_sd_remove,
- .detect = mmc_sd_detect,
.runtime_suspend = mmc_sd_runtime_suspend,
.runtime_resume = mmc_sd_runtime_resume,
.suspend = mmc_sd_suspend,
@@ -1228,17 +1218,6 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
.shutdown = mmc_sd_suspend,
};
-static void mmc_sd_attach_bus_ops(struct mmc_host *host)
-{
- const struct mmc_bus_ops *bus_ops;
-
- if (!mmc_card_is_removable(host))
- bus_ops = &mmc_sd_ops_unsafe;
- else
- bus_ops = &mmc_sd_ops;
- mmc_attach_bus(host, bus_ops);
-}
-
/*
* Starting point for SD card init.
*/
@@ -1254,7 +1233,7 @@ int mmc_attach_sd(struct mmc_host *host)
if (err)
return err;
- mmc_sd_attach_bus_ops(host);
+ mmc_attach_bus(host, &mmc_sd_ops);
if (host->ocr_avail_sd)
host->ocr_avail = host->ocr_avail_sd;
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index 46596b71a32f..f7650b899e3d 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -10,6 +10,7 @@
#include <linux/err.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/mmc/host.h>
@@ -18,8 +19,10 @@
#include <linux/slab.h>
struct mmc_gpio {
- int ro_gpio;
- int cd_gpio;
+ struct gpio_desc *ro_gpio;
+ struct gpio_desc *cd_gpio;
+ bool override_ro_active_level;
+ bool override_cd_active_level;
char *ro_label;
char cd_label[0];
};
@@ -57,8 +60,6 @@ static int mmc_gpio_alloc(struct mmc_host *host)
ctx->ro_label = ctx->cd_label + len;
snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
- ctx->cd_gpio = -EINVAL;
- ctx->ro_gpio = -EINVAL;
host->slot.handler_priv = ctx;
}
}
@@ -72,11 +73,14 @@ int mmc_gpio_get_ro(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
- if (!ctx || !gpio_is_valid(ctx->ro_gpio))
+ if (!ctx || !ctx->ro_gpio)
return -ENOSYS;
- return !gpio_get_value_cansleep(ctx->ro_gpio) ^
- !!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
+ if (ctx->override_ro_active_level)
+ return !gpiod_get_raw_value_cansleep(ctx->ro_gpio) ^
+ !!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
+
+ return gpiod_get_value_cansleep(ctx->ro_gpio);
}
EXPORT_SYMBOL(mmc_gpio_get_ro);
@@ -84,11 +88,14 @@ int mmc_gpio_get_cd(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
- if (!ctx || !gpio_is_valid(ctx->cd_gpio))
+ if (!ctx || !ctx->cd_gpio)
return -ENOSYS;
- return !gpio_get_value_cansleep(ctx->cd_gpio) ^
- !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
+ if (ctx->override_cd_active_level)
+ return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^
+ !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
+
+ return gpiod_get_value_cansleep(ctx->cd_gpio);
}
EXPORT_SYMBOL(mmc_gpio_get_cd);
@@ -125,12 +132,47 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
if (ret < 0)
return ret;
- ctx->ro_gpio = gpio;
+ ctx->override_ro_active_level = true;
+ ctx->ro_gpio = gpio_to_desc(gpio);
return 0;
}
EXPORT_SYMBOL(mmc_gpio_request_ro);
+void mmc_gpiod_request_cd_irq(struct mmc_host *host)
+{
+ struct mmc_gpio *ctx = host->slot.handler_priv;
+ int ret, irq;
+
+ if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio)
+ return;
+
+ irq = gpiod_to_irq(ctx->cd_gpio);
+
+ /*
+ * Even if gpiod_to_irq() returns a valid IRQ number, the platform might
+ * still prefer to poll, e.g., because that IRQ number is already used
+ * by another unit and cannot be shared.
+ */
+ if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
+ irq = -EINVAL;
+
+ if (irq >= 0) {
+ ret = devm_request_threaded_irq(&host->class_dev, irq,
+ NULL, mmc_gpio_cd_irqt,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ ctx->cd_label, host);
+ if (ret < 0)
+ irq = ret;
+ }
+
+ host->slot.cd_irq = irq;
+
+ if (irq < 0)
+ host->caps |= MMC_CAP_NEEDS_POLL;
+}
+EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
+
/**
* mmc_gpio_request_cd - request a gpio for card-detection
* @host: mmc host
@@ -154,7 +196,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
unsigned int debounce)
{
struct mmc_gpio *ctx;
- int irq = gpio_to_irq(gpio);
int ret;
ret = mmc_gpio_alloc(host);
@@ -179,29 +220,10 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
return ret;
}
- /*
- * Even if gpio_to_irq() returns a valid IRQ number, the platform might
- * still prefer to poll, e.g., because that IRQ number is already used
- * by another unit and cannot be shared.
- */
- if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
- irq = -EINVAL;
-
- if (irq >= 0) {
- ret = devm_request_threaded_irq(&host->class_dev, irq,
- NULL, mmc_gpio_cd_irqt,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- ctx->cd_label, host);
- if (ret < 0)
- irq = ret;
- }
-
- host->slot.cd_irq = irq;
-
- if (irq < 0)
- host->caps |= MMC_CAP_NEEDS_POLL;
+ ctx->override_cd_active_level = true;
+ ctx->cd_gpio = gpio_to_desc(gpio);
- ctx->cd_gpio = gpio;
+ mmc_gpiod_request_cd_irq(host);
return 0;
}
@@ -219,11 +241,11 @@ void mmc_gpio_free_ro(struct mmc_host *host)
struct mmc_gpio *ctx = host->slot.handler_priv;
int gpio;
- if (!ctx || !gpio_is_valid(ctx->ro_gpio))
+ if (!ctx || !ctx->ro_gpio)
return;
- gpio = ctx->ro_gpio;
- ctx->ro_gpio = -EINVAL;
+ gpio = desc_to_gpio(ctx->ro_gpio);
+ ctx->ro_gpio = NULL;
devm_gpio_free(&host->class_dev, gpio);
}
@@ -241,7 +263,7 @@ void mmc_gpio_free_cd(struct mmc_host *host)
struct mmc_gpio *ctx = host->slot.handler_priv;
int gpio;
- if (!ctx || !gpio_is_valid(ctx->cd_gpio))
+ if (!ctx || !ctx->cd_gpio)
return;
if (host->slot.cd_irq >= 0) {
@@ -249,9 +271,87 @@ void mmc_gpio_free_cd(struct mmc_host *host)
host->slot.cd_irq = -EINVAL;
}
- gpio = ctx->cd_gpio;
- ctx->cd_gpio = -EINVAL;
+ gpio = desc_to_gpio(ctx->cd_gpio);
+ ctx->cd_gpio = NULL;
devm_gpio_free(&host->class_dev, gpio);
}
EXPORT_SYMBOL(mmc_gpio_free_cd);
+
+/**
+ * mmc_gpiod_request_cd - request a gpio descriptor for card-detection
+ * @host: mmc host
+ * @con_id: function within the GPIO consumer
+ * @idx: index of the GPIO to obtain in the consumer
+ * @override_active_level: ignore %GPIO_ACTIVE_LOW flag
+ * @debounce: debounce time in microseconds
+ *
+ * Use this function in place of mmc_gpio_request_cd() to use the GPIO
+ * descriptor API. Note that it is paired with mmc_gpiod_free_cd() not
+ * mmc_gpio_free_cd(). Note also that it must be called prior to mmc_add_host()
+ * otherwise the caller must also call mmc_gpiod_request_cd_irq().
+ *
+ * Returns zero on success, else an error.
+ */
+int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
+ unsigned int idx, bool override_active_level,
+ unsigned int debounce)
+{
+ struct mmc_gpio *ctx;
+ struct gpio_desc *desc;
+ int ret;
+
+ ret = mmc_gpio_alloc(host);
+ if (ret < 0)
+ return ret;
+
+ ctx = host->slot.handler_priv;
+
+ if (!con_id)
+ con_id = ctx->cd_label;
+
+ desc = devm_gpiod_get_index(host->parent, con_id, idx);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ ret = gpiod_direction_input(desc);
+ if (ret < 0)
+ return ret;
+
+ if (debounce) {
+ ret = gpiod_set_debounce(desc, debounce);
+ if (ret < 0)
+ return ret;
+ }
+
+ ctx->override_cd_active_level = override_active_level;
+ ctx->cd_gpio = desc;
+
+ return 0;
+}
+EXPORT_SYMBOL(mmc_gpiod_request_cd);
+
+/**
+ * mmc_gpiod_free_cd - free the card-detection gpio descriptor
+ * @host: mmc host
+ *
+ * It's provided only for cases that client drivers need to manually free
+ * up the card-detection gpio requested by mmc_gpiod_request_cd().
+ */
+void mmc_gpiod_free_cd(struct mmc_host *host)
+{
+ struct mmc_gpio *ctx = host->slot.handler_priv;
+
+ if (!ctx || !ctx->cd_gpio)
+ return;
+
+ if (host->slot.cd_irq >= 0) {
+ devm_free_irq(&host->class_dev, host->slot.cd_irq, host);
+ host->slot.cd_irq = -EINVAL;
+ }
+
+ devm_gpiod_put(&host->class_dev, ctx->cd_gpio);
+
+ ctx->cd_gpio = NULL;
+}
+EXPORT_SYMBOL(mmc_gpiod_free_cd);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1384f67abe21..8aaf8c1f3f63 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -263,7 +263,7 @@ config MMC_SDHCI_S3C_DMA
config MMC_SDHCI_BCM_KONA
tristate "SDHCI support on Broadcom KONA platform"
- depends on ARCH_BCM
+ depends on ARCH_BCM_MOBILE
select MMC_SDHCI_PLTFM
help
This selects the Broadcom Kona Secure Digital Host Controller
@@ -334,6 +334,19 @@ config MMC_ATMELMCI
If unsure, say N.
+config MMC_SDHCI_MSM
+ tristate "Qualcomm SDHCI Controller Support"
+ depends on ARCH_QCOM
+ depends on MMC_SDHCI_PLTFM
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ support present in Qualcomm SOCs. The controller supports
+ SD/MMC/SDIO devices.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_MSM
tristate "Qualcomm SDCC Controller Support"
depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50)
@@ -580,14 +593,6 @@ config MMC_DW_EXYNOS
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Exynos4 and Exynos5 SoC's.
-config MMC_DW_SOCFPGA
- tristate "SOCFPGA specific extensions for Synopsys DW Memory Card Interface"
- depends on MMC_DW && MFD_SYSCON
- select MMC_DW_PLTFM
- help
- This selects support for Altera SoCFPGA specific extensions to the
- Synopsys DesignWare Memory Card Interface driver.
-
config MMC_DW_K3
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3483b6b6b880..0c8aa5e1e304 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -43,7 +43,6 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
-obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
@@ -64,6 +63,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
+obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index d6153740b77f..5d4c5e0fba2f 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -1192,7 +1192,7 @@ static struct davinci_mmc_config
struct device_node *np;
struct davinci_mmc_config *pdata = pdev->dev.platform_data;
const struct of_device_id *match =
- of_match_device(of_match_ptr(davinci_mmc_dt_ids), &pdev->dev);
+ of_match_device(davinci_mmc_dt_ids, &pdev->dev);
u32 data;
np = pdev->dev.of_node;
@@ -1468,7 +1468,7 @@ static struct platform_driver davinci_mmcsd_driver = {
.name = "davinci_mmc",
.owner = THIS_MODULE,
.pm = davinci_mmcsd_pm_ops,
- .of_match_table = of_match_ptr(davinci_mmc_dt_ids),
+ .of_match_table = davinci_mmc_dt_ids,
},
.remove = __exit_p(davinci_mmcsd_remove),
.id_table = davinci_mmc_devtype,
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
index f567c219cff4..650f9cc3f7a6 100644
--- a/drivers/mmc/host/dw_mmc-k3.c
+++ b/drivers/mmc/host/dw_mmc-k3.c
@@ -50,6 +50,7 @@ static int dw_mci_k3_probe(struct platform_device *pdev)
return dw_mci_pltfm_register(pdev, drv_data);
}
+#ifdef CONFIG_PM_SLEEP
static int dw_mci_k3_suspend(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
@@ -75,6 +76,7 @@ static int dw_mci_k3_resume(struct device *dev)
return dw_mci_resume(host);
}
+#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume);
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 5c4965655297..d4a47a9f5584 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -25,13 +25,17 @@
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
-static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
+static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr)
{
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
}
static const struct dw_mci_drv_data rockchip_drv_data = {
- .prepare_command = dw_mci_rockchip_prepare_command,
+ .prepare_command = dw_mci_pltfm_prepare_command,
+};
+
+static const struct dw_mci_drv_data socfpga_drv_data = {
+ .prepare_command = dw_mci_pltfm_prepare_command,
};
int dw_mci_pltfm_register(struct platform_device *pdev,
@@ -92,6 +96,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = {
{ .compatible = "snps,dw-mshc", },
{ .compatible = "rockchip,rk2928-dw-mshc",
.data = &rockchip_drv_data },
+ { .compatible = "altr,socfpga-dw-mshc",
+ .data = &socfpga_drv_data },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
@@ -123,7 +129,7 @@ static struct platform_driver dw_mci_pltfm_driver = {
.remove = dw_mci_pltfm_remove,
.driver = {
.name = "dw_mmc",
- .of_match_table = of_match_ptr(dw_mci_pltfm_match),
+ .of_match_table = dw_mci_pltfm_match,
.pm = &dw_mci_pltfm_pmops,
},
};
diff --git a/drivers/mmc/host/dw_mmc-socfpga.c b/drivers/mmc/host/dw_mmc-socfpga.c
deleted file mode 100644
index 3e8e53ae3302..000000000000
--- a/drivers/mmc/host/dw_mmc-socfpga.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Altera SoCFPGA Specific Extensions for Synopsys DW Multimedia Card Interface
- * driver
- *
- * Copyright (C) 2012, Samsung Electronics Co., Ltd.
- * Copyright (C) 2013 Altera 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.
- *
- * Taken from dw_mmc-exynos.c
- */
-#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.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-
-#include "dw_mmc.h"
-#include "dw_mmc-pltfm.h"
-
-#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108
-#define DRV_CLK_PHASE_SHIFT_SEL_MASK 0x7
-#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \
- ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
-
-/* SOCFPGA implementation specific driver private data */
-struct dw_mci_socfpga_priv_data {
- u8 ciu_div; /* card interface unit divisor */
- u32 hs_timing; /* bitmask for CIU clock phase shift */
- struct regmap *sysreg; /* regmap for system manager register */
-};
-
-static int dw_mci_socfpga_priv_init(struct dw_mci *host)
-{
- return 0;
-}
-
-static int dw_mci_socfpga_setup_clock(struct dw_mci *host)
-{
- struct dw_mci_socfpga_priv_data *priv = host->priv;
-
- clk_disable_unprepare(host->ciu_clk);
- regmap_write(priv->sysreg, SYSMGR_SDMMCGRP_CTRL_OFFSET,
- priv->hs_timing);
- clk_prepare_enable(host->ciu_clk);
-
- host->bus_hz /= (priv->ciu_div + 1);
- return 0;
-}
-
-static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr)
-{
- struct dw_mci_socfpga_priv_data *priv = host->priv;
-
- if (priv->hs_timing & DRV_CLK_PHASE_SHIFT_SEL_MASK)
- *cmdr |= SDMMC_CMD_USE_HOLD_REG;
-}
-
-static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
-{
- struct dw_mci_socfpga_priv_data *priv;
- struct device_node *np = host->dev->of_node;
- u32 timing[2];
- u32 div = 0;
- int ret;
-
- priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(host->dev, "mem alloc failed for private data\n");
- return -ENOMEM;
- }
-
- priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
- if (IS_ERR(priv->sysreg)) {
- dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
- return PTR_ERR(priv->sysreg);
- }
-
- ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div);
- if (ret)
- dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1");
- priv->ciu_div = div;
-
- ret = of_property_read_u32_array(np,
- "altr,dw-mshc-sdr-timing", timing, 2);
- if (ret)
- return ret;
-
- priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
- host->priv = priv;
- return 0;
-}
-
-static const struct dw_mci_drv_data socfpga_drv_data = {
- .init = dw_mci_socfpga_priv_init,
- .setup_clock = dw_mci_socfpga_setup_clock,
- .prepare_command = dw_mci_socfpga_prepare_command,
- .parse_dt = dw_mci_socfpga_parse_dt,
-};
-
-static const struct of_device_id dw_mci_socfpga_match[] = {
- { .compatible = "altr,socfpga-dw-mshc",
- .data = &socfpga_drv_data, },
- {},
-};
-MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match);
-
-static int dw_mci_socfpga_probe(struct platform_device *pdev)
-{
- const struct dw_mci_drv_data *drv_data;
- const struct of_device_id *match;
-
- match = of_match_node(dw_mci_socfpga_match, pdev->dev.of_node);
- drv_data = match->data;
- return dw_mci_pltfm_register(pdev, drv_data);
-}
-
-static struct platform_driver dw_mci_socfpga_pltfm_driver = {
- .probe = dw_mci_socfpga_probe,
- .remove = __exit_p(dw_mci_pltfm_remove),
- .driver = {
- .name = "dwmmc_socfpga",
- .of_match_table = dw_mci_socfpga_match,
- .pm = &dw_mci_pltfm_pmops,
- },
-};
-
-module_platform_driver(dw_mci_socfpga_pltfm_driver);
-
-MODULE_DESCRIPTION("Altera SOCFPGA Specific DW-MSHC Driver Extension");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:dwmmc-socfpga");
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index c204b7d1532c..cced599d5aeb 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1345,7 +1345,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
if (!err) {
if (!data->stop || mrq->sbc) {
- if (mrq->sbc)
+ if (mrq->sbc && data->stop)
data->stop->error = 0;
dw_mci_request_end(host, mrq);
goto unlock;
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 6bf24ab917e6..68349779c396 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -185,7 +185,7 @@
extern int dw_mci_probe(struct dw_mci *host);
extern void dw_mci_remove(struct dw_mci *host);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
extern int dw_mci_suspend(struct dw_mci *host);
extern int dw_mci_resume(struct dw_mci *host);
#endif
@@ -244,6 +244,7 @@ struct dw_mci_tuning_data {
* @prepare_command: handle CMD register extensions.
* @set_ios: handle bus specific extensions.
* @parse_dt: parse implementation specific device tree properties.
+ * @execute_tuning: implementation specific tuning procedure.
*
* Provide controller implementation specific extensions. The usage of this
* data structure is fully optional and usage of each member in this structure
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index b93122636531..771c60ab4a32 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -921,6 +921,29 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
{
void __iomem *base = host->base;
bool sbc = (cmd == host->mrq->sbc);
+ bool busy_resp = host->variant->busy_detect &&
+ (cmd->flags & MMC_RSP_BUSY);
+
+ /* Check if we need to wait for busy completion. */
+ if (host->busy_status && (status & MCI_ST_CARDBUSY))
+ return;
+
+ /* Enable busy completion if needed and supported. */
+ if (!host->busy_status && busy_resp &&
+ !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
+ (readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) {
+ writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND,
+ base + MMCIMASK0);
+ host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND);
+ return;
+ }
+
+ /* At busy completion, mask the IRQ and complete the request. */
+ if (host->busy_status) {
+ writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND,
+ base + MMCIMASK0);
+ host->busy_status = 0;
+ }
host->cmd = NULL;
@@ -1139,20 +1162,30 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
status &= ~MCI_IRQ1MASK;
}
+ /*
+ * We intentionally clear the MCI_ST_CARDBUSY IRQ here (if it's
+ * enabled) since the HW seems to be triggering the IRQ on both
+ * edges while monitoring DAT0 for busy completion.
+ */
status &= readl(host->base + MMCIMASK0);
writel(status, host->base + MMCICLEAR);
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
+ cmd = host->cmd;
+ if ((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
+ MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
+ mmci_cmd_irq(host, cmd, status);
+
data = host->data;
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_DATAEND|
MCI_DATABLOCKEND) && data)
mmci_data_irq(host, data, status);
- cmd = host->cmd;
- if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
- mmci_cmd_irq(host, cmd, status);
+ /* Don't poll for busy completion in irq context. */
+ if (host->busy_status)
+ status &= ~MCI_ST_CARDBUSY;
ret = 1;
} while (status);
@@ -1503,12 +1536,6 @@ static int mmci_probe(struct amba_device *dev,
goto clk_disable;
}
- if (variant->busy_detect) {
- mmci_ops.card_busy = mmci_card_busy;
- mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
- }
-
- mmc->ops = &mmci_ops;
/*
* The ARM and ST versions of the block have slightly different
* clock divider equations which means that the minimum divider
@@ -1542,6 +1569,15 @@ static int mmci_probe(struct amba_device *dev,
mmc->caps = plat->capabilities;
mmc->caps2 = plat->capabilities2;
+ if (variant->busy_detect) {
+ mmci_ops.card_busy = mmci_card_busy;
+ mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
+ mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
+ mmc->max_busy_timeout = 0;
+ }
+
+ mmc->ops = &mmci_ops;
+
/* We support these PM capabilities. */
mmc->pm_caps = MMC_PM_KEEP_POWER;
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 84c0e59b792a..58b1b8896bf2 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -140,6 +140,7 @@
/* Extended status bits for the ST Micro variants */
#define MCI_ST_SDIOITMASK (1 << 22)
#define MCI_ST_CEATAENDMASK (1 << 23)
+#define MCI_ST_BUSYEND (1 << 24)
#define MMCIMASK1 0x040
#define MMCIFIFOCNT 0x048
@@ -187,6 +188,7 @@ struct mmci_host {
u32 pwr_reg;
u32 clk_reg;
u32 datactrl_reg;
+ u32 busy_status;
bool vqmmc_enabled;
struct mmci_platform_data *plat;
struct variant_data *variant;
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 98b6b6ef7e5c..5c2e58b29305 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -26,6 +26,7 @@
#include <linux/omap-dma.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
#include <linux/clk.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
@@ -130,7 +131,6 @@ struct mmc_omap_host {
u32 dma_rx_burst;
struct dma_chan *dma_tx;
u32 dma_tx_burst;
- struct resource *mem_res;
void __iomem *virt_base;
unsigned int phys_base;
int irq;
@@ -153,7 +153,6 @@ struct mmc_omap_host {
u32 total_bytes_left;
unsigned features;
- unsigned use_dma:1;
unsigned brs_received:1, dma_done:1;
unsigned dma_in_use:1;
spinlock_t dma_lock;
@@ -338,6 +337,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
u32 cmdreg;
u32 resptype;
u32 cmdtype;
+ u16 irq_mask;
host->cmd = cmd;
@@ -390,12 +390,14 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
OMAP_MMC_WRITE(host, CTO, 200);
OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
- OMAP_MMC_WRITE(host, IE,
- OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL |
- OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT |
- OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT |
- OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR |
- OMAP_MMC_STAT_END_OF_DATA);
+ irq_mask = OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL |
+ OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT |
+ OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT |
+ OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR |
+ OMAP_MMC_STAT_END_OF_DATA;
+ if (cmd->opcode == MMC_ERASE)
+ irq_mask &= ~OMAP_MMC_STAT_DATA_TOUT;
+ OMAP_MMC_WRITE(host, IE, irq_mask);
OMAP_MMC_WRITE(host, CMD, cmdreg);
}
@@ -945,7 +947,7 @@ static void
mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
{
struct mmc_data *data = req->data;
- int i, use_dma, block_size;
+ int i, use_dma = 1, block_size;
unsigned sg_len;
host->data = data;
@@ -970,13 +972,10 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
sg_len = (data->blocks == 1) ? 1 : data->sg_len;
/* Only do DMA for entire blocks */
- use_dma = host->use_dma;
- if (use_dma) {
- for (i = 0; i < sg_len; i++) {
- if ((data->sg[i].length % block_size) != 0) {
- use_dma = 0;
- break;
- }
+ for (i = 0; i < sg_len; i++) {
+ if ((data->sg[i].length % block_size) != 0) {
+ use_dma = 0;
+ break;
}
}
@@ -1239,7 +1238,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
mmc->caps = 0;
if (host->pdata->slots[id].wires >= 4)
- mmc->caps |= MMC_CAP_4_BIT_DATA;
+ mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_ERASE;
mmc->ops = &mmc_omap_ops;
mmc->f_min = 400000;
@@ -1262,6 +1261,13 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size;
+ if (slot->pdata->get_cover_state != NULL) {
+ setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
+ (unsigned long)slot);
+ tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
+ (unsigned long)slot);
+ }
+
r = mmc_add_host(mmc);
if (r < 0)
goto err_remove_host;
@@ -1278,11 +1284,6 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
&dev_attr_cover_switch);
if (r < 0)
goto err_remove_slot_name;
-
- setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
- (unsigned long)slot);
- tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
- (unsigned long)slot);
tasklet_schedule(&slot->cover_tasklet);
}
@@ -1333,21 +1334,19 @@ static int mmc_omap_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host = devm_kzalloc(&pdev->dev, sizeof(struct mmc_omap_host),
+ GFP_KERNEL);
+ if (host == NULL)
+ return -ENOMEM;
+
irq = platform_get_irq(pdev, 0);
- if (res == NULL || irq < 0)
+ if (irq < 0)
return -ENXIO;
- res = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (res == NULL)
- return -EBUSY;
-
- host = kzalloc(sizeof(struct mmc_omap_host), GFP_KERNEL);
- if (host == NULL) {
- ret = -ENOMEM;
- goto err_free_mem_region;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->virt_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->virt_base))
+ return PTR_ERR(host->virt_base);
INIT_WORK(&host->slot_release_work, mmc_omap_slot_release_work);
INIT_WORK(&host->send_stop_work, mmc_omap_send_stop_work);
@@ -1369,20 +1368,11 @@ static int mmc_omap_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
host->id = pdev->id;
- host->mem_res = res;
- host->irq = irq;
- host->use_dma = 1;
host->irq = irq;
- host->phys_base = host->mem_res->start;
- host->virt_base = ioremap(res->start, resource_size(res));
- if (!host->virt_base)
- goto err_ioremap;
-
+ host->phys_base = res->start;
host->iclk = clk_get(&pdev->dev, "ick");
- if (IS_ERR(host->iclk)) {
- ret = PTR_ERR(host->iclk);
- goto err_free_mmc_host;
- }
+ if (IS_ERR(host->iclk))
+ return PTR_ERR(host->iclk);
clk_enable(host->iclk);
host->fclk = clk_get(&pdev->dev, "fck");
@@ -1460,12 +1450,6 @@ err_free_dma:
err_free_iclk:
clk_disable(host->iclk);
clk_put(host->iclk);
-err_free_mmc_host:
- iounmap(host->virt_base);
-err_ioremap:
- kfree(host);
-err_free_mem_region:
- release_mem_region(res->start, resource_size(res));
return ret;
}
@@ -1493,13 +1477,8 @@ static int mmc_omap_remove(struct platform_device *pdev)
if (host->dma_rx)
dma_release_channel(host->dma_rx);
- iounmap(host->virt_base);
- release_mem_region(pdev->resource[0].start,
- pdev->resource[0].end - pdev->resource[0].start + 1);
destroy_workqueue(host->mmc_omap_wq);
- kfree(host);
-
return 0;
}
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index dbd32ad3b749..e91ee21549d0 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -45,6 +45,7 @@
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSSTATUS 0x0014
#define OMAP_HSMMC_CON 0x002C
+#define OMAP_HSMMC_SDMASA 0x0100
#define OMAP_HSMMC_BLK 0x0104
#define OMAP_HSMMC_ARG 0x0108
#define OMAP_HSMMC_CMD 0x010C
@@ -58,6 +59,7 @@
#define OMAP_HSMMC_STAT 0x0130
#define OMAP_HSMMC_IE 0x0134
#define OMAP_HSMMC_ISE 0x0138
+#define OMAP_HSMMC_AC12 0x013C
#define OMAP_HSMMC_CAPA 0x0140
#define VS18 (1 << 26)
@@ -81,6 +83,7 @@
#define DTO_MASK 0x000F0000
#define DTO_SHIFT 16
#define INIT_STREAM (1 << 1)
+#define ACEN_ACMD23 (2 << 2)
#define DP_SELECT (1 << 21)
#define DDIR (1 << 4)
#define DMAE 0x1
@@ -97,7 +100,6 @@
#define SRC (1 << 25)
#define SRD (1 << 26)
#define SOFTRESET (1 << 1)
-#define RESETDONE (1 << 0)
/* Interrupt masks for IE and ISE register */
#define CC_EN (1 << 0)
@@ -112,13 +114,21 @@
#define DTO_EN (1 << 20)
#define DCRC_EN (1 << 21)
#define DEB_EN (1 << 22)
+#define ACE_EN (1 << 24)
#define CERR_EN (1 << 28)
#define BADA_EN (1 << 29)
-#define INT_EN_MASK (BADA_EN | CERR_EN | DEB_EN | DCRC_EN |\
+#define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\
DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \
BRR_EN | BWR_EN | TC_EN | CC_EN)
+#define CNI (1 << 7)
+#define ACIE (1 << 4)
+#define ACEB (1 << 3)
+#define ACCE (1 << 2)
+#define ACTO (1 << 1)
+#define ACNE (1 << 0)
+
#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20 /* 20 mSec */
#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */
@@ -126,6 +136,11 @@
#define OMAP_MMC_MAX_CLOCK 52000000
#define DRIVER_NAME "omap_hsmmc"
+#define VDD_1V8 1800000 /* 180000 uV */
+#define VDD_3V0 3000000 /* 300000 uV */
+#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)
+
+#define AUTO_CMD23 (1 << 1) /* Auto CMD23 support */
/*
* One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known
@@ -164,7 +179,8 @@ struct omap_hsmmc_host {
*/
struct regulator *vcc;
struct regulator *vcc_aux;
- int pbias_disable;
+ struct regulator *pbias;
+ bool pbias_enabled;
void __iomem *base;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
@@ -188,10 +204,19 @@ struct omap_hsmmc_host {
int reqs_blocked;
int use_reg;
int req_in_progress;
+ unsigned long clk_rate;
+ unsigned int flags;
struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata;
};
+struct omap_mmc_of_data {
+ u32 reg_offset;
+ u8 controller_flags;
+};
+
+static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
+
static int omap_hsmmc_card_detect(struct device *dev, int slot)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
@@ -261,17 +286,19 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
*/
if (!host->vcc)
return 0;
- /*
- * With DT, never turn OFF the regulator for MMC1. This is because
- * the pbias cell programming support is still missing when
- * booting with Device tree
- */
- if (host->pbias_disable && !vdd)
- return 0;
if (mmc_slot(host).before_set_reg)
mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
+ if (host->pbias) {
+ if (host->pbias_enabled == 1) {
+ ret = regulator_disable(host->pbias);
+ if (!ret)
+ host->pbias_enabled = 0;
+ }
+ regulator_set_voltage(host->pbias, VDD_3V0, VDD_3V0);
+ }
+
/*
* Assume Vcc regulator is used only to power the card ... OMAP
* VDDS is used to power the pins, optionally with a transceiver to
@@ -286,11 +313,12 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
* chips/cards need an interface voltage rail too.
*/
if (power_on) {
- ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
+ if (host->vcc)
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
/* Enable interface voltage rail, if needed */
if (ret == 0 && host->vcc_aux) {
ret = regulator_enable(host->vcc_aux);
- if (ret < 0)
+ if (ret < 0 && host->vcc)
ret = mmc_regulator_set_ocr(host->mmc,
host->vcc, 0);
}
@@ -298,16 +326,34 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
/* Shut down the rail */
if (host->vcc_aux)
ret = regulator_disable(host->vcc_aux);
- if (!ret) {
+ if (host->vcc) {
/* Then proceed to shut down the local regulator */
ret = mmc_regulator_set_ocr(host->mmc,
host->vcc, 0);
}
}
+ if (host->pbias) {
+ if (vdd <= VDD_165_195)
+ ret = regulator_set_voltage(host->pbias, VDD_1V8,
+ VDD_1V8);
+ else
+ ret = regulator_set_voltage(host->pbias, VDD_3V0,
+ VDD_3V0);
+ if (ret < 0)
+ goto error_set_power;
+
+ if (host->pbias_enabled == 0) {
+ ret = regulator_enable(host->pbias);
+ if (!ret)
+ host->pbias_enabled = 1;
+ }
+ }
+
if (mmc_slot(host).after_set_reg)
mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
+error_set_power:
return ret;
}
@@ -316,12 +362,12 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
struct regulator *reg;
int ocr_value = 0;
- reg = regulator_get(host->dev, "vmmc");
+ reg = devm_regulator_get(host->dev, "vmmc");
if (IS_ERR(reg)) {
- dev_err(host->dev, "vmmc regulator missing\n");
+ dev_err(host->dev, "unable to get vmmc regulator %ld\n",
+ PTR_ERR(reg));
return PTR_ERR(reg);
} else {
- mmc_slot(host).set_power = omap_hsmmc_set_power;
host->vcc = reg;
ocr_value = mmc_regulator_get_ocrmask(reg);
if (!mmc_slot(host).ocr_mask) {
@@ -334,31 +380,29 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
return -EINVAL;
}
}
+ }
+ mmc_slot(host).set_power = omap_hsmmc_set_power;
- /* Allow an aux regulator */
- reg = regulator_get(host->dev, "vmmc_aux");
- host->vcc_aux = IS_ERR(reg) ? NULL : reg;
+ /* Allow an aux regulator */
+ reg = devm_regulator_get_optional(host->dev, "vmmc_aux");
+ host->vcc_aux = IS_ERR(reg) ? NULL : reg;
- /* For eMMC do not power off when not in sleep state */
- if (mmc_slot(host).no_regulator_off_init)
- return 0;
- /*
- * UGLY HACK: workaround regulator framework bugs.
- * When the bootloader leaves a supply active, it's
- * initialized with zero usecount ... and we can't
- * disable it without first enabling it. Until the
- * framework is fixed, we need a workaround like this
- * (which is safe for MMC, but not in general).
- */
- if (regulator_is_enabled(host->vcc) > 0 ||
- (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
- int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
+ reg = devm_regulator_get_optional(host->dev, "pbias");
+ host->pbias = IS_ERR(reg) ? NULL : reg;
- mmc_slot(host).set_power(host->dev, host->slot_id,
- 1, vdd);
- mmc_slot(host).set_power(host->dev, host->slot_id,
- 0, 0);
- }
+ /* For eMMC do not power off when not in sleep state */
+ if (mmc_slot(host).no_regulator_off_init)
+ return 0;
+ /*
+ * To disable boot_on regulator, enable regulator
+ * to increase usecount and then disable it.
+ */
+ if ((host->vcc && regulator_is_enabled(host->vcc) > 0) ||
+ (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
+ int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
+
+ mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd);
+ mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
}
return 0;
@@ -366,8 +410,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
{
- regulator_put(host->vcc);
- regulator_put(host->vcc_aux);
mmc_slot(host).set_power = NULL;
}
@@ -605,9 +647,6 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
u32 hctl, capa;
unsigned long timeout;
- if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
- return 1;
-
if (host->con == OMAP_HSMMC_READ(host->base, CON) &&
host->hctl == OMAP_HSMMC_READ(host->base, HCTL) &&
host->sysctl == OMAP_HSMMC_READ(host->base, SYSCTL) &&
@@ -787,6 +826,11 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
+ if ((host->flags & AUTO_CMD23) && mmc_op_multi(cmd->opcode) &&
+ host->mrq->sbc) {
+ cmdreg |= ACEN_ACMD23;
+ OMAP_HSMMC_WRITE(host->base, SDMASA, host->mrq->sbc->arg);
+ }
if (data) {
cmdreg |= DP_SELECT | MSBS | BCE;
if (data->flags & MMC_DATA_READ)
@@ -864,11 +908,10 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
else
data->bytes_xfered = 0;
- if (!data->stop) {
+ if (data->stop && (data->error || !host->mrq->sbc))
+ omap_hsmmc_start_command(host, data->stop, NULL);
+ else
omap_hsmmc_request_done(host, data->mrq);
- return;
- }
- omap_hsmmc_start_command(host, data->stop, NULL);
}
/*
@@ -879,6 +922,14 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
{
host->cmd = NULL;
+ if (host->mrq->sbc && (host->cmd == host->mrq->sbc) &&
+ !host->mrq->sbc->error && !(host->flags & AUTO_CMD23)) {
+ omap_hsmmc_start_dma_transfer(host);
+ omap_hsmmc_start_command(host, host->mrq->cmd,
+ host->mrq->data);
+ return;
+ }
+
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
/* response type 2 */
@@ -892,7 +943,7 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
}
}
if ((host->data == NULL && !host->response_busy) || cmd->error)
- omap_hsmmc_request_done(host, cmd->mrq);
+ omap_hsmmc_request_done(host, host->mrq);
}
/*
@@ -1015,6 +1066,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
{
struct mmc_data *data;
int end_cmd = 0, end_trans = 0;
+ int error = 0;
data = host->data;
dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
@@ -1029,6 +1081,20 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
else if (status & (CCRC_EN | DCRC_EN))
hsmmc_command_incomplete(host, -EILSEQ, end_cmd);
+ if (status & ACE_EN) {
+ u32 ac12;
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ if (!(ac12 & ACNE) && host->mrq->sbc) {
+ end_cmd = 1;
+ if (ac12 & ACTO)
+ error = -ETIMEDOUT;
+ else if (ac12 & (ACCE | ACEB | ACIE))
+ error = -EILSEQ;
+ host->mrq->sbc->error = error;
+ hsmmc_command_incomplete(host, error, end_cmd);
+ }
+ dev_dbg(mmc_dev(host->mmc), "AC12 err: 0x%x\n", ac12);
+ }
if (host->data || host->response_busy) {
end_trans = !end_cmd;
host->response_busy = 0;
@@ -1236,8 +1302,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
}
/* Check if next job is already prepared */
- if (next ||
- (!next && data->host_cookie != host->next_data.cookie)) {
+ if (next || data->host_cookie != host->next_data.cookie) {
dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
omap_hsmmc_get_dma_dir(host, data));
@@ -1262,7 +1327,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
/*
* Routine to configure and start DMA for the MMC card
*/
-static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
+static int omap_hsmmc_setup_dma_transfer(struct omap_hsmmc_host *host,
struct mmc_request *req)
{
struct dma_slave_config cfg;
@@ -1321,8 +1386,6 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
host->dma_ch = 1;
- dma_async_issue_pending(chan);
-
return 0;
}
@@ -1338,7 +1401,7 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
if (clkd == 0)
clkd = 1;
- cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd);
+ cycle_ns = 1000000000 / (host->clk_rate / clkd);
timeout = timeout_ns / cycle_ns;
timeout += timeout_clks;
if (timeout) {
@@ -1363,6 +1426,21 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
OMAP_HSMMC_WRITE(host->base, SYSCTL, reg);
}
+static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host)
+{
+ struct mmc_request *req = host->mrq;
+ struct dma_chan *chan;
+
+ if (!req->data)
+ return;
+ OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
+ | (req->data->blocks << 16));
+ set_data_timeout(host, req->data->timeout_ns,
+ req->data->timeout_clks);
+ chan = omap_hsmmc_get_dma_chan(host, req->data);
+ dma_async_issue_pending(chan);
+}
+
/*
* Configure block length for MMC/SD cards and initiate the transfer.
*/
@@ -1383,12 +1461,8 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
return 0;
}
- OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
- | (req->data->blocks << 16));
- set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks);
-
if (host->use_dma) {
- ret = omap_hsmmc_start_dma_transfer(host, req);
+ ret = omap_hsmmc_setup_dma_transfer(host, req);
if (ret != 0) {
dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
return ret;
@@ -1462,6 +1536,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
host->reqs_blocked = 0;
WARN_ON(host->mrq != NULL);
host->mrq = req;
+ host->clk_rate = clk_get_rate(host->fclk);
err = omap_hsmmc_prepare_data(host, req);
if (err) {
req->cmd->error = err;
@@ -1471,7 +1546,12 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
mmc_request_done(mmc, req);
return;
}
+ if (req->sbc && !(host->flags & AUTO_CMD23)) {
+ omap_hsmmc_start_command(host, req->sbc, NULL);
+ return;
+ }
+ omap_hsmmc_start_dma_transfer(host);
omap_hsmmc_start_command(host, req->cmd, req->data);
}
@@ -1509,13 +1589,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* of external transceiver; but they all handle 1.8V.
*/
if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
- (ios->vdd == DUAL_VOLT_OCR_BIT) &&
- /*
- * With pbias cell programming missing, this
- * can't be allowed on MMC1 when booting with device
- * tree.
- */
- !host->pbias_disable) {
+ (ios->vdd == DUAL_VOLT_OCR_BIT)) {
/*
* The mmc_select_voltage fn of the core does
* not seem to set the power_mode to
@@ -1678,18 +1752,29 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc)
#endif
#ifdef CONFIG_OF
-static u16 omap4_reg_offset = 0x100;
+static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = {
+ /* See 35xx errata 2.1.1.128 in SPRZ278F */
+ .controller_flags = OMAP_HSMMC_BROKEN_MULTIBLOCK_READ,
+};
+
+static const struct omap_mmc_of_data omap4_mmc_of_data = {
+ .reg_offset = 0x100,
+};
static const struct of_device_id omap_mmc_of_match[] = {
{
.compatible = "ti,omap2-hsmmc",
},
{
+ .compatible = "ti,omap3-pre-es3-hsmmc",
+ .data = &omap3_pre_es3_mmc_of_data,
+ },
+ {
.compatible = "ti,omap3-hsmmc",
},
{
.compatible = "ti,omap4-hsmmc",
- .data = &omap4_reg_offset,
+ .data = &omap4_mmc_of_data,
},
{},
};
@@ -1709,7 +1794,7 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
- return NULL; /* out of memory */
+ return ERR_PTR(-ENOMEM); /* out of memory */
if (of_find_property(np, "ti,dual-volt", NULL))
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
@@ -1738,13 +1823,19 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT;
+ if (of_find_property(np, "keep-power-in-suspend", NULL))
+ pdata->slots[0].pm_caps |= MMC_PM_KEEP_POWER;
+
+ if (of_find_property(np, "enable-sdio-wakeup", NULL))
+ pdata->slots[0].pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+
return pdata;
}
#else
static inline struct omap_mmc_platform_data
*of_get_hsmmc_pdata(struct device *dev)
{
- return NULL;
+ return ERR_PTR(-EINVAL);
}
#endif
@@ -1759,6 +1850,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
dma_cap_mask_t mask;
unsigned tx_req, rx_req;
struct pinctrl *pinctrl;
+ const struct omap_mmc_of_data *data;
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
if (match) {
@@ -1768,8 +1860,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
return PTR_ERR(pdata);
if (match->data) {
- const u16 *offsetp = match->data;
- pdata->reg_offset = *offsetp;
+ data = match->data;
+ pdata->reg_offset = data->reg_offset;
+ pdata->controller_flags |= data->controller_flags;
}
}
@@ -1814,6 +1907,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->base = ioremap(host->mapbase, SZ_4K);
host->power_mode = MMC_POWER_OFF;
host->next_data.cookie = 1;
+ host->pbias_enabled = 0;
platform_set_drvdata(pdev, host);
@@ -1847,10 +1941,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_context_save(host);
- /* This can be removed once we support PBIAS with DT */
- if (host->dev->of_node && res->start == 0x4809c000)
- host->pbias_disable = 1;
-
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
/*
* MMC can still work without debounce clock.
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index c46feda07d56..5fb994f9a653 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -31,14 +31,9 @@
#include <linux/mfd/rtsx_pci.h>
#include <asm/unaligned.h>
-/* SD Tuning Data Structure
- * Record continuous timing phase path
- */
-struct timing_phase_path {
- int start;
- int end;
- int mid;
- int len;
+struct realtek_next {
+ unsigned int sg_count;
+ s32 cookie;
};
struct realtek_pci_sdmmc {
@@ -46,9 +41,18 @@ struct realtek_pci_sdmmc {
struct rtsx_pcr *pcr;
struct mmc_host *mmc;
struct mmc_request *mrq;
-
- struct mutex host_mutex;
-
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ spinlock_t lock;
+ struct timer_list timer;
+ struct tasklet_struct cmd_tasklet;
+ struct tasklet_struct data_tasklet;
+ struct tasklet_struct finish_tasklet;
+
+ u8 rsp_type;
+ u8 rsp_len;
+ int sg_count;
u8 ssc_depth;
unsigned int clock;
bool vpclk;
@@ -58,8 +62,13 @@ struct realtek_pci_sdmmc {
int power_state;
#define SDMMC_POWER_ON 1
#define SDMMC_POWER_OFF 0
+
+ struct realtek_next next_data;
};
+static int sd_start_multi_rw(struct realtek_pci_sdmmc *host,
+ struct mmc_request *mrq);
+
static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host)
{
return &(host->pdev->dev);
@@ -96,6 +105,95 @@ static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)
#define sd_print_debug_regs(host)
#endif /* DEBUG */
+static void sd_isr_done_transfer(struct platform_device *pdev)
+{
+ struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+
+ spin_lock(&host->lock);
+ if (host->cmd)
+ tasklet_schedule(&host->cmd_tasklet);
+ if (host->data)
+ tasklet_schedule(&host->data_tasklet);
+ spin_unlock(&host->lock);
+}
+
+static void sd_request_timeout(unsigned long host_addr)
+{
+ struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!host->mrq) {
+ dev_err(sdmmc_dev(host), "error: no request exist\n");
+ goto out;
+ }
+
+ if (host->cmd)
+ host->cmd->error = -ETIMEDOUT;
+ if (host->data)
+ host->data->error = -ETIMEDOUT;
+
+ dev_dbg(sdmmc_dev(host), "timeout for request\n");
+
+out:
+ tasklet_schedule(&host->finish_tasklet);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sd_finish_request(unsigned long host_addr)
+{
+ struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ unsigned long flags;
+ bool any_error;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ del_timer(&host->timer);
+ mrq = host->mrq;
+ if (!mrq) {
+ dev_err(sdmmc_dev(host), "error: no request need finish\n");
+ goto out;
+ }
+
+ cmd = mrq->cmd;
+ data = mrq->data;
+
+ any_error = (mrq->sbc && mrq->sbc->error) ||
+ (mrq->stop && mrq->stop->error) ||
+ (cmd && cmd->error) || (data && data->error);
+
+ if (any_error) {
+ rtsx_pci_stop_cmd(pcr);
+ sd_clear_error(host);
+ }
+
+ if (data) {
+ if (any_error)
+ data->bytes_xfered = 0;
+ else
+ data->bytes_xfered = data->blocks * data->blksz;
+
+ if (!data->host_cookie)
+ rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len,
+ data->flags & MMC_DATA_READ);
+
+ }
+
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+
+out:
+ spin_unlock_irqrestore(&host->lock, flags);
+ mutex_unlock(&pcr->pcr_mutex);
+ mmc_request_done(host->mmc, mrq);
+}
+
static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
u8 *buf, int buf_len, int timeout)
{
@@ -213,8 +311,7 @@ static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
return 0;
}
-static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
- struct mmc_command *cmd)
+static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd)
{
struct rtsx_pcr *pcr = host->pcr;
u8 cmd_idx = (u8)cmd->opcode;
@@ -222,11 +319,14 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
int err = 0;
int timeout = 100;
int i;
- u8 *ptr;
- int stat_idx = 0;
u8 rsp_type;
int rsp_len = 5;
- bool clock_toggled = false;
+ unsigned long flags;
+
+ if (host->cmd)
+ dev_err(sdmmc_dev(host), "error: cmd already exist\n");
+
+ host->cmd = cmd;
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
__func__, cmd_idx, arg);
@@ -261,6 +361,8 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
err = -EINVAL;
goto out;
}
+ host->rsp_type = rsp_type;
+ host->rsp_len = rsp_len;
if (rsp_type == SD_RSP_TYPE_R1b)
timeout = 3000;
@@ -270,8 +372,6 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
0xFF, SD_CLK_TOGGLE_EN);
if (err < 0)
goto out;
-
- clock_toggled = true;
}
rtsx_pci_init_cmd(pcr);
@@ -295,25 +395,60 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
/* Read data from ping-pong buffer */
for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
- stat_idx = 16;
} else if (rsp_type != SD_RSP_TYPE_R0) {
/* Read data from SD_CMDx registers */
for (i = SD_CMD0; i <= SD_CMD4; i++)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
- stat_idx = 5;
}
rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0);
- err = rtsx_pci_send_cmd(pcr, timeout);
- if (err < 0) {
- sd_print_debug_regs(host);
- sd_clear_error(host);
- dev_dbg(sdmmc_dev(host),
- "rtsx_pci_send_cmd error (err = %d)\n", err);
+ mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout));
+
+ spin_lock_irqsave(&pcr->lock, flags);
+ pcr->trans_result = TRANS_NOT_READY;
+ rtsx_pci_send_cmd_no_wait(pcr);
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+ return;
+
+out:
+ cmd->error = err;
+ tasklet_schedule(&host->finish_tasklet);
+}
+
+static void sd_get_rsp(unsigned long host_addr)
+{
+ struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_command *cmd;
+ int i, err = 0, stat_idx;
+ u8 *ptr, rsp_type;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ cmd = host->cmd;
+ host->cmd = NULL;
+
+ if (!cmd) {
+ dev_err(sdmmc_dev(host), "error: cmd not exist\n");
goto out;
}
+ spin_lock(&pcr->lock);
+ if (pcr->trans_result == TRANS_NO_DEVICE)
+ err = -ENODEV;
+ else if (pcr->trans_result != TRANS_RESULT_OK)
+ err = -EINVAL;
+ spin_unlock(&pcr->lock);
+
+ if (err < 0)
+ goto out;
+
+ rsp_type = host->rsp_type;
+ stat_idx = host->rsp_len;
+
if (rsp_type == SD_RSP_TYPE_R0) {
err = 0;
goto out;
@@ -350,26 +485,106 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
cmd->resp[0]);
}
+ if (cmd == host->mrq->sbc) {
+ sd_send_cmd(host, host->mrq->cmd);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+ if (cmd == host->mrq->stop)
+ goto out;
+
+ if (cmd->data) {
+ sd_start_multi_rw(host, host->mrq);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
out:
cmd->error = err;
- if (err && clock_toggled)
- rtsx_pci_write_register(pcr, SD_BUS_STAT,
- SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+ tasklet_schedule(&host->finish_tasklet);
+ spin_unlock_irqrestore(&host->lock, flags);
}
-static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
+static int sd_pre_dma_transfer(struct realtek_pci_sdmmc *host,
+ struct mmc_data *data, struct realtek_next *next)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int read = data->flags & MMC_DATA_READ;
+ int sg_count = 0;
+
+ if (!next && data->host_cookie &&
+ data->host_cookie != host->next_data.cookie) {
+ dev_err(sdmmc_dev(host),
+ "error: invalid cookie data[%d] host[%d]\n",
+ data->host_cookie, host->next_data.cookie);
+ data->host_cookie = 0;
+ }
+
+ if (next || (!next && data->host_cookie != host->next_data.cookie))
+ sg_count = rtsx_pci_dma_map_sg(pcr,
+ data->sg, data->sg_len, read);
+ else
+ sg_count = host->next_data.sg_count;
+
+ if (next) {
+ next->sg_count = sg_count;
+ if (++next->cookie < 0)
+ next->cookie = 1;
+ data->host_cookie = next->cookie;
+ }
+
+ return sg_count;
+}
+
+static void sdmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ bool is_first_req)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (data->host_cookie) {
+ dev_err(sdmmc_dev(host),
+ "error: descard already cookie data[%d]\n",
+ data->host_cookie);
+ data->host_cookie = 0;
+ }
+
+ dev_dbg(sdmmc_dev(host), "dma sg prepared: %d\n",
+ sd_pre_dma_transfer(host, data, &host->next_data));
+}
+
+static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ int err)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_data *data = mrq->data;
+ int read = data->flags & MMC_DATA_READ;
+
+ rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len, read);
+ data->host_cookie = 0;
+}
+
+static int sd_start_multi_rw(struct realtek_pci_sdmmc *host,
+ struct mmc_request *mrq)
{
struct rtsx_pcr *pcr = host->pcr;
struct mmc_host *mmc = host->mmc;
struct mmc_card *card = mmc->card;
struct mmc_data *data = mrq->data;
int uhs = mmc_card_uhs(card);
- int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+ int read = data->flags & MMC_DATA_READ;
u8 cfg2, trans_mode;
int err;
size_t data_len = data->blksz * data->blocks;
+ if (host->data)
+ dev_err(sdmmc_dev(host), "error: data already exist\n");
+
+ host->data = data;
+
if (read) {
cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0;
@@ -420,17 +635,56 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
SD_TRANSFER_END, SD_TRANSFER_END);
+ mod_timer(&host->timer, jiffies + 10 * HZ);
rtsx_pci_send_cmd_no_wait(pcr);
- err = rtsx_pci_transfer_data(pcr, data->sg, data->sg_len, read, 10000);
+ err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, read);
if (err < 0) {
- sd_clear_error(host);
- return err;
+ data->error = err;
+ tasklet_schedule(&host->finish_tasklet);
}
-
return 0;
}
+static void sd_finish_multi_rw(unsigned long host_addr)
+{
+ struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_data *data;
+ int err = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!host->data) {
+ dev_err(sdmmc_dev(host), "error: no data exist\n");
+ goto out;
+ }
+
+ data = host->data;
+ host->data = NULL;
+
+ if (pcr->trans_result == TRANS_NO_DEVICE)
+ err = -ENODEV;
+ else if (pcr->trans_result != TRANS_RESULT_OK)
+ err = -EINVAL;
+
+ if (err < 0) {
+ data->error = err;
+ goto out;
+ }
+
+ if (!host->mrq->sbc && data->stop) {
+ sd_send_cmd(host, data->stop);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+out:
+ tasklet_schedule(&host->finish_tasklet);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host)
{
rtsx_pci_write_register(host->pcr, SD_CFG1,
@@ -511,85 +765,47 @@ static int sd_change_phase(struct realtek_pci_sdmmc *host,
return 0;
}
-static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map)
+static inline u32 test_phase_bit(u32 phase_map, unsigned int bit)
{
- struct timing_phase_path path[MAX_PHASE + 1];
- int i, j, cont_path_cnt;
- int new_block, max_len, final_path_idx;
- u8 final_phase = 0xFF;
+ bit %= RTSX_PHASE_MAX;
+ return phase_map & (1 << bit);
+}
- /* Parse phase_map, take it as a bit-ring */
- cont_path_cnt = 0;
- new_block = 1;
- j = 0;
- for (i = 0; i < MAX_PHASE + 1; i++) {
- if (phase_map & (1 << i)) {
- if (new_block) {
- new_block = 0;
- j = cont_path_cnt++;
- path[j].start = i;
- path[j].end = i;
- } else {
- path[j].end = i;
- }
- } else {
- new_block = 1;
- if (cont_path_cnt) {
- /* Calculate path length and middle point */
- int idx = cont_path_cnt - 1;
- path[idx].len =
- path[idx].end - path[idx].start + 1;
- path[idx].mid =
- path[idx].start + path[idx].len / 2;
- }
- }
- }
+static int sd_get_phase_len(u32 phase_map, unsigned int start_bit)
+{
+ int i;
- if (cont_path_cnt == 0) {
- dev_dbg(sdmmc_dev(host), "No continuous phase path\n");
- goto finish;
- } else {
- /* Calculate last continuous path length and middle point */
- int idx = cont_path_cnt - 1;
- path[idx].len = path[idx].end - path[idx].start + 1;
- path[idx].mid = path[idx].start + path[idx].len / 2;
+ for (i = 0; i < RTSX_PHASE_MAX; i++) {
+ if (test_phase_bit(phase_map, start_bit + i) == 0)
+ return i;
}
+ return RTSX_PHASE_MAX;
+}
+
+static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map)
+{
+ int start = 0, len = 0;
+ int start_final = 0, len_final = 0;
+ u8 final_phase = 0xFF;
- /* Connect the first and last continuous paths if they are adjacent */
- if (!path[0].start && (path[cont_path_cnt - 1].end == MAX_PHASE)) {
- /* Using negative index */
- path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1;
- path[0].len += path[cont_path_cnt - 1].len;
- path[0].mid = path[0].start + path[0].len / 2;
- /* Convert negative middle point index to positive one */
- if (path[0].mid < 0)
- path[0].mid += MAX_PHASE + 1;
- cont_path_cnt--;
+ if (phase_map == 0) {
+ dev_err(sdmmc_dev(host), "phase error: [map:%x]\n", phase_map);
+ return final_phase;
}
- /* Choose the longest continuous phase path */
- max_len = 0;
- final_phase = 0;
- final_path_idx = 0;
- for (i = 0; i < cont_path_cnt; i++) {
- if (path[i].len > max_len) {
- max_len = path[i].len;
- final_phase = (u8)path[i].mid;
- final_path_idx = i;
+ while (start < RTSX_PHASE_MAX) {
+ len = sd_get_phase_len(phase_map, start);
+ if (len_final < len) {
+ start_final = start;
+ len_final = len;
}
-
- dev_dbg(sdmmc_dev(host), "path[%d].start = %d\n",
- i, path[i].start);
- dev_dbg(sdmmc_dev(host), "path[%d].end = %d\n",
- i, path[i].end);
- dev_dbg(sdmmc_dev(host), "path[%d].len = %d\n",
- i, path[i].len);
- dev_dbg(sdmmc_dev(host), "path[%d].mid = %d\n",
- i, path[i].mid);
+ start += len ? len : 1;
}
-finish:
- dev_dbg(sdmmc_dev(host), "Final chosen phase: %d\n", final_phase);
+ final_phase = (start_final + len_final / 2) % RTSX_PHASE_MAX;
+ dev_dbg(sdmmc_dev(host), "phase: [map:%x] [maxlen:%d] [final:%d]\n",
+ phase_map, len_final, final_phase);
+
return final_phase;
}
@@ -635,7 +851,7 @@ static int sd_tuning_phase(struct realtek_pci_sdmmc *host,
int err, i;
u32 raw_phase_map = 0;
- for (i = MAX_PHASE; i >= 0; i--) {
+ for (i = 0; i < RTSX_PHASE_MAX; i++) {
err = sd_tuning_rx_cmd(host, opcode, (u8)i);
if (err == 0)
raw_phase_map |= 1 << i;
@@ -685,6 +901,13 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode)
return 0;
}
+static inline bool sd_use_muti_rw(struct mmc_command *cmd)
+{
+ return mmc_op_multi(cmd->opcode) ||
+ (cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
+ (cmd->opcode == MMC_WRITE_BLOCK);
+}
+
static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct realtek_pci_sdmmc *host = mmc_priv(mmc);
@@ -693,6 +916,14 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct mmc_data *data = mrq->data;
unsigned int data_size = 0;
int err;
+ unsigned long flags;
+
+ mutex_lock(&pcr->pcr_mutex);
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->mrq)
+ dev_err(sdmmc_dev(host), "error: request already exist\n");
+ host->mrq = mrq;
if (host->eject) {
cmd->error = -ENOMEDIUM;
@@ -705,8 +936,6 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
goto finish;
}
- mutex_lock(&pcr->pcr_mutex);
-
rtsx_pci_start_run(pcr);
rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth,
@@ -715,46 +944,28 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
rtsx_pci_write_register(pcr, CARD_SHARE_MODE,
CARD_SHARE_MASK, CARD_SHARE_48_SD);
- mutex_lock(&host->host_mutex);
- host->mrq = mrq;
- mutex_unlock(&host->host_mutex);
-
if (mrq->data)
data_size = data->blocks * data->blksz;
- if (!data_size || mmc_op_multi(cmd->opcode) ||
- (cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
- (cmd->opcode == MMC_WRITE_BLOCK)) {
- sd_send_cmd_get_rsp(host, cmd);
-
- if (!cmd->error && data_size) {
- sd_rw_multi(host, mrq);
+ if (sd_use_muti_rw(cmd))
+ host->sg_count = sd_pre_dma_transfer(host, data, NULL);
- if (mmc_op_multi(cmd->opcode) && mrq->stop)
- sd_send_cmd_get_rsp(host, mrq->stop);
- }
+ if (!data_size || sd_use_muti_rw(cmd)) {
+ if (mrq->sbc)
+ sd_send_cmd(host, mrq->sbc);
+ else
+ sd_send_cmd(host, cmd);
+ spin_unlock_irqrestore(&host->lock, flags);
} else {
+ spin_unlock_irqrestore(&host->lock, flags);
sd_normal_rw(host, mrq);
+ tasklet_schedule(&host->finish_tasklet);
}
-
- if (mrq->data) {
- if (cmd->error || data->error)
- data->bytes_xfered = 0;
- else
- data->bytes_xfered = data->blocks * data->blksz;
- }
-
- mutex_unlock(&pcr->pcr_mutex);
+ return;
finish:
- if (cmd->error)
- dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error);
-
- mutex_lock(&host->host_mutex);
- host->mrq = NULL;
- mutex_unlock(&host->host_mutex);
-
- mmc_request_done(mmc, mrq);
+ tasklet_schedule(&host->finish_tasklet);
+ spin_unlock_irqrestore(&host->lock, flags);
}
static int sd_set_bus_width(struct realtek_pci_sdmmc *host,
@@ -1189,6 +1400,8 @@ out:
}
static const struct mmc_host_ops realtek_pci_sdmmc_ops = {
+ .pre_req = sdmmc_pre_req,
+ .post_req = sdmmc_post_req,
.request = sdmmc_request,
.set_ios = sdmmc_set_ios,
.get_ro = sdmmc_get_ro,
@@ -1252,6 +1465,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
struct realtek_pci_sdmmc *host;
struct rtsx_pcr *pcr;
struct pcr_handle *handle = pdev->dev.platform_data;
+ unsigned long host_addr;
if (!handle)
return -ENXIO;
@@ -1275,8 +1489,15 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
pcr->slots[RTSX_SD_CARD].p_dev = pdev;
pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event;
- mutex_init(&host->host_mutex);
+ host_addr = (unsigned long)host;
+ host->next_data.cookie = 1;
+ setup_timer(&host->timer, sd_request_timeout, host_addr);
+ tasklet_init(&host->cmd_tasklet, sd_get_rsp, host_addr);
+ tasklet_init(&host->data_tasklet, sd_finish_multi_rw, host_addr);
+ tasklet_init(&host->finish_tasklet, sd_finish_request, host_addr);
+ spin_lock_init(&host->lock);
+ pcr->slots[RTSX_SD_CARD].done_transfer = sd_isr_done_transfer;
realtek_init_host(host);
mmc_add_host(mmc);
@@ -1289,6 +1510,8 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
struct rtsx_pcr *pcr;
struct mmc_host *mmc;
+ struct mmc_request *mrq;
+ unsigned long flags;
if (!host)
return 0;
@@ -1296,25 +1519,37 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
pcr = host->pcr;
pcr->slots[RTSX_SD_CARD].p_dev = NULL;
pcr->slots[RTSX_SD_CARD].card_event = NULL;
+ pcr->slots[RTSX_SD_CARD].done_transfer = NULL;
mmc = host->mmc;
- host->eject = true;
+ mrq = host->mrq;
- mutex_lock(&host->host_mutex);
+ spin_lock_irqsave(&host->lock, flags);
if (host->mrq) {
dev_dbg(&(pdev->dev),
"%s: Controller removed during transfer\n",
mmc_hostname(mmc));
- rtsx_pci_complete_unfinished_transfer(pcr);
+ if (mrq->sbc)
+ mrq->sbc->error = -ENOMEDIUM;
+ if (mrq->cmd)
+ mrq->cmd->error = -ENOMEDIUM;
+ if (mrq->stop)
+ mrq->stop->error = -ENOMEDIUM;
+ if (mrq->data)
+ mrq->data->error = -ENOMEDIUM;
- host->mrq->cmd->error = -ENOMEDIUM;
- if (host->mrq->stop)
- host->mrq->stop->error = -ENOMEDIUM;
- mmc_request_done(mmc, host->mrq);
+ tasklet_schedule(&host->finish_tasklet);
}
- mutex_unlock(&host->host_mutex);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ del_timer_sync(&host->timer);
+ tasklet_kill(&host->cmd_tasklet);
+ tasklet_kill(&host->data_tasklet);
+ tasklet_kill(&host->finish_tasklet);
mmc_remove_host(mmc);
+ host->eject = true;
+
mmc_free_host(mmc);
dev_dbg(&(pdev->dev),
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index 9ce17f6e4014..ebb3f392b589 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -31,7 +31,6 @@
#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/err.h>
-#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/acpi.h>
#include <linux/pm.h>
@@ -40,13 +39,15 @@
#include <linux/mmc/host.h>
#include <linux/mmc/pm.h>
+#include <linux/mmc/slot-gpio.h>
#include <linux/mmc/sdhci.h>
#include "sdhci.h"
enum {
- SDHCI_ACPI_SD_CD = BIT(0),
- SDHCI_ACPI_RUNTIME_PM = BIT(1),
+ SDHCI_ACPI_SD_CD = BIT(0),
+ SDHCI_ACPI_RUNTIME_PM = BIT(1),
+ SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL = BIT(2),
};
struct sdhci_acpi_chip {
@@ -121,6 +122,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
};
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
+ .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
.caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
.flags = SDHCI_ACPI_RUNTIME_PM,
@@ -128,7 +130,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
};
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
- .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_RUNTIME_PM,
+ .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
+ SDHCI_ACPI_RUNTIME_PM,
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
};
@@ -141,6 +144,7 @@ struct sdhci_acpi_uid_slot {
static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
{ "80860F14" , "1" , &sdhci_acpi_slot_int_emmc },
{ "80860F14" , "3" , &sdhci_acpi_slot_int_sd },
+ { "80860F16" , NULL, &sdhci_acpi_slot_int_sd },
{ "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio },
{ "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio },
{ "INT3436" , NULL, &sdhci_acpi_slot_int_sdio },
@@ -150,6 +154,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
static const struct acpi_device_id sdhci_acpi_ids[] = {
{ "80860F14" },
+ { "80860F16" },
{ "INT33BB" },
{ "INT33C6" },
{ "INT3436" },
@@ -192,59 +197,6 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle,
return slot;
}
-#ifdef CONFIG_PM_RUNTIME
-
-static irqreturn_t sdhci_acpi_sd_cd(int irq, void *dev_id)
-{
- mmc_detect_change(dev_id, msecs_to_jiffies(200));
- return IRQ_HANDLED;
-}
-
-static int sdhci_acpi_add_own_cd(struct device *dev, struct mmc_host *mmc)
-{
- struct gpio_desc *desc;
- unsigned long flags;
- int err, irq;
-
- desc = devm_gpiod_get_index(dev, "sd_cd", 0);
- if (IS_ERR(desc)) {
- err = PTR_ERR(desc);
- goto out;
- }
-
- err = gpiod_direction_input(desc);
- if (err)
- goto out_free;
-
- irq = gpiod_to_irq(desc);
- if (irq < 0) {
- err = irq;
- goto out_free;
- }
-
- flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
- err = devm_request_irq(dev, irq, sdhci_acpi_sd_cd, flags, "sd_cd", mmc);
- if (err)
- goto out_free;
-
- return 0;
-
-out_free:
- devm_gpiod_put(dev, desc);
-out:
- dev_warn(dev, "failed to setup card detect wake up\n");
- return err;
-}
-
-#else
-
-static int sdhci_acpi_add_own_cd(struct device *dev, struct mmc_host *mmc)
-{
- return 0;
-}
-
-#endif
-
static int sdhci_acpi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -332,15 +284,19 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
- err = sdhci_add_host(host);
- if (err)
- goto err_free;
-
if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
- if (sdhci_acpi_add_own_cd(dev, host->mmc))
+ bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL);
+
+ if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0)) {
+ dev_warn(dev, "failed to setup card detect gpio\n");
c->use_runtime_pm = false;
+ }
}
+ err = sdhci_add_host(host);
+ if (err)
+ goto err_free;
+
if (c->use_runtime_pm) {
pm_runtime_set_active(dev);
pm_suspend_ignore_children(dev, 1);
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
index 7a190fe4dff1..6f166e63b817 100644
--- a/drivers/mmc/host/sdhci-bcm-kona.c
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -54,6 +54,7 @@
struct sdhci_bcm_kona_dev {
struct mutex write_lock; /* protect back to back writes */
+ struct clk *external_clk;
};
@@ -257,6 +258,24 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
goto err_pltfm_free;
}
+ /* Get and enable the external clock */
+ kona_dev->external_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(kona_dev->external_clk)) {
+ dev_err(dev, "Failed to get external clock\n");
+ ret = PTR_ERR(kona_dev->external_clk);
+ goto err_pltfm_free;
+ }
+
+ if (clk_set_rate(kona_dev->external_clk, host->mmc->f_max) != 0) {
+ dev_err(dev, "Failed to set rate external clock\n");
+ goto err_pltfm_free;
+ }
+
+ if (clk_prepare_enable(kona_dev->external_clk) != 0) {
+ dev_err(dev, "Failed to enable external clock\n");
+ goto err_pltfm_free;
+ }
+
dev_dbg(dev, "non-removable=%c\n",
(host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N');
dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n",
@@ -271,7 +290,7 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
ret = sdhci_bcm_kona_sd_reset(host);
if (ret)
- goto err_pltfm_free;
+ goto err_clk_disable;
sdhci_bcm_kona_sd_init(host);
@@ -307,6 +326,9 @@ err_remove_host:
err_reset:
sdhci_bcm_kona_sd_reset(host);
+err_clk_disable:
+ clk_disable_unprepare(kona_dev->external_clk);
+
err_pltfm_free:
sdhci_pltfm_free(pdev);
@@ -314,9 +336,20 @@ err_pltfm_free:
return ret;
}
-static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev)
+static int sdhci_bcm_kona_remove(struct platform_device *pdev)
{
- return sdhci_pltfm_unregister(pdev);
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
+ struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv);
+ int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
+
+ sdhci_remove_host(host, dead);
+
+ clk_disable_unprepare(kona_dev->external_clk);
+
+ sdhci_pltfm_free(pdev);
+
+ return 0;
}
static struct platform_driver sdhci_bcm_kona_driver = {
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c
index 8424839660f8..736d7a2eb7ec 100644
--- a/drivers/mmc/host/sdhci-dove.c
+++ b/drivers/mmc/host/sdhci-dove.c
@@ -208,7 +208,7 @@ static struct platform_driver sdhci_dove_driver = {
.name = "sdhci-dove",
.owner = THIS_MODULE,
.pm = SDHCI_PLTFM_PMOPS,
- .of_match_table = of_match_ptr(sdhci_dove_of_match_table),
+ .of_match_table = sdhci_dove_of_match_table,
},
.probe = sdhci_dove_probe,
.remove = sdhci_dove_remove,
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
new file mode 100644
index 000000000000..acb0e9eb55f1
--- /dev/null
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -0,0 +1,618 @@
+/*
+ * drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver
+ *
+ * Copyright (c) 2013-2014, 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/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/mmc/mmc.h>
+#include <linux/slab.h>
+
+#include "sdhci-pltfm.h"
+
+#define CORE_HC_MODE 0x78
+#define HC_MODE_EN 0x1
+#define CORE_POWER 0x0
+#define CORE_SW_RST BIT(7)
+
+#define MAX_PHASES 16
+#define CORE_DLL_LOCK BIT(7)
+#define CORE_DLL_EN BIT(16)
+#define CORE_CDR_EN BIT(17)
+#define CORE_CK_OUT_EN BIT(18)
+#define CORE_CDR_EXT_EN BIT(19)
+#define CORE_DLL_PDN BIT(29)
+#define CORE_DLL_RST BIT(30)
+#define CORE_DLL_CONFIG 0x100
+#define CORE_DLL_STATUS 0x108
+
+#define CORE_VENDOR_SPEC 0x10c
+#define CORE_CLK_PWRSAVE BIT(1)
+
+#define CDR_SELEXT_SHIFT 20
+#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
+#define CMUX_SHIFT_PHASE_SHIFT 24
+#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT)
+
+static const u32 tuning_block_64[] = {
+ 0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe,
+ 0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777,
+ 0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff,
+ 0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7
+};
+
+static const u32 tuning_block_128[] = {
+ 0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc,
+ 0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff,
+ 0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff,
+ 0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb,
+ 0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc,
+ 0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff,
+ 0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb,
+ 0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77
+};
+
+struct sdhci_msm_host {
+ struct platform_device *pdev;
+ void __iomem *core_mem; /* MSM SDCC mapped address */
+ struct clk *clk; /* main SD/MMC bus clock */
+ struct clk *pclk; /* SDHC peripheral bus clock */
+ struct clk *bus_clk; /* SDHC bus voter clock */
+ struct mmc_host *mmc;
+ struct sdhci_pltfm_data sdhci_msm_pdata;
+};
+
+/* Platform specific tuning */
+static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll)
+{
+ u32 wait_cnt = 50;
+ u8 ck_out_en;
+ struct mmc_host *mmc = host->mmc;
+
+ /* Poll for CK_OUT_EN bit. max. poll time = 50us */
+ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
+ CORE_CK_OUT_EN);
+
+ while (ck_out_en != poll) {
+ if (--wait_cnt == 0) {
+ dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n",
+ mmc_hostname(mmc), poll);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+
+ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
+ CORE_CK_OUT_EN);
+ }
+
+ return 0;
+}
+
+static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
+{
+ int rc;
+ static const u8 grey_coded_phase_table[] = {
+ 0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4,
+ 0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8
+ };
+ unsigned long flags;
+ u32 config;
+ struct mmc_host *mmc = host->mmc;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN);
+ config |= (CORE_CDR_EXT_EN | CORE_DLL_EN);
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */
+ rc = msm_dll_poll_ck_out_en(host, 0);
+ if (rc)
+ goto err_out;
+
+ /*
+ * Write the selected DLL clock output phase (0 ... 15)
+ * to CDR_SELEXT bit field of DLL_CONFIG register.
+ */
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config &= ~CDR_SELEXT_MASK;
+ config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT;
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
+ rc = msm_dll_poll_ck_out_en(host, 1);
+ if (rc)
+ goto err_out;
+
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config |= CORE_CDR_EN;
+ config &= ~CORE_CDR_EXT_EN;
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+ goto out;
+
+err_out:
+ dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n",
+ mmc_hostname(mmc), phase);
+out:
+ spin_unlock_irqrestore(&host->lock, flags);
+ return rc;
+}
+
+/*
+ * Find out the greatest range of consecuitive selected
+ * DLL clock output phases that can be used as sampling
+ * setting for SD3.0 UHS-I card read operation (in SDR104
+ * timing mode) or for eMMC4.5 card read operation (in HS200
+ * timing mode).
+ * Select the 3/4 of the range and configure the DLL with the
+ * selected DLL clock output phase.
+ */
+
+static int msm_find_most_appropriate_phase(struct sdhci_host *host,
+ u8 *phase_table, u8 total_phases)
+{
+ int ret;
+ u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} };
+ u8 phases_per_row[MAX_PHASES] = { 0 };
+ int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0;
+ int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0;
+ bool phase_0_found = false, phase_15_found = false;
+ struct mmc_host *mmc = host->mmc;
+
+ if (!total_phases || (total_phases > MAX_PHASES)) {
+ dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n",
+ mmc_hostname(mmc), total_phases);
+ return -EINVAL;
+ }
+
+ for (cnt = 0; cnt < total_phases; cnt++) {
+ ranges[row_index][col_index] = phase_table[cnt];
+ phases_per_row[row_index] += 1;
+ col_index++;
+
+ if ((cnt + 1) == total_phases) {
+ continue;
+ /* check if next phase in phase_table is consecutive or not */
+ } else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) {
+ row_index++;
+ col_index = 0;
+ }
+ }
+
+ if (row_index >= MAX_PHASES)
+ return -EINVAL;
+
+ /* Check if phase-0 is present in first valid window? */
+ if (!ranges[0][0]) {
+ phase_0_found = true;
+ phase_0_raw_index = 0;
+ /* Check if cycle exist between 2 valid windows */
+ for (cnt = 1; cnt <= row_index; cnt++) {
+ if (phases_per_row[cnt]) {
+ for (i = 0; i < phases_per_row[cnt]; i++) {
+ if (ranges[cnt][i] == 15) {
+ phase_15_found = true;
+ phase_15_raw_index = cnt;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* If 2 valid windows form cycle then merge them as single window */
+ if (phase_0_found && phase_15_found) {
+ /* number of phases in raw where phase 0 is present */
+ u8 phases_0 = phases_per_row[phase_0_raw_index];
+ /* number of phases in raw where phase 15 is present */
+ u8 phases_15 = phases_per_row[phase_15_raw_index];
+
+ if (phases_0 + phases_15 >= MAX_PHASES)
+ /*
+ * If there are more than 1 phase windows then total
+ * number of phases in both the windows should not be
+ * more than or equal to MAX_PHASES.
+ */
+ return -EINVAL;
+
+ /* Merge 2 cyclic windows */
+ i = phases_15;
+ for (cnt = 0; cnt < phases_0; cnt++) {
+ ranges[phase_15_raw_index][i] =
+ ranges[phase_0_raw_index][cnt];
+ if (++i >= MAX_PHASES)
+ break;
+ }
+
+ phases_per_row[phase_0_raw_index] = 0;
+ phases_per_row[phase_15_raw_index] = phases_15 + phases_0;
+ }
+
+ for (cnt = 0; cnt <= row_index; cnt++) {
+ if (phases_per_row[cnt] > curr_max) {
+ curr_max = phases_per_row[cnt];
+ selected_row_index = cnt;
+ }
+ }
+
+ i = (curr_max * 3) / 4;
+ if (i)
+ i--;
+
+ ret = ranges[selected_row_index][i];
+
+ if (ret >= MAX_PHASES) {
+ ret = -EINVAL;
+ dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n",
+ mmc_hostname(mmc), ret);
+ }
+
+ return ret;
+}
+
+static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
+{
+ u32 mclk_freq = 0, config;
+
+ /* Program the MCLK value to MCLK_FREQ bit field */
+ if (host->clock <= 112000000)
+ mclk_freq = 0;
+ else if (host->clock <= 125000000)
+ mclk_freq = 1;
+ else if (host->clock <= 137000000)
+ mclk_freq = 2;
+ else if (host->clock <= 150000000)
+ mclk_freq = 3;
+ else if (host->clock <= 162000000)
+ mclk_freq = 4;
+ else if (host->clock <= 175000000)
+ mclk_freq = 5;
+ else if (host->clock <= 187000000)
+ mclk_freq = 6;
+ else if (host->clock <= 200000000)
+ mclk_freq = 7;
+
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config &= ~CMUX_SHIFT_PHASE_MASK;
+ config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT;
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+}
+
+/* Initialize the DLL (Programmable Delay Line) */
+static int msm_init_cm_dll(struct sdhci_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ int wait_cnt = 50;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /*
+ * Make sure that clock is always enabled when DLL
+ * tuning is in progress. Keeping PWRSAVE ON may
+ * turn off the clock.
+ */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ & ~CORE_CLK_PWRSAVE), host->ioaddr + CORE_VENDOR_SPEC);
+
+ /* Write 1 to DLL_RST bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Write 1 to DLL_PDN bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
+ msm_cm_dll_set_freq(host);
+
+ /* Write 0 to DLL_RST bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ & ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Write 0 to DLL_PDN bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ & ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Set DLL_EN bit to 1. */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Set CK_OUT_EN bit to 1. */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
+ while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
+ CORE_DLL_LOCK)) {
+ /* max. wait for 50us sec for LOCK bit to be set */
+ if (--wait_cnt == 0) {
+ dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n",
+ mmc_hostname(mmc));
+ spin_unlock_irqrestore(&host->lock, flags);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ return 0;
+}
+
+static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ int tuning_seq_cnt = 3;
+ u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
+ const u32 *tuning_block_pattern = tuning_block_64;
+ int size = sizeof(tuning_block_64); /* Pattern size in bytes */
+ int rc;
+ struct mmc_host *mmc = host->mmc;
+ struct mmc_ios ios = host->mmc->ios;
+
+ /*
+ * Tuning is required for SDR104, HS200 and HS400 cards and
+ * if clock frequency is greater than 100MHz in these modes.
+ */
+ if (host->clock <= 100 * 1000 * 1000 ||
+ !((ios.timing == MMC_TIMING_MMC_HS200) ||
+ (ios.timing == MMC_TIMING_UHS_SDR104)))
+ return 0;
+
+ if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
+ (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
+ tuning_block_pattern = tuning_block_128;
+ size = sizeof(tuning_block_128);
+ }
+
+ data_buf = kmalloc(size, GFP_KERNEL);
+ if (!data_buf)
+ return -ENOMEM;
+
+retry:
+ /* First of all reset the tuning block */
+ rc = msm_init_cm_dll(host);
+ if (rc)
+ goto out;
+
+ phase = 0;
+ do {
+ struct mmc_command cmd = { 0 };
+ struct mmc_data data = { 0 };
+ struct mmc_request mrq = {
+ .cmd = &cmd,
+ .data = &data
+ };
+ struct scatterlist sg;
+
+ /* Set the phase in delay line hw block */
+ rc = msm_config_cm_dll_phase(host, phase);
+ if (rc)
+ goto out;
+
+ cmd.opcode = opcode;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = size;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.timeout_ns = NSEC_PER_SEC; /* 1 second */
+
+ data.sg = &sg;
+ data.sg_len = 1;
+ sg_init_one(&sg, data_buf, size);
+ memset(data_buf, 0, size);
+ mmc_wait_for_req(mmc, &mrq);
+
+ if (!cmd.error && !data.error &&
+ !memcmp(data_buf, tuning_block_pattern, size)) {
+ /* Tuning is successful at this tuning point */
+ tuned_phases[tuned_phase_cnt++] = phase;
+ dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
+ mmc_hostname(mmc), phase);
+ }
+ } while (++phase < ARRAY_SIZE(tuned_phases));
+
+ if (tuned_phase_cnt) {
+ rc = msm_find_most_appropriate_phase(host, tuned_phases,
+ tuned_phase_cnt);
+ if (rc < 0)
+ goto out;
+ else
+ phase = rc;
+
+ /*
+ * Finally set the selected phase in delay
+ * line hw block.
+ */
+ rc = msm_config_cm_dll_phase(host, phase);
+ if (rc)
+ goto out;
+ dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
+ mmc_hostname(mmc), phase);
+ } else {
+ if (--tuning_seq_cnt)
+ goto retry;
+ /* Tuning failed */
+ dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
+ mmc_hostname(mmc));
+ rc = -EIO;
+ }
+
+out:
+ kfree(data_buf);
+ return rc;
+}
+
+static const struct of_device_id sdhci_msm_dt_match[] = {
+ { .compatible = "qcom,sdhci-msm-v4" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
+
+static struct sdhci_ops sdhci_msm_ops = {
+ .platform_execute_tuning = sdhci_msm_execute_tuning,
+};
+
+static int sdhci_msm_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_msm_host *msm_host;
+ struct resource *core_memres;
+ int ret;
+ u16 host_version;
+
+ msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL);
+ if (!msm_host)
+ return -ENOMEM;
+
+ msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
+ host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+ pltfm_host->priv = msm_host;
+ msm_host->mmc = host->mmc;
+ msm_host->pdev = pdev;
+
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto pltfm_free;
+
+ sdhci_get_of_property(pdev);
+
+ /* Setup SDCC bus voter clock. */
+ msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (!IS_ERR(msm_host->bus_clk)) {
+ /* Vote for max. clk rate for max. performance */
+ ret = clk_set_rate(msm_host->bus_clk, INT_MAX);
+ if (ret)
+ goto pltfm_free;
+ ret = clk_prepare_enable(msm_host->bus_clk);
+ if (ret)
+ goto pltfm_free;
+ }
+
+ /* Setup main peripheral bus clock */
+ msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(msm_host->pclk)) {
+ ret = PTR_ERR(msm_host->pclk);
+ dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret);
+ goto bus_clk_disable;
+ }
+
+ ret = clk_prepare_enable(msm_host->pclk);
+ if (ret)
+ goto bus_clk_disable;
+
+ /* Setup SDC MMC clock */
+ msm_host->clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(msm_host->clk)) {
+ ret = PTR_ERR(msm_host->clk);
+ dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret);
+ goto pclk_disable;
+ }
+
+ ret = clk_prepare_enable(msm_host->clk);
+ if (ret)
+ goto pclk_disable;
+
+ core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres);
+
+ if (IS_ERR(msm_host->core_mem)) {
+ dev_err(&pdev->dev, "Failed to remap registers\n");
+ ret = PTR_ERR(msm_host->core_mem);
+ goto clk_disable;
+ }
+
+ /* Reset the core and Enable SDHC mode */
+ writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
+ CORE_SW_RST, msm_host->core_mem + CORE_POWER);
+
+ /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
+ usleep_range(1000, 5000);
+ if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) {
+ dev_err(&pdev->dev, "Stuck in reset\n");
+ ret = -ETIMEDOUT;
+ goto clk_disable;
+ }
+
+ /* Set HC_MODE_EN bit in HC_MODE register */
+ writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
+
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
+
+ host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
+ dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
+ host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
+ SDHCI_VENDOR_VER_SHIFT));
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto clk_disable;
+
+ return 0;
+
+clk_disable:
+ clk_disable_unprepare(msm_host->clk);
+pclk_disable:
+ clk_disable_unprepare(msm_host->pclk);
+bus_clk_disable:
+ if (!IS_ERR(msm_host->bus_clk))
+ clk_disable_unprepare(msm_host->bus_clk);
+pltfm_free:
+ sdhci_pltfm_free(pdev);
+ return ret;
+}
+
+static int sdhci_msm_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) ==
+ 0xffffffff);
+
+ sdhci_remove_host(host, dead);
+ sdhci_pltfm_free(pdev);
+ clk_disable_unprepare(msm_host->clk);
+ clk_disable_unprepare(msm_host->pclk);
+ if (!IS_ERR(msm_host->bus_clk))
+ clk_disable_unprepare(msm_host->bus_clk);
+ return 0;
+}
+
+static struct platform_driver sdhci_msm_driver = {
+ .probe = sdhci_msm_probe,
+ .remove = sdhci_msm_remove,
+ .driver = {
+ .name = "sdhci_msm",
+ .owner = THIS_MODULE,
+ .of_match_table = sdhci_msm_dt_match,
+ },
+};
+
+module_platform_driver(sdhci_msm_driver);
+
+MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 0955777b6c7e..fdc612120362 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -610,6 +610,18 @@ static const struct sdhci_pci_fixes sdhci_via = {
.probe = via_probe,
};
+static int rtsx_probe_slot(struct sdhci_pci_slot *slot)
+{
+ slot->host->mmc->caps2 |= MMC_CAP2_HS200;
+ return 0;
+}
+
+static const struct sdhci_pci_fixes sdhci_rtsx = {
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_BROKEN_DDR50,
+ .probe_slot = rtsx_probe_slot,
+};
+
static const struct pci_device_id pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_RICOH,
@@ -732,6 +744,14 @@ static const struct pci_device_id pci_ids[] = {
},
{
+ .vendor = PCI_VENDOR_ID_REALTEK,
+ .device = 0x5250,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_rtsx,
+ },
+
+ {
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_MRST_SD0,
.subvendor = PCI_ANY_ID,
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index 793dacd3b841..2fd73b38c303 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -34,6 +34,7 @@
#include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/mbus.h>
#include "sdhci.h"
#include "sdhci-pltfm.h"
@@ -57,6 +58,60 @@
#define SDCE_MISC_INT (1<<2)
#define SDCE_MISC_INT_EN (1<<1)
+/*
+ * These registers are relative to the second register region, for the
+ * MBus bridge.
+ */
+#define SDHCI_WINDOW_CTRL(i) (0x80 + ((i) << 3))
+#define SDHCI_WINDOW_BASE(i) (0x84 + ((i) << 3))
+#define SDHCI_MAX_WIN_NUM 8
+
+static int mv_conf_mbus_windows(struct platform_device *pdev,
+ const struct mbus_dram_target_info *dram)
+{
+ int i;
+ void __iomem *regs;
+ struct resource *res;
+
+ if (!dram) {
+ dev_err(&pdev->dev, "no mbus dram info\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot get mbus registers\n");
+ return -EINVAL;
+ }
+
+ regs = ioremap(res->start, resource_size(res));
+ if (!regs) {
+ dev_err(&pdev->dev, "cannot map mbus registers\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < SDHCI_MAX_WIN_NUM; i++) {
+ writel(0, regs + SDHCI_WINDOW_CTRL(i));
+ writel(0, regs + SDHCI_WINDOW_BASE(i));
+ }
+
+ for (i = 0; i < dram->num_cs; i++) {
+ const struct mbus_dram_window *cs = dram->cs + i;
+
+ /* Write size, attributes and target id to control register */
+ writel(((cs->size - 1) & 0xffff0000) |
+ (cs->mbus_attr << 8) |
+ (dram->mbus_dram_target_id << 4) | 1,
+ regs + SDHCI_WINDOW_CTRL(i));
+ /* Write base address to base register */
+ writel(cs->base, regs + SDHCI_WINDOW_BASE(i));
+ }
+
+ iounmap(regs);
+
+ return 0;
+}
+
static void pxav3_set_private_registers(struct sdhci_host *host, u8 mask)
{
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
@@ -187,6 +242,9 @@ static const struct of_device_id sdhci_pxav3_of_match[] = {
{
.compatible = "mrvl,pxav3-mmc",
},
+ {
+ .compatible = "marvell,armada-380-sdhci",
+ },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_pxav3_of_match);
@@ -219,6 +277,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
struct sdhci_host *host = NULL;
struct sdhci_pxa *pxa = NULL;
const struct of_device_id *match;
@@ -235,6 +294,14 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
kfree(pxa);
return PTR_ERR(host);
}
+
+ if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) {
+ ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info());
+ if (ret < 0)
+ goto err_mbus_win;
+ }
+
+
pltfm_host = sdhci_priv(host);
pltfm_host->priv = pxa;
@@ -321,6 +388,7 @@ err_add_host:
clk_disable_unprepare(clk);
clk_put(clk);
err_clk_get:
+err_mbus_win:
sdhci_pltfm_free(pdev);
kfree(pxa);
return ret;
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 6debda952155..d61eb5a70833 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -51,12 +51,13 @@ struct sdhci_s3c {
struct platform_device *pdev;
struct resource *ioarea;
struct s3c_sdhci_platdata *pdata;
- unsigned int cur_clk;
+ int cur_clk;
int ext_cd_irq;
int ext_cd_gpio;
struct clk *clk_io;
struct clk *clk_bus[MAX_BUS_CLK];
+ unsigned long clk_rates[MAX_BUS_CLK];
};
/**
@@ -77,32 +78,6 @@ static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
}
/**
- * get_curclk - convert ctrl2 register to clock source number
- * @ctrl2: Control2 register value.
- */
-static u32 get_curclk(u32 ctrl2)
-{
- ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK;
- ctrl2 >>= S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
-
- return ctrl2;
-}
-
-static void sdhci_s3c_check_sclk(struct sdhci_host *host)
-{
- struct sdhci_s3c *ourhost = to_s3c(host);
- u32 tmp = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
-
- if (get_curclk(tmp) != ourhost->cur_clk) {
- dev_dbg(&ourhost->pdev->dev, "restored ctrl2 clock setting\n");
-
- tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
- tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
- writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2);
- }
-}
-
-/**
* sdhci_s3c_get_max_clk - callback to get maximum clock frequency.
* @host: The SDHCI host instance.
*
@@ -111,20 +86,11 @@ static void sdhci_s3c_check_sclk(struct sdhci_host *host)
static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)
{
struct sdhci_s3c *ourhost = to_s3c(host);
- struct clk *busclk;
- unsigned int rate, max;
- int clk;
-
- /* note, a reset will reset the clock source */
-
- sdhci_s3c_check_sclk(host);
-
- for (max = 0, clk = 0; clk < MAX_BUS_CLK; clk++) {
- busclk = ourhost->clk_bus[clk];
- if (!busclk)
- continue;
+ unsigned long rate, max = 0;
+ int src;
- rate = clk_get_rate(busclk);
+ for (src = 0; src < MAX_BUS_CLK; src++) {
+ rate = ourhost->clk_rates[src];
if (rate > max)
max = rate;
}
@@ -144,9 +110,9 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
{
unsigned long rate;
struct clk *clksrc = ourhost->clk_bus[src];
- int div;
+ int shift;
- if (!clksrc)
+ if (IS_ERR(clksrc))
return UINT_MAX;
/*
@@ -158,17 +124,24 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
return wanted - rate;
}
- rate = clk_get_rate(clksrc);
+ rate = ourhost->clk_rates[src];
- for (div = 1; div < 256; div *= 2) {
- if ((rate / div) <= wanted)
+ for (shift = 0; shift <= 8; ++shift) {
+ if ((rate >> shift) <= wanted)
break;
}
+ if (shift > 8) {
+ dev_dbg(&ourhost->pdev->dev,
+ "clk %d: rate %ld, min rate %lu > wanted %u\n",
+ src, rate, rate / 256, wanted);
+ return UINT_MAX;
+ }
+
dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n",
- src, rate, wanted, rate / div);
+ src, rate, wanted, rate >> shift);
- return wanted - (rate / div);
+ return wanted - (rate >> shift);
}
/**
@@ -209,20 +182,22 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
struct clk *clk = ourhost->clk_bus[best_src];
clk_prepare_enable(clk);
- clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
-
- /* turn clock off to card before changing clock source */
- writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+ if (ourhost->cur_clk >= 0)
+ clk_disable_unprepare(
+ ourhost->clk_bus[ourhost->cur_clk]);
ourhost->cur_clk = best_src;
- host->max_clk = clk_get_rate(clk);
-
- ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
- ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
- ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
- writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
+ host->max_clk = ourhost->clk_rates[best_src];
}
+ /* turn clock off to card before changing clock source */
+ writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
+ ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
+ ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
+ writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
+
/* reprogram default hardware configuration */
writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA,
host->ioaddr + S3C64XX_SDHCI_CONTROL4);
@@ -254,17 +229,17 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
{
struct sdhci_s3c *ourhost = to_s3c(host);
- unsigned int delta, min = UINT_MAX;
+ unsigned long rate, min = ULONG_MAX;
int src;
for (src = 0; src < MAX_BUS_CLK; src++) {
- delta = sdhci_s3c_consider_clock(ourhost, src, 0);
- if (delta == UINT_MAX)
+ rate = ourhost->clk_rates[src] / 256;
+ if (!rate)
continue;
- /* delta is a negative value in this case */
- if (-delta < min)
- min = -delta;
+ if (rate < min)
+ min = rate;
}
+
return min;
}
@@ -272,20 +247,44 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
static unsigned int sdhci_cmu_get_max_clock(struct sdhci_host *host)
{
struct sdhci_s3c *ourhost = to_s3c(host);
+ unsigned long rate, max = 0;
+ int src;
- return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], UINT_MAX);
+ for (src = 0; src < MAX_BUS_CLK; src++) {
+ struct clk *clk;
+
+ clk = ourhost->clk_bus[src];
+ if (IS_ERR(clk))
+ continue;
+
+ rate = clk_round_rate(clk, ULONG_MAX);
+ if (rate > max)
+ max = rate;
+ }
+
+ return max;
}
/* sdhci_cmu_get_min_clock - callback to get minimal supported clock value. */
static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)
{
struct sdhci_s3c *ourhost = to_s3c(host);
+ unsigned long rate, min = ULONG_MAX;
+ int src;
- /*
- * initial clock can be in the frequency range of
- * 100KHz-400KHz, so we set it as max value.
- */
- return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], 400000);
+ for (src = 0; src < MAX_BUS_CLK; src++) {
+ struct clk *clk;
+
+ clk = ourhost->clk_bus[src];
+ if (IS_ERR(clk))
+ continue;
+
+ rate = clk_round_rate(clk, 0);
+ if (rate < min)
+ min = rate;
+ }
+
+ return min;
}
/* sdhci_cmu_set_clock - callback on clock change.*/
@@ -552,6 +551,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
sc->host = host;
sc->pdev = pdev;
sc->pdata = pdata;
+ sc->cur_clk = -1;
platform_set_drvdata(pdev, host);
@@ -566,25 +566,18 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
clk_prepare_enable(sc->clk_io);
for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
- struct clk *clk;
char name[14];
snprintf(name, 14, "mmc_busclk.%d", ptr);
- clk = devm_clk_get(dev, name);
- if (IS_ERR(clk))
+ sc->clk_bus[ptr] = devm_clk_get(dev, name);
+ if (IS_ERR(sc->clk_bus[ptr]))
continue;
clks++;
- sc->clk_bus[ptr] = clk;
-
- /*
- * save current clock index to know which clock bus
- * is used later in overriding functions.
- */
- sc->cur_clk = ptr;
+ sc->clk_rates[ptr] = clk_get_rate(sc->clk_bus[ptr]);
dev_info(dev, "clock source %d: %s (%ld Hz)\n",
- ptr, name, clk_get_rate(clk));
+ ptr, name, sc->clk_rates[ptr]);
}
if (clks == 0) {
@@ -593,10 +586,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
goto err_no_busclks;
}
-#ifndef CONFIG_PM_RUNTIME
- clk_prepare_enable(sc->clk_bus[sc->cur_clk]);
-#endif
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(host->ioaddr)) {
@@ -709,10 +698,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
return 0;
err_req_regs:
-#ifndef CONFIG_PM_RUNTIME
- clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
-#endif
-
err_no_busclks:
clk_disable_unprepare(sc->clk_io);
@@ -743,9 +728,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-#ifndef CONFIG_PM_RUNTIME
- clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
-#endif
clk_disable_unprepare(sc->clk_io);
sdhci_free_host(host);
@@ -779,7 +761,8 @@ static int sdhci_s3c_runtime_suspend(struct device *dev)
ret = sdhci_runtime_suspend_host(host);
- clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
+ if (ourhost->cur_clk >= 0)
+ clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
clk_disable_unprepare(busclk);
return ret;
}
@@ -792,7 +775,8 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
int ret;
clk_prepare_enable(busclk);
- clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
+ if (ourhost->cur_clk >= 0)
+ clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
ret = sdhci_runtime_resume_host(host);
return ret;
}
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index 2dba9f8d1760..0316dec3f006 100644
--- a/drivers/mmc/host/sdhci-spear.c
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdhci-spear.h>
+#include <linux/mmc/slot-gpio.h>
#include <linux/io.h>
#include "sdhci.h"
@@ -40,36 +41,6 @@ static const struct sdhci_ops sdhci_pltfm_ops = {
/* Nothing to do for now. */
};
-/* gpio card detection interrupt handler */
-static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
-{
- struct platform_device *pdev = dev_id;
- struct sdhci_host *host = platform_get_drvdata(pdev);
- struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
- unsigned long gpio_irq_type;
- int val;
-
- val = gpio_get_value(sdhci->data->card_int_gpio);
-
- /* val == 1 -> card removed, val == 0 -> card inserted */
- /* if card removed - set irq for low level, else vice versa */
- gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
- irq_set_irq_type(irq, gpio_irq_type);
-
- if (sdhci->data->card_power_gpio >= 0) {
- if (!sdhci->data->power_always_enb) {
- /* if card inserted, give power, otherwise remove it */
- val = sdhci->data->power_active_high ? !val : val ;
- gpio_set_value(sdhci->data->card_power_gpio, val);
- }
- }
-
- /* inform sdhci driver about card insertion/removal */
- tasklet_schedule(&host->card_tasklet);
-
- return IRQ_HANDLED;
-}
-
#ifdef CONFIG_OF
static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
{
@@ -84,14 +55,12 @@ static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pde
/* If pdata is required */
if (cd_gpio != -1) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
+ if (!pdata)
dev_err(&pdev->dev, "DT: kzalloc failed\n");
- return ERR_PTR(-ENOMEM);
- }
+ else
+ pdata->card_int_gpio = cd_gpio;
}
- pdata->card_int_gpio = cd_gpio;
-
return pdata;
}
#else
@@ -107,41 +76,44 @@ static int sdhci_probe(struct platform_device *pdev)
struct sdhci_host *host;
struct resource *iomem;
struct spear_sdhci *sdhci;
+ struct device *dev;
int ret;
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!iomem) {
- ret = -ENOMEM;
- dev_dbg(&pdev->dev, "memory resource not defined\n");
+ dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev;
+ host = sdhci_alloc_host(dev, sizeof(*sdhci));
+ if (IS_ERR(host)) {
+ ret = PTR_ERR(host);
+ dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
goto err;
}
- if (!devm_request_mem_region(&pdev->dev, iomem->start,
- resource_size(iomem), "spear-sdhci")) {
- ret = -EBUSY;
- dev_dbg(&pdev->dev, "cannot request region\n");
- goto err;
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
+ if (IS_ERR(host->ioaddr)) {
+ ret = PTR_ERR(host->ioaddr);
+ dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret);
+ goto err_host;
}
- sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL);
- if (!sdhci) {
- ret = -ENOMEM;
- dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
- goto err;
- }
+ host->hw_name = "sdhci";
+ host->ops = &sdhci_pltfm_ops;
+ host->irq = platform_get_irq(pdev, 0);
+ host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
+
+ sdhci = sdhci_priv(host);
/* clk enable */
- sdhci->clk = clk_get(&pdev->dev, NULL);
+ sdhci->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sdhci->clk)) {
ret = PTR_ERR(sdhci->clk);
dev_dbg(&pdev->dev, "Error getting clock\n");
- goto err;
+ goto err_host;
}
ret = clk_prepare_enable(sdhci->clk);
if (ret) {
dev_dbg(&pdev->dev, "Error enabling clock\n");
- goto put_clk;
+ goto err_host;
}
ret = clk_set_rate(sdhci->clk, 50000000);
@@ -153,118 +125,42 @@ static int sdhci_probe(struct platform_device *pdev)
sdhci->data = sdhci_probe_config_dt(pdev);
if (IS_ERR(sdhci->data)) {
dev_err(&pdev->dev, "DT: Failed to get pdata\n");
- return -ENODEV;
+ goto disable_clk;
}
} else {
sdhci->data = dev_get_platdata(&pdev->dev);
}
- pdev->dev.platform_data = sdhci;
-
- if (pdev->dev.parent)
- host = sdhci_alloc_host(pdev->dev.parent, 0);
- else
- host = sdhci_alloc_host(&pdev->dev, 0);
-
- if (IS_ERR(host)) {
- ret = PTR_ERR(host);
- dev_dbg(&pdev->dev, "error allocating host\n");
- goto disable_clk;
- }
-
- host->hw_name = "sdhci";
- host->ops = &sdhci_pltfm_ops;
- host->irq = platform_get_irq(pdev, 0);
- host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
-
- host->ioaddr = devm_ioremap(&pdev->dev, iomem->start,
- resource_size(iomem));
- if (!host->ioaddr) {
- ret = -ENOMEM;
- dev_dbg(&pdev->dev, "failed to remap registers\n");
- goto free_host;
+ /*
+ * It is optional to use GPIOs for sdhci card detection. If
+ * sdhci->data is NULL, then use original sdhci lines otherwise
+ * GPIO lines. We use the built-in GPIO support for this.
+ */
+ if (sdhci->data && sdhci->data->card_int_gpio >= 0) {
+ ret = mmc_gpio_request_cd(host->mmc,
+ sdhci->data->card_int_gpio, 0);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev,
+ "failed to request card-detect gpio%d\n",
+ sdhci->data->card_int_gpio);
+ goto disable_clk;
+ }
}
ret = sdhci_add_host(host);
if (ret) {
dev_dbg(&pdev->dev, "error adding host\n");
- goto free_host;
+ goto disable_clk;
}
platform_set_drvdata(pdev, host);
- /*
- * It is optional to use GPIOs for sdhci Power control & sdhci card
- * interrupt detection. If sdhci->data is NULL, then use original sdhci
- * lines otherwise GPIO lines.
- * If GPIO is selected for power control, then power should be disabled
- * after card removal and should be enabled when card insertion
- * interrupt occurs
- */
- if (!sdhci->data)
- return 0;
-
- if (sdhci->data->card_power_gpio >= 0) {
- int val = 0;
-
- ret = devm_gpio_request(&pdev->dev,
- sdhci->data->card_power_gpio, "sdhci");
- if (ret < 0) {
- dev_dbg(&pdev->dev, "gpio request fail: %d\n",
- sdhci->data->card_power_gpio);
- goto set_drvdata;
- }
-
- if (sdhci->data->power_always_enb)
- val = sdhci->data->power_active_high;
- else
- val = !sdhci->data->power_active_high;
-
- ret = gpio_direction_output(sdhci->data->card_power_gpio, val);
- if (ret) {
- dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
- sdhci->data->card_power_gpio);
- goto set_drvdata;
- }
- }
-
- if (sdhci->data->card_int_gpio >= 0) {
- ret = devm_gpio_request(&pdev->dev, sdhci->data->card_int_gpio,
- "sdhci");
- if (ret < 0) {
- dev_dbg(&pdev->dev, "gpio request fail: %d\n",
- sdhci->data->card_int_gpio);
- goto set_drvdata;
- }
-
- ret = gpio_direction_input(sdhci->data->card_int_gpio);
- if (ret) {
- dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
- sdhci->data->card_int_gpio);
- goto set_drvdata;
- }
- ret = devm_request_irq(&pdev->dev,
- gpio_to_irq(sdhci->data->card_int_gpio),
- sdhci_gpio_irq, IRQF_TRIGGER_LOW,
- mmc_hostname(host->mmc), pdev);
- if (ret) {
- dev_dbg(&pdev->dev, "gpio request irq fail: %d\n",
- sdhci->data->card_int_gpio);
- goto set_drvdata;
- }
-
- }
-
return 0;
-set_drvdata:
- sdhci_remove_host(host, 1);
-free_host:
- sdhci_free_host(host);
disable_clk:
clk_disable_unprepare(sdhci->clk);
-put_clk:
- clk_put(sdhci->clk);
+err_host:
+ sdhci_free_host(host);
err:
dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
return ret;
@@ -273,7 +169,7 @@ err:
static int sdhci_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
- struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
+ struct spear_sdhci *sdhci = sdhci_priv(host);
int dead = 0;
u32 scratch;
@@ -282,9 +178,8 @@ static int sdhci_remove(struct platform_device *pdev)
dead = 1;
sdhci_remove_host(host, dead);
- sdhci_free_host(host);
clk_disable_unprepare(sdhci->clk);
- clk_put(sdhci->clk);
+ sdhci_free_host(host);
return 0;
}
@@ -293,7 +188,7 @@ static int sdhci_remove(struct platform_device *pdev)
static int sdhci_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
- struct spear_sdhci *sdhci = dev_get_platdata(dev);
+ struct spear_sdhci *sdhci = sdhci_priv(host);
int ret;
ret = sdhci_suspend_host(host);
@@ -306,7 +201,7 @@ static int sdhci_suspend(struct device *dev)
static int sdhci_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
- struct spear_sdhci *sdhci = dev_get_platdata(dev);
+ struct spear_sdhci *sdhci = sdhci_priv(host);
int ret;
ret = clk_enable(sdhci->clk);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9ddef4763541..9a79fc4b60ca 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -675,12 +675,12 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
return 0xE;
/* Unspecified timeout, assume max */
- if (!data && !cmd->cmd_timeout_ms)
+ if (!data && !cmd->busy_timeout)
return 0xE;
/* timeout in us */
if (!data)
- target_timeout = cmd->cmd_timeout_ms * 1000;
+ target_timeout = cmd->busy_timeout * 1000;
else {
target_timeout = data->timeout_ns / 1000;
if (host->clock)
@@ -1019,8 +1019,8 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
}
timeout = jiffies;
- if (!cmd->data && cmd->cmd_timeout_ms > 9000)
- timeout += DIV_ROUND_UP(cmd->cmd_timeout_ms, 1000) * HZ + HZ;
+ if (!cmd->data && cmd->busy_timeout > 9000)
+ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
else
timeout += 10 * HZ;
mod_timer(&host->timer, timeout);
@@ -2026,12 +2026,11 @@ out:
host->tuning_count * HZ);
/* Tuning mode 1 limits the maximum data length to 4MB */
mmc->max_blk_count = (4 * 1024 * 1024) / mmc->max_blk_size;
- } else {
+ } else if (host->flags & SDHCI_USING_RETUNING_TIMER) {
host->flags &= ~SDHCI_NEEDS_RETUNING;
/* Reload the new initial value for timer */
- if (host->tuning_mode == SDHCI_TUNING_MODE_1)
- mod_timer(&host->tuning_timer, jiffies +
- host->tuning_count * HZ);
+ mod_timer(&host->tuning_timer, jiffies +
+ host->tuning_count * HZ);
}
/*
@@ -2434,9 +2433,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
if (host->runtime_suspended) {
spin_unlock(&host->lock);
- pr_warning("%s: got irq while runtime suspended\n",
- mmc_hostname(host->mmc));
- return IRQ_HANDLED;
+ return IRQ_NONE;
}
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
@@ -2941,7 +2938,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
host->timeout_clk = mmc->f_max / 1000;
- mmc->max_discard_to = (1 << 27) / host->timeout_clk;
+ mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
@@ -3020,7 +3017,8 @@ int sdhci_add_host(struct sdhci_host *host)
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
mmc->caps |= MMC_CAP_UHS_SDR50;
- if (caps[1] & SDHCI_SUPPORT_DDR50)
+ if ((caps[1] & SDHCI_SUPPORT_DDR50) &&
+ !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
mmc->caps |= MMC_CAP_UHS_DDR50;
/* Does the host need tuning for SDR50? */
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 2d6ce257a273..91058dabd11a 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -37,6 +37,8 @@
struct sh_mobile_sdhi_of_data {
unsigned long tmio_flags;
+ unsigned long capabilities;
+ unsigned long capabilities2;
};
static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
@@ -45,6 +47,31 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
},
};
+static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = {
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE,
+ .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
+};
+
+static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE,
+ .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
+ .capabilities2 = MMC_CAP2_NO_MULTI_READ,
+};
+
+static const struct of_device_id sh_mobile_sdhi_of_match[] = {
+ { .compatible = "renesas,sdhi-shmobile" },
+ { .compatible = "renesas,sdhi-sh7372" },
+ { .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], },
+ { .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], },
+ { .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], },
+ { .compatible = "renesas,sdhi-r8a7778", .data = &of_rcar_gen1_compatible, },
+ { .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, },
+ { .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, },
+ { .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
+
struct sh_mobile_sdhi {
struct clk *clk;
struct tmio_mmc_data mmc_data;
@@ -114,19 +141,6 @@ static const struct sh_mobile_sdhi_ops sdhi_ops = {
.cd_wakeup = sh_mobile_sdhi_cd_wakeup,
};
-static const struct of_device_id sh_mobile_sdhi_of_match[] = {
- { .compatible = "renesas,sdhi-shmobile" },
- { .compatible = "renesas,sdhi-sh7372" },
- { .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], },
- { .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], },
- { .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], },
- { .compatible = "renesas,sdhi-r8a7778", .data = &sh_mobile_sdhi_of_cfg[0], },
- { .compatible = "renesas,sdhi-r8a7779", .data = &sh_mobile_sdhi_of_cfg[0], },
- { .compatible = "renesas,sdhi-r8a7790", .data = &sh_mobile_sdhi_of_cfg[0], },
- {},
-};
-MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
-
static int sh_mobile_sdhi_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
@@ -212,6 +226,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
if (of_id && of_id->data) {
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
mmc_data->flags |= of_data->tmio_flags;
+ mmc_data->capabilities |= of_data->capabilities;
+ mmc_data->capabilities2 |= of_data->capabilities2;
}
/* SD control register space size is 0x100, 0x200 for bus_shift=1 */
@@ -316,10 +332,10 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
- .suspend = tmio_mmc_host_suspend,
- .resume = tmio_mmc_host_resume,
- .runtime_suspend = tmio_mmc_host_runtime_suspend,
- .runtime_resume = tmio_mmc_host_runtime_resume,
+ SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_host_suspend, tmio_mmc_host_resume)
+ SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
+ tmio_mmc_host_runtime_resume,
+ NULL)
};
static struct platform_driver sh_mobile_sdhi_driver = {
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 1900abb04236..cfad844730d8 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -23,38 +23,37 @@
#include "tmio_mmc.h"
-#ifdef CONFIG_PM
-static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int tmio_mmc_suspend(struct device *dev)
{
- const struct mfd_cell *cell = mfd_get_cell(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
int ret;
- ret = tmio_mmc_host_suspend(&dev->dev);
+ ret = tmio_mmc_host_suspend(dev);
/* Tell MFD core it can disable us now.*/
if (!ret && cell->disable)
- cell->disable(dev);
+ cell->disable(pdev);
return ret;
}
-static int tmio_mmc_resume(struct platform_device *dev)
+static int tmio_mmc_resume(struct device *dev)
{
- const struct mfd_cell *cell = mfd_get_cell(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
int ret = 0;
/* Tell the MFD core we are ready to be enabled */
if (cell->resume)
- ret = cell->resume(dev);
+ ret = cell->resume(pdev);
if (!ret)
- ret = tmio_mmc_host_resume(&dev->dev);
+ ret = tmio_mmc_host_resume(dev);
return ret;
}
-#else
-#define tmio_mmc_suspend NULL
-#define tmio_mmc_resume NULL
#endif
static int tmio_mmc_probe(struct platform_device *pdev)
@@ -134,15 +133,18 @@ static int tmio_mmc_remove(struct platform_device *pdev)
/* ------------------- device registration ----------------------- */
+static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume)
+};
+
static struct platform_driver tmio_mmc_driver = {
.driver = {
.name = "tmio-mmc",
.owner = THIS_MODULE,
+ .pm = &tmio_mmc_dev_pm_ops,
},
.probe = tmio_mmc_probe,
.remove = tmio_mmc_remove,
- .suspend = tmio_mmc_suspend,
- .resume = tmio_mmc_resume,
};
module_platform_driver(tmio_mmc_driver);
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index aaa9c7e9e730..100ffe0b2faf 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -162,16 +162,15 @@ static inline void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
}
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int tmio_mmc_host_suspend(struct device *dev);
int tmio_mmc_host_resume(struct device *dev);
-#else
-#define tmio_mmc_host_suspend NULL
-#define tmio_mmc_host_resume NULL
#endif
+#ifdef CONFIG_PM_RUNTIME
int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev);
+#endif
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
{
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 8d8abf23a611..faf0924e71cb 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -1142,7 +1142,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
}
EXPORT_SYMBOL(tmio_mmc_host_remove);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int tmio_mmc_host_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
@@ -1165,9 +1165,9 @@ int tmio_mmc_host_resume(struct device *dev)
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_resume);
+#endif
-#endif /* CONFIG_PM */
-
+#ifdef CONFIG_PM_RUNTIME
int tmio_mmc_host_runtime_suspend(struct device *dev)
{
return 0;
@@ -1184,5 +1184,6 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);
+#endif
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c
index c0105a2e269a..d2c386f09d69 100644
--- a/drivers/mmc/host/ushc.c
+++ b/drivers/mmc/host/ushc.c
@@ -504,7 +504,7 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id
ret = -ENOMEM;
goto err;
}
- ushc->csw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL);
+ ushc->csw = kzalloc(sizeof(struct ushc_csw), GFP_KERNEL);
if (ushc->csw == NULL) {
ret = -ENOMEM;
goto err;
diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c
index e902ed7846b0..498d1f799085 100644
--- a/drivers/mmc/host/wmt-sdmmc.c
+++ b/drivers/mmc/host/wmt-sdmmc.c
@@ -757,7 +757,7 @@ static int wmt_mci_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(wmt_mci_dt_ids, &pdev->dev);
- const struct wmt_mci_caps *wmt_caps = of_id->data;
+ const struct wmt_mci_caps *wmt_caps;
int ret;
int regular_irq, dma_irq;
@@ -766,6 +766,8 @@ static int wmt_mci_probe(struct platform_device *pdev)
return -EFAULT;
}
+ wmt_caps = of_id->data;
+
if (!np) {
dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n");
return -EFAULT;
diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c
index f3cdf64997d6..63aa9d9e34c5 100644
--- a/drivers/net/ntb_netdev.c
+++ b/drivers/net/ntb_netdev.c
@@ -78,11 +78,19 @@ static void ntb_netdev_event_handler(void *data, int status)
netdev_dbg(ndev, "Event %x, Link %x\n", status,
ntb_transport_link_query(dev->qp));
- /* Currently, only link status event is supported */
- if (status)
- netif_carrier_on(ndev);
- else
+ switch (status) {
+ case NTB_LINK_DOWN:
netif_carrier_off(ndev);
+ break;
+ case NTB_LINK_UP:
+ if (!ntb_transport_link_query(dev->qp))
+ return;
+
+ netif_carrier_on(ndev);
+ break;
+ default:
+ netdev_warn(ndev, "Unsupported event type %d\n", status);
+ }
}
static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
@@ -182,8 +190,10 @@ static int ntb_netdev_open(struct net_device *ndev)
rc = ntb_transport_rx_enqueue(dev->qp, skb, skb->data,
ndev->mtu + ETH_HLEN);
- if (rc == -EINVAL)
+ if (rc == -EINVAL) {
+ dev_kfree_skb(skb);
goto err;
+ }
}
netif_carrier_off(ndev);
@@ -367,12 +377,15 @@ static void ntb_netdev_remove(struct pci_dev *pdev)
{
struct net_device *ndev;
struct ntb_netdev *dev;
+ bool found = false;
list_for_each_entry(dev, &dev_list, list) {
- if (dev->pdev == pdev)
+ if (dev->pdev == pdev) {
+ found = true;
break;
+ }
}
- if (dev == NULL)
+ if (!found)
return;
list_del(&dev->list);
diff --git a/drivers/ntb/ntb_hw.c b/drivers/ntb/ntb_hw.c
index 170e8e60cdb7..372e08c4ffef 100644
--- a/drivers/ntb/ntb_hw.c
+++ b/drivers/ntb/ntb_hw.c
@@ -91,7 +91,7 @@ static struct dentry *debugfs_dir;
/* Translate memory window 0,1 to BAR 2,4 */
#define MW_TO_BAR(mw) (mw * NTB_MAX_NUM_MW + 2)
-static DEFINE_PCI_DEVICE_TABLE(ntb_pci_tbl) = {
+static const struct pci_device_id ntb_pci_tbl[] = {
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_BWD)},
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_JSF)},
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_SNB)},
@@ -120,7 +120,8 @@ MODULE_DEVICE_TABLE(pci, ntb_pci_tbl);
* RETURNS: An appropriate -ERRNO error value on error, or zero for success.
*/
int ntb_register_event_callback(struct ntb_device *ndev,
- void (*func)(void *handle, enum ntb_hw_event event))
+ void (*func)(void *handle,
+ enum ntb_hw_event event))
{
if (ndev->event_cb)
return -EINVAL;
@@ -715,9 +716,9 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
SNB_PBAR4LMT_OFFSET);
/* HW errata on the Limit registers. They can only be
* written when the base register is 4GB aligned and
- * < 32bit. This should already be the case based on the
- * driver defaults, but write the Limit registers first
- * just in case.
+ * < 32bit. This should already be the case based on
+ * the driver defaults, but write the Limit registers
+ * first just in case.
*/
} else {
ndev->limits.max_mw = SNB_MAX_MW;
@@ -739,9 +740,9 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
writeq(0, ndev->reg_base + SNB_PBAR4LMT_OFFSET);
/* HW errata on the Limit registers. They can only be
* written when the base register is 4GB aligned and
- * < 32bit. This should already be the case based on the
- * driver defaults, but write the Limit registers first
- * just in case.
+ * < 32bit. This should already be the case based on
+ * the driver defaults, but write the Limit registers
+ * first just in case.
*/
}
@@ -785,7 +786,7 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
/* B2B_XLAT_OFFSET is a 64bit register, but can
* only take 32bit writes
*/
- writel(SNB_MBAR01_DSD_ADDR & 0xffffffff,
+ writel(SNB_MBAR01_USD_ADDR & 0xffffffff,
ndev->reg_base + SNB_B2B_XLAT_OFFSETL);
writel(SNB_MBAR01_USD_ADDR >> 32,
ndev->reg_base + SNB_B2B_XLAT_OFFSETU);
@@ -803,7 +804,7 @@ static int ntb_xeon_setup(struct ntb_device *ndev)
ndev->conn_type = NTB_CONN_RP;
if (xeon_errata_workaround) {
- dev_err(&ndev->pdev->dev,
+ dev_err(&ndev->pdev->dev,
"NTB-RP disabled due to hardware errata. To disregard this warning and potentially lock-up the system, add the parameter 'xeon_errata_workaround=0'.\n");
return -EINVAL;
}
@@ -1079,111 +1080,131 @@ static irqreturn_t ntb_interrupt(int irq, void *dev)
return IRQ_HANDLED;
}
-static int ntb_setup_msix(struct ntb_device *ndev)
+static int ntb_setup_snb_msix(struct ntb_device *ndev, int msix_entries)
{
struct pci_dev *pdev = ndev->pdev;
struct msix_entry *msix;
- int msix_entries;
int rc, i;
- u16 val;
- if (!pdev->msix_cap) {
- rc = -EIO;
- goto err;
- }
+ if (msix_entries < ndev->limits.msix_cnt)
+ return -ENOSPC;
- rc = pci_read_config_word(pdev, pdev->msix_cap + PCI_MSIX_FLAGS, &val);
- if (rc)
- goto err;
+ rc = pci_enable_msix_exact(pdev, ndev->msix_entries, msix_entries);
+ if (rc < 0)
+ return rc;
- msix_entries = msix_table_size(val);
- if (msix_entries > ndev->limits.msix_cnt) {
- rc = -EINVAL;
- goto err;
+ for (i = 0; i < msix_entries; i++) {
+ msix = &ndev->msix_entries[i];
+ WARN_ON(!msix->vector);
+
+ if (i == msix_entries - 1) {
+ rc = request_irq(msix->vector,
+ xeon_event_msix_irq, 0,
+ "ntb-event-msix", ndev);
+ if (rc)
+ goto err;
+ } else {
+ rc = request_irq(msix->vector,
+ xeon_callback_msix_irq, 0,
+ "ntb-callback-msix",
+ &ndev->db_cb[i]);
+ if (rc)
+ goto err;
+ }
}
- ndev->msix_entries = kmalloc(sizeof(struct msix_entry) * msix_entries,
- GFP_KERNEL);
- if (!ndev->msix_entries) {
- rc = -ENOMEM;
- goto err;
+ ndev->num_msix = msix_entries;
+ ndev->max_cbs = msix_entries - 1;
+
+ return 0;
+
+err:
+ while (--i >= 0) {
+ /* Code never reaches here for entry nr 'ndev->num_msix - 1' */
+ msix = &ndev->msix_entries[i];
+ free_irq(msix->vector, &ndev->db_cb[i]);
}
- for (i = 0; i < msix_entries; i++)
- ndev->msix_entries[i].entry = i;
+ pci_disable_msix(pdev);
+ ndev->num_msix = 0;
- rc = pci_enable_msix(pdev, ndev->msix_entries, msix_entries);
- if (rc < 0)
- goto err1;
- if (rc > 0) {
- /* On SNB, the link interrupt is always tied to 4th vector. If
- * we can't get all 4, then we can't use MSI-X.
- */
- if (ndev->hw_type != BWD_HW) {
- rc = -EIO;
- goto err1;
- }
+ return rc;
+}
- dev_warn(&pdev->dev,
- "Only %d MSI-X vectors. Limiting the number of queues to that number.\n",
- rc);
- msix_entries = rc;
+static int ntb_setup_bwd_msix(struct ntb_device *ndev, int msix_entries)
+{
+ struct pci_dev *pdev = ndev->pdev;
+ struct msix_entry *msix;
+ int rc, i;
- rc = pci_enable_msix(pdev, ndev->msix_entries, msix_entries);
- if (rc)
- goto err1;
- }
+ msix_entries = pci_enable_msix_range(pdev, ndev->msix_entries,
+ 1, msix_entries);
+ if (msix_entries < 0)
+ return msix_entries;
for (i = 0; i < msix_entries; i++) {
msix = &ndev->msix_entries[i];
WARN_ON(!msix->vector);
- /* Use the last MSI-X vector for Link status */
- if (ndev->hw_type == BWD_HW) {
- rc = request_irq(msix->vector, bwd_callback_msix_irq, 0,
- "ntb-callback-msix", &ndev->db_cb[i]);
- if (rc)
- goto err2;
- } else {
- if (i == msix_entries - 1) {
- rc = request_irq(msix->vector,
- xeon_event_msix_irq, 0,
- "ntb-event-msix", ndev);
- if (rc)
- goto err2;
- } else {
- rc = request_irq(msix->vector,
- xeon_callback_msix_irq, 0,
- "ntb-callback-msix",
- &ndev->db_cb[i]);
- if (rc)
- goto err2;
- }
- }
+ rc = request_irq(msix->vector, bwd_callback_msix_irq, 0,
+ "ntb-callback-msix", &ndev->db_cb[i]);
+ if (rc)
+ goto err;
}
ndev->num_msix = msix_entries;
+ ndev->max_cbs = msix_entries;
+
+ return 0;
+
+err:
+ while (--i >= 0)
+ free_irq(msix->vector, &ndev->db_cb[i]);
+
+ pci_disable_msix(pdev);
+ ndev->num_msix = 0;
+
+ return rc;
+}
+
+static int ntb_setup_msix(struct ntb_device *ndev)
+{
+ struct pci_dev *pdev = ndev->pdev;
+ int msix_entries;
+ int rc, i;
+
+ msix_entries = pci_msix_vec_count(pdev);
+ if (msix_entries < 0) {
+ rc = msix_entries;
+ goto err;
+ } else if (msix_entries > ndev->limits.msix_cnt) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ ndev->msix_entries = kmalloc(sizeof(struct msix_entry) * msix_entries,
+ GFP_KERNEL);
+ if (!ndev->msix_entries) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < msix_entries; i++)
+ ndev->msix_entries[i].entry = i;
+
if (ndev->hw_type == BWD_HW)
- ndev->max_cbs = msix_entries;
+ rc = ntb_setup_bwd_msix(ndev, msix_entries);
else
- ndev->max_cbs = msix_entries - 1;
+ rc = ntb_setup_snb_msix(ndev, msix_entries);
+ if (rc)
+ goto err1;
return 0;
-err2:
- while (--i >= 0) {
- msix = &ndev->msix_entries[i];
- if (ndev->hw_type != BWD_HW && i == ndev->num_msix - 1)
- free_irq(msix->vector, ndev);
- else
- free_irq(msix->vector, &ndev->db_cb[i]);
- }
- pci_disable_msix(pdev);
err1:
kfree(ndev->msix_entries);
- dev_err(&pdev->dev, "Error allocating MSI-X interrupt\n");
err:
- ndev->num_msix = 0;
+ dev_err(&pdev->dev, "Error allocating MSI-X interrupt\n");
return rc;
}
@@ -1281,6 +1302,7 @@ static void ntb_free_interrupts(struct ntb_device *ndev)
free_irq(msix->vector, &ndev->db_cb[i]);
}
pci_disable_msix(pdev);
+ kfree(ndev->msix_entries);
} else {
free_irq(pdev->irq, ndev);
diff --git a/drivers/ntb/ntb_hw.h b/drivers/ntb/ntb_hw.h
index bbdb7edca10c..465517b7393e 100644
--- a/drivers/ntb/ntb_hw.h
+++ b/drivers/ntb/ntb_hw.h
@@ -45,6 +45,7 @@
* Contact Information:
* Jon Mason <jon.mason@intel.com>
*/
+#include <linux/ntb.h>
#define PCI_DEVICE_ID_INTEL_NTB_B2B_JSF 0x3725
#define PCI_DEVICE_ID_INTEL_NTB_PS_JSF 0x3726
@@ -60,8 +61,6 @@
#define PCI_DEVICE_ID_INTEL_NTB_SS_HSX 0x2F0F
#define PCI_DEVICE_ID_INTEL_NTB_B2B_BWD 0x0C4E
-#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1)
-
#ifndef readq
static inline u64 readq(void __iomem *addr)
{
@@ -83,9 +82,6 @@ static inline void writeq(u64 val, void __iomem *addr)
#define NTB_BAR_MASK ((1 << NTB_BAR_MMIO) | (1 << NTB_BAR_23) |\
(1 << NTB_BAR_45))
-#define NTB_LINK_DOWN 0
-#define NTB_LINK_UP 1
-
#define NTB_HB_TIMEOUT msecs_to_jiffies(1000)
#define NTB_MAX_NUM_MW 2
@@ -233,7 +229,7 @@ int ntb_register_db_callback(struct ntb_device *ndev, unsigned int idx,
int db_num));
void ntb_unregister_db_callback(struct ntb_device *ndev, unsigned int idx);
int ntb_register_event_callback(struct ntb_device *ndev,
- void (*event_cb_func) (void *handle,
+ void (*event_cb_func)(void *handle,
enum ntb_hw_event event));
void ntb_unregister_event_callback(struct ntb_device *ndev);
int ntb_get_max_spads(struct ntb_device *ndev);
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 3217f394d45b..9dd63b822025 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -56,7 +56,6 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/types.h>
-#include <linux/ntb.h>
#include "ntb_hw.h"
#define NTB_TRANSPORT_VERSION 3
@@ -107,8 +106,8 @@ struct ntb_transport_qp {
struct ntb_rx_info __iomem *rx_info;
struct ntb_rx_info *remote_rx_info;
- void (*tx_handler) (struct ntb_transport_qp *qp, void *qp_data,
- void *data, int len);
+ void (*tx_handler)(struct ntb_transport_qp *qp, void *qp_data,
+ void *data, int len);
struct list_head tx_free_q;
spinlock_t ntb_tx_free_q_lock;
void __iomem *tx_mw;
@@ -117,8 +116,8 @@ struct ntb_transport_qp {
unsigned int tx_max_entry;
unsigned int tx_max_frame;
- void (*rx_handler) (struct ntb_transport_qp *qp, void *qp_data,
- void *data, int len);
+ void (*rx_handler)(struct ntb_transport_qp *qp, void *qp_data,
+ void *data, int len);
struct list_head rx_pend_q;
struct list_head rx_free_q;
spinlock_t ntb_rx_pend_q_lock;
@@ -129,7 +128,7 @@ struct ntb_transport_qp {
unsigned int rx_max_frame;
dma_cookie_t last_cookie;
- void (*event_handler) (void *data, int status);
+ void (*event_handler)(void *data, int status);
struct delayed_work link_work;
struct work_struct link_cleanup;
@@ -480,7 +479,7 @@ static void ntb_list_add(spinlock_t *lock, struct list_head *entry,
}
static struct ntb_queue_entry *ntb_list_rm(spinlock_t *lock,
- struct list_head *list)
+ struct list_head *list)
{
struct ntb_queue_entry *entry;
unsigned long flags;
@@ -839,7 +838,7 @@ static void ntb_qp_link_work(struct work_struct *work)
}
static int ntb_transport_init_queue(struct ntb_transport *nt,
- unsigned int qp_num)
+ unsigned int qp_num)
{
struct ntb_transport_qp *qp;
unsigned int num_qps_mw, tx_size;
@@ -1055,7 +1054,7 @@ static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset,
if (!chan)
goto err;
- if (len < copy_bytes)
+ if (len < copy_bytes)
goto err_wait;
device = chan->device;
@@ -1190,8 +1189,7 @@ out:
return 0;
err:
- ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry,
- &qp->rx_pend_q);
+ ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry, &qp->rx_pend_q);
/* Ensure that the data is fully copied out before clearing the flag */
wmb();
hdr->flags = 0;
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 5f67843c7fb7..27df2c533b09 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -53,6 +53,18 @@ config ACERHDF
If you have an Acer Aspire One netbook, say Y or M
here.
+config ALIENWARE_WMI
+ tristate "Alienware Special feature control"
+ depends on ACPI
+ depends on LEDS_CLASS
+ depends on NEW_LEDS
+ depends on ACPI_WMI
+ ---help---
+ This is a driver for controlling Alienware BIOS driven
+ features. It exposes an interface for controlling the AlienFX
+ zones on Alienware machines that don't contain a dedicated AlienFX
+ USB MCU such as the X51 and X51-R2.
+
config ASUS_LAPTOP
tristate "Asus Laptop Extras"
depends on ACPI
@@ -196,7 +208,7 @@ config HP_ACCEL
be called hp_accel.
config HP_WIRELESS
- tristate "HP WIRELESS"
+ tristate "HP wireless button"
depends on ACPI
depends on INPUT
help
@@ -817,12 +829,4 @@ config PVPANIC
a paravirtualized device provided by QEMU; it lets a virtual machine
(guest) communicate panic events to the host.
-config INTEL_BAYTRAIL_MBI
- tristate
- depends on PCI
- ---help---
- Needed on Baytrail platforms for access to the IOSF Sideband Mailbox
- Interface. This is a requirement for systems that need to configure
- the PUNIT for power management features such as RAPL.
-
endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 9b87cfc42b84..1a2eafc9d48e 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -55,4 +55,4 @@ obj-$(CONFIG_INTEL_RST) += intel-rst.o
obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
-obj-$(CONFIG_INTEL_BAYTRAIL_MBI) += intel_baytrail.o
+obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c
new file mode 100644
index 000000000000..541f9514f76f
--- /dev/null
+++ b/drivers/platform/x86/alienware-wmi.c
@@ -0,0 +1,565 @@
+/*
+ * Alienware AlienFX control
+ *
+ * Copyright (C) 2014 Dell Inc <mario_limonciello@dell.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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <linux/leds.h>
+
+#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492"
+#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
+#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
+
+#define WMAX_METHOD_HDMI_SOURCE 0x1
+#define WMAX_METHOD_HDMI_STATUS 0x2
+#define WMAX_METHOD_BRIGHTNESS 0x3
+#define WMAX_METHOD_ZONE_CONTROL 0x4
+
+MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
+MODULE_DESCRIPTION("Alienware special feature control");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
+MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID);
+
+enum INTERFACE_FLAGS {
+ LEGACY,
+ WMAX,
+};
+
+enum LEGACY_CONTROL_STATES {
+ LEGACY_RUNNING = 1,
+ LEGACY_BOOTING = 0,
+ LEGACY_SUSPEND = 3,
+};
+
+enum WMAX_CONTROL_STATES {
+ WMAX_RUNNING = 0xFF,
+ WMAX_BOOTING = 0,
+ WMAX_SUSPEND = 3,
+};
+
+struct quirk_entry {
+ u8 num_zones;
+};
+
+static struct quirk_entry *quirks;
+
+static struct quirk_entry quirk_unknown = {
+ .num_zones = 2,
+};
+
+static struct quirk_entry quirk_x51_family = {
+ .num_zones = 3,
+};
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+ quirks = dmi->driver_data;
+ return 1;
+}
+
+static struct dmi_system_id alienware_quirks[] = {
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+ },
+ .driver_data = &quirk_x51_family,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
+ },
+ .driver_data = &quirk_x51_family,
+ },
+ {}
+};
+
+struct color_platform {
+ u8 blue;
+ u8 green;
+ u8 red;
+} __packed;
+
+struct platform_zone {
+ u8 location;
+ struct device_attribute *attr;
+ struct color_platform colors;
+};
+
+struct wmax_brightness_args {
+ u32 led_mask;
+ u32 percentage;
+};
+
+struct hdmi_args {
+ u8 arg;
+};
+
+struct legacy_led_args {
+ struct color_platform colors;
+ u8 brightness;
+ u8 state;
+} __packed;
+
+struct wmax_led_args {
+ u32 led_mask;
+ struct color_platform colors;
+ u8 state;
+} __packed;
+
+static struct platform_device *platform_device;
+static struct device_attribute *zone_dev_attrs;
+static struct attribute **zone_attrs;
+static struct platform_zone *zone_data;
+
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = "alienware-wmi",
+ .owner = THIS_MODULE,
+ }
+};
+
+static struct attribute_group zone_attribute_group = {
+ .name = "rgb_zones",
+};
+
+static u8 interface;
+static u8 lighting_control_state;
+static u8 global_brightness;
+
+/*
+ * Helpers used for zone control
+*/
+static int parse_rgb(const char *buf, struct platform_zone *zone)
+{
+ long unsigned int rgb;
+ int ret;
+ union color_union {
+ struct color_platform cp;
+ int package;
+ } repackager;
+
+ ret = kstrtoul(buf, 16, &rgb);
+ if (ret)
+ return ret;
+
+ /* RGB triplet notation is 24-bit hexadecimal */
+ if (rgb > 0xFFFFFF)
+ return -EINVAL;
+
+ repackager.package = rgb & 0x0f0f0f0f;
+ pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
+ repackager.cp.red, repackager.cp.green, repackager.cp.blue);
+ zone->colors = repackager.cp;
+ return 0;
+}
+
+static struct platform_zone *match_zone(struct device_attribute *attr)
+{
+ int i;
+ for (i = 0; i < quirks->num_zones; i++) {
+ if ((struct device_attribute *)zone_data[i].attr == attr) {
+ pr_debug("alienware-wmi: matched zone location: %d\n",
+ zone_data[i].location);
+ return &zone_data[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Individual RGB zone control
+*/
+static int alienware_update_led(struct platform_zone *zone)
+{
+ int method_id;
+ acpi_status status;
+ char *guid;
+ struct acpi_buffer input;
+ struct legacy_led_args legacy_args;
+ struct wmax_led_args wmax_args;
+ if (interface == WMAX) {
+ wmax_args.led_mask = 1 << zone->location;
+ wmax_args.colors = zone->colors;
+ wmax_args.state = lighting_control_state;
+ guid = WMAX_CONTROL_GUID;
+ method_id = WMAX_METHOD_ZONE_CONTROL;
+
+ input.length = (acpi_size) sizeof(wmax_args);
+ input.pointer = &wmax_args;
+ } else {
+ legacy_args.colors = zone->colors;
+ legacy_args.brightness = global_brightness;
+ legacy_args.state = 0;
+ if (lighting_control_state == LEGACY_BOOTING ||
+ lighting_control_state == LEGACY_SUSPEND) {
+ guid = LEGACY_POWER_CONTROL_GUID;
+ legacy_args.state = lighting_control_state;
+ } else
+ guid = LEGACY_CONTROL_GUID;
+ method_id = zone->location + 1;
+
+ input.length = (acpi_size) sizeof(legacy_args);
+ input.pointer = &legacy_args;
+ }
+ pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
+
+ status = wmi_evaluate_method(guid, 1, method_id, &input, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: zone set failure: %u\n", status);
+ return ACPI_FAILURE(status);
+}
+
+static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_zone *target_zone;
+ target_zone = match_zone(attr);
+ if (target_zone == NULL)
+ return sprintf(buf, "red: -1, green: -1, blue: -1\n");
+ return sprintf(buf, "red: %d, green: %d, blue: %d\n",
+ target_zone->colors.red,
+ target_zone->colors.green, target_zone->colors.blue);
+
+}
+
+static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_zone *target_zone;
+ int ret;
+ target_zone = match_zone(attr);
+ if (target_zone == NULL) {
+ pr_err("alienware-wmi: invalid target zone\n");
+ return 1;
+ }
+ ret = parse_rgb(buf, target_zone);
+ if (ret)
+ return ret;
+ ret = alienware_update_led(target_zone);
+ return ret ? ret : count;
+}
+
+/*
+ * LED Brightness (Global)
+*/
+static int wmax_brightness(int brightness)
+{
+ acpi_status status;
+ struct acpi_buffer input;
+ struct wmax_brightness_args args = {
+ .led_mask = 0xFF,
+ .percentage = brightness,
+ };
+ input.length = (acpi_size) sizeof(args);
+ input.pointer = &args;
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ WMAX_METHOD_BRIGHTNESS, &input, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: brightness set failure: %u\n", status);
+ return ACPI_FAILURE(status);
+}
+
+static void global_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int ret;
+ global_brightness = brightness;
+ if (interface == WMAX)
+ ret = wmax_brightness(brightness);
+ else
+ ret = alienware_update_led(&zone_data[0]);
+ if (ret)
+ pr_err("LED brightness update failed\n");
+}
+
+static enum led_brightness global_led_get(struct led_classdev *led_cdev)
+{
+ return global_brightness;
+}
+
+static struct led_classdev global_led = {
+ .brightness_set = global_led_set,
+ .brightness_get = global_led_get,
+ .name = "alienware::global_brightness",
+};
+
+/*
+ * Lighting control state device attribute (Global)
+*/
+static ssize_t show_control_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (lighting_control_state == LEGACY_BOOTING)
+ return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n");
+ else if (lighting_control_state == LEGACY_SUSPEND)
+ return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n");
+ return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n");
+}
+
+static ssize_t store_control_state(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ long unsigned int val;
+ if (strcmp(buf, "booting\n") == 0)
+ val = LEGACY_BOOTING;
+ else if (strcmp(buf, "suspend\n") == 0)
+ val = LEGACY_SUSPEND;
+ else if (interface == LEGACY)
+ val = LEGACY_RUNNING;
+ else
+ val = WMAX_RUNNING;
+ lighting_control_state = val;
+ pr_debug("alienware-wmi: updated control state to %d\n",
+ lighting_control_state);
+ return count;
+}
+
+static DEVICE_ATTR(lighting_control_state, 0644, show_control_state,
+ store_control_state);
+
+static int alienware_zone_init(struct platform_device *dev)
+{
+ int i;
+ char buffer[10];
+ char *name;
+
+ if (interface == WMAX) {
+ global_led.max_brightness = 100;
+ lighting_control_state = WMAX_RUNNING;
+ } else if (interface == LEGACY) {
+ global_led.max_brightness = 0x0F;
+ lighting_control_state = LEGACY_RUNNING;
+ }
+ global_brightness = global_led.max_brightness;
+
+ /*
+ * - zone_dev_attrs num_zones + 1 is for individual zones and then
+ * null terminated
+ * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs +
+ * the lighting control + null terminated
+ * - zone_data num_zones is for the distinct zones
+ */
+ zone_dev_attrs =
+ kzalloc(sizeof(struct device_attribute) * (quirks->num_zones + 1),
+ GFP_KERNEL);
+ if (!zone_dev_attrs)
+ return -ENOMEM;
+
+ zone_attrs =
+ kzalloc(sizeof(struct attribute *) * (quirks->num_zones + 2),
+ GFP_KERNEL);
+ if (!zone_attrs)
+ return -ENOMEM;
+
+ zone_data =
+ kzalloc(sizeof(struct platform_zone) * (quirks->num_zones),
+ GFP_KERNEL);
+ if (!zone_data)
+ return -ENOMEM;
+
+ for (i = 0; i < quirks->num_zones; i++) {
+ sprintf(buffer, "zone%02X", i);
+ name = kstrdup(buffer, GFP_KERNEL);
+ if (name == NULL)
+ return 1;
+ sysfs_attr_init(&zone_dev_attrs[i].attr);
+ zone_dev_attrs[i].attr.name = name;
+ zone_dev_attrs[i].attr.mode = 0644;
+ zone_dev_attrs[i].show = zone_show;
+ zone_dev_attrs[i].store = zone_set;
+ zone_data[i].location = i;
+ zone_attrs[i] = &zone_dev_attrs[i].attr;
+ zone_data[i].attr = &zone_dev_attrs[i];
+ }
+ zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr;
+ zone_attribute_group.attrs = zone_attrs;
+
+ led_classdev_register(&dev->dev, &global_led);
+
+ return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group);
+}
+
+static void alienware_zone_exit(struct platform_device *dev)
+{
+ sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group);
+ led_classdev_unregister(&global_led);
+ if (zone_dev_attrs) {
+ int i;
+ for (i = 0; i < quirks->num_zones; i++)
+ kfree(zone_dev_attrs[i].attr.name);
+ }
+ kfree(zone_dev_attrs);
+ kfree(zone_data);
+ kfree(zone_attrs);
+}
+
+/*
+ The HDMI mux sysfs node indicates the status of the HDMI input mux.
+ It can toggle between standard system GPU output and HDMI input.
+*/
+static ssize_t show_hdmi(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ acpi_status status;
+ struct acpi_buffer input;
+ union acpi_object *obj;
+ u32 tmp = 0;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct hdmi_args in_args = {
+ .arg = 0,
+ };
+ input.length = (acpi_size) sizeof(in_args);
+ input.pointer = &in_args;
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ WMAX_METHOD_HDMI_STATUS, &input, &output);
+
+ if (ACPI_SUCCESS(status)) {
+ obj = (union acpi_object *)output.pointer;
+ if (obj && obj->type == ACPI_TYPE_INTEGER)
+ tmp = (u32) obj->integer.value;
+ if (tmp == 1)
+ return scnprintf(buf, PAGE_SIZE,
+ "[input] gpu unknown\n");
+ else if (tmp == 2)
+ return scnprintf(buf, PAGE_SIZE,
+ "input [gpu] unknown\n");
+ }
+ pr_err("alienware-wmi: unknown HDMI status: %d\n", status);
+ return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n");
+}
+
+static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_buffer input;
+ acpi_status status;
+ struct hdmi_args args;
+ if (strcmp(buf, "gpu\n") == 0)
+ args.arg = 1;
+ else if (strcmp(buf, "input\n") == 0)
+ args.arg = 2;
+ else
+ args.arg = 3;
+ pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
+ input.length = (acpi_size) sizeof(args);
+ input.pointer = &args;
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ WMAX_METHOD_HDMI_SOURCE, &input, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
+ status);
+ return count;
+}
+
+static DEVICE_ATTR(hdmi, S_IRUGO | S_IWUSR, show_hdmi, toggle_hdmi);
+
+static void remove_hdmi(struct platform_device *device)
+{
+ device_remove_file(&device->dev, &dev_attr_hdmi);
+}
+
+static int create_hdmi(void)
+{
+ int ret = -ENOMEM;
+ ret = device_create_file(&platform_device->dev, &dev_attr_hdmi);
+ if (ret)
+ goto error_create_hdmi;
+ return 0;
+
+error_create_hdmi:
+ remove_hdmi(platform_device);
+ return ret;
+}
+
+static int __init alienware_wmi_init(void)
+{
+ int ret;
+
+ if (wmi_has_guid(LEGACY_CONTROL_GUID))
+ interface = LEGACY;
+ else if (wmi_has_guid(WMAX_CONTROL_GUID))
+ interface = WMAX;
+ else {
+ pr_warn("alienware-wmi: No known WMI GUID found\n");
+ return -ENODEV;
+ }
+
+ dmi_check_system(alienware_quirks);
+ if (quirks == NULL)
+ quirks = &quirk_unknown;
+
+ ret = platform_driver_register(&platform_driver);
+ if (ret)
+ goto fail_platform_driver;
+ platform_device = platform_device_alloc("alienware-wmi", -1);
+ if (!platform_device) {
+ ret = -ENOMEM;
+ goto fail_platform_device1;
+ }
+ ret = platform_device_add(platform_device);
+ if (ret)
+ goto fail_platform_device2;
+
+ if (interface == WMAX) {
+ ret = create_hdmi();
+ if (ret)
+ goto fail_prep_hdmi;
+ }
+
+ ret = alienware_zone_init(platform_device);
+ if (ret)
+ goto fail_prep_zones;
+
+ return 0;
+
+fail_prep_zones:
+ alienware_zone_exit(platform_device);
+fail_prep_hdmi:
+ platform_device_del(platform_device);
+fail_platform_device2:
+ platform_device_put(platform_device);
+fail_platform_device1:
+ platform_driver_unregister(&platform_driver);
+fail_platform_driver:
+ return ret;
+}
+
+module_init(alienware_wmi_init);
+
+static void __exit alienware_wmi_exit(void)
+{
+ if (platform_device) {
+ alienware_zone_exit(platform_device);
+ remove_hdmi(platform_device);
+ platform_device_unregister(platform_device);
+ platform_driver_unregister(&platform_driver);
+ }
+}
+
+module_exit(alienware_wmi_exit);
diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c
index 570926c10014..c3784baceae3 100644
--- a/drivers/platform/x86/fujitsu-tablet.c
+++ b/drivers/platform/x86/fujitsu-tablet.c
@@ -71,6 +71,44 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = {
KEY_LEFTALT
};
+static unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_SCROLLDOWN,
+ KEY_SCROLLUP,
+ KEY_CYCLEWINDOWS,
+ KEY_LEFTCTRL,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_LEFTMETA
+};
+
+static unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = {
+ KEY_RESERVED,
+ KEY_VOLUMEDOWN,
+ KEY_VOLUMEUP,
+ KEY_CYCLEWINDOWS,
+ KEY_PROG1,
+ KEY_PROG2,
+ KEY_LEFTMETA,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+};
+
static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = {
KEY_RESERVED,
KEY_RESERVED,
@@ -302,6 +340,33 @@ static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
static const struct dmi_system_id dmi_ids[] __initconst = {
{
.callback = fujitsu_dmi_lifebook,
+ .ident = "Fujitsu Lifebook T901",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901")
+ },
+ .driver_data = keymap_Lifebook_T901
+ },
+ {
+ .callback = fujitsu_dmi_lifebook,
+ .ident = "Fujitsu Lifebook T901",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901")
+ },
+ .driver_data = keymap_Lifebook_T901
+ },
+ {
+ .callback = fujitsu_dmi_lifebook,
+ .ident = "Fujitsu Lifebook T902",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902")
+ },
+ .driver_data = keymap_Lifebook_T902
+ },
+ {
+ .callback = fujitsu_dmi_lifebook,
.ident = "Fujitsu Siemens P/T Series",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
diff --git a/drivers/platform/x86/intel_baytrail.c b/drivers/platform/x86/intel_baytrail.c
deleted file mode 100644
index f96626b17260..000000000000
--- a/drivers/platform/x86/intel_baytrail.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Baytrail IOSF-SB MailBox Interface Driver
- * Copyright (c) 2013, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- *
- * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a
- * mailbox interface (MBI) to communicate with mutiple devices. This
- * driver implements BayTrail-specific access to this interface.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/pci.h>
-
-#include "intel_baytrail.h"
-
-static DEFINE_SPINLOCK(iosf_mbi_lock);
-
-static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
-{
- return (op << 24) | (port << 16) | (offset << 8) | BT_MBI_ENABLE;
-}
-
-static struct pci_dev *mbi_pdev; /* one mbi device */
-
-/* Hold lock before calling */
-static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
-{
- int result;
-
- if (!mbi_pdev)
- return -ENODEV;
-
- if (mcrx) {
- result = pci_write_config_dword(mbi_pdev,
- BT_MBI_MCRX_OFFSET, mcrx);
- if (result < 0)
- goto iosf_mbi_read_err;
- }
-
- result = pci_write_config_dword(mbi_pdev,
- BT_MBI_MCR_OFFSET, mcr);
- if (result < 0)
- goto iosf_mbi_read_err;
-
- result = pci_read_config_dword(mbi_pdev,
- BT_MBI_MDR_OFFSET, mdr);
- if (result < 0)
- goto iosf_mbi_read_err;
-
- return 0;
-
-iosf_mbi_read_err:
- dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n",
- result);
- return result;
-}
-
-/* Hold lock before calling */
-static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
-{
- int result;
-
- if (!mbi_pdev)
- return -ENODEV;
-
- result = pci_write_config_dword(mbi_pdev,
- BT_MBI_MDR_OFFSET, mdr);
- if (result < 0)
- goto iosf_mbi_write_err;
-
- if (mcrx) {
- result = pci_write_config_dword(mbi_pdev,
- BT_MBI_MCRX_OFFSET, mcrx);
- if (result < 0)
- goto iosf_mbi_write_err;
- }
-
- result = pci_write_config_dword(mbi_pdev,
- BT_MBI_MCR_OFFSET, mcr);
- if (result < 0)
- goto iosf_mbi_write_err;
-
- return 0;
-
-iosf_mbi_write_err:
- dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n",
- result);
- return result;
-}
-
-int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
-{
- u32 mcr, mcrx;
- unsigned long flags;
- int ret;
-
- /*Access to the GFX unit is handled by GPU code */
- BUG_ON(port == BT_MBI_UNIT_GFX);
-
- mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
- mcrx = offset & BT_MBI_MASK_HI;
-
- spin_lock_irqsave(&iosf_mbi_lock, flags);
- ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
- spin_unlock_irqrestore(&iosf_mbi_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(bt_mbi_read);
-
-int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
-{
- u32 mcr, mcrx;
- unsigned long flags;
- int ret;
-
- /*Access to the GFX unit is handled by GPU code */
- BUG_ON(port == BT_MBI_UNIT_GFX);
-
- mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
- mcrx = offset & BT_MBI_MASK_HI;
-
- spin_lock_irqsave(&iosf_mbi_lock, flags);
- ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
- spin_unlock_irqrestore(&iosf_mbi_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(bt_mbi_write);
-
-int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
-{
- u32 mcr, mcrx;
- u32 value;
- unsigned long flags;
- int ret;
-
- /*Access to the GFX unit is handled by GPU code */
- BUG_ON(port == BT_MBI_UNIT_GFX);
-
- mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
- mcrx = offset & BT_MBI_MASK_HI;
-
- spin_lock_irqsave(&iosf_mbi_lock, flags);
-
- /* Read current mdr value */
- ret = iosf_mbi_pci_read_mdr(mcrx, mcr & BT_MBI_RD_MASK, &value);
- if (ret < 0) {
- spin_unlock_irqrestore(&iosf_mbi_lock, flags);
- return ret;
- }
-
- /* Apply mask */
- value &= ~mask;
- mdr &= mask;
- value |= mdr;
-
- /* Write back */
- ret = iosf_mbi_pci_write_mdr(mcrx, mcr | BT_MBI_WR_MASK, value);
-
- spin_unlock_irqrestore(&iosf_mbi_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(bt_mbi_modify);
-
-static int iosf_mbi_probe(struct pci_dev *pdev,
- const struct pci_device_id *unused)
-{
- int ret;
-
- ret = pci_enable_device(pdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "error: could not enable device\n");
- return ret;
- }
-
- mbi_pdev = pci_dev_get(pdev);
- return 0;
-}
-
-static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F00) },
- { 0, },
-};
-MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
-
-static struct pci_driver iosf_mbi_pci_driver = {
- .name = "iosf_mbi_pci",
- .probe = iosf_mbi_probe,
- .id_table = iosf_mbi_pci_ids,
-};
-
-static int __init bt_mbi_init(void)
-{
- return pci_register_driver(&iosf_mbi_pci_driver);
-}
-
-static void __exit bt_mbi_exit(void)
-{
- pci_unregister_driver(&iosf_mbi_pci_driver);
- if (mbi_pdev) {
- pci_dev_put(mbi_pdev);
- mbi_pdev = NULL;
- }
-}
-
-module_init(bt_mbi_init);
-module_exit(bt_mbi_exit);
-
-MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
-MODULE_DESCRIPTION("BayTrail Mailbox Interface accessor");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel_baytrail.h b/drivers/platform/x86/intel_baytrail.h
deleted file mode 100644
index 8bcc311262e9..000000000000
--- a/drivers/platform/x86/intel_baytrail.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * intel_baytrail.h: MailBox access support for Intel BayTrail platforms
- */
-
-#ifndef INTEL_BAYTRAIL_MBI_SYMS_H
-#define INTEL_BAYTRAIL_MBI_SYMS_H
-
-#define BT_MBI_MCR_OFFSET 0xD0
-#define BT_MBI_MDR_OFFSET 0xD4
-#define BT_MBI_MCRX_OFFSET 0xD8
-
-#define BT_MBI_RD_MASK 0xFEFFFFFF
-#define BT_MBI_WR_MASK 0X01000000
-
-#define BT_MBI_MASK_HI 0xFFFFFF00
-#define BT_MBI_MASK_LO 0x000000FF
-#define BT_MBI_ENABLE 0xF0
-
-/* BT-SB unit access methods */
-#define BT_MBI_UNIT_AUNIT 0x00
-#define BT_MBI_UNIT_SMC 0x01
-#define BT_MBI_UNIT_CPU 0x02
-#define BT_MBI_UNIT_BUNIT 0x03
-#define BT_MBI_UNIT_PMC 0x04
-#define BT_MBI_UNIT_GFX 0x06
-#define BT_MBI_UNIT_SMI 0x0C
-#define BT_MBI_UNIT_USB 0x43
-#define BT_MBI_UNIT_SATA 0xA3
-#define BT_MBI_UNIT_PCIE 0xA6
-
-/* Read/write opcodes */
-#define BT_MBI_AUNIT_READ 0x10
-#define BT_MBI_AUNIT_WRITE 0x11
-#define BT_MBI_SMC_READ 0x10
-#define BT_MBI_SMC_WRITE 0x11
-#define BT_MBI_CPU_READ 0x10
-#define BT_MBI_CPU_WRITE 0x11
-#define BT_MBI_BUNIT_READ 0x10
-#define BT_MBI_BUNIT_WRITE 0x11
-#define BT_MBI_PMC_READ 0x06
-#define BT_MBI_PMC_WRITE 0x07
-#define BT_MBI_GFX_READ 0x00
-#define BT_MBI_GFX_WRITE 0x01
-#define BT_MBI_SMIO_READ 0x06
-#define BT_MBI_SMIO_WRITE 0x07
-#define BT_MBI_USB_READ 0x06
-#define BT_MBI_USB_WRITE 0x07
-#define BT_MBI_SATA_READ 0x00
-#define BT_MBI_SATA_WRITE 0x01
-#define BT_MBI_PCIE_READ 0x00
-#define BT_MBI_PCIE_WRITE 0x01
-
-/**
- * bt_mbi_read() - MailBox Interface read command
- * @port: port indicating subunit being accessed
- * @opcode: port specific read or write opcode
- * @offset: register address offset
- * @mdr: register data to be read
- *
- * Locking is handled by spinlock - cannot sleep.
- * Return: Nonzero on error
- */
-int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr);
-
-/**
- * bt_mbi_write() - MailBox unmasked write command
- * @port: port indicating subunit being accessed
- * @opcode: port specific read or write opcode
- * @offset: register address offset
- * @mdr: register data to be written
- *
- * Locking is handled by spinlock - cannot sleep.
- * Return: Nonzero on error
- */
-int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr);
-
-/**
- * bt_mbi_modify() - MailBox masked write command
- * @port: port indicating subunit being accessed
- * @opcode: port specific read or write opcode
- * @offset: register address offset
- * @mdr: register data being modified
- * @mask: mask indicating bits in mdr to be modified
- *
- * Locking is handled by spinlock - cannot sleep.
- * Return: Nonzero on error
- */
-int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask);
-
-#endif /* INTEL_BAYTRAIL_MBI_SYMS_H */
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 609d38779b26..3f870972247c 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -449,6 +449,7 @@ static struct attribute_group pcc_attr_group = {
/* hotkey input device driver */
+static int sleep_keydown_seen;
static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
{
struct input_dev *hotk_input_dev = pcc->input_dev;
@@ -462,6 +463,16 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
"error getting hotkey status\n"));
return;
}
+
+ /* hack: some firmware sends no key down for sleep / hibernate */
+ if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) {
+ if (result & 0x80)
+ sleep_keydown_seen = 1;
+ if (!sleep_keydown_seen)
+ sparse_keymap_report_event(hotk_input_dev,
+ result & 0xf, 0x80, false);
+ }
+
if (!sparse_keymap_report_event(hotk_input_dev,
result & 0xf, result & 0x80, false))
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 8f8551a63cc0..9c5a07417b2b 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -76,8 +76,6 @@ do { \
pr_warn(fmt, ##__VA_ARGS__); \
} while (0)
-#define SONY_LAPTOP_DRIVER_VERSION "0.6"
-
#define SONY_NC_CLASS "sony-nc"
#define SONY_NC_HID "SNY5001"
#define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver"
@@ -89,7 +87,6 @@ do { \
MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
MODULE_LICENSE("GPL");
-MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
static int debug;
module_param(debug, int, 0);
@@ -129,7 +126,8 @@ static int kbd_backlight = -1;
module_param(kbd_backlight, int, 0444);
MODULE_PARM_DESC(kbd_backlight,
"set this to 0 to disable keyboard backlight, "
- "1 to enable it (default: no change from current value)");
+ "1 to enable it with automatic control and 2 to have it always "
+ "on (default: no change from current value)");
static int kbd_backlight_timeout = -1;
module_param(kbd_backlight_timeout, int, 0444);
@@ -152,7 +150,8 @@ static void sony_nc_battery_care_cleanup(struct platform_device *pd);
static int sony_nc_thermal_setup(struct platform_device *pd);
static void sony_nc_thermal_cleanup(struct platform_device *pd);
-static int sony_nc_lid_resume_setup(struct platform_device *pd);
+static int sony_nc_lid_resume_setup(struct platform_device *pd,
+ unsigned int handle);
static void sony_nc_lid_resume_cleanup(struct platform_device *pd);
static int sony_nc_gfx_switch_setup(struct platform_device *pd,
@@ -163,6 +162,21 @@ static int __sony_nc_gfx_switch_status_get(void);
static int sony_nc_highspeed_charging_setup(struct platform_device *pd);
static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd);
+static int sony_nc_lowbatt_setup(struct platform_device *pd);
+static void sony_nc_lowbatt_cleanup(struct platform_device *pd);
+
+static int sony_nc_fanspeed_setup(struct platform_device *pd);
+static void sony_nc_fanspeed_cleanup(struct platform_device *pd);
+
+static int sony_nc_usb_charge_setup(struct platform_device *pd);
+static void sony_nc_usb_charge_cleanup(struct platform_device *pd);
+
+static int sony_nc_panelid_setup(struct platform_device *pd);
+static void sony_nc_panelid_cleanup(struct platform_device *pd);
+
+static int sony_nc_smart_conn_setup(struct platform_device *pd);
+static void sony_nc_smart_conn_cleanup(struct platform_device *pd);
+
static int sony_nc_touchpad_setup(struct platform_device *pd,
unsigned int handle);
static void sony_nc_touchpad_cleanup(struct platform_device *pd);
@@ -1122,6 +1136,8 @@ static struct sony_nc_event sony_100_events[] = {
{ 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0xa6, SONYPI_EVENT_HELP_PRESSED },
{ 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED },
+ { 0xa8, SONYPI_EVENT_FNKEY_1 },
+ { 0x28, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0, 0 },
};
@@ -1339,7 +1355,8 @@ static void sony_nc_function_setup(struct acpi_device *device,
result);
break;
case 0x0119:
- result = sony_nc_lid_resume_setup(pf_device);
+ case 0x015D:
+ result = sony_nc_lid_resume_setup(pf_device, handle);
if (result)
pr_err("couldn't set up lid resume function (%d)\n",
result);
@@ -1381,6 +1398,36 @@ static void sony_nc_function_setup(struct acpi_device *device,
pr_err("couldn't set up keyboard backlight function (%d)\n",
result);
break;
+ case 0x0121:
+ result = sony_nc_lowbatt_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up low battery function (%d)\n",
+ result);
+ break;
+ case 0x0149:
+ result = sony_nc_fanspeed_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up fan speed function (%d)\n",
+ result);
+ break;
+ case 0x0155:
+ result = sony_nc_usb_charge_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up USB charge support (%d)\n",
+ result);
+ break;
+ case 0x011D:
+ result = sony_nc_panelid_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up panel ID function (%d)\n",
+ result);
+ break;
+ case 0x0168:
+ result = sony_nc_smart_conn_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up smart connect support (%d)\n",
+ result);
+ break;
default:
continue;
}
@@ -1420,6 +1467,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
sony_nc_battery_care_cleanup(pd);
break;
case 0x0119:
+ case 0x015D:
sony_nc_lid_resume_cleanup(pd);
break;
case 0x0122:
@@ -1444,6 +1492,21 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
case 0x0163:
sony_nc_kbd_backlight_cleanup(pd, handle);
break;
+ case 0x0121:
+ sony_nc_lowbatt_cleanup(pd);
+ break;
+ case 0x0149:
+ sony_nc_fanspeed_cleanup(pd);
+ break;
+ case 0x0155:
+ sony_nc_usb_charge_cleanup(pd);
+ break;
+ case 0x011D:
+ sony_nc_panelid_cleanup(pd);
+ break;
+ case 0x0168:
+ sony_nc_smart_conn_cleanup(pd);
+ break;
default:
continue;
}
@@ -1719,7 +1782,7 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
{
int result;
- if (value > 1)
+ if (value > 2)
return -EINVAL;
if (sony_call_snc_handle(kbdbl_ctl->handle,
@@ -1727,8 +1790,10 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
return -EIO;
/* Try to turn the light on/off immediately */
- sony_call_snc_handle(kbdbl_ctl->handle,
- (value << 0x10) | (kbdbl_ctl->base + 0x100), &result);
+ if (value != 1)
+ sony_call_snc_handle(kbdbl_ctl->handle,
+ (value << 0x0f) | (kbdbl_ctl->base + 0x100),
+ &result);
kbdbl_ctl->mode = value;
@@ -2221,9 +2286,14 @@ static void sony_nc_thermal_resume(void)
#endif
/* resume on LID open */
+#define LID_RESUME_S5 0
+#define LID_RESUME_S4 1
+#define LID_RESUME_S3 2
+#define LID_RESUME_MAX 3
struct snc_lid_resume_control {
- struct device_attribute attrs[3];
+ struct device_attribute attrs[LID_RESUME_MAX];
unsigned int status;
+ int handle;
};
static struct snc_lid_resume_control *lid_ctl;
@@ -2231,8 +2301,9 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
- unsigned int result, pos;
+ unsigned int result;
unsigned long value;
+ unsigned int pos = LID_RESUME_S5;
if (count > 31)
return -EINVAL;
@@ -2245,21 +2316,21 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,
* +--------------+
* 2 1 0
*/
- if (strcmp(attr->attr.name, "lid_resume_S3") == 0)
- pos = 2;
- else if (strcmp(attr->attr.name, "lid_resume_S4") == 0)
- pos = 1;
- else if (strcmp(attr->attr.name, "lid_resume_S5") == 0)
- pos = 0;
- else
- return -EINVAL;
+ while (pos < LID_RESUME_MAX) {
+ if (&lid_ctl->attrs[pos].attr == &attr->attr)
+ break;
+ pos++;
+ }
+ if (pos == LID_RESUME_MAX)
+ return -EINVAL;
if (value)
value = lid_ctl->status | (1 << pos);
else
value = lid_ctl->status & ~(1 << pos);
- if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result))
+ if (sony_call_snc_handle(lid_ctl->handle, value << 0x10 | 0x0100,
+ &result))
return -EIO;
lid_ctl->status = value;
@@ -2268,29 +2339,27 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,
}
static ssize_t sony_nc_lid_resume_show(struct device *dev,
- struct device_attribute *attr, char *buffer)
+ struct device_attribute *attr,
+ char *buffer)
{
- unsigned int pos;
+ unsigned int pos = LID_RESUME_S5;
- if (strcmp(attr->attr.name, "lid_resume_S3") == 0)
- pos = 2;
- else if (strcmp(attr->attr.name, "lid_resume_S4") == 0)
- pos = 1;
- else if (strcmp(attr->attr.name, "lid_resume_S5") == 0)
- pos = 0;
- else
- return -EINVAL;
-
- return snprintf(buffer, PAGE_SIZE, "%d\n",
- (lid_ctl->status >> pos) & 0x01);
+ while (pos < LID_RESUME_MAX) {
+ if (&lid_ctl->attrs[pos].attr == &attr->attr)
+ return snprintf(buffer, PAGE_SIZE, "%d\n",
+ (lid_ctl->status >> pos) & 0x01);
+ pos++;
+ }
+ return -EINVAL;
}
-static int sony_nc_lid_resume_setup(struct platform_device *pd)
+static int sony_nc_lid_resume_setup(struct platform_device *pd,
+ unsigned int handle)
{
unsigned int result;
int i;
- if (sony_call_snc_handle(0x0119, 0x0000, &result))
+ if (sony_call_snc_handle(handle, 0x0000, &result))
return -EIO;
lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL);
@@ -2298,26 +2367,29 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd)
return -ENOMEM;
lid_ctl->status = result & 0x7;
+ lid_ctl->handle = handle;
sysfs_attr_init(&lid_ctl->attrs[0].attr);
- lid_ctl->attrs[0].attr.name = "lid_resume_S3";
- lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR;
- lid_ctl->attrs[0].show = sony_nc_lid_resume_show;
- lid_ctl->attrs[0].store = sony_nc_lid_resume_store;
-
- sysfs_attr_init(&lid_ctl->attrs[1].attr);
- lid_ctl->attrs[1].attr.name = "lid_resume_S4";
- lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR;
- lid_ctl->attrs[1].show = sony_nc_lid_resume_show;
- lid_ctl->attrs[1].store = sony_nc_lid_resume_store;
-
- sysfs_attr_init(&lid_ctl->attrs[2].attr);
- lid_ctl->attrs[2].attr.name = "lid_resume_S5";
- lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR;
- lid_ctl->attrs[2].show = sony_nc_lid_resume_show;
- lid_ctl->attrs[2].store = sony_nc_lid_resume_store;
-
- for (i = 0; i < 3; i++) {
+ lid_ctl->attrs[LID_RESUME_S5].attr.name = "lid_resume_S5";
+ lid_ctl->attrs[LID_RESUME_S5].attr.mode = S_IRUGO | S_IWUSR;
+ lid_ctl->attrs[LID_RESUME_S5].show = sony_nc_lid_resume_show;
+ lid_ctl->attrs[LID_RESUME_S5].store = sony_nc_lid_resume_store;
+
+ if (handle == 0x0119) {
+ sysfs_attr_init(&lid_ctl->attrs[1].attr);
+ lid_ctl->attrs[LID_RESUME_S4].attr.name = "lid_resume_S4";
+ lid_ctl->attrs[LID_RESUME_S4].attr.mode = S_IRUGO | S_IWUSR;
+ lid_ctl->attrs[LID_RESUME_S4].show = sony_nc_lid_resume_show;
+ lid_ctl->attrs[LID_RESUME_S4].store = sony_nc_lid_resume_store;
+
+ sysfs_attr_init(&lid_ctl->attrs[2].attr);
+ lid_ctl->attrs[LID_RESUME_S3].attr.name = "lid_resume_S3";
+ lid_ctl->attrs[LID_RESUME_S3].attr.mode = S_IRUGO | S_IWUSR;
+ lid_ctl->attrs[LID_RESUME_S3].show = sony_nc_lid_resume_show;
+ lid_ctl->attrs[LID_RESUME_S3].store = sony_nc_lid_resume_store;
+ }
+ for (i = 0; i < LID_RESUME_MAX &&
+ lid_ctl->attrs[LID_RESUME_S3].attr.name; i++) {
result = device_create_file(&pd->dev, &lid_ctl->attrs[i]);
if (result)
goto liderror;
@@ -2340,8 +2412,12 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd)
int i;
if (lid_ctl) {
- for (i = 0; i < 3; i++)
+ for (i = 0; i < LID_RESUME_MAX; i++) {
+ if (!lid_ctl->attrs[i].attr.name)
+ break;
+
device_remove_file(&pd->dev, &lid_ctl->attrs[i]);
+ }
kfree(lid_ctl);
lid_ctl = NULL;
@@ -2524,6 +2600,355 @@ static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd)
}
}
+/* low battery function */
+static struct device_attribute *lowbatt_handle;
+
+static ssize_t sony_nc_lowbatt_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0121, value << 8, &result))
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t sony_nc_lowbatt_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0121, 0x0200, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 1);
+}
+
+static int sony_nc_lowbatt_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ lowbatt_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!lowbatt_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&lowbatt_handle->attr);
+ lowbatt_handle->attr.name = "lowbatt_hibernate";
+ lowbatt_handle->attr.mode = S_IRUGO | S_IWUSR;
+ lowbatt_handle->show = sony_nc_lowbatt_show;
+ lowbatt_handle->store = sony_nc_lowbatt_store;
+
+ result = device_create_file(&pd->dev, lowbatt_handle);
+ if (result) {
+ kfree(lowbatt_handle);
+ lowbatt_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_lowbatt_cleanup(struct platform_device *pd)
+{
+ if (lowbatt_handle) {
+ device_remove_file(&pd->dev, lowbatt_handle);
+ kfree(lowbatt_handle);
+ lowbatt_handle = NULL;
+ }
+}
+
+/* fan speed function */
+static struct device_attribute *fan_handle, *hsf_handle;
+
+static ssize_t sony_nc_hsfan_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0149, value << 0x10 | 0x0200, &result))
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t sony_nc_hsfan_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0149, 0x0100, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
+}
+
+static ssize_t sony_nc_fanspeed_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0149, 0x0300, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff);
+}
+
+static int sony_nc_fanspeed_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ fan_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!fan_handle)
+ return -ENOMEM;
+
+ hsf_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!hsf_handle) {
+ result = -ENOMEM;
+ goto out_hsf_handle_alloc;
+ }
+
+ sysfs_attr_init(&fan_handle->attr);
+ fan_handle->attr.name = "fanspeed";
+ fan_handle->attr.mode = S_IRUGO;
+ fan_handle->show = sony_nc_fanspeed_show;
+ fan_handle->store = NULL;
+
+ sysfs_attr_init(&hsf_handle->attr);
+ hsf_handle->attr.name = "fan_forced";
+ hsf_handle->attr.mode = S_IRUGO | S_IWUSR;
+ hsf_handle->show = sony_nc_hsfan_show;
+ hsf_handle->store = sony_nc_hsfan_store;
+
+ result = device_create_file(&pd->dev, fan_handle);
+ if (result)
+ goto out_fan_handle;
+
+ result = device_create_file(&pd->dev, hsf_handle);
+ if (result)
+ goto out_hsf_handle;
+
+ return 0;
+
+out_hsf_handle:
+ device_remove_file(&pd->dev, fan_handle);
+
+out_fan_handle:
+ kfree(hsf_handle);
+ hsf_handle = NULL;
+
+out_hsf_handle_alloc:
+ kfree(fan_handle);
+ fan_handle = NULL;
+ return result;
+}
+
+static void sony_nc_fanspeed_cleanup(struct platform_device *pd)
+{
+ if (fan_handle) {
+ device_remove_file(&pd->dev, fan_handle);
+ kfree(fan_handle);
+ fan_handle = NULL;
+ }
+ if (hsf_handle) {
+ device_remove_file(&pd->dev, hsf_handle);
+ kfree(hsf_handle);
+ hsf_handle = NULL;
+ }
+}
+
+/* USB charge function */
+static struct device_attribute *uc_handle;
+
+static ssize_t sony_nc_usb_charge_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0155, value << 0x10 | 0x0100, &result))
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t sony_nc_usb_charge_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0155, 0x0000, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
+}
+
+static int sony_nc_usb_charge_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0155, 0x0000, &result) || !(result & 0x01)) {
+ /* some models advertise the handle but have no implementation
+ * for it
+ */
+ pr_info("No USB Charge capability found\n");
+ return 0;
+ }
+
+ uc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!uc_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&uc_handle->attr);
+ uc_handle->attr.name = "usb_charge";
+ uc_handle->attr.mode = S_IRUGO | S_IWUSR;
+ uc_handle->show = sony_nc_usb_charge_show;
+ uc_handle->store = sony_nc_usb_charge_store;
+
+ result = device_create_file(&pd->dev, uc_handle);
+ if (result) {
+ kfree(uc_handle);
+ uc_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_usb_charge_cleanup(struct platform_device *pd)
+{
+ if (uc_handle) {
+ device_remove_file(&pd->dev, uc_handle);
+ kfree(uc_handle);
+ uc_handle = NULL;
+ }
+}
+
+/* Panel ID function */
+static struct device_attribute *panel_handle;
+
+static ssize_t sony_nc_panelid_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x011D, 0x0000, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result);
+}
+
+static int sony_nc_panelid_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ panel_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!panel_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&panel_handle->attr);
+ panel_handle->attr.name = "panel_id";
+ panel_handle->attr.mode = S_IRUGO;
+ panel_handle->show = sony_nc_panelid_show;
+ panel_handle->store = NULL;
+
+ result = device_create_file(&pd->dev, panel_handle);
+ if (result) {
+ kfree(panel_handle);
+ panel_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_panelid_cleanup(struct platform_device *pd)
+{
+ if (panel_handle) {
+ device_remove_file(&pd->dev, panel_handle);
+ kfree(panel_handle);
+ panel_handle = NULL;
+ }
+}
+
+/* smart connect function */
+static struct device_attribute *sc_handle;
+
+static ssize_t sony_nc_smart_conn_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0168, value << 0x10, &result))
+ return -EIO;
+
+ return count;
+}
+
+static int sony_nc_smart_conn_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ sc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!sc_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&sc_handle->attr);
+ sc_handle->attr.name = "smart_connect";
+ sc_handle->attr.mode = S_IWUSR;
+ sc_handle->show = NULL;
+ sc_handle->store = sony_nc_smart_conn_store;
+
+ result = device_create_file(&pd->dev, sc_handle);
+ if (result) {
+ kfree(sc_handle);
+ sc_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_smart_conn_cleanup(struct platform_device *pd)
+{
+ if (sc_handle) {
+ device_remove_file(&pd->dev, sc_handle);
+ kfree(sc_handle);
+ sc_handle = NULL;
+ }
+}
+
/* Touchpad enable/disable */
struct touchpad_control {
struct device_attribute attr;
@@ -2726,8 +3151,6 @@ static int sony_nc_add(struct acpi_device *device)
int result = 0;
struct sony_nc_value *item;
- pr_info("%s v%s\n", SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
-
sony_nc_acpi_device = device;
strcpy(acpi_device_class(device), "sony/hotkey");
@@ -2821,6 +3244,7 @@ static int sony_nc_add(struct acpi_device *device)
}
}
+ pr_info("SNC setup done.\n");
return 0;
out_sysfs:
@@ -4259,8 +4683,6 @@ static int sony_pic_add(struct acpi_device *device)
struct sony_pic_ioport *io, *tmp_io;
struct sony_pic_irq *irq, *tmp_irq;
- pr_info("%s v%s\n", SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
-
spic_dev.acpi_dev = device;
strcpy(acpi_device_class(device), "sony/hotkey");
sony_pic_detect_device_type(&spic_dev);
@@ -4360,6 +4782,7 @@ static int sony_pic_add(struct acpi_device *device)
if (result)
goto err_remove_pf;
+ pr_info("SPIC setup done.\n");
return 0;
err_remove_pf:
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 94bb6157c957..15e61c16736e 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -2321,53 +2321,55 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
}
}
-static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
- struct tp_nvram_state *newn,
- const u32 event_mask)
-{
-
#define TPACPI_COMPARE_KEY(__scancode, __member) \
- do { \
- if ((event_mask & (1 << __scancode)) && \
- oldn->__member != newn->__member) \
- tpacpi_hotkey_send_key(__scancode); \
- } while (0)
+do { \
+ if ((event_mask & (1 << __scancode)) && \
+ oldn->__member != newn->__member) \
+ tpacpi_hotkey_send_key(__scancode); \
+} while (0)
#define TPACPI_MAY_SEND_KEY(__scancode) \
- do { \
- if (event_mask & (1 << __scancode)) \
- tpacpi_hotkey_send_key(__scancode); \
- } while (0)
+do { \
+ if (event_mask & (1 << __scancode)) \
+ tpacpi_hotkey_send_key(__scancode); \
+} while (0)
- void issue_volchange(const unsigned int oldvol,
- const unsigned int newvol)
- {
- unsigned int i = oldvol;
+static void issue_volchange(const unsigned int oldvol,
+ const unsigned int newvol,
+ const u32 event_mask)
+{
+ unsigned int i = oldvol;
- while (i > newvol) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
- i--;
- }
- while (i < newvol) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
- i++;
- }
+ while (i > newvol) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
+ i--;
+ }
+ while (i < newvol) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
+ i++;
}
+}
- void issue_brightnesschange(const unsigned int oldbrt,
- const unsigned int newbrt)
- {
- unsigned int i = oldbrt;
+static void issue_brightnesschange(const unsigned int oldbrt,
+ const unsigned int newbrt,
+ const u32 event_mask)
+{
+ unsigned int i = oldbrt;
- while (i > newbrt) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
- i--;
- }
- while (i < newbrt) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
- i++;
- }
+ while (i > newbrt) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
+ i--;
}
+ while (i < newbrt) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
+ i++;
+ }
+}
+
+static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
+ struct tp_nvram_state *newn,
+ const u32 event_mask)
+{
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
@@ -2402,7 +2404,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
oldn->volume_level != newn->volume_level) {
/* recently muted, or repeated mute keypress, or
* multiple presses ending in mute */
- issue_volchange(oldn->volume_level, newn->volume_level);
+ issue_volchange(oldn->volume_level, newn->volume_level,
+ event_mask);
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
}
} else {
@@ -2412,7 +2415,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
}
if (oldn->volume_level != newn->volume_level) {
- issue_volchange(oldn->volume_level, newn->volume_level);
+ issue_volchange(oldn->volume_level, newn->volume_level,
+ event_mask);
} else if (oldn->volume_toggle != newn->volume_toggle) {
/* repeated vol up/down keypress at end of scale ? */
if (newn->volume_level == 0)
@@ -2425,7 +2429,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
/* handle brightness */
if (oldn->brightness_level != newn->brightness_level) {
issue_brightnesschange(oldn->brightness_level,
- newn->brightness_level);
+ newn->brightness_level, event_mask);
} else if (oldn->brightness_toggle != newn->brightness_toggle) {
/* repeated key presses that didn't change state */
if (newn->brightness_level == 0)
@@ -3437,6 +3441,106 @@ err_exit:
return (res < 0)? res : 1;
}
+/* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser
+ * mode, Web conference mode, Function mode and Lay-flat mode.
+ * We support Home mode and Function mode currently.
+ *
+ * Will consider support rest of modes in future.
+ *
+ */
+enum ADAPTIVE_KEY_MODE {
+ HOME_MODE,
+ WEB_BROWSER_MODE,
+ WEB_CONFERENCE_MODE,
+ FUNCTION_MODE,
+ LAYFLAT_MODE
+};
+
+const int adaptive_keyboard_modes[] = {
+ HOME_MODE,
+/* WEB_BROWSER_MODE = 2,
+ WEB_CONFERENCE_MODE = 3, */
+ FUNCTION_MODE
+};
+
+#define DFR_CHANGE_ROW 0x101
+#define DFR_SHOW_QUICKVIEW_ROW 0x102
+
+/* press Fn key a while second, it will switch to Function Mode. Then
+ * release Fn key, previous mode be restored.
+ */
+static bool adaptive_keyboard_mode_is_saved;
+static int adaptive_keyboard_prev_mode;
+
+static int adaptive_keyboard_get_next_mode(int mode)
+{
+ size_t i;
+ size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1;
+
+ for (i = 0; i <= max_mode; i++) {
+ if (adaptive_keyboard_modes[i] == mode)
+ break;
+ }
+
+ if (i >= max_mode)
+ i = 0;
+ else
+ i++;
+
+ return adaptive_keyboard_modes[i];
+}
+
+static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)
+{
+ u32 current_mode = 0;
+ int new_mode = 0;
+
+ switch (scancode) {
+ case DFR_CHANGE_ROW:
+ if (adaptive_keyboard_mode_is_saved) {
+ new_mode = adaptive_keyboard_prev_mode;
+ adaptive_keyboard_mode_is_saved = false;
+ } else {
+ if (!acpi_evalf(
+ hkey_handle, &current_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode\n");
+ return false;
+ } else {
+ new_mode = adaptive_keyboard_get_next_mode(
+ current_mode);
+ }
+ }
+
+ if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) {
+ pr_err("Cannot set adaptive keyboard mode\n");
+ return false;
+ }
+
+ return true;
+
+ case DFR_SHOW_QUICKVIEW_ROW:
+ if (!acpi_evalf(hkey_handle,
+ &adaptive_keyboard_prev_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode\n");
+ return false;
+ } else {
+ adaptive_keyboard_mode_is_saved = true;
+
+ if (!acpi_evalf(hkey_handle,
+ NULL, "STRW", "vd", FUNCTION_MODE)) {
+ pr_err("Cannot set adaptive keyboard mode\n");
+ return false;
+ }
+ }
+ return true;
+
+ default:
+ return false;
+ }
+}
+
static bool hotkey_notify_hotkey(const u32 hkey,
bool *send_acpi_ev,
bool *ignore_acpi_ev)
@@ -3456,6 +3560,8 @@ static bool hotkey_notify_hotkey(const u32 hkey,
*ignore_acpi_ev = true;
}
return true;
+ } else {
+ return adaptive_keyboard_hotkey_notify_hotkey(scancode);
}
return false;
}
@@ -3728,13 +3834,28 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
static void hotkey_suspend(void)
{
+ int hkeyv;
+
/* Do these on suspend, we get the events on early resume! */
hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
hotkey_autosleep_ack = 0;
+
+ /* save previous mode of adaptive keyboard of X1 Carbon */
+ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+ if ((hkeyv >> 8) == 2) {
+ if (!acpi_evalf(hkey_handle,
+ &adaptive_keyboard_prev_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode.\n");
+ }
+ }
+ }
}
static void hotkey_resume(void)
{
+ int hkeyv;
+
tpacpi_disable_brightness_delay();
if (hotkey_status_set(true) < 0 ||
@@ -3747,6 +3868,18 @@ static void hotkey_resume(void)
hotkey_wakeup_reason_notify_change();
hotkey_wakeup_hotunplug_complete_notify_change();
hotkey_poll_setup_safe(false);
+
+ /* restore previous mode of adapive keyboard of X1 Carbon */
+ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+ if ((hkeyv >> 8) == 2) {
+ if (!acpi_evalf(hkey_handle,
+ NULL,
+ "STRW", "vd",
+ adaptive_keyboard_prev_mode)) {
+ pr_err("Cannot set adaptive keyboard mode.\n");
+ }
+ }
+ }
}
/* procfs -------------------------------------------------------------- */
@@ -8447,9 +8580,21 @@ static void mute_led_exit(void)
tpacpi_led_set(i, false);
}
+static void mute_led_resume(void)
+{
+ int i;
+
+ for (i = 0; i < TPACPI_LED_MAX; i++) {
+ struct tp_led_table *t = &led_tables[i];
+ if (t->state >= 0)
+ mute_led_on_off(t, t->state);
+ }
+}
+
static struct ibm_struct mute_led_driver_data = {
.name = "mute_led",
.exit = mute_led_exit,
+ .resume = mute_led_resume,
};
/****************************************************************************
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 90dd7645a9e5..46473ca7566b 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -5,6 +5,7 @@
* Copyright (C) 2002-2004 John Belmonte
* Copyright (C) 2008 Philip Langdale
* Copyright (C) 2010 Pierre Ducroquet
+ * Copyright (C) 2014 Azael Avalos
*
* 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
@@ -37,7 +38,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define TOSHIBA_ACPI_VERSION "0.19"
+#define TOSHIBA_ACPI_VERSION "0.20"
#define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h>
@@ -77,6 +78,9 @@ MODULE_LICENSE("GPL");
* However the ACPI methods seem to be incomplete in some areas (for
* example they allow setting, but not reading, the LCD brightness value),
* so this is still useful.
+ *
+ * SCI stands for "System Configuration Interface" which aim is to
+ * conceal differences in hardware between different models.
*/
#define HCI_WORDS 6
@@ -84,12 +88,23 @@ MODULE_LICENSE("GPL");
/* operations */
#define HCI_SET 0xff00
#define HCI_GET 0xfe00
+#define SCI_OPEN 0xf100
+#define SCI_CLOSE 0xf200
+#define SCI_GET 0xf300
+#define SCI_SET 0xf400
/* return codes */
#define HCI_SUCCESS 0x0000
#define HCI_FAILURE 0x1000
#define HCI_NOT_SUPPORTED 0x8000
#define HCI_EMPTY 0x8c00
+#define HCI_DATA_NOT_AVAILABLE 0x8d20
+#define HCI_NOT_INITIALIZED 0x8d50
+#define SCI_OPEN_CLOSE_OK 0x0044
+#define SCI_ALREADY_OPEN 0x8100
+#define SCI_NOT_OPENED 0x8200
+#define SCI_INPUT_DATA_ERROR 0x8300
+#define SCI_NOT_PRESENT 0x8600
/* registers */
#define HCI_FAN 0x0004
@@ -99,13 +114,22 @@ MODULE_LICENSE("GPL");
#define HCI_HOTKEY_EVENT 0x001e
#define HCI_LCD_BRIGHTNESS 0x002a
#define HCI_WIRELESS 0x0056
+#define HCI_ACCELEROMETER 0x006d
+#define HCI_KBD_ILLUMINATION 0x0095
+#define HCI_ECO_MODE 0x0097
+#define HCI_ACCELEROMETER2 0x00a6
+#define SCI_ILLUMINATION 0x014e
+#define SCI_KBD_ILLUM_STATUS 0x015c
+#define SCI_TOUCHPAD 0x050e
/* field definitions */
+#define HCI_ACCEL_MASK 0x7fff
#define HCI_HOTKEY_DISABLE 0x0b
#define HCI_HOTKEY_ENABLE 0x09
#define HCI_LCD_BRIGHTNESS_BITS 3
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
+#define HCI_MISC_SHIFT 0x10
#define HCI_VIDEO_OUT_LCD 0x1
#define HCI_VIDEO_OUT_CRT 0x2
#define HCI_VIDEO_OUT_TV 0x4
@@ -113,6 +137,8 @@ MODULE_LICENSE("GPL");
#define HCI_WIRELESS_BT_PRESENT 0x0f
#define HCI_WIRELESS_BT_ATTACH 0x40
#define HCI_WIRELESS_BT_POWER 0x80
+#define SCI_KBD_MODE_FNZ 0x1
+#define SCI_KBD_MODE_AUTO 0x2
struct toshiba_acpi_dev {
struct acpi_device *acpi_dev;
@@ -122,10 +148,14 @@ struct toshiba_acpi_dev {
struct work_struct hotkey_work;
struct backlight_device *backlight_dev;
struct led_classdev led_dev;
+ struct led_classdev kbd_led;
+ struct led_classdev eco_led;
int force_fan;
int last_key_event;
int key_event_valid;
+ int kbd_mode;
+ int kbd_time;
unsigned int illumination_supported:1;
unsigned int video_supported:1;
@@ -134,6 +164,12 @@ struct toshiba_acpi_dev {
unsigned int ntfy_supported:1;
unsigned int info_supported:1;
unsigned int tr_backlight_supported:1;
+ unsigned int kbd_illum_supported:1;
+ unsigned int kbd_led_registered:1;
+ unsigned int touchpad_supported:1;
+ unsigned int eco_supported:1;
+ unsigned int accelerometer_supported:1;
+ unsigned int sysfs_created:1;
struct mutex mutex;
};
@@ -280,21 +316,94 @@ static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,
return status;
}
+/* common sci tasks
+ */
+
+static int sci_open(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to open SCI failed\n");
+ return 0;
+ }
+
+ if (out[0] == SCI_OPEN_CLOSE_OK) {
+ return 1;
+ } else if (out[0] == SCI_ALREADY_OPEN) {
+ pr_info("Toshiba SCI already opened\n");
+ return 1;
+ } else if (out[0] == SCI_NOT_PRESENT) {
+ pr_info("Toshiba SCI is not present\n");
+ }
+
+ return 0;
+}
+
+static void sci_close(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to close SCI failed\n");
+ return;
+ }
+
+ if (out[0] == SCI_OPEN_CLOSE_OK)
+ return;
+ else if (out[0] == SCI_NOT_OPENED)
+ pr_info("Toshiba SCI not opened\n");
+ else if (out[0] == SCI_NOT_PRESENT)
+ pr_info("Toshiba SCI is not present\n");
+}
+
+static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 *out1, u32 *result)
+{
+ u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status = hci_raw(dev, in, out);
+ *out1 = out[2];
+ *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
+ return status;
+}
+
+static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 in1, u32 *result)
+{
+ u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status = hci_raw(dev, in, out);
+ *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
+ return status;
+}
+
/* Illumination support */
static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
{
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+ u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
- in[0] = 0xf100;
+ if (!sci_open(dev))
+ return 0;
+
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to query Illumination support failed\n");
+ return 0;
+ } else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) {
pr_info("Illumination device not available\n");
return 0;
}
- in[0] = 0xf400;
- status = hci_raw(dev, in, out);
+
return 1;
}
@@ -303,82 +412,270 @@ static void toshiba_illumination_set(struct led_classdev *cdev,
{
struct toshiba_acpi_dev *dev = container_of(cdev,
struct toshiba_acpi_dev, led_dev);
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
- u32 out[HCI_WORDS];
+ u32 state, result;
acpi_status status;
/* First request : initialize communication. */
- in[0] = 0xf100;
- status = hci_raw(dev, in, out);
+ if (!sci_open(dev))
+ return;
+
+ /* Switch the illumination on/off */
+ state = brightness ? 1 : 0;
+ status = sci_write(dev, SCI_ILLUMINATION, state, &result);
+ sci_close(dev);
if (ACPI_FAILURE(status)) {
- pr_info("Illumination device not available\n");
+ pr_err("ACPI call for illumination failed\n");
+ return;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Illumination not supported\n");
return;
}
+}
- if (brightness) {
- /* Switch the illumination on */
- in[0] = 0xf400;
- in[1] = 0x14e;
- in[2] = 1;
- status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed\n");
- return;
- }
- } else {
- /* Switch the illumination off */
- in[0] = 0xf400;
- in[1] = 0x14e;
- in[2] = 0;
- status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed.\n");
- return;
- }
+static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, led_dev);
+ u32 state, result;
+ acpi_status status;
+
+ /* First request : initialize communication. */
+ if (!sci_open(dev))
+ return LED_OFF;
+
+ /* Check the illumination */
+ status = sci_read(dev, SCI_ILLUMINATION, &state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call for illumination failed\n");
+ return LED_OFF;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Illumination not supported\n");
+ return LED_OFF;
}
- /* Last request : close communication. */
- in[0] = 0xf200;
- in[1] = 0;
- in[2] = 0;
- hci_raw(dev, in, out);
+ return state ? LED_FULL : LED_OFF;
}
-static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
+/* KBD Illumination */
+static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set KBD backlight status failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight status not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get KBD backlight status failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight status not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev)
{
struct toshiba_acpi_dev *dev = container_of(cdev,
- struct toshiba_acpi_dev, led_dev);
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+ struct toshiba_acpi_dev, kbd_led);
+ u32 state, result;
+ acpi_status status;
+
+ /* Check the keyboard backlight state */
+ status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get the keyboard backlight failed\n");
+ return LED_OFF;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight not supported\n");
+ return LED_OFF;
+ }
+
+ return state ? LED_FULL : LED_OFF;
+}
+
+static void toshiba_kbd_backlight_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, kbd_led);
+ u32 state, result;
+ acpi_status status;
+
+ /* Set the keyboard backlight state */
+ state = brightness ? 1 : 0;
+ status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set KBD Illumination mode failed\n");
+ return;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight not supported\n");
+ return;
+ }
+}
+
+/* TouchPad support */
+static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_write(dev, SCI_TOUCHPAD, state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status)) {
+ pr_err("ACPI call to set the touchpad failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_read(dev, SCI_TOUCHPAD, state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status)) {
+ pr_err("ACPI call to query the touchpad failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* Eco Mode support */
+static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
+{
+ acpi_status status;
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_info("ACPI call to get ECO led failed\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static enum led_brightness toshiba_eco_mode_get_status(struct led_classdev *cdev)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, eco_led);
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
- enum led_brightness result;
- /* First request : initialize communication. */
- in[0] = 0xf100;
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("Illumination device not available\n");
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get ECO led failed\n");
return LED_OFF;
}
- /* Check the illumination */
- in[0] = 0xf300;
- in[1] = 0x14e;
+ return out[2] ? LED_FULL : LED_OFF;
+}
+
+static void toshiba_eco_mode_set_status(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, eco_led);
+ u32 in[HCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Switch the Eco Mode led on/off */
+ in[2] = (brightness) ? 1 : 0;
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed.\n");
- return LED_OFF;
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set ECO led failed\n");
+ return;
}
+}
- result = out[2] ? LED_FULL : LED_OFF;
+/* Accelerometer support */
+static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Check if the accelerometer call exists,
+ * this call also serves as initialization
+ */
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to query the accelerometer failed\n");
+ return -EIO;
+ } else if (out[0] == HCI_DATA_NOT_AVAILABLE ||
+ out[0] == HCI_NOT_INITIALIZED) {
+ pr_err("Accelerometer not initialized\n");
+ return -EIO;
+ } else if (out[0] == HCI_NOT_SUPPORTED) {
+ pr_info("Accelerometer not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
+ u32 *xy, u32 *z)
+{
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Check the Accelerometer status */
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to query the accelerometer failed\n");
+ return -EIO;
+ }
- /* Last request : close communication. */
- in[0] = 0xf200;
- in[1] = 0;
- in[2] = 0;
- hci_raw(dev, in, out);
+ *xy = out[2];
+ *z = out[4];
- return result;
+ return 0;
}
/* Bluetooth rfkill handlers */
@@ -904,6 +1201,177 @@ static const struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
+/*
+ * Sysfs files
+ */
+
+static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int mode = -1;
+ int time = -1;
+
+ if (sscanf(buf, "%i", &mode) != 1 && (mode != 2 || mode != 1))
+ return -EINVAL;
+
+ /* Set the Keyboard Backlight Mode where:
+ * Mode - Auto (2) | FN-Z (1)
+ * Auto - KBD backlight turns off automatically in given time
+ * FN-Z - KBD backlight "toggles" when hotkey pressed
+ */
+ if (mode != -1 && toshiba->kbd_mode != mode) {
+ time = toshiba->kbd_time << HCI_MISC_SHIFT;
+ time = time + toshiba->kbd_mode;
+ if (toshiba_kbd_illum_status_set(toshiba, time) < 0)
+ return -EIO;
+ toshiba->kbd_mode = mode;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_kbd_bl_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 time;
+
+ if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
+ return -EIO;
+
+ return sprintf(buf, "%i\n", time & 0x07);
+}
+
+static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int time = -1;
+
+ if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60))
+ return -EINVAL;
+
+ /* Set the Keyboard Backlight Timeout: 0-60 seconds */
+ if (time != -1 && toshiba->kbd_time != time) {
+ time = time << HCI_MISC_SHIFT;
+ time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ?
+ time + 1 : time + 2;
+ if (toshiba_kbd_illum_status_set(toshiba, time) < 0)
+ return -EIO;
+ toshiba->kbd_time = time >> HCI_MISC_SHIFT;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 time;
+
+ if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
+ return -EIO;
+
+ return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT);
+}
+
+static ssize_t toshiba_touchpad_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int state;
+
+ /* Set the TouchPad on/off, 0 - Disable | 1 - Enable */
+ if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) {
+ if (toshiba_touchpad_set(toshiba, state) < 0)
+ return -EIO;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_touchpad_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 state;
+ int ret;
+
+ ret = toshiba_touchpad_get(toshiba, &state);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%i\n", state);
+}
+
+static ssize_t toshiba_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 xyval, zval, tmp;
+ u16 x, y, z;
+ int ret;
+
+ xyval = zval = 0;
+ ret = toshiba_accelerometer_get(toshiba, &xyval, &zval);
+ if (ret < 0)
+ return ret;
+
+ x = xyval & HCI_ACCEL_MASK;
+ tmp = xyval >> HCI_MISC_SHIFT;
+ y = tmp & HCI_ACCEL_MASK;
+ z = zval & HCI_ACCEL_MASK;
+
+ return sprintf(buf, "%d %d %d\n", x, y, z);
+}
+
+static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR,
+ toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store);
+static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR,
+ toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store);
+static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR,
+ toshiba_touchpad_show, toshiba_touchpad_store);
+static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL);
+
+static struct attribute *toshiba_attributes[] = {
+ &dev_attr_kbd_backlight_mode.attr,
+ &dev_attr_kbd_backlight_timeout.attr,
+ &dev_attr_touchpad.attr,
+ &dev_attr_position.attr,
+ NULL,
+};
+
+static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct toshiba_acpi_dev *drv = dev_get_drvdata(dev);
+ bool exists = true;
+
+ if (attr == &dev_attr_kbd_backlight_mode.attr)
+ exists = (drv->kbd_illum_supported) ? true : false;
+ else if (attr == &dev_attr_kbd_backlight_timeout.attr)
+ exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false;
+ else if (attr == &dev_attr_touchpad.attr)
+ exists = (drv->touchpad_supported) ? true : false;
+ else if (attr == &dev_attr_position.attr)
+ exists = (drv->accelerometer_supported) ? true : false;
+
+ return exists ? attr->mode : 0;
+}
+
+static struct attribute_group toshiba_attr_group = {
+ .is_visible = toshiba_sysfs_is_visible,
+ .attrs = toshiba_attributes,
+};
+
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
{
@@ -1106,6 +1574,10 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
remove_toshiba_proc_entries(dev);
+ if (dev->sysfs_created)
+ sysfs_remove_group(&dev->acpi_dev->dev.kobj,
+ &toshiba_attr_group);
+
if (dev->ntfy_supported) {
i8042_remove_filter(toshiba_acpi_i8042_filter);
cancel_work_sync(&dev->hotkey_work);
@@ -1127,6 +1599,12 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
if (dev->illumination_supported)
led_classdev_unregister(&dev->led_dev);
+ if (dev->kbd_led_registered)
+ led_classdev_unregister(&dev->kbd_led);
+
+ if (dev->eco_supported)
+ led_classdev_unregister(&dev->eco_led);
+
if (toshiba_acpi)
toshiba_acpi = NULL;
@@ -1172,6 +1650,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->acpi_dev = acpi_dev;
dev->method_hci = hci_method;
acpi_dev->driver_data = dev;
+ dev_set_drvdata(&acpi_dev->dev, dev);
if (toshiba_acpi_setup_keyboard(dev))
pr_info("Unable to activate hotkeys\n");
@@ -1212,6 +1691,40 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->illumination_supported = 1;
}
+ if (toshiba_eco_mode_available(dev)) {
+ dev->eco_led.name = "toshiba::eco_mode";
+ dev->eco_led.max_brightness = 1;
+ dev->eco_led.brightness_set = toshiba_eco_mode_set_status;
+ dev->eco_led.brightness_get = toshiba_eco_mode_get_status;
+ if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led))
+ dev->eco_supported = 1;
+ }
+
+ ret = toshiba_kbd_illum_status_get(dev, &dummy);
+ if (!ret) {
+ dev->kbd_time = dummy >> HCI_MISC_SHIFT;
+ dev->kbd_mode = dummy & 0x07;
+ }
+ dev->kbd_illum_supported = !ret;
+ /*
+ * Only register the LED if KBD illumination is supported
+ * and the keyboard backlight operation mode is set to FN-Z
+ */
+ if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) {
+ dev->kbd_led.name = "toshiba::kbd_backlight";
+ dev->kbd_led.max_brightness = 1;
+ dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
+ dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
+ if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led))
+ dev->kbd_led_registered = 1;
+ }
+
+ ret = toshiba_touchpad_get(dev, &dummy);
+ dev->touchpad_supported = !ret;
+
+ ret = toshiba_accelerometer_supported(dev);
+ dev->accelerometer_supported = !ret;
+
/* Determine whether or not BIOS supports fan and video interfaces */
ret = get_video_status(dev, &dummy);
@@ -1220,6 +1733,14 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
ret = get_fan_status(dev, &dummy);
dev->fan_supported = !ret;
+ ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
+ &toshiba_attr_group);
+ if (ret) {
+ dev->sysfs_created = 0;
+ goto error;
+ }
+ dev->sysfs_created = !ret;
+
create_toshiba_proc_entries(dev);
toshiba_acpi = dev;
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 1cd8584a7b88..903eb37f047a 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -392,6 +392,15 @@ config REGULATOR_PALMAS
on the muxing. This is handled automatically in the driver by
reading the mux info from OTP.
+config REGULATOR_PBIAS
+ tristate "PBIAS OMAP regulator driver"
+ depends on (ARCH_OMAP || COMPILE_TEST) && MFD_SYSCON
+ help
+ Say y here to support pbias regulator for mmc1:SD card i/o
+ on OMAP SoCs.
+ This driver provides support for OMAP pbias modelled
+ regulators.
+
config REGULATOR_PCAP
tristate "Motorola PCAP2 regulator driver"
depends on EZX_PCAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index f0fe0c50b59c..12ef277a48b4 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
+obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o
diff --git a/drivers/regulator/bcm590xx-regulator.c b/drivers/regulator/bcm590xx-regulator.c
index ab08ca7cfb08..c3750c5b382b 100644
--- a/drivers/regulator/bcm590xx-regulator.c
+++ b/drivers/regulator/bcm590xx-regulator.c
@@ -123,6 +123,7 @@ struct bcm590xx_info {
#define BCM590XX_REG_RANGES(_name, _ranges) \
{ \
.name = #_name, \
+ .n_voltages = 64, \
.n_linear_ranges = ARRAY_SIZE(_ranges), \
.linear_ranges = _ranges, \
}
diff --git a/drivers/regulator/pbias-regulator.c b/drivers/regulator/pbias-regulator.c
new file mode 100644
index 000000000000..ded3b3574209
--- /dev/null
+++ b/drivers/regulator/pbias-regulator.c
@@ -0,0 +1,255 @@
+/*
+ * pbias-regulator.c
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Balaji T K <balajitk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+struct pbias_reg_info {
+ u32 enable;
+ u32 enable_mask;
+ u32 vmode;
+ unsigned int enable_time;
+ char *name;
+};
+
+struct pbias_regulator_data {
+ struct regulator_desc desc;
+ void __iomem *pbias_addr;
+ unsigned int pbias_reg;
+ struct regulator_dev *dev;
+ struct regmap *syscon;
+ const struct pbias_reg_info *info;
+ int voltage;
+};
+
+static int pbias_regulator_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct pbias_regulator_data *data = rdev_get_drvdata(dev);
+ const struct pbias_reg_info *info = data->info;
+ int ret, vmode;
+
+ if (min_uV <= 1800000)
+ vmode = 0;
+ else if (min_uV > 1800000)
+ vmode = info->vmode;
+
+ ret = regmap_update_bits(data->syscon, data->pbias_reg,
+ info->vmode, vmode);
+
+ return ret;
+}
+
+static int pbias_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+ const struct pbias_reg_info *info = data->info;
+ int value, voltage;
+
+ regmap_read(data->syscon, data->pbias_reg, &value);
+ value &= info->vmode;
+
+ voltage = value ? 3000000 : 1800000;
+
+ return voltage;
+}
+
+static int pbias_regulator_enable(struct regulator_dev *rdev)
+{
+ struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+ const struct pbias_reg_info *info = data->info;
+ int ret;
+
+ ret = regmap_update_bits(data->syscon, data->pbias_reg,
+ info->enable_mask, info->enable);
+
+ return ret;
+}
+
+static int pbias_regulator_disable(struct regulator_dev *rdev)
+{
+ struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+ const struct pbias_reg_info *info = data->info;
+ int ret;
+
+ ret = regmap_update_bits(data->syscon, data->pbias_reg,
+ info->enable_mask, 0);
+ return ret;
+}
+
+static int pbias_regulator_is_enable(struct regulator_dev *rdev)
+{
+ struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+ const struct pbias_reg_info *info = data->info;
+ int value;
+
+ regmap_read(data->syscon, data->pbias_reg, &value);
+
+ return (value & info->enable_mask) == info->enable_mask;
+}
+
+static struct regulator_ops pbias_regulator_voltage_ops = {
+ .set_voltage = pbias_regulator_set_voltage,
+ .get_voltage = pbias_regulator_get_voltage,
+ .enable = pbias_regulator_enable,
+ .disable = pbias_regulator_disable,
+ .is_enabled = pbias_regulator_is_enable,
+};
+
+static const struct pbias_reg_info pbias_mmc_omap2430 = {
+ .enable = BIT(1),
+ .enable_mask = BIT(1),
+ .vmode = BIT(0),
+ .enable_time = 100,
+ .name = "pbias_mmc_omap2430"
+};
+
+static const struct pbias_reg_info pbias_sim_omap3 = {
+ .enable = BIT(9),
+ .enable_mask = BIT(9),
+ .vmode = BIT(8),
+ .enable_time = 100,
+ .name = "pbias_sim_omap3"
+};
+
+static const struct pbias_reg_info pbias_mmc_omap4 = {
+ .enable = BIT(26) | BIT(22),
+ .enable_mask = BIT(26) | BIT(25) | BIT(22),
+ .vmode = BIT(21),
+ .enable_time = 100,
+ .name = "pbias_mmc_omap4"
+};
+
+static const struct pbias_reg_info pbias_mmc_omap5 = {
+ .enable = BIT(27) | BIT(26),
+ .enable_mask = BIT(27) | BIT(25) | BIT(26),
+ .vmode = BIT(21),
+ .enable_time = 100,
+ .name = "pbias_mmc_omap5"
+};
+
+static struct of_regulator_match pbias_matches[] = {
+ { .name = "pbias_mmc_omap2430", .driver_data = (void *)&pbias_mmc_omap2430},
+ { .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3},
+ { .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4},
+ { .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5},
+};
+#define PBIAS_NUM_REGS ARRAY_SIZE(pbias_matches)
+
+static const struct of_device_id pbias_of_match[] = {
+ { .compatible = "ti,pbias-omap", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pbias_of_match);
+
+static int pbias_regulator_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pbias_regulator_data *drvdata;
+ struct resource *res;
+ struct regulator_config cfg = { };
+ struct regmap *syscon;
+ const struct pbias_reg_info *info;
+ int ret = 0;
+ int count, idx, data_idx = 0;
+
+ count = of_regulator_match(&pdev->dev, np, pbias_matches,
+ PBIAS_NUM_REGS);
+ if (count < 0)
+ return count;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pbias_regulator_data)
+ * count, GFP_KERNEL);
+ if (drvdata == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate device data\n");
+ return -ENOMEM;
+ }
+
+ syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(syscon))
+ return PTR_ERR(syscon);
+
+ cfg.dev = &pdev->dev;
+
+ for (idx = 0; idx < PBIAS_NUM_REGS && data_idx < count; idx++) {
+ if (!pbias_matches[idx].init_data ||
+ !pbias_matches[idx].of_node)
+ continue;
+
+ info = pbias_matches[idx].driver_data;
+ if (!info)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ drvdata[data_idx].pbias_reg = res->start;
+ drvdata[data_idx].syscon = syscon;
+ drvdata[data_idx].info = info;
+ drvdata[data_idx].desc.name = info->name;
+ drvdata[data_idx].desc.owner = THIS_MODULE;
+ drvdata[data_idx].desc.type = REGULATOR_VOLTAGE;
+ drvdata[data_idx].desc.ops = &pbias_regulator_voltage_ops;
+ drvdata[data_idx].desc.n_voltages = 2;
+ drvdata[data_idx].desc.enable_time = info->enable_time;
+
+ cfg.init_data = pbias_matches[idx].init_data;
+ cfg.driver_data = &drvdata[data_idx];
+ cfg.of_node = pbias_matches[idx].of_node;
+
+ drvdata[data_idx].dev = devm_regulator_register(&pdev->dev,
+ &drvdata[data_idx].desc, &cfg);
+ if (IS_ERR(drvdata[data_idx].dev)) {
+ ret = PTR_ERR(drvdata[data_idx].dev);
+ dev_err(&pdev->dev,
+ "Failed to register regulator: %d\n", ret);
+ goto err_regulator;
+ }
+ data_idx++;
+ }
+
+ platform_set_drvdata(pdev, drvdata);
+
+err_regulator:
+ return ret;
+}
+
+static struct platform_driver pbias_regulator_driver = {
+ .probe = pbias_regulator_probe,
+ .driver = {
+ .name = "pbias-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pbias_of_match),
+ },
+};
+
+module_platform_driver(pbias_regulator_driver);
+
+MODULE_AUTHOR("Balaji T K <balajitk@ti.com>");
+MODULE_DESCRIPTION("pbias voltage regulator");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pbias-regulator");
diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c
index 808b3aa7a42c..f19a30f0fb42 100644
--- a/drivers/regulator/s2mpa01.c
+++ b/drivers/regulator/s2mpa01.c
@@ -192,13 +192,11 @@ static int s2mpa01_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
if (!ramp_enable)
goto ramp_disable;
- if (enable_shift) {
- ret = regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1,
- 1 << enable_shift, 1 << enable_shift);
- if (ret) {
- dev_err(&rdev->dev, "failed to enable ramp rate\n");
- return ret;
- }
+ ret = regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1,
+ 1 << enable_shift, 1 << enable_shift);
+ if (ret) {
+ dev_err(&rdev->dev, "failed to enable ramp rate\n");
+ return ret;
}
ramp_val = get_ramp_delay(ramp_delay);
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index 68fd54702edb..e713c162fbd4 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -202,13 +202,11 @@ static int s2mps11_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
if (!ramp_enable)
goto ramp_disable;
- if (enable_shift) {
- ret = regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP,
- 1 << enable_shift, 1 << enable_shift);
- if (ret) {
- dev_err(&rdev->dev, "failed to enable ramp rate\n");
- return ret;
- }
+ ret = regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP,
+ 1 << enable_shift, 1 << enable_shift);
+ if (ret) {
+ dev_err(&rdev->dev, "failed to enable ramp rate\n");
+ return ret;
}
ramp_val = get_ramp_delay(ramp_delay);
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c
index f05badabd69e..92f19a005dc3 100644
--- a/drivers/regulator/s5m8767.c
+++ b/drivers/regulator/s5m8767.c
@@ -964,6 +964,7 @@ static int s5m8767_pmic_probe(struct platform_device *pdev)
config.driver_data = s5m8767;
config.regmap = iodev->regmap_pmic;
config.of_node = pdata->regulators[i].reg_node;
+ config.ena_gpio = config.ena_gpio_flags = 0;
if (pdata->regulators[i].ext_control_gpio)
s5m8767_regulator_config_ext_control(s5m8767,
&pdata->regulators[i], &config);
diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c
index 129f7b997866..3841b9813109 100644
--- a/drivers/remoteproc/da8xx_remoteproc.c
+++ b/drivers/remoteproc/da8xx_remoteproc.c
@@ -201,23 +201,11 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
}
bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!bootreg_res) {
- dev_err(dev,
- "platform_get_resource(IORESOURCE_MEM, 0): NULL\n");
- return -EADDRNOTAVAIL;
- }
-
- chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!chipsig_res) {
- dev_err(dev,
- "platform_get_resource(IORESOURCE_MEM, 1): NULL\n");
- return -EADDRNOTAVAIL;
- }
-
bootreg = devm_ioremap_resource(dev, bootreg_res);
if (IS_ERR(bootreg))
return PTR_ERR(bootreg);
+ chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
chipsig = devm_ioremap_resource(dev, chipsig_res);
if (IS_ERR(chipsig))
return PTR_ERR(chipsig);
@@ -301,8 +289,6 @@ static int da8xx_rproc_remove(struct platform_device *pdev)
*/
disable_irq(drproc->irq);
- devm_clk_put(dev, drproc->dsp_clk);
-
rproc_del(rproc);
rproc_put(rproc);
diff --git a/drivers/remoteproc/ste_modem_rproc.c b/drivers/remoteproc/ste_modem_rproc.c
index 1ec39a4c0b3e..c4ac9104dd8e 100644
--- a/drivers/remoteproc/ste_modem_rproc.c
+++ b/drivers/remoteproc/ste_modem_rproc.c
@@ -164,7 +164,7 @@ sproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
}
/* STE modem firmware handler operations */
-const struct rproc_fw_ops sproc_fw_ops = {
+static const struct rproc_fw_ops sproc_fw_ops = {
.load = sproc_load_segments,
.find_rsc_table = sproc_find_rsc_table,
.find_loaded_rsc_table = sproc_find_loaded_rsc_table,
@@ -193,7 +193,7 @@ static void sproc_kick_callback(struct ste_modem_device *mdev, int vqid)
sproc_dbg(sproc, "no message was found in vqid %d\n", vqid);
}
-struct ste_modem_dev_cb sproc_dev_cb = {
+static struct ste_modem_dev_cb sproc_dev_cb = {
.kick = sproc_kick_callback,
};
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index c8bd092fc945..02832d64d918 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -263,6 +263,9 @@ config SCSI_SCAN_ASYNC
You can override this choice by specifying "scsi_mod.scan=sync"
or async on the kernel's command line.
+ Note that this setting also affects whether resuming from
+ system suspend will be performed asynchronously.
+
menu "SCSI Transports"
depends on SCSI
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
index 788c4fe2b0c9..68fb66fdb757 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
@@ -684,6 +684,20 @@ static void tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd)
qlt_xmit_tm_rsp(mcmd);
}
+static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd)
+{
+ struct qla_tgt_cmd *cmd = container_of(se_cmd,
+ struct qla_tgt_cmd, se_cmd);
+ struct scsi_qla_host *vha = cmd->vha;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!cmd->sg_mapped)
+ return;
+
+ pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction);
+ cmd->sg_mapped = 0;
+}
+
/* Local pointer to allocated TCM configfs fabric module */
struct target_fabric_configfs *tcm_qla2xxx_fabric_configfs;
struct target_fabric_configfs *tcm_qla2xxx_npiv_fabric_configfs;
@@ -1468,7 +1482,7 @@ static int tcm_qla2xxx_check_initiator_node_acl(
}
se_tpg = &tpg->se_tpg;
- se_sess = transport_init_session();
+ se_sess = transport_init_session(TARGET_PROT_NORMAL);
if (IS_ERR(se_sess)) {
pr_err("Unable to initialize struct se_session\n");
return PTR_ERR(se_sess);
@@ -1877,6 +1891,7 @@ static struct target_core_fabric_ops tcm_qla2xxx_ops = {
.queue_data_in = tcm_qla2xxx_queue_data_in,
.queue_status = tcm_qla2xxx_queue_status,
.queue_tm_rsp = tcm_qla2xxx_queue_tm_rsp,
+ .aborted_task = tcm_qla2xxx_aborted_task,
/*
* Setup function pointers for generic logic in
* target_core_fabric_configfs.c
@@ -1926,6 +1941,7 @@ static struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = {
.queue_data_in = tcm_qla2xxx_queue_data_in,
.queue_status = tcm_qla2xxx_queue_status,
.queue_tm_rsp = tcm_qla2xxx_queue_tm_rsp,
+ .aborted_task = tcm_qla2xxx_aborted_task,
/*
* Setup function pointers for generic logic in
* target_core_fabric_configfs.c
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index c4d632c27a3e..88d46fe6bf98 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -91,6 +91,15 @@ EXPORT_SYMBOL(scsi_logging_level);
ASYNC_DOMAIN(scsi_sd_probe_domain);
EXPORT_SYMBOL(scsi_sd_probe_domain);
+/*
+ * Separate domain (from scsi_sd_probe_domain) to maximize the benefit of
+ * asynchronous system resume operations. It is marked 'exclusive' to avoid
+ * being included in the async_synchronize_full() that is invoked by
+ * dpm_resume()
+ */
+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
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 5681c05ac506..65a123d9c676 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -184,7 +184,7 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
*/
int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
- unsigned char *sense, int timeout, int retries, int flags,
+ unsigned char *sense, int timeout, int retries, u64 flags,
int *resid)
{
struct request *req;
@@ -235,7 +235,7 @@ EXPORT_SYMBOL(scsi_execute);
int scsi_execute_req_flags(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
struct scsi_sense_hdr *sshdr, int timeout, int retries,
- int *resid, int flags)
+ int *resid, u64 flags)
{
char *sense = NULL;
int result;
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c
index 001e9ceda4c3..7454498c4091 100644
--- a/drivers/scsi/scsi_pm.c
+++ b/drivers/scsi/scsi_pm.c
@@ -18,35 +18,77 @@
#ifdef CONFIG_PM_SLEEP
-static int scsi_dev_type_suspend(struct device *dev, int (*cb)(struct device *))
+static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)
{
+ return pm && pm->suspend ? pm->suspend(dev) : 0;
+}
+
+static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm)
+{
+ return pm && pm->freeze ? pm->freeze(dev) : 0;
+}
+
+static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm)
+{
+ return pm && pm->poweroff ? pm->poweroff(dev) : 0;
+}
+
+static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm)
+{
+ return pm && pm->resume ? pm->resume(dev) : 0;
+}
+
+static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm)
+{
+ return pm && pm->thaw ? pm->thaw(dev) : 0;
+}
+
+static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm)
+{
+ return pm && pm->restore ? pm->restore(dev) : 0;
+}
+
+static int scsi_dev_type_suspend(struct device *dev,
+ int (*cb)(struct device *, const struct dev_pm_ops *))
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int err;
+ /* flush pending in-flight resume operations, suspend is synchronous */
+ async_synchronize_full_domain(&scsi_sd_pm_domain);
+
err = scsi_device_quiesce(to_scsi_device(dev));
if (err == 0) {
- if (cb) {
- err = cb(dev);
- if (err)
- scsi_device_resume(to_scsi_device(dev));
- }
+ err = cb(dev, pm);
+ if (err)
+ scsi_device_resume(to_scsi_device(dev));
}
dev_dbg(dev, "scsi suspend: %d\n", err);
return err;
}
-static int scsi_dev_type_resume(struct device *dev, int (*cb)(struct device *))
+static int scsi_dev_type_resume(struct device *dev,
+ int (*cb)(struct device *, const struct dev_pm_ops *))
{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int err = 0;
- if (cb)
- err = cb(dev);
+ err = cb(dev, pm);
scsi_device_resume(to_scsi_device(dev));
dev_dbg(dev, "scsi resume: %d\n", err);
+
+ if (err == 0) {
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ }
+
return err;
}
static int
-scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *))
+scsi_bus_suspend_common(struct device *dev,
+ int (*cb)(struct device *, const struct dev_pm_ops *))
{
int err = 0;
@@ -66,20 +108,54 @@ scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *))
return err;
}
-static int
-scsi_bus_resume_common(struct device *dev, int (*cb)(struct device *))
+static void async_sdev_resume(void *dev, async_cookie_t cookie)
{
- int err = 0;
+ scsi_dev_type_resume(dev, do_scsi_resume);
+}
- if (scsi_is_sdev_device(dev))
- err = scsi_dev_type_resume(dev, cb);
+static void async_sdev_thaw(void *dev, async_cookie_t cookie)
+{
+ scsi_dev_type_resume(dev, do_scsi_thaw);
+}
- if (err == 0) {
+static void async_sdev_restore(void *dev, async_cookie_t cookie)
+{
+ scsi_dev_type_resume(dev, do_scsi_restore);
+}
+
+static int scsi_bus_resume_common(struct device *dev,
+ int (*cb)(struct device *, const struct dev_pm_ops *))
+{
+ async_func_t fn;
+
+ if (!scsi_is_sdev_device(dev))
+ fn = NULL;
+ else if (cb == do_scsi_resume)
+ fn = async_sdev_resume;
+ else if (cb == do_scsi_thaw)
+ fn = async_sdev_thaw;
+ else if (cb == do_scsi_restore)
+ fn = async_sdev_restore;
+ else
+ fn = NULL;
+
+ if (fn) {
+ async_schedule_domain(fn, dev, &scsi_sd_pm_domain);
+
+ /*
+ * If a user has disabled async probing a likely reason
+ * is due to a storage enclosure that does not inject
+ * staggered spin-ups. For safety, make resume
+ * synchronous as well in that case.
+ */
+ if (strncmp(scsi_scan_type, "async", 5) != 0)
+ async_synchronize_full_domain(&scsi_sd_pm_domain);
+ } else {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
}
- return err;
+ return 0;
}
static int scsi_bus_prepare(struct device *dev)
@@ -97,38 +173,32 @@ static int scsi_bus_prepare(struct device *dev)
static int scsi_bus_suspend(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- return scsi_bus_suspend_common(dev, pm ? pm->suspend : NULL);
+ return scsi_bus_suspend_common(dev, do_scsi_suspend);
}
static int scsi_bus_resume(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- return scsi_bus_resume_common(dev, pm ? pm->resume : NULL);
+ return scsi_bus_resume_common(dev, do_scsi_resume);
}
static int scsi_bus_freeze(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- return scsi_bus_suspend_common(dev, pm ? pm->freeze : NULL);
+ return scsi_bus_suspend_common(dev, do_scsi_freeze);
}
static int scsi_bus_thaw(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- return scsi_bus_resume_common(dev, pm ? pm->thaw : NULL);
+ return scsi_bus_resume_common(dev, do_scsi_thaw);
}
static int scsi_bus_poweroff(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- return scsi_bus_suspend_common(dev, pm ? pm->poweroff : NULL);
+ return scsi_bus_suspend_common(dev, do_scsi_poweroff);
}
static int scsi_bus_restore(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- return scsi_bus_resume_common(dev, pm ? pm->restore : NULL);
+ return scsi_bus_resume_common(dev, do_scsi_restore);
}
#else /* CONFIG_PM_SLEEP */
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index f079a598bed4..48e5b657e79f 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -112,6 +112,7 @@ extern void scsi_exit_procfs(void);
#endif /* CONFIG_PROC_FS */
/* scsi_scan.c */
+extern char scsi_scan_type[];
extern int scsi_complete_async_scans(void);
extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int,
unsigned int, unsigned int, int);
@@ -166,6 +167,7 @@ static inline int scsi_autopm_get_host(struct Scsi_Host *h) { return 0; }
static inline void scsi_autopm_put_host(struct Scsi_Host *h) {}
#endif /* CONFIG_PM_RUNTIME */
+extern struct async_domain scsi_sd_pm_domain;
extern struct async_domain scsi_sd_probe_domain;
/*
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 27f96d5b7680..e02b3aab56ce 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -97,7 +97,7 @@ MODULE_PARM_DESC(max_luns,
#define SCSI_SCAN_TYPE_DEFAULT "sync"
#endif
-static char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT;
+char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT;
module_param_string(scan, scsi_scan_type, sizeof(scsi_scan_type), S_IRUGO);
MODULE_PARM_DESC(scan, "sync, async or none");
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 89e6c04ac595..efcbcd182863 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -3026,6 +3026,7 @@ static int sd_remove(struct device *dev)
devt = disk_devt(sdkp->disk);
scsi_autopm_get_device(sdkp->device);
+ async_synchronize_full_domain(&scsi_sd_pm_domain);
async_synchronize_full_domain(&scsi_sd_probe_domain);
blk_queue_prep_rq(sdkp->device->request_queue, scsi_prep_fn);
blk_queue_unprep_rq(sdkp->device->request_queue, NULL);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index efe1960af2b3..60f2b41c7310 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -383,7 +383,7 @@ config SPI_RSPI
config SPI_QUP
tristate "Qualcomm SPI controller with QUP interface"
- depends on ARCH_MSM_DT || (ARM && COMPILE_TEST)
+ depends on ARCH_QCOM || (ARM && COMPILE_TEST)
help
Qualcomm Universal Peripheral (QUP) core is an AHB slave that
provides a common data path (an output FIFO and an input FIFO)
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 6fb2b75df821..e767f5831b9c 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -441,7 +441,8 @@ static void fsl_espi_do_one_msg(struct spi_message *m)
m->actual_length = espi_trans.actual_length;
m->status = espi_trans.status;
- m->complete(m->context);
+ if (m->complete)
+ m->complete(m->context);
}
static int fsl_espi_setup(struct spi_device *spi)
diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c
index f35488ed62a9..b3e7775034db 100644
--- a/drivers/spi/spi-fsl-spi.c
+++ b/drivers/spi/spi-fsl-spi.c
@@ -408,7 +408,8 @@ static void fsl_spi_do_one_msg(struct spi_message *m)
}
m->status = status;
- m->complete(m->context);
+ if (m->complete)
+ m->complete(m->context);
if (status || !cs_change) {
ndelay(nsecs);
diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c
index 3822eef2ef9d..577d23a12763 100644
--- a/drivers/spi/spi-mpc512x-psc.c
+++ b/drivers/spi/spi-mpc512x-psc.c
@@ -300,7 +300,8 @@ static int mpc512x_psc_spi_msg_xfer(struct spi_master *master,
}
m->status = status;
- m->complete(m->context);
+ if (m->complete)
+ m->complete(m->context);
if (status || !cs_change)
mpc512x_psc_spi_deactivate_cs(spi);
diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c
index 3d18d9351185..de532aa11d34 100644
--- a/drivers/spi/spi-mpc52xx-psc.c
+++ b/drivers/spi/spi-mpc52xx-psc.c
@@ -247,7 +247,8 @@ static void mpc52xx_psc_spi_work(struct work_struct *work)
}
m->status = status;
- m->complete(m->context);
+ if (m->complete)
+ m->complete(m->context);
if (status || !cs_change)
mpc52xx_psc_spi_deactivate_cs(spi);
diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c
index aac2a5ddd964..b07db4b62d80 100644
--- a/drivers/spi/spi-mpc52xx.c
+++ b/drivers/spi/spi-mpc52xx.c
@@ -234,7 +234,8 @@ static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms,
dev_err(&ms->master->dev, "mode fault\n");
mpc52xx_spi_chipsel(ms, 0);
ms->message->status = -EIO;
- ms->message->complete(ms->message->context);
+ if (ms->message->complete)
+ ms->message->complete(ms->message->context);
ms->state = mpc52xx_spi_fsmstate_idle;
return FSM_CONTINUE;
}
@@ -288,7 +289,8 @@ mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms, u8 status, u8 data)
ms->msg_count++;
mpc52xx_spi_chipsel(ms, 0);
ms->message->status = 0;
- ms->message->complete(ms->message->context);
+ if (ms->message->complete)
+ ms->message->complete(ms->message->context);
ms->state = mpc52xx_spi_fsmstate_idle;
return FSM_CONTINUE;
}
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 2941c5b96ebc..4dc77df38864 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -1379,12 +1379,13 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mcspi->ctx.cs);
- mcspi->dma_channels = kcalloc(master->num_chipselect,
- sizeof(struct omap2_mcspi_dma),
- GFP_KERNEL);
-
- if (mcspi->dma_channels == NULL)
+ mcspi->dma_channels = devm_kcalloc(&pdev->dev, master->num_chipselect,
+ sizeof(struct omap2_mcspi_dma),
+ GFP_KERNEL);
+ if (mcspi->dma_channels == NULL) {
+ status = -ENOMEM;
goto free_master;
+ }
for (i = 0; i < master->num_chipselect; i++) {
char *dma_rx_ch_name = mcspi->dma_channels[i].dma_rx_ch_name;
@@ -1426,7 +1427,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
}
if (status < 0)
- goto dma_chnl_free;
+ goto free_master;
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
@@ -1444,8 +1445,6 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
disable_pm:
pm_runtime_disable(&pdev->dev);
-dma_chnl_free:
- kfree(mcspi->dma_channels);
free_master:
spi_master_put(master);
return status;
@@ -1453,19 +1452,12 @@ free_master:
static int omap2_mcspi_remove(struct platform_device *pdev)
{
- struct spi_master *master;
- struct omap2_mcspi *mcspi;
- struct omap2_mcspi_dma *dma_channels;
-
- master = platform_get_drvdata(pdev);
- mcspi = spi_master_get_devdata(master);
- dma_channels = mcspi->dma_channels;
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
pm_runtime_put_sync(mcspi->dev);
pm_runtime_disable(&pdev->dev);
- kfree(dma_channels);
-
return 0;
}
diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c
index f6f2c7010177..03edf5ed0e9f 100644
--- a/drivers/spi/spi-sh.c
+++ b/drivers/spi/spi-sh.c
@@ -322,7 +322,8 @@ static void spi_sh_work(struct work_struct *work)
spin_lock_irqsave(&ss->lock, flags);
mesg->status = 0;
- mesg->complete(mesg->context);
+ if (mesg->complete)
+ mesg->complete(mesg->context);
}
clear_fifo(ss);
@@ -340,7 +341,8 @@ static void spi_sh_work(struct work_struct *work)
error:
mesg->status = ret;
- mesg->complete(mesg->context);
+ if (mesg->complete)
+ mesg->complete(mesg->context);
spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD,
SPI_SH_CR1);
diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c
index 820b499816f8..5f183baa91a9 100644
--- a/drivers/spi/spi-txx9.c
+++ b/drivers/spi/spi-txx9.c
@@ -262,7 +262,8 @@ static void txx9spi_work_one(struct txx9spi *c, struct spi_message *m)
exit:
m->status = status;
- m->complete(m->context);
+ if (m->complete)
+ m->complete(m->context);
/* normally deactivate chipselect ... unless no error and
* cs_change has hinted that the next message will probably
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c
index a9b5898347c2..37758d1c8a68 100644
--- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c
@@ -99,16 +99,7 @@ ksocknal_lib_send_iov (ksock_conn_t *conn, ksock_tx_t *tx)
struct iovec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov;
unsigned int niov = tx->tx_niov;
#endif
- struct msghdr msg = {
- .msg_name = NULL,
- .msg_namelen = 0,
- .msg_iov = scratchiov,
- .msg_iovlen = niov,
- .msg_control = NULL,
- .msg_controllen = 0,
- .msg_flags = MSG_DONTWAIT
- };
- mm_segment_t oldmm = get_fs();
+ struct msghdr msg = {.msg_flags = MSG_DONTWAIT};
int i;
for (nob = i = 0; i < niov; i++) {
@@ -120,9 +111,7 @@ ksocknal_lib_send_iov (ksock_conn_t *conn, ksock_tx_t *tx)
nob < tx->tx_resid)
msg.msg_flags |= MSG_MORE;
- set_fs (KERNEL_DS);
- rc = sock_sendmsg(sock, &msg, nob);
- set_fs (oldmm);
+ rc = kernel_sendmsg(sock, &msg, (struct kvec *)scratchiov, niov, nob);
}
return rc;
}
@@ -174,16 +163,7 @@ ksocknal_lib_send_kiov (ksock_conn_t *conn, ksock_tx_t *tx)
struct iovec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov;
unsigned int niov = tx->tx_nkiov;
#endif
- struct msghdr msg = {
- .msg_name = NULL,
- .msg_namelen = 0,
- .msg_iov = scratchiov,
- .msg_iovlen = niov,
- .msg_control = NULL,
- .msg_controllen = 0,
- .msg_flags = MSG_DONTWAIT
- };
- mm_segment_t oldmm = get_fs();
+ struct msghdr msg = {.msg_flags = MSG_DONTWAIT};
int i;
for (nob = i = 0; i < niov; i++) {
@@ -196,9 +176,7 @@ ksocknal_lib_send_kiov (ksock_conn_t *conn, ksock_tx_t *tx)
nob < tx->tx_resid)
msg.msg_flags |= MSG_MORE;
- set_fs (KERNEL_DS);
- rc = sock_sendmsg(sock, &msg, nob);
- set_fs (oldmm);
+ rc = kernel_sendmsg(sock, &msg, (struct kvec *)scratchiov, niov, nob);
for (i = 0; i < niov; i++)
kunmap(kiov[i].kiov_page);
@@ -237,15 +215,8 @@ ksocknal_lib_recv_iov (ksock_conn_t *conn)
#endif
struct iovec *iov = conn->ksnc_rx_iov;
struct msghdr msg = {
- .msg_name = NULL,
- .msg_namelen = 0,
- .msg_iov = scratchiov,
- .msg_iovlen = niov,
- .msg_control = NULL,
- .msg_controllen = 0,
.msg_flags = 0
};
- mm_segment_t oldmm = get_fs();
int nob;
int i;
int rc;
@@ -263,10 +234,8 @@ ksocknal_lib_recv_iov (ksock_conn_t *conn)
}
LASSERT (nob <= conn->ksnc_rx_nob_wanted);
- set_fs (KERNEL_DS);
- rc = sock_recvmsg (conn->ksnc_sock, &msg, nob, MSG_DONTWAIT);
- /* NB this is just a boolean..........................^ */
- set_fs (oldmm);
+ rc = kernel_recvmsg(conn->ksnc_sock, &msg,
+ (struct kvec *)scratchiov, niov, nob, MSG_DONTWAIT);
saved_csum = 0;
if (conn->ksnc_proto == &ksocknal_protocol_v2x) {
@@ -355,14 +324,8 @@ ksocknal_lib_recv_kiov (ksock_conn_t *conn)
#endif
lnet_kiov_t *kiov = conn->ksnc_rx_kiov;
struct msghdr msg = {
- .msg_name = NULL,
- .msg_namelen = 0,
- .msg_iov = scratchiov,
- .msg_control = NULL,
- .msg_controllen = 0,
.msg_flags = 0
};
- mm_segment_t oldmm = get_fs();
int nob;
int i;
int rc;
@@ -370,13 +333,14 @@ ksocknal_lib_recv_kiov (ksock_conn_t *conn)
void *addr;
int sum;
int fragnob;
+ int n;
/* NB we can't trust socket ops to either consume our iovs
* or leave them alone. */
addr = ksocknal_lib_kiov_vmap(kiov, niov, scratchiov, pages);
if (addr != NULL) {
nob = scratchiov[0].iov_len;
- msg.msg_iovlen = 1;
+ n = 1;
} else {
for (nob = i = 0; i < niov; i++) {
@@ -384,15 +348,13 @@ ksocknal_lib_recv_kiov (ksock_conn_t *conn)
scratchiov[i].iov_base = kmap(kiov[i].kiov_page) +
kiov[i].kiov_offset;
}
- msg.msg_iovlen = niov;
+ n = niov;
}
LASSERT (nob <= conn->ksnc_rx_nob_wanted);
- set_fs (KERNEL_DS);
- rc = sock_recvmsg (conn->ksnc_sock, &msg, nob, MSG_DONTWAIT);
- /* NB this is just a boolean.......................^ */
- set_fs (oldmm);
+ rc = kernel_recvmsg(conn->ksnc_sock, &msg,
+ (struct kvec *)scratchiov, n, nob, MSG_DONTWAIT);
if (conn->ksnc_msg.ksm_csum != 0) {
for (i = 0, sum = rc; sum > 0; i++, sum -= fragnob) {
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c
index e6069d78af6b..7539fe16d76f 100644
--- a/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c
@@ -265,17 +265,11 @@ libcfs_sock_write (struct socket *sock, void *buffer, int nob, int timeout)
* empty enough to take the whole message immediately */
for (;;) {
- struct iovec iov = {
+ struct kvec iov = {
.iov_base = buffer,
.iov_len = nob
};
struct msghdr msg = {
- .msg_name = NULL,
- .msg_namelen = 0,
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = NULL,
- .msg_controllen = 0,
.msg_flags = (timeout == 0) ? MSG_DONTWAIT : 0
};
@@ -297,11 +291,9 @@ libcfs_sock_write (struct socket *sock, void *buffer, int nob, int timeout)
}
}
- set_fs (KERNEL_DS);
then = jiffies;
- rc = sock_sendmsg (sock, &msg, iov.iov_len);
+ rc = kernel_sendmsg(sock, &msg, &iov, 1, nob);
ticks -= jiffies - then;
- set_fs (oldmm);
if (rc == nob)
return 0;
@@ -338,17 +330,11 @@ libcfs_sock_read (struct socket *sock, void *buffer, int nob, int timeout)
LASSERT (ticks > 0);
for (;;) {
- struct iovec iov = {
+ struct kvec iov = {
.iov_base = buffer,
.iov_len = nob
};
struct msghdr msg = {
- .msg_name = NULL,
- .msg_namelen = 0,
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = NULL,
- .msg_controllen = 0,
.msg_flags = 0
};
@@ -367,11 +353,9 @@ libcfs_sock_read (struct socket *sock, void *buffer, int nob, int timeout)
return rc;
}
- set_fs(KERNEL_DS);
then = jiffies;
- rc = sock_recvmsg(sock, &msg, iov.iov_len, 0);
+ rc = kernel_recvmsg(sock, &msg, &iov, 1, nob, 0);
ticks -= jiffies - then;
- set_fs(oldmm);
if (rc < 0)
return rc;
diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c
index ab06891f7fc7..80d48b5ae247 100644
--- a/drivers/staging/lustre/lustre/llite/symlink.c
+++ b/drivers/staging/lustre/lustre/llite/symlink.c
@@ -115,27 +115,6 @@ failed:
return rc;
}
-static int ll_readlink(struct dentry *dentry, char *buffer, int buflen)
-{
- struct inode *inode = dentry->d_inode;
- struct ptlrpc_request *request;
- char *symname;
- int rc;
-
- CDEBUG(D_VFSTRACE, "VFS Op\n");
-
- ll_inode_size_lock(inode);
- rc = ll_readlink_internal(inode, &request, &symname);
- if (rc)
- GOTO(out, rc);
-
- rc = vfs_readlink(dentry, buffer, buflen, symname);
- out:
- ptlrpc_req_finished(request);
- ll_inode_size_unlock(inode);
- return rc;
-}
-
static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct inode *inode = dentry->d_inode;
@@ -175,7 +154,7 @@ static void ll_put_link(struct dentry *dentry, struct nameidata *nd, void *cooki
}
struct inode_operations ll_fast_symlink_inode_operations = {
- .readlink = ll_readlink,
+ .readlink = generic_readlink,
.setattr = ll_setattr,
.follow_link = ll_follow_link,
.put_link = ll_put_link,
diff --git a/drivers/staging/media/msi3101/msi001.c b/drivers/staging/media/msi3101/msi001.c
index ac43bae10102..bd0b93cb6c53 100644
--- a/drivers/staging/media/msi3101/msi001.c
+++ b/drivers/staging/media/msi3101/msi001.c
@@ -201,7 +201,7 @@ static int msi001_set_tuner(struct msi001 *s)
dev_dbg(&s->spi->dev, "%s: bandwidth selected=%d\n",
__func__, bandwidth_lut[i].freq);
- f_vco = (f_rf + f_if + f_if1) * lo_div;
+ f_vco = (u64) (f_rf + f_if + f_if1) * lo_div;
tmp64 = f_vco;
m = do_div(tmp64, F_REF * R_REF);
n = (unsigned int) tmp64;
diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c
index 260d1b736721..65d351f99da2 100644
--- a/drivers/staging/media/msi3101/sdr-msi3101.c
+++ b/drivers/staging/media/msi3101/sdr-msi3101.c
@@ -913,7 +913,6 @@ static int msi3101_set_usb_adc(struct msi3101_state *s)
/* set tuner, subdev, filters according to sampling rate */
bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO);
- bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH);
if (v4l2_ctrl_g_ctrl(bandwidth_auto)) {
bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH);
v4l2_ctrl_s_ctrl(bandwidth, s->f_adc);
@@ -1078,6 +1077,7 @@ static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count)
static int msi3101_stop_streaming(struct vb2_queue *vq)
{
struct msi3101_state *s = vb2_get_drv_priv(vq);
+ int ret;
dev_dbg(&s->udev->dev, "%s:\n", __func__);
if (mutex_lock_interruptible(&s->v4l2_lock))
@@ -1090,17 +1090,22 @@ static int msi3101_stop_streaming(struct vb2_queue *vq)
/* according to tests, at least 700us delay is required */
msleep(20);
- msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0);
+ ret = msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0);
+ if (ret)
+ goto err_sleep_tuner;
/* sleep USB IF / ADC */
- msi3101_ctrl_msg(s, CMD_WREG, 0x01000003);
+ ret = msi3101_ctrl_msg(s, CMD_WREG, 0x01000003);
+ if (ret)
+ goto err_sleep_tuner;
+err_sleep_tuner:
/* sleep tuner */
- v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0);
+ ret = v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0);
mutex_unlock(&s->v4l2_lock);
- return 0;
+ return ret;
}
static struct vb2_ops msi3101_vb2_ops = {
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index 773d8ca07a00..de692d7011a5 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -86,7 +86,6 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
struct stub_device *sdev = dev_get_drvdata(dev);
int sockfd = 0;
struct socket *socket;
- ssize_t err = -EINVAL;
int rv;
if (!sdev) {
@@ -99,6 +98,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
return -EINVAL;
if (sockfd != -1) {
+ int err;
dev_info(dev, "stub up\n");
spin_lock_irq(&sdev->ud.lock);
@@ -108,7 +108,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
goto err;
}
- socket = sockfd_to_socket(sockfd);
+ socket = sockfd_lookup(sockfd, &err);
if (!socket)
goto err;
@@ -141,7 +141,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
err:
spin_unlock_irq(&sdev->ud.lock);
- return err;
+ return -EINVAL;
}
static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
@@ -211,7 +211,7 @@ static void stub_shutdown_connection(struct usbip_device *ud)
* not touch NULL socket.
*/
if (ud->tcp_socket) {
- fput(ud->tcp_socket->file);
+ sockfd_put(ud->tcp_socket);
ud->tcp_socket = NULL;
}
diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
index 184fa70365db..facaaf003f19 100644
--- a/drivers/staging/usbip/usbip_common.c
+++ b/drivers/staging/usbip/usbip_common.c
@@ -382,31 +382,6 @@ err:
}
EXPORT_SYMBOL_GPL(usbip_recv);
-struct socket *sockfd_to_socket(unsigned int sockfd)
-{
- struct socket *socket;
- struct file *file;
- struct inode *inode;
-
- file = fget(sockfd);
- if (!file) {
- pr_err("invalid sockfd\n");
- return NULL;
- }
-
- inode = file_inode(file);
-
- if (!inode || !S_ISSOCK(inode->i_mode)) {
- fput(file);
- return NULL;
- }
-
- socket = SOCKET_I(inode);
-
- return socket;
-}
-EXPORT_SYMBOL_GPL(sockfd_to_socket);
-
/* there may be more cases to tweak the flags. */
static unsigned int tweak_transfer_flags(unsigned int flags)
{
diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index 732fb636a1e5..f555d834f134 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -299,7 +299,6 @@ void usbip_dump_urb(struct urb *purb);
void usbip_dump_header(struct usbip_header *pdu);
int usbip_recv(struct socket *sock, void *buf, int size);
-struct socket *sockfd_to_socket(unsigned int sockfd);
void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
int pack);
diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c
index 1e84577230ef..70e17551943d 100644
--- a/drivers/staging/usbip/vhci_hcd.c
+++ b/drivers/staging/usbip/vhci_hcd.c
@@ -788,7 +788,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
/* active connection is closed */
if (vdev->ud.tcp_socket) {
- fput(vdev->ud.tcp_socket->file);
+ sockfd_put(vdev->ud.tcp_socket);
vdev->ud.tcp_socket = NULL;
}
pr_info("release socket\n");
@@ -835,7 +835,7 @@ static void vhci_device_reset(struct usbip_device *ud)
vdev->udev = NULL;
if (ud->tcp_socket) {
- fput(ud->tcp_socket->file);
+ sockfd_put(ud->tcp_socket);
ud->tcp_socket = NULL;
}
ud->status = VDEV_ST_NULL;
diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c
index e0980324fb03..47bddcdde0a6 100644
--- a/drivers/staging/usbip/vhci_sysfs.c
+++ b/drivers/staging/usbip/vhci_sysfs.c
@@ -176,6 +176,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
struct socket *socket;
int sockfd = 0;
__u32 rhport = 0, devid = 0, speed = 0;
+ int err;
/*
* @rhport: port number of vhci_hcd
@@ -194,8 +195,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
return -EINVAL;
/* Extract socket from fd. */
- /* The correct way to clean this up is to fput(socket->file). */
- socket = sockfd_to_socket(sockfd);
+ socket = sockfd_lookup(sockfd, &err);
if (!socket)
return -EINVAL;
@@ -211,7 +211,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
spin_unlock(&vdev->ud.lock);
spin_unlock(&the_controller->lock);
- fput(socket->file);
+ sockfd_put(socket);
dev_err(dev, "port %d already used\n", rhport);
return -EINVAL;
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index b83ec378d04f..78cab13bbb1b 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -499,6 +499,23 @@ static int iscsit_queue_rsp(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
return 0;
}
+static void iscsit_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
+{
+ bool scsi_cmd = (cmd->iscsi_opcode == ISCSI_OP_SCSI_CMD);
+
+ spin_lock_bh(&conn->cmd_lock);
+ if (!list_empty(&cmd->i_conn_node))
+ list_del_init(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ __iscsit_free_cmd(cmd, scsi_cmd, true);
+}
+
+static enum target_prot_op iscsit_get_sup_prot_ops(struct iscsi_conn *conn)
+{
+ return TARGET_PROT_NORMAL;
+}
+
static struct iscsit_transport iscsi_target_transport = {
.name = "iSCSI/TCP",
.transport_type = ISCSI_TCP,
@@ -513,6 +530,8 @@ static struct iscsit_transport iscsi_target_transport = {
.iscsit_response_queue = iscsit_response_queue,
.iscsit_queue_data_in = iscsit_queue_rsp,
.iscsit_queue_status = iscsit_queue_rsp,
+ .iscsit_aborted_task = iscsit_aborted_task,
+ .iscsit_get_sup_prot_ops = iscsit_get_sup_prot_ops,
};
static int __init iscsi_target_init_module(void)
@@ -1503,6 +1522,16 @@ int iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
{
u32 payload_length = ntoh24(hdr->dlength);
+ if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
+ pr_err("NopOUT Flag's, Left Most Bit not set, protocol error.\n");
+ if (!cmd)
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+ }
+
if (hdr->itt == RESERVED_ITT && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
pr_err("NOPOUT ITT is reserved, but Immediate Bit is"
" not set, protocol error.\n");
@@ -2468,6 +2497,7 @@ static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn)
{
struct iscsi_cmd *cmd;
struct iscsi_conn *conn_p;
+ bool found = false;
/*
* Only send a Asynchronous Message on connections whos network
@@ -2476,11 +2506,12 @@ static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn)
list_for_each_entry(conn_p, &conn->sess->sess_conn_list, conn_list) {
if (conn_p->conn_state == TARG_CONN_STATE_LOGGED_IN) {
iscsit_inc_conn_usage_count(conn_p);
+ found = true;
break;
}
}
- if (!conn_p)
+ if (!found)
return;
cmd = iscsit_allocate_cmd(conn_p, TASK_RUNNING);
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
index 1c0088fe9e99..ae03f3e5de1e 100644
--- a/drivers/target/iscsi/iscsi_target_configfs.c
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -1052,6 +1052,11 @@ TPG_ATTR(demo_mode_discovery, S_IRUGO | S_IWUSR);
*/
DEF_TPG_ATTRIB(default_erl);
TPG_ATTR(default_erl, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_t10_pi
+ */
+DEF_TPG_ATTRIB(t10_pi);
+TPG_ATTR(t10_pi, S_IRUGO | S_IWUSR);
static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = {
&iscsi_tpg_attrib_authentication.attr,
@@ -1064,6 +1069,7 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = {
&iscsi_tpg_attrib_prod_mode_write_protect.attr,
&iscsi_tpg_attrib_demo_mode_discovery.attr,
&iscsi_tpg_attrib_default_erl.attr,
+ &iscsi_tpg_attrib_t10_pi.attr,
NULL,
};
@@ -1815,6 +1821,13 @@ static void lio_queue_tm_rsp(struct se_cmd *se_cmd)
iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
}
+static void lio_aborted_task(struct se_cmd *se_cmd)
+{
+ struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+ cmd->conn->conn_transport->iscsit_aborted_task(cmd->conn, cmd);
+}
+
static char *lio_tpg_get_endpoint_wwn(struct se_portal_group *se_tpg)
{
struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
@@ -1999,6 +2012,7 @@ int iscsi_target_register_configfs(void)
fabric->tf_ops.queue_data_in = &lio_queue_data_in;
fabric->tf_ops.queue_status = &lio_queue_status;
fabric->tf_ops.queue_tm_rsp = &lio_queue_tm_rsp;
+ fabric->tf_ops.aborted_task = &lio_aborted_task;
/*
* Setup function pointers for generic logic in target_core_fabric_configfs.c
*/
diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h
index f452398fe0f0..6960f22909ae 100644
--- a/drivers/target/iscsi/iscsi_target_core.h
+++ b/drivers/target/iscsi/iscsi_target_core.h
@@ -58,7 +58,8 @@
#define TA_DEMO_MODE_DISCOVERY 1
#define TA_DEFAULT_ERL 0
#define TA_CACHE_CORE_NPS 0
-
+/* T10 protection information disabled by default */
+#define TA_DEFAULT_T10_PI 0
#define ISCSI_IOV_DATA_BUFFER 5
@@ -765,6 +766,7 @@ struct iscsi_tpg_attrib {
u32 prod_mode_write_protect;
u32 demo_mode_discovery;
u32 default_erl;
+ u8 t10_pi;
struct iscsi_portal_group *tpg;
};
@@ -787,6 +789,7 @@ struct iscsi_np {
void *np_context;
struct iscsit_transport *np_transport;
struct list_head np_list;
+ struct iscsi_tpg_np *tpg_np;
} ____cacheline_aligned;
struct iscsi_tpg_np {
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index e29279e6b577..8739b98f6f93 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -259,6 +259,7 @@ static int iscsi_login_zero_tsih_s1(
{
struct iscsi_session *sess = NULL;
struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
+ enum target_prot_op sup_pro_ops;
int ret;
sess = kzalloc(sizeof(struct iscsi_session), GFP_KERNEL);
@@ -320,8 +321,9 @@ static int iscsi_login_zero_tsih_s1(
kfree(sess);
return -ENOMEM;
}
+ sup_pro_ops = conn->conn_transport->iscsit_get_sup_prot_ops(conn);
- sess->se_sess = transport_init_session();
+ sess->se_sess = transport_init_session(sup_pro_ops);
if (IS_ERR(sess->se_sess)) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_NO_RESOURCES);
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
index 44a5471de00f..eb96b20dc09e 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.c
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -225,6 +225,7 @@ static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg)
a->prod_mode_write_protect = TA_PROD_MODE_WRITE_PROTECT;
a->demo_mode_discovery = TA_DEMO_MODE_DISCOVERY;
a->default_erl = TA_DEFAULT_ERL;
+ a->t10_pi = TA_DEFAULT_T10_PI;
}
int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg)
@@ -500,6 +501,7 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
init_completion(&tpg_np->tpg_np_comp);
kref_init(&tpg_np->tpg_np_kref);
tpg_np->tpg_np = np;
+ np->tpg_np = tpg_np;
tpg_np->tpg = tpg;
spin_lock(&tpg->tpg_np_lock);
@@ -858,3 +860,22 @@ int iscsit_ta_default_erl(
return 0;
}
+
+int iscsit_ta_t10_pi(
+ struct iscsi_portal_group *tpg,
+ u32 flag)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if ((flag != 0) && (flag != 1)) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+
+ a->t10_pi = flag;
+ pr_debug("iSCSI_TPG[%hu] - T10 Protection information bit:"
+ " %s\n", tpg->tpgt, (a->t10_pi) ?
+ "ON" : "OFF");
+
+ return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h
index 213c0fc7fdc9..0a182f2aa8a2 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.h
+++ b/drivers/target/iscsi/iscsi_target_tpg.h
@@ -39,5 +39,6 @@ extern int iscsit_ta_demo_mode_write_protect(struct iscsi_portal_group *, u32);
extern int iscsit_ta_prod_mode_write_protect(struct iscsi_portal_group *, u32);
extern int iscsit_ta_demo_mode_discovery(struct iscsi_portal_group *, u32);
extern int iscsit_ta_default_erl(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_t10_pi(struct iscsi_portal_group *, u32);
#endif /* ISCSI_TARGET_TPG_H */
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
index e655b042ed18..53e157cb8c54 100644
--- a/drivers/target/iscsi/iscsi_target_util.c
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -705,8 +705,8 @@ void iscsit_release_cmd(struct iscsi_cmd *cmd)
}
EXPORT_SYMBOL(iscsit_release_cmd);
-static void __iscsit_free_cmd(struct iscsi_cmd *cmd, bool scsi_cmd,
- bool check_queues)
+void __iscsit_free_cmd(struct iscsi_cmd *cmd, bool scsi_cmd,
+ bool check_queues)
{
struct iscsi_conn *conn = cmd->conn;
diff --git a/drivers/target/iscsi/iscsi_target_util.h b/drivers/target/iscsi/iscsi_target_util.h
index 561a424d1980..a68508c4fec8 100644
--- a/drivers/target/iscsi/iscsi_target_util.h
+++ b/drivers/target/iscsi/iscsi_target_util.h
@@ -30,6 +30,7 @@ extern void iscsit_remove_cmd_from_tx_queues(struct iscsi_cmd *, struct iscsi_co
extern bool iscsit_conn_all_queues_empty(struct iscsi_conn *);
extern void iscsit_free_queue_reqs_for_conn(struct iscsi_conn *);
extern void iscsit_release_cmd(struct iscsi_cmd *);
+extern void __iscsit_free_cmd(struct iscsi_cmd *, bool, bool);
extern void iscsit_free_cmd(struct iscsi_cmd *, bool);
extern int iscsit_check_session_usage_count(struct iscsi_session *);
extern void iscsit_dec_session_usage_count(struct iscsi_session *);
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c
index fadad7c5f635..c886ad1c39fb 100644
--- a/drivers/target/loopback/tcm_loop.c
+++ b/drivers/target/loopback/tcm_loop.c
@@ -212,6 +212,10 @@ static void tcm_loop_submission_work(struct work_struct *work)
se_cmd->se_cmd_flags |= SCF_BIDI;
}
+
+ if (!scsi_prot_sg_count(sc) && scsi_get_prot_op(sc) != SCSI_PROT_NORMAL)
+ se_cmd->prot_pto = true;
+
rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd,
&tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun,
scsi_bufflen(sc), tcm_loop_sam_attr(sc),
@@ -915,6 +919,11 @@ static void tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd)
wake_up(&tl_tmr->tl_tmr_wait);
}
+static void tcm_loop_aborted_task(struct se_cmd *se_cmd)
+{
+ return;
+}
+
static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba)
{
switch (tl_hba->tl_proto_id) {
@@ -1009,7 +1018,7 @@ static int tcm_loop_make_nexus(
/*
* Initialize the struct se_session pointer
*/
- tl_nexus->se_sess = transport_init_session();
+ tl_nexus->se_sess = transport_init_session(TARGET_PROT_ALL);
if (IS_ERR(tl_nexus->se_sess)) {
ret = PTR_ERR(tl_nexus->se_sess);
goto out;
@@ -1483,6 +1492,7 @@ static int tcm_loop_register_configfs(void)
fabric->tf_ops.queue_data_in = &tcm_loop_queue_data_in;
fabric->tf_ops.queue_status = &tcm_loop_queue_status;
fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp;
+ fabric->tf_ops.aborted_task = &tcm_loop_aborted_task;
/*
* Setup function pointers for generic logic in target_core_fabric_configfs.c
diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c
index 24884cac19ce..e7e93727553c 100644
--- a/drivers/target/sbp/sbp_target.c
+++ b/drivers/target/sbp/sbp_target.c
@@ -210,7 +210,7 @@ static struct sbp_session *sbp_session_create(
return ERR_PTR(-ENOMEM);
}
- sess->se_sess = transport_init_session();
+ sess->se_sess = transport_init_session(TARGET_PROT_NORMAL);
if (IS_ERR(sess->se_sess)) {
pr_err("failed to init se_session\n");
@@ -1846,6 +1846,11 @@ static void sbp_queue_tm_rsp(struct se_cmd *se_cmd)
{
}
+static void sbp_aborted_task(struct se_cmd *se_cmd)
+{
+ return;
+}
+
static int sbp_check_stop_free(struct se_cmd *se_cmd)
{
struct sbp_target_request *req = container_of(se_cmd,
@@ -2526,6 +2531,7 @@ static struct target_core_fabric_ops sbp_ops = {
.queue_data_in = sbp_queue_data_in,
.queue_status = sbp_queue_status,
.queue_tm_rsp = sbp_queue_tm_rsp,
+ .aborted_task = sbp_aborted_task,
.check_stop_free = sbp_check_stop_free,
.fabric_make_wwn = sbp_make_tport,
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index c3d9df6aaf5f..fcbe6125b73e 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -455,11 +455,26 @@ out:
return rc;
}
-static inline int core_alua_state_nonoptimized(
+static inline void set_ascq(struct se_cmd *cmd, u8 alua_ascq)
+{
+ /*
+ * Set SCSI additional sense code (ASC) to 'LUN Not Accessible';
+ * The ALUA additional sense code qualifier (ASCQ) is determined
+ * by the ALUA primary or secondary access state..
+ */
+ pr_debug("[%s]: ALUA TG Port not available, "
+ "SenseKey: NOT_READY, ASC/ASCQ: "
+ "0x04/0x%02x\n",
+ cmd->se_tfo->get_fabric_name(), alua_ascq);
+
+ cmd->scsi_asc = 0x04;
+ cmd->scsi_ascq = alua_ascq;
+}
+
+static inline void core_alua_state_nonoptimized(
struct se_cmd *cmd,
unsigned char *cdb,
- int nonop_delay_msecs,
- u8 *alua_ascq)
+ int nonop_delay_msecs)
{
/*
* Set SCF_ALUA_NON_OPTIMIZED here, this value will be checked
@@ -468,13 +483,11 @@ static inline int core_alua_state_nonoptimized(
*/
cmd->se_cmd_flags |= SCF_ALUA_NON_OPTIMIZED;
cmd->alua_nonop_delay = nonop_delay_msecs;
- return 0;
}
static inline int core_alua_state_lba_dependent(
struct se_cmd *cmd,
- struct t10_alua_tg_pt_gp *tg_pt_gp,
- u8 *alua_ascq)
+ struct t10_alua_tg_pt_gp *tg_pt_gp)
{
struct se_device *dev = cmd->se_dev;
u64 segment_size, segment_mult, sectors, lba;
@@ -520,7 +533,7 @@ static inline int core_alua_state_lba_dependent(
}
if (!cur_map) {
spin_unlock(&dev->t10_alua.lba_map_lock);
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);
return 1;
}
list_for_each_entry(map_mem, &cur_map->lba_map_mem_list,
@@ -531,11 +544,11 @@ static inline int core_alua_state_lba_dependent(
switch(map_mem->lba_map_mem_alua_state) {
case ALUA_ACCESS_STATE_STANDBY:
spin_unlock(&dev->t10_alua.lba_map_lock);
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);
return 1;
case ALUA_ACCESS_STATE_UNAVAILABLE:
spin_unlock(&dev->t10_alua.lba_map_lock);
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);
return 1;
default:
break;
@@ -548,8 +561,7 @@ static inline int core_alua_state_lba_dependent(
static inline int core_alua_state_standby(
struct se_cmd *cmd,
- unsigned char *cdb,
- u8 *alua_ascq)
+ unsigned char *cdb)
{
/*
* Allowed CDBs for ALUA_ACCESS_STATE_STANDBY as defined by
@@ -570,7 +582,7 @@ static inline int core_alua_state_standby(
case MI_REPORT_TARGET_PGS:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);
return 1;
}
case MAINTENANCE_OUT:
@@ -578,7 +590,7 @@ static inline int core_alua_state_standby(
case MO_SET_TARGET_PGS:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);
return 1;
}
case REQUEST_SENSE:
@@ -588,7 +600,7 @@ static inline int core_alua_state_standby(
case WRITE_BUFFER:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);
return 1;
}
@@ -597,8 +609,7 @@ static inline int core_alua_state_standby(
static inline int core_alua_state_unavailable(
struct se_cmd *cmd,
- unsigned char *cdb,
- u8 *alua_ascq)
+ unsigned char *cdb)
{
/*
* Allowed CDBs for ALUA_ACCESS_STATE_UNAVAILABLE as defined by
@@ -613,7 +624,7 @@ static inline int core_alua_state_unavailable(
case MI_REPORT_TARGET_PGS:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);
return 1;
}
case MAINTENANCE_OUT:
@@ -621,7 +632,7 @@ static inline int core_alua_state_unavailable(
case MO_SET_TARGET_PGS:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);
return 1;
}
case REQUEST_SENSE:
@@ -629,7 +640,7 @@ static inline int core_alua_state_unavailable(
case WRITE_BUFFER:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);
return 1;
}
@@ -638,8 +649,7 @@ static inline int core_alua_state_unavailable(
static inline int core_alua_state_transition(
struct se_cmd *cmd,
- unsigned char *cdb,
- u8 *alua_ascq)
+ unsigned char *cdb)
{
/*
* Allowed CDBs for ALUA_ACCESS_STATE_TRANSITION as defined by
@@ -654,7 +664,7 @@ static inline int core_alua_state_transition(
case MI_REPORT_TARGET_PGS:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_STATE_TRANSITION;
+ set_ascq(cmd, ASCQ_04H_ALUA_STATE_TRANSITION);
return 1;
}
case REQUEST_SENSE:
@@ -662,7 +672,7 @@ static inline int core_alua_state_transition(
case WRITE_BUFFER:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_STATE_TRANSITION;
+ set_ascq(cmd, ASCQ_04H_ALUA_STATE_TRANSITION);
return 1;
}
@@ -684,8 +694,6 @@ target_alua_state_check(struct se_cmd *cmd)
struct t10_alua_tg_pt_gp *tg_pt_gp;
struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
int out_alua_state, nonop_delay_msecs;
- u8 alua_ascq;
- int ret;
if (dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)
return 0;
@@ -701,9 +709,8 @@ target_alua_state_check(struct se_cmd *cmd)
if (atomic_read(&port->sep_tg_pt_secondary_offline)) {
pr_debug("ALUA: Got secondary offline status for local"
" target port\n");
- alua_ascq = ASCQ_04H_ALUA_OFFLINE;
- ret = 1;
- goto out;
+ set_ascq(cmd, ASCQ_04H_ALUA_OFFLINE);
+ return TCM_CHECK_CONDITION_NOT_READY;
}
/*
* Second, obtain the struct t10_alua_tg_pt_gp_member pointer to the
@@ -731,20 +738,23 @@ target_alua_state_check(struct se_cmd *cmd)
switch (out_alua_state) {
case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
- ret = core_alua_state_nonoptimized(cmd, cdb,
- nonop_delay_msecs, &alua_ascq);
+ core_alua_state_nonoptimized(cmd, cdb, nonop_delay_msecs);
break;
case ALUA_ACCESS_STATE_STANDBY:
- ret = core_alua_state_standby(cmd, cdb, &alua_ascq);
+ if (core_alua_state_standby(cmd, cdb))
+ return TCM_CHECK_CONDITION_NOT_READY;
break;
case ALUA_ACCESS_STATE_UNAVAILABLE:
- ret = core_alua_state_unavailable(cmd, cdb, &alua_ascq);
+ if (core_alua_state_unavailable(cmd, cdb))
+ return TCM_CHECK_CONDITION_NOT_READY;
break;
case ALUA_ACCESS_STATE_TRANSITION:
- ret = core_alua_state_transition(cmd, cdb, &alua_ascq);
+ if (core_alua_state_transition(cmd, cdb))
+ return TCM_CHECK_CONDITION_NOT_READY;
break;
case ALUA_ACCESS_STATE_LBA_DEPENDENT:
- ret = core_alua_state_lba_dependent(cmd, tg_pt_gp, &alua_ascq);
+ if (core_alua_state_lba_dependent(cmd, tg_pt_gp))
+ return TCM_CHECK_CONDITION_NOT_READY;
break;
/*
* OFFLINE is a secondary ALUA target port group access state, that is
@@ -757,23 +767,6 @@ target_alua_state_check(struct se_cmd *cmd)
return TCM_INVALID_CDB_FIELD;
}
-out:
- if (ret > 0) {
- /*
- * Set SCSI additional sense code (ASC) to 'LUN Not Accessible';
- * The ALUA additional sense code qualifier (ASCQ) is determined
- * by the ALUA primary or secondary access state..
- */
- pr_debug("[%s]: ALUA TG Port not available, "
- "SenseKey: NOT_READY, ASC/ASCQ: "
- "0x04/0x%02x\n",
- cmd->se_tfo->get_fabric_name(), alua_ascq);
-
- cmd->scsi_asc = 0x04;
- cmd->scsi_ascq = alua_ascq;
- return TCM_CHECK_CONDITION_NOT_READY;
- }
-
return 0;
}
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c
index f0e85b119692..60a9ae6df763 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -457,6 +457,10 @@ static int target_fabric_tf_ops_check(
pr_err("Missing tfo->queue_tm_rsp()\n");
return -EINVAL;
}
+ if (!tfo->aborted_task) {
+ pr_err("Missing tfo->aborted_task()\n");
+ return -EINVAL;
+ }
/*
* We at least require tfo->fabric_make_wwn(), tfo->fabric_drop_wwn()
* tfo->fabric_make_tpg() and tfo->fabric_drop_tpg() in
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index cf991a91a8a9..7d6cddaec525 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -854,25 +854,6 @@ static int fd_init_prot(struct se_device *dev)
return 0;
}
-static void fd_init_format_buf(struct se_device *dev, unsigned char *buf,
- u32 unit_size, u32 *ref_tag, u16 app_tag,
- bool inc_reftag)
-{
- unsigned char *p = buf;
- int i;
-
- for (i = 0; i < unit_size; i += dev->prot_length) {
- *((u16 *)&p[0]) = 0xffff;
- *((__be16 *)&p[2]) = cpu_to_be16(app_tag);
- *((__be32 *)&p[4]) = cpu_to_be32(*ref_tag);
-
- if (inc_reftag)
- (*ref_tag)++;
-
- p += dev->prot_length;
- }
-}
-
static int fd_format_prot(struct se_device *dev)
{
struct fd_dev *fd_dev = FD_DEV(dev);
@@ -880,10 +861,8 @@ static int fd_format_prot(struct se_device *dev)
sector_t prot_length, prot;
unsigned char *buf;
loff_t pos = 0;
- u32 ref_tag = 0;
int unit_size = FDBD_FORMAT_UNIT_SIZE * dev->dev_attrib.block_size;
int rc, ret = 0, size, len;
- bool inc_reftag = false;
if (!dev->dev_attrib.pi_prot_type) {
pr_err("Unable to format_prot while pi_prot_type == 0\n");
@@ -894,37 +873,20 @@ static int fd_format_prot(struct se_device *dev)
return -ENODEV;
}
- switch (dev->dev_attrib.pi_prot_type) {
- case TARGET_DIF_TYPE3_PROT:
- ref_tag = 0xffffffff;
- break;
- case TARGET_DIF_TYPE2_PROT:
- case TARGET_DIF_TYPE1_PROT:
- inc_reftag = true;
- break;
- default:
- break;
- }
-
buf = vzalloc(unit_size);
if (!buf) {
pr_err("Unable to allocate FILEIO prot buf\n");
return -ENOMEM;
}
-
prot_length = (dev->transport->get_blocks(dev) + 1) * dev->prot_length;
size = prot_length;
pr_debug("Using FILEIO prot_length: %llu\n",
(unsigned long long)prot_length);
+ memset(buf, 0xff, unit_size);
for (prot = 0; prot < prot_length; prot += unit_size) {
-
- fd_init_format_buf(dev, buf, unit_size, &ref_tag, 0xffff,
- inc_reftag);
-
len = min(unit_size, size);
-
rc = kernel_write(prot_fd, buf, len, pos);
if (rc != len) {
pr_err("vfs_write to prot file failed: %d\n", rc);
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
index 554d4f75a75a..9e0232cca92e 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -203,10 +203,9 @@ static void iblock_free_device(struct se_device *dev)
if (ib_dev->ibd_bd != NULL)
blkdev_put(ib_dev->ibd_bd, FMODE_WRITE|FMODE_READ|FMODE_EXCL);
- if (ib_dev->ibd_bio_set != NULL) {
- bioset_integrity_free(ib_dev->ibd_bio_set);
+ if (ib_dev->ibd_bio_set != NULL)
bioset_free(ib_dev->ibd_bio_set);
- }
+
kfree(ib_dev);
}
diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c
index 66a5aba5a0d9..b920db3388cd 100644
--- a/drivers/target/target_core_rd.c
+++ b/drivers/target/target_core_rd.c
@@ -242,7 +242,7 @@ static void rd_release_prot_space(struct rd_dev *rd_dev)
rd_dev->sg_prot_count = 0;
}
-static int rd_build_prot_space(struct rd_dev *rd_dev, int prot_length)
+static int rd_build_prot_space(struct rd_dev *rd_dev, int prot_length, int block_size)
{
struct rd_dev_sg_table *sg_table;
u32 total_sg_needed, sg_tables;
@@ -252,8 +252,13 @@ static int rd_build_prot_space(struct rd_dev *rd_dev, int prot_length)
if (rd_dev->rd_flags & RDF_NULLIO)
return 0;
-
- total_sg_needed = rd_dev->rd_page_count / prot_length;
+ /*
+ * prot_length=8byte dif data
+ * tot sg needed = rd_page_count * (PGSZ/block_size) *
+ * (prot_length/block_size) + pad
+ * PGSZ canceled each other.
+ */
+ total_sg_needed = (rd_dev->rd_page_count * prot_length / block_size) + 1;
sg_tables = (total_sg_needed / max_sg_per_table) + 1;
@@ -606,7 +611,8 @@ static int rd_init_prot(struct se_device *dev)
if (!dev->dev_attrib.pi_prot_type)
return 0;
- return rd_build_prot_space(rd_dev, dev->prot_length);
+ return rd_build_prot_space(rd_dev, dev->prot_length,
+ dev->dev_attrib.block_size);
}
static void rd_free_prot(struct se_device *dev)
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 77e6531fb0a1..e0229592ec55 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -89,6 +89,7 @@ static sense_reason_t
sbc_emulate_readcapacity_16(struct se_cmd *cmd)
{
struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
unsigned char *rbuf;
unsigned char buf[32];
unsigned long long blocks = dev->transport->get_blocks(dev);
@@ -109,8 +110,10 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd)
/*
* Set P_TYPE and PROT_EN bits for DIF support
*/
- if (dev->dev_attrib.pi_prot_type)
- buf[12] = (dev->dev_attrib.pi_prot_type - 1) << 1 | 0x1;
+ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+ if (dev->dev_attrib.pi_prot_type)
+ buf[12] = (dev->dev_attrib.pi_prot_type - 1) << 1 | 0x1;
+ }
if (dev->transport->get_lbppbe)
buf[13] = dev->transport->get_lbppbe(dev) & 0x0f;
@@ -425,13 +428,14 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd)
goto out;
}
- write_sg = kzalloc(sizeof(struct scatterlist) * cmd->t_data_nents,
+ write_sg = kmalloc(sizeof(struct scatterlist) * cmd->t_data_nents,
GFP_KERNEL);
if (!write_sg) {
pr_err("Unable to allocate compare_and_write sg\n");
ret = TCM_OUT_OF_RESOURCES;
goto out;
}
+ sg_init_table(write_sg, cmd->t_data_nents);
/*
* Setup verify and write data payloads from total NumberLBAs.
*/
@@ -569,30 +573,85 @@ sbc_compare_and_write(struct se_cmd *cmd)
return TCM_NO_SENSE;
}
+static int
+sbc_set_prot_op_checks(u8 protect, enum target_prot_type prot_type,
+ bool is_write, struct se_cmd *cmd)
+{
+ if (is_write) {
+ cmd->prot_op = protect ? TARGET_PROT_DOUT_PASS :
+ TARGET_PROT_DOUT_INSERT;
+ switch (protect) {
+ case 0x0:
+ case 0x3:
+ cmd->prot_checks = 0;
+ break;
+ case 0x1:
+ case 0x5:
+ cmd->prot_checks = TARGET_DIF_CHECK_GUARD;
+ if (prot_type == TARGET_DIF_TYPE1_PROT)
+ cmd->prot_checks |= TARGET_DIF_CHECK_REFTAG;
+ break;
+ case 0x2:
+ if (prot_type == TARGET_DIF_TYPE1_PROT)
+ cmd->prot_checks = TARGET_DIF_CHECK_REFTAG;
+ break;
+ case 0x4:
+ cmd->prot_checks = TARGET_DIF_CHECK_GUARD;
+ break;
+ default:
+ pr_err("Unsupported protect field %d\n", protect);
+ return -EINVAL;
+ }
+ } else {
+ cmd->prot_op = protect ? TARGET_PROT_DIN_PASS :
+ TARGET_PROT_DIN_STRIP;
+ switch (protect) {
+ case 0x0:
+ case 0x1:
+ case 0x5:
+ cmd->prot_checks = TARGET_DIF_CHECK_GUARD;
+ if (prot_type == TARGET_DIF_TYPE1_PROT)
+ cmd->prot_checks |= TARGET_DIF_CHECK_REFTAG;
+ break;
+ case 0x2:
+ if (prot_type == TARGET_DIF_TYPE1_PROT)
+ cmd->prot_checks = TARGET_DIF_CHECK_REFTAG;
+ break;
+ case 0x3:
+ cmd->prot_checks = 0;
+ break;
+ case 0x4:
+ cmd->prot_checks = TARGET_DIF_CHECK_GUARD;
+ break;
+ default:
+ pr_err("Unsupported protect field %d\n", protect);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static bool
sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb,
- u32 sectors)
+ u32 sectors, bool is_write)
{
- if (!cmd->t_prot_sg || !cmd->t_prot_nents)
+ u8 protect = cdb[1] >> 5;
+
+ if ((!cmd->t_prot_sg || !cmd->t_prot_nents) && cmd->prot_pto)
return true;
switch (dev->dev_attrib.pi_prot_type) {
case TARGET_DIF_TYPE3_PROT:
- if (!(cdb[1] & 0xe0))
- return true;
-
cmd->reftag_seed = 0xffffffff;
break;
case TARGET_DIF_TYPE2_PROT:
- if (cdb[1] & 0xe0)
+ if (protect)
return false;
cmd->reftag_seed = cmd->t_task_lba;
break;
case TARGET_DIF_TYPE1_PROT:
- if (!(cdb[1] & 0xe0))
- return true;
-
cmd->reftag_seed = cmd->t_task_lba;
break;
case TARGET_DIF_TYPE0_PROT:
@@ -600,9 +659,15 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb,
return true;
}
+ if (sbc_set_prot_op_checks(protect, dev->dev_attrib.pi_prot_type,
+ is_write, cmd))
+ return false;
+
cmd->prot_type = dev->dev_attrib.pi_prot_type;
cmd->prot_length = dev->prot_length * sectors;
- cmd->prot_handover = PROT_SEPERATED;
+ pr_debug("%s: prot_type=%d, prot_length=%d prot_op=%d prot_checks=%d\n",
+ __func__, cmd->prot_type, cmd->prot_length,
+ cmd->prot_op, cmd->prot_checks);
return true;
}
@@ -628,7 +693,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
sectors = transport_get_sectors_10(cdb);
cmd->t_task_lba = transport_lba_32(cdb);
- if (!sbc_check_prot(dev, cmd, cdb, sectors))
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, false))
return TCM_UNSUPPORTED_SCSI_OPCODE;
cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
@@ -639,7 +704,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
sectors = transport_get_sectors_12(cdb);
cmd->t_task_lba = transport_lba_32(cdb);
- if (!sbc_check_prot(dev, cmd, cdb, sectors))
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, false))
return TCM_UNSUPPORTED_SCSI_OPCODE;
cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
@@ -650,7 +715,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
sectors = transport_get_sectors_16(cdb);
cmd->t_task_lba = transport_lba_64(cdb);
- if (!sbc_check_prot(dev, cmd, cdb, sectors))
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, false))
return TCM_UNSUPPORTED_SCSI_OPCODE;
cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
@@ -669,7 +734,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
sectors = transport_get_sectors_10(cdb);
cmd->t_task_lba = transport_lba_32(cdb);
- if (!sbc_check_prot(dev, cmd, cdb, sectors))
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, true))
return TCM_UNSUPPORTED_SCSI_OPCODE;
if (cdb[1] & 0x8)
@@ -682,7 +747,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
sectors = transport_get_sectors_12(cdb);
cmd->t_task_lba = transport_lba_32(cdb);
- if (!sbc_check_prot(dev, cmd, cdb, sectors))
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, true))
return TCM_UNSUPPORTED_SCSI_OPCODE;
if (cdb[1] & 0x8)
@@ -695,7 +760,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
sectors = transport_get_sectors_16(cdb);
cmd->t_task_lba = transport_lba_64(cdb);
- if (!sbc_check_prot(dev, cmd, cdb, sectors))
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, true))
return TCM_UNSUPPORTED_SCSI_OPCODE;
if (cdb[1] & 0x8)
@@ -1031,6 +1096,50 @@ err:
}
EXPORT_SYMBOL(sbc_execute_unmap);
+void
+sbc_dif_generate(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_dif_v1_tuple *sdt;
+ struct scatterlist *dsg, *psg = cmd->t_prot_sg;
+ sector_t sector = cmd->t_task_lba;
+ void *daddr, *paddr;
+ int i, j, offset = 0;
+
+ for_each_sg(cmd->t_data_sg, dsg, cmd->t_data_nents, i) {
+ daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
+ paddr = kmap_atomic(sg_page(psg)) + psg->offset;
+
+ for (j = 0; j < dsg->length; j += dev->dev_attrib.block_size) {
+
+ if (offset >= psg->length) {
+ kunmap_atomic(paddr);
+ psg = sg_next(psg);
+ paddr = kmap_atomic(sg_page(psg)) + psg->offset;
+ offset = 0;
+ }
+
+ sdt = paddr + offset;
+ sdt->guard_tag = cpu_to_be16(crc_t10dif(daddr + j,
+ dev->dev_attrib.block_size));
+ if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT)
+ sdt->ref_tag = cpu_to_be32(sector & 0xffffffff);
+ sdt->app_tag = 0;
+
+ pr_debug("DIF WRITE INSERT sector: %llu guard_tag: 0x%04x"
+ " app_tag: 0x%04x ref_tag: %u\n",
+ (unsigned long long)sector, sdt->guard_tag,
+ sdt->app_tag, be32_to_cpu(sdt->ref_tag));
+
+ sector++;
+ offset += sizeof(struct se_dif_v1_tuple);
+ }
+
+ kunmap_atomic(paddr);
+ kunmap_atomic(daddr);
+ }
+}
+
static sense_reason_t
sbc_dif_v1_verify(struct se_device *dev, struct se_dif_v1_tuple *sdt,
const void *p, sector_t sector, unsigned int ei_lba)
@@ -1162,9 +1271,9 @@ sbc_dif_verify_write(struct se_cmd *cmd, sector_t start, unsigned int sectors,
}
EXPORT_SYMBOL(sbc_dif_verify_write);
-sense_reason_t
-sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors,
- unsigned int ei_lba, struct scatterlist *sg, int sg_off)
+static sense_reason_t
+__sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors,
+ unsigned int ei_lba, struct scatterlist *sg, int sg_off)
{
struct se_device *dev = cmd->se_dev;
struct se_dif_v1_tuple *sdt;
@@ -1217,8 +1326,31 @@ sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors,
kunmap_atomic(paddr);
kunmap_atomic(daddr);
}
- sbc_dif_copy_prot(cmd, sectors, true, sg, sg_off);
return 0;
}
+
+sense_reason_t
+sbc_dif_read_strip(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ u32 sectors = cmd->prot_length / dev->prot_length;
+
+ return __sbc_dif_verify_read(cmd, cmd->t_task_lba, sectors, 0,
+ cmd->t_prot_sg, 0);
+}
+
+sense_reason_t
+sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors,
+ unsigned int ei_lba, struct scatterlist *sg, int sg_off)
+{
+ sense_reason_t rc;
+
+ rc = __sbc_dif_verify_read(cmd, start, sectors, ei_lba, sg, sg_off);
+ if (rc)
+ return rc;
+
+ sbc_dif_copy_prot(cmd, sectors, true, sg, sg_off);
+ return 0;
+}
EXPORT_SYMBOL(sbc_dif_verify_read);
diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c
index 3bebc71ea033..8653666612a8 100644
--- a/drivers/target/target_core_spc.c
+++ b/drivers/target/target_core_spc.c
@@ -71,6 +71,7 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf)
{
struct se_lun *lun = cmd->se_lun;
struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
/* Set RMB (removable media) for tape devices */
if (dev->transport->get_device_type(dev) == TYPE_TAPE)
@@ -101,10 +102,13 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf)
if (dev->dev_attrib.emulate_3pc)
buf[5] |= 0x8;
/*
- * Set Protection (PROTECT) bit when DIF has been enabled.
+ * Set Protection (PROTECT) bit when DIF has been enabled on the
+ * device, and the transport supports VERIFY + PASS.
*/
- if (dev->dev_attrib.pi_prot_type)
- buf[5] |= 0x1;
+ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+ if (dev->dev_attrib.pi_prot_type)
+ buf[5] |= 0x1;
+ }
buf[7] = 0x2; /* CmdQue=1 */
@@ -473,16 +477,19 @@ static sense_reason_t
spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
{
struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
buf[3] = 0x3c;
/*
* Set GRD_CHK + REF_CHK for TYPE1 protection, or GRD_CHK
* only for TYPE3 protection.
*/
- if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT)
- buf[4] = 0x5;
- else if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE3_PROT)
- buf[4] = 0x4;
+ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+ if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT)
+ buf[4] = 0x5;
+ else if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE3_PROT)
+ buf[4] = 0x4;
+ }
/* Set HEADSUP, ORDSUP, SIMPSUP */
buf[5] = 0x07;
@@ -762,7 +769,7 @@ out:
return ret;
}
-static int spc_modesense_rwrecovery(struct se_device *dev, u8 pc, u8 *p)
+static int spc_modesense_rwrecovery(struct se_cmd *cmd, u8 pc, u8 *p)
{
p[0] = 0x01;
p[1] = 0x0a;
@@ -775,8 +782,11 @@ out:
return 12;
}
-static int spc_modesense_control(struct se_device *dev, u8 pc, u8 *p)
+static int spc_modesense_control(struct se_cmd *cmd, u8 pc, u8 *p)
{
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+
p[0] = 0x0a;
p[1] = 0x0a;
@@ -868,8 +878,10 @@ static int spc_modesense_control(struct se_device *dev, u8 pc, u8 *p)
* type, shall not modify the contents of the LOGICAL BLOCK REFERENCE
* TAG field.
*/
- if (dev->dev_attrib.pi_prot_type)
- p[5] |= 0x80;
+ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+ if (dev->dev_attrib.pi_prot_type)
+ p[5] |= 0x80;
+ }
p[8] = 0xff;
p[9] = 0xff;
@@ -879,8 +891,10 @@ out:
return 12;
}
-static int spc_modesense_caching(struct se_device *dev, u8 pc, u8 *p)
+static int spc_modesense_caching(struct se_cmd *cmd, u8 pc, u8 *p)
{
+ struct se_device *dev = cmd->se_dev;
+
p[0] = 0x08;
p[1] = 0x12;
@@ -896,7 +910,7 @@ out:
return 20;
}
-static int spc_modesense_informational_exceptions(struct se_device *dev, u8 pc, unsigned char *p)
+static int spc_modesense_informational_exceptions(struct se_cmd *cmd, u8 pc, unsigned char *p)
{
p[0] = 0x1c;
p[1] = 0x0a;
@@ -912,7 +926,7 @@ out:
static struct {
uint8_t page;
uint8_t subpage;
- int (*emulate)(struct se_device *, u8, unsigned char *);
+ int (*emulate)(struct se_cmd *, u8, unsigned char *);
} modesense_handlers[] = {
{ .page = 0x01, .subpage = 0x00, .emulate = spc_modesense_rwrecovery },
{ .page = 0x08, .subpage = 0x00, .emulate = spc_modesense_caching },
@@ -1050,7 +1064,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd)
* the only two possibilities).
*/
if ((modesense_handlers[i].subpage & ~subpage) == 0) {
- ret = modesense_handlers[i].emulate(dev, pc, &buf[length]);
+ ret = modesense_handlers[i].emulate(cmd, pc, &buf[length]);
if (!ten && length + ret >= 255)
break;
length += ret;
@@ -1063,7 +1077,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd)
for (i = 0; i < ARRAY_SIZE(modesense_handlers); ++i)
if (modesense_handlers[i].page == page &&
modesense_handlers[i].subpage == subpage) {
- length += modesense_handlers[i].emulate(dev, pc, &buf[length]);
+ length += modesense_handlers[i].emulate(cmd, pc, &buf[length]);
goto set_length;
}
@@ -1095,7 +1109,6 @@ set_length:
static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd)
{
- struct se_device *dev = cmd->se_dev;
char *cdb = cmd->t_task_cdb;
bool ten = cdb[0] == MODE_SELECT_10;
int off = ten ? 8 : 4;
@@ -1131,7 +1144,7 @@ static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd)
if (modesense_handlers[i].page == page &&
modesense_handlers[i].subpage == subpage) {
memset(tbuf, 0, SE_MODE_PAGE_BUF);
- length = modesense_handlers[i].emulate(dev, 0, tbuf);
+ length = modesense_handlers[i].emulate(cmd, 0, tbuf);
goto check_contents;
}
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index 70c638f730af..f7cd95e8111a 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -87,14 +87,17 @@ static void core_tmr_handle_tas_abort(
struct se_cmd *cmd,
int tas)
{
+ bool remove = true;
/*
* TASK ABORTED status (TAS) bit support
*/
if ((tmr_nacl &&
- (tmr_nacl == cmd->se_sess->se_node_acl)) || tas)
+ (tmr_nacl != cmd->se_sess->se_node_acl)) && tas) {
+ remove = false;
transport_send_task_abort(cmd);
+ }
- transport_cmd_finish_abort(cmd, 0);
+ transport_cmd_finish_abort(cmd, remove);
}
static int target_check_cdb_and_preempt(struct list_head *list,
@@ -127,6 +130,11 @@ void core_tmr_abort_task(
if (dev != se_cmd->se_dev)
continue;
+
+ /* skip se_cmd associated with tmr */
+ if (tmr->task_cmd == se_cmd)
+ continue;
+
ref_tag = se_cmd->se_tfo->get_task_tag(se_cmd);
if (tmr->ref_task_tag != ref_tag)
continue;
@@ -150,18 +158,9 @@ void core_tmr_abort_task(
cancel_work_sync(&se_cmd->work);
transport_wait_for_tasks(se_cmd);
- /*
- * Now send SAM_STAT_TASK_ABORTED status for the referenced
- * se_cmd descriptor..
- */
- transport_send_task_abort(se_cmd);
- /*
- * Also deal with possible extra acknowledge reference..
- */
- if (se_cmd->se_cmd_flags & SCF_ACK_KREF)
- target_put_sess_cmd(se_sess, se_cmd);
target_put_sess_cmd(se_sess, se_cmd);
+ transport_cmd_finish_abort(se_cmd, true);
printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for"
" ref_tag: %d\n", ref_tag);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 2956250b7225..d4b98690a736 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -235,7 +235,7 @@ void transport_subsystem_check_init(void)
sub_api_initialized = 1;
}
-struct se_session *transport_init_session(void)
+struct se_session *transport_init_session(enum target_prot_op sup_prot_ops)
{
struct se_session *se_sess;
@@ -251,6 +251,7 @@ struct se_session *transport_init_session(void)
INIT_LIST_HEAD(&se_sess->sess_wait_list);
spin_lock_init(&se_sess->sess_cmd_lock);
kref_init(&se_sess->sess_kref);
+ se_sess->sup_prot_ops = sup_prot_ops;
return se_sess;
}
@@ -288,12 +289,13 @@ int transport_alloc_session_tags(struct se_session *se_sess,
EXPORT_SYMBOL(transport_alloc_session_tags);
struct se_session *transport_init_session_tags(unsigned int tag_num,
- unsigned int tag_size)
+ unsigned int tag_size,
+ enum target_prot_op sup_prot_ops)
{
struct se_session *se_sess;
int rc;
- se_sess = transport_init_session();
+ se_sess = transport_init_session(sup_prot_ops);
if (IS_ERR(se_sess))
return se_sess;
@@ -603,6 +605,15 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd)
void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
{
+ if (cmd->se_cmd_flags & SCF_SE_LUN_CMD)
+ transport_lun_remove_cmd(cmd);
+ /*
+ * Allow the fabric driver to unmap any resources before
+ * releasing the descriptor via TFO->release_cmd()
+ */
+ if (remove)
+ cmd->se_tfo->aborted_task(cmd);
+
if (transport_cmd_check_stop_to_fabric(cmd))
return;
if (remove)
@@ -1365,6 +1376,13 @@ int target_submit_cmd_map_sgls(struct se_cmd *se_cmd, struct se_session *se_sess
target_put_sess_cmd(se_sess, se_cmd);
return 0;
}
+
+ rc = target_setup_cmd_from_cdb(se_cmd, cdb);
+ if (rc != 0) {
+ transport_generic_request_failure(se_cmd, rc);
+ return 0;
+ }
+
/*
* Save pointers for SGLs containing protection information,
* if present.
@@ -1374,11 +1392,6 @@ int target_submit_cmd_map_sgls(struct se_cmd *se_cmd, struct se_session *se_sess
se_cmd->t_prot_nents = sgl_prot_count;
}
- rc = target_setup_cmd_from_cdb(se_cmd, cdb);
- if (rc != 0) {
- transport_generic_request_failure(se_cmd, rc);
- return 0;
- }
/*
* When a non zero sgl_count has been passed perform SGL passthrough
* mapping for pre-allocated fabric memory instead of having target
@@ -1754,6 +1767,15 @@ void target_execute_cmd(struct se_cmd *cmd)
cmd->t_state = TRANSPORT_PROCESSING;
cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT;
spin_unlock_irq(&cmd->t_state_lock);
+ /*
+ * Perform WRITE_INSERT of PI using software emulation when backend
+ * device has PI enabled, if the transport has not already generated
+ * PI using hardware WRITE_INSERT offload.
+ */
+ if (cmd->prot_op == TARGET_PROT_DOUT_INSERT) {
+ if (!(cmd->se_sess->sup_prot_ops & TARGET_PROT_DOUT_INSERT))
+ sbc_dif_generate(cmd);
+ }
if (target_handle_task_attr(cmd)) {
spin_lock_irq(&cmd->t_state_lock);
@@ -1883,6 +1905,21 @@ static void transport_handle_queue_full(
schedule_work(&cmd->se_dev->qf_work_queue);
}
+static bool target_check_read_strip(struct se_cmd *cmd)
+{
+ sense_reason_t rc;
+
+ if (!(cmd->se_sess->sup_prot_ops & TARGET_PROT_DIN_STRIP)) {
+ rc = sbc_dif_read_strip(cmd);
+ if (rc) {
+ cmd->pi_err = rc;
+ return true;
+ }
+ }
+
+ return false;
+}
+
static void target_complete_ok_work(struct work_struct *work)
{
struct se_cmd *cmd = container_of(work, struct se_cmd, work);
@@ -1947,6 +1984,22 @@ static void target_complete_ok_work(struct work_struct *work)
cmd->data_length;
}
spin_unlock(&cmd->se_lun->lun_sep_lock);
+ /*
+ * Perform READ_STRIP of PI using software emulation when
+ * backend had PI enabled, if the transport will not be
+ * performing hardware READ_STRIP offload.
+ */
+ if (cmd->prot_op == TARGET_PROT_DIN_STRIP &&
+ target_check_read_strip(cmd)) {
+ ret = transport_send_check_condition_and_sense(cmd,
+ cmd->pi_err, 0);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
+
+ transport_lun_remove_cmd(cmd);
+ transport_cmd_check_stop_to_fabric(cmd);
+ return;
+ }
trace_target_cmd_complete(cmd);
ret = cmd->se_tfo->queue_data_in(cmd);
@@ -2039,6 +2092,10 @@ static inline void transport_free_pages(struct se_cmd *cmd)
transport_free_sgl(cmd->t_bidi_data_sg, cmd->t_bidi_data_nents);
cmd->t_bidi_data_sg = NULL;
cmd->t_bidi_data_nents = 0;
+
+ transport_free_sgl(cmd->t_prot_sg, cmd->t_prot_nents);
+ cmd->t_prot_sg = NULL;
+ cmd->t_prot_nents = 0;
}
/**
@@ -2202,6 +2259,14 @@ transport_generic_new_cmd(struct se_cmd *cmd)
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
+ if (cmd->prot_op != TARGET_PROT_NORMAL) {
+ ret = target_alloc_sgl(&cmd->t_prot_sg,
+ &cmd->t_prot_nents,
+ cmd->prot_length, true);
+ if (ret < 0)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+
ret = target_alloc_sgl(&cmd->t_data_sg, &cmd->t_data_nents,
cmd->data_length, zero_flag);
if (ret < 0)
@@ -2770,13 +2835,17 @@ int transport_check_aborted_status(struct se_cmd *cmd, int send_status)
if (!(cmd->transport_state & CMD_T_ABORTED))
return 0;
- if (!send_status || (cmd->se_cmd_flags & SCF_SENT_DELAYED_TAS))
+ /*
+ * If cmd has been aborted but either no status is to be sent or it has
+ * already been sent, just return
+ */
+ if (!send_status || !(cmd->se_cmd_flags & SCF_SEND_DELAYED_TAS))
return 1;
pr_debug("Sending delayed SAM_STAT_TASK_ABORTED status for CDB: 0x%02x ITT: 0x%08x\n",
cmd->t_task_cdb[0], cmd->se_tfo->get_task_tag(cmd));
- cmd->se_cmd_flags |= SCF_SENT_DELAYED_TAS;
+ cmd->se_cmd_flags &= ~SCF_SEND_DELAYED_TAS;
cmd->scsi_status = SAM_STAT_TASK_ABORTED;
trace_target_cmd_complete(cmd);
cmd->se_tfo->queue_status(cmd);
@@ -2790,7 +2859,7 @@ void transport_send_task_abort(struct se_cmd *cmd)
unsigned long flags;
spin_lock_irqsave(&cmd->t_state_lock, flags);
- if (cmd->se_cmd_flags & (SCF_SENT_CHECK_CONDITION | SCF_SENT_DELAYED_TAS)) {
+ if (cmd->se_cmd_flags & (SCF_SENT_CHECK_CONDITION)) {
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
return;
}
@@ -2805,6 +2874,7 @@ void transport_send_task_abort(struct se_cmd *cmd)
if (cmd->data_direction == DMA_TO_DEVICE) {
if (cmd->se_tfo->write_pending_status(cmd) != 0) {
cmd->transport_state |= CMD_T_ABORTED;
+ cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS;
smp_mb__after_atomic_inc();
return;
}
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h
index 752863acecb8..a0bcfd3e7e7d 100644
--- a/drivers/target/tcm_fc/tcm_fc.h
+++ b/drivers/target/tcm_fc/tcm_fc.h
@@ -94,20 +94,19 @@ struct ft_lun {
*/
struct ft_tpg {
u32 index;
- struct ft_lport_acl *lport_acl;
+ struct ft_lport_wwn *lport_wwn;
struct ft_tport *tport; /* active tport or NULL */
- struct list_head list; /* linkage in ft_lport_acl tpg_list */
struct list_head lun_list; /* head of LUNs */
struct se_portal_group se_tpg;
struct workqueue_struct *workqueue;
};
-struct ft_lport_acl {
+struct ft_lport_wwn {
u64 wwpn;
char name[FT_NAMELEN];
- struct list_head list;
- struct list_head tpg_list;
- struct se_wwn fc_lport_wwn;
+ struct list_head ft_wwn_node;
+ struct ft_tpg *tpg;
+ struct se_wwn se_wwn;
};
/*
@@ -128,7 +127,6 @@ struct ft_cmd {
u32 sg_cnt; /* No. of item in scatterlist */
};
-extern struct list_head ft_lport_list;
extern struct mutex ft_lport_lock;
extern struct fc4_prov ft_prov;
extern struct target_fabric_configfs *ft_configfs;
@@ -163,6 +161,7 @@ int ft_write_pending_status(struct se_cmd *);
u32 ft_get_task_tag(struct se_cmd *);
int ft_get_cmd_state(struct se_cmd *);
void ft_queue_tm_resp(struct se_cmd *);
+void ft_aborted_task(struct se_cmd *);
/*
* other internal functions.
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
index 8b2c1aaf81de..01cf37f212c3 100644
--- a/drivers/target/tcm_fc/tfc_cmd.c
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -426,6 +426,11 @@ void ft_queue_tm_resp(struct se_cmd *se_cmd)
ft_send_resp_code(cmd, code);
}
+void ft_aborted_task(struct se_cmd *se_cmd)
+{
+ return;
+}
+
static void ft_send_work(struct work_struct *work);
/*
diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c
index e879da81ad93..efdcb9663a1a 100644
--- a/drivers/target/tcm_fc/tfc_conf.c
+++ b/drivers/target/tcm_fc/tfc_conf.c
@@ -50,7 +50,7 @@
struct target_fabric_configfs *ft_configfs;
-LIST_HEAD(ft_lport_list);
+static LIST_HEAD(ft_wwn_list);
DEFINE_MUTEX(ft_lport_lock);
unsigned int ft_debug_logging;
@@ -298,7 +298,7 @@ static struct se_portal_group *ft_add_tpg(
struct config_group *group,
const char *name)
{
- struct ft_lport_acl *lacl;
+ struct ft_lport_wwn *ft_wwn;
struct ft_tpg *tpg;
struct workqueue_struct *wq;
unsigned long index;
@@ -318,12 +318,17 @@ static struct se_portal_group *ft_add_tpg(
if (index > UINT_MAX)
return NULL;
- lacl = container_of(wwn, struct ft_lport_acl, fc_lport_wwn);
+ if ((index != 1)) {
+ pr_err("Error, a single TPG=1 is used for HW port mappings\n");
+ return ERR_PTR(-ENOSYS);
+ }
+
+ ft_wwn = container_of(wwn, struct ft_lport_wwn, se_wwn);
tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
if (!tpg)
return NULL;
tpg->index = index;
- tpg->lport_acl = lacl;
+ tpg->lport_wwn = ft_wwn;
INIT_LIST_HEAD(&tpg->lun_list);
wq = alloc_workqueue("tcm_fc", 0, 1);
@@ -342,7 +347,7 @@ static struct se_portal_group *ft_add_tpg(
tpg->workqueue = wq;
mutex_lock(&ft_lport_lock);
- list_add_tail(&tpg->list, &lacl->tpg_list);
+ ft_wwn->tpg = tpg;
mutex_unlock(&ft_lport_lock);
return &tpg->se_tpg;
@@ -351,6 +356,7 @@ static struct se_portal_group *ft_add_tpg(
static void ft_del_tpg(struct se_portal_group *se_tpg)
{
struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg);
+ struct ft_lport_wwn *ft_wwn = tpg->lport_wwn;
pr_debug("del tpg %s\n",
config_item_name(&tpg->se_tpg.tpg_group.cg_item));
@@ -361,7 +367,7 @@ static void ft_del_tpg(struct se_portal_group *se_tpg)
synchronize_rcu();
mutex_lock(&ft_lport_lock);
- list_del(&tpg->list);
+ ft_wwn->tpg = NULL;
if (tpg->tport) {
tpg->tport->tpg = NULL;
tpg->tport = NULL;
@@ -380,15 +386,11 @@ static void ft_del_tpg(struct se_portal_group *se_tpg)
*/
struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport)
{
- struct ft_lport_acl *lacl;
- struct ft_tpg *tpg;
+ struct ft_lport_wwn *ft_wwn;
- list_for_each_entry(lacl, &ft_lport_list, list) {
- if (lacl->wwpn == lport->wwpn) {
- list_for_each_entry(tpg, &lacl->tpg_list, list)
- return tpg; /* XXX for now return first entry */
- return NULL;
- }
+ list_for_each_entry(ft_wwn, &ft_wwn_list, ft_wwn_node) {
+ if (ft_wwn->wwpn == lport->wwpn)
+ return ft_wwn->tpg;
}
return NULL;
}
@@ -401,50 +403,49 @@ struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport)
* Add lport to allowed config.
* The name is the WWPN in lower-case ASCII, colon-separated bytes.
*/
-static struct se_wwn *ft_add_lport(
+static struct se_wwn *ft_add_wwn(
struct target_fabric_configfs *tf,
struct config_group *group,
const char *name)
{
- struct ft_lport_acl *lacl;
- struct ft_lport_acl *old_lacl;
+ struct ft_lport_wwn *ft_wwn;
+ struct ft_lport_wwn *old_ft_wwn;
u64 wwpn;
- pr_debug("add lport %s\n", name);
+ pr_debug("add wwn %s\n", name);
if (ft_parse_wwn(name, &wwpn, 1) < 0)
return NULL;
- lacl = kzalloc(sizeof(*lacl), GFP_KERNEL);
- if (!lacl)
+ ft_wwn = kzalloc(sizeof(*ft_wwn), GFP_KERNEL);
+ if (!ft_wwn)
return NULL;
- lacl->wwpn = wwpn;
- INIT_LIST_HEAD(&lacl->tpg_list);
+ ft_wwn->wwpn = wwpn;
mutex_lock(&ft_lport_lock);
- list_for_each_entry(old_lacl, &ft_lport_list, list) {
- if (old_lacl->wwpn == wwpn) {
+ list_for_each_entry(old_ft_wwn, &ft_wwn_list, ft_wwn_node) {
+ if (old_ft_wwn->wwpn == wwpn) {
mutex_unlock(&ft_lport_lock);
- kfree(lacl);
+ kfree(ft_wwn);
return NULL;
}
}
- list_add_tail(&lacl->list, &ft_lport_list);
- ft_format_wwn(lacl->name, sizeof(lacl->name), wwpn);
+ list_add_tail(&ft_wwn->ft_wwn_node, &ft_wwn_list);
+ ft_format_wwn(ft_wwn->name, sizeof(ft_wwn->name), wwpn);
mutex_unlock(&ft_lport_lock);
- return &lacl->fc_lport_wwn;
+ return &ft_wwn->se_wwn;
}
-static void ft_del_lport(struct se_wwn *wwn)
+static void ft_del_wwn(struct se_wwn *wwn)
{
- struct ft_lport_acl *lacl = container_of(wwn,
- struct ft_lport_acl, fc_lport_wwn);
+ struct ft_lport_wwn *ft_wwn = container_of(wwn,
+ struct ft_lport_wwn, se_wwn);
- pr_debug("del lport %s\n", lacl->name);
+ pr_debug("del wwn %s\n", ft_wwn->name);
mutex_lock(&ft_lport_lock);
- list_del(&lacl->list);
+ list_del(&ft_wwn->ft_wwn_node);
mutex_unlock(&ft_lport_lock);
- kfree(lacl);
+ kfree(ft_wwn);
}
static ssize_t ft_wwn_show_attr_version(
@@ -471,7 +472,7 @@ static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg)
{
struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr;
- return tpg->lport_acl->name;
+ return tpg->lport_wwn->name;
}
static u16 ft_get_tag(struct se_portal_group *se_tpg)
@@ -536,12 +537,13 @@ static struct target_core_fabric_ops ft_fabric_ops = {
.queue_data_in = ft_queue_data_in,
.queue_status = ft_queue_status,
.queue_tm_rsp = ft_queue_tm_resp,
+ .aborted_task = ft_aborted_task,
/*
* Setup function pointers for generic logic in
* target_core_fabric_configfs.c
*/
- .fabric_make_wwn = &ft_add_lport,
- .fabric_drop_wwn = &ft_del_lport,
+ .fabric_make_wwn = &ft_add_wwn,
+ .fabric_drop_wwn = &ft_del_wwn,
.fabric_make_tpg = &ft_add_tpg,
.fabric_drop_tpg = &ft_del_tpg,
.fabric_post_link = NULL,
diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c
index ae52c08dad09..21ce50880c79 100644
--- a/drivers/target/tcm_fc/tfc_sess.c
+++ b/drivers/target/tcm_fc/tfc_sess.c
@@ -51,7 +51,7 @@ static void ft_sess_delete_all(struct ft_tport *);
* Lookup or allocate target local port.
* Caller holds ft_lport_lock.
*/
-static struct ft_tport *ft_tport_create(struct fc_lport *lport)
+static struct ft_tport *ft_tport_get(struct fc_lport *lport)
{
struct ft_tpg *tpg;
struct ft_tport *tport;
@@ -68,6 +68,7 @@ static struct ft_tport *ft_tport_create(struct fc_lport *lport)
if (tport) {
tport->tpg = tpg;
+ tpg->tport = tport;
return tport;
}
@@ -114,7 +115,7 @@ static void ft_tport_delete(struct ft_tport *tport)
void ft_lport_add(struct fc_lport *lport, void *arg)
{
mutex_lock(&ft_lport_lock);
- ft_tport_create(lport);
+ ft_tport_get(lport);
mutex_unlock(&ft_lport_lock);
}
@@ -211,7 +212,8 @@ static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id,
return NULL;
sess->se_sess = transport_init_session_tags(TCM_FC_DEFAULT_TAGS,
- sizeof(struct ft_cmd));
+ sizeof(struct ft_cmd),
+ TARGET_PROT_NORMAL);
if (IS_ERR(sess->se_sess)) {
kfree(sess);
return NULL;
@@ -350,7 +352,7 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
struct ft_node_acl *acl;
u32 fcp_parm;
- tport = ft_tport_create(rdata->local_port);
+ tport = ft_tport_get(rdata->local_port);
if (!tport)
goto not_target; /* not a target for this local port */
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
index 45af765a3198..a99c63152b8d 100644
--- a/drivers/thermal/imx_thermal.c
+++ b/drivers/thermal/imx_thermal.c
@@ -62,12 +62,16 @@ enum imx_thermal_trip {
#define IMX_POLLING_DELAY 2000 /* millisecond */
#define IMX_PASSIVE_DELAY 1000
+#define FACTOR0 10000000
+#define FACTOR1 15976
+#define FACTOR2 4297157
+
struct imx_thermal_data {
struct thermal_zone_device *tz;
struct thermal_cooling_device *cdev;
enum thermal_device_mode mode;
struct regmap *tempmon;
- int c1, c2; /* See formula in imx_get_sensor_data() */
+ u32 c1, c2; /* See formula in imx_get_sensor_data() */
unsigned long temp_passive;
unsigned long temp_critical;
unsigned long alarm_temp;
@@ -84,7 +88,7 @@ static void imx_set_alarm_temp(struct imx_thermal_data *data,
int alarm_value;
data->alarm_temp = alarm_temp;
- alarm_value = (alarm_temp - data->c2) / data->c1;
+ alarm_value = (data->c2 - alarm_temp) / data->c1;
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_ALARM_VALUE_MASK);
regmap_write(map, TEMPSENSE0 + REG_SET, alarm_value <<
TEMPSENSE0_ALARM_VALUE_SHIFT);
@@ -136,7 +140,7 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
n_meas = (val & TEMPSENSE0_TEMP_CNT_MASK) >> TEMPSENSE0_TEMP_CNT_SHIFT;
/* See imx_get_sensor_data() for formula derivation */
- *temp = data->c2 + data->c1 * n_meas;
+ *temp = data->c2 - n_meas * data->c1;
/* Update alarm value to next higher trip point */
if (data->alarm_temp == data->temp_passive && *temp >= data->temp_passive)
@@ -305,6 +309,7 @@ static int imx_get_sensor_data(struct platform_device *pdev)
int t1, t2, n1, n2;
int ret;
u32 val;
+ u64 temp64;
map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"fsl,tempmon-data");
@@ -330,6 +335,8 @@ static int imx_get_sensor_data(struct platform_device *pdev)
* [31:20] - sensor value @ 25C
* [19:8] - sensor value of hot
* [7:0] - hot temperature value
+ * Use universal formula now and only need sensor value @ 25C
+ * slope = 0.4297157 - (0.0015976 * 25C fuse)
*/
n1 = val >> 20;
n2 = (val & 0xfff00) >> 8;
@@ -337,20 +344,26 @@ static int imx_get_sensor_data(struct platform_device *pdev)
t1 = 25; /* t1 always 25C */
/*
- * Derived from linear interpolation,
- * Tmeas = T2 + (Nmeas - N2) * (T1 - T2) / (N1 - N2)
+ * Derived from linear interpolation:
+ * slope = 0.4297157 - (0.0015976 * 25C fuse)
+ * slope = (FACTOR2 - FACTOR1 * n1) / FACTOR0
+ * (Nmeas - n1) / (Tmeas - t1) = slope
* We want to reduce this down to the minimum computation necessary
* for each temperature read. Also, we want Tmeas in millicelsius
* and we don't want to lose precision from integer division. So...
- * milli_Tmeas = 1000 * T2 + 1000 * (Nmeas - N2) * (T1 - T2) / (N1 - N2)
- * Let constant c1 = 1000 * (T1 - T2) / (N1 - N2)
- * milli_Tmeas = (1000 * T2) + c1 * (Nmeas - N2)
- * milli_Tmeas = (1000 * T2) + (c1 * Nmeas) - (c1 * N2)
- * Let constant c2 = (1000 * T2) - (c1 * N2)
- * milli_Tmeas = c2 + (c1 * Nmeas)
+ * Tmeas = (Nmeas - n1) / slope + t1
+ * milli_Tmeas = 1000 * (Nmeas - n1) / slope + 1000 * t1
+ * milli_Tmeas = -1000 * (n1 - Nmeas) / slope + 1000 * t1
+ * Let constant c1 = (-1000 / slope)
+ * milli_Tmeas = (n1 - Nmeas) * c1 + 1000 * t1
+ * Let constant c2 = n1 *c1 + 1000 * t1
+ * milli_Tmeas = c2 - Nmeas * c1
*/
- data->c1 = 1000 * (t1 - t2) / (n1 - n2);
- data->c2 = 1000 * t2 - data->c1 * n2;
+ temp64 = FACTOR0;
+ temp64 *= 1000;
+ do_div(temp64, FACTOR1 * n1 - FACTOR2);
+ data->c1 = temp64;
+ data->c2 = n1 * data->c1 + 1000 * t1;
/*
* Set the default passive cooling trip point to 20 °C below the
diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c
index 79a09d02bbca..5a37940b02c9 100644
--- a/drivers/thermal/rcar_thermal.c
+++ b/drivers/thermal/rcar_thermal.c
@@ -299,12 +299,17 @@ static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable)
static void rcar_thermal_work(struct work_struct *work)
{
struct rcar_thermal_priv *priv;
+ unsigned long cctemp, nctemp;
priv = container_of(work, struct rcar_thermal_priv, work.work);
+ rcar_thermal_get_temp(priv->zone, &cctemp);
rcar_thermal_update_temp(priv);
rcar_thermal_irq_enable(priv);
- thermal_zone_device_update(priv->zone);
+
+ rcar_thermal_get_temp(priv->zone, &nctemp);
+ if (nctemp != cctemp)
+ thermal_zone_device_update(priv->zone);
}
static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status)
@@ -313,7 +318,7 @@ static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status)
status = (status >> rcar_id_to_shift(priv)) & 0x3;
- if (status & 0x3) {
+ if (status) {
dev_dbg(dev, "thermal%d %s%s\n",
priv->id,
(status & 0x2) ? "Rising " : "",
diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
index 74c0e3474d6e..3ab12ee359b7 100644
--- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c
+++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
@@ -1500,10 +1500,8 @@ static int ti_bandgap_resume(struct device *dev)
return ti_bandgap_restore_ctxt(bgp);
}
-static const struct dev_pm_ops ti_bandgap_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(ti_bandgap_suspend,
- ti_bandgap_resume)
-};
+static SIMPLE_DEV_PM_OPS(ti_bandgap_dev_pm_ops, ti_bandgap_suspend,
+ ti_bandgap_resume);
#define DEV_PM_OPS (&ti_bandgap_dev_pm_ops)
#else
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
index b01659bd4f7c..a585079b4b38 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -61,6 +61,7 @@ static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES];
/* For early boot console */
static struct hvc_opal_priv hvc_opal_boot_priv;
static u32 hvc_opal_boot_termno;
+static bool hvc_opal_event_registered;
static const struct hv_ops hvc_opal_raw_ops = {
.get_chars = opal_get_chars,
@@ -161,6 +162,18 @@ static const struct hv_ops hvc_opal_hvsi_ops = {
.tiocmset = hvc_opal_hvsi_tiocmset,
};
+static int hvc_opal_console_event(struct notifier_block *nb,
+ unsigned long events, void *change)
+{
+ if (events & OPAL_EVENT_CONSOLE_INPUT)
+ hvc_kick();
+ return 0;
+}
+
+static struct notifier_block hvc_opal_console_nb = {
+ .notifier_call = hvc_opal_console_event,
+};
+
static int hvc_opal_probe(struct platform_device *dev)
{
const struct hv_ops *ops;
@@ -170,6 +183,7 @@ static int hvc_opal_probe(struct platform_device *dev)
unsigned int termno, boot = 0;
const __be32 *reg;
+
if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) {
proto = HV_PROTOCOL_RAW;
ops = &hvc_opal_raw_ops;
@@ -213,12 +227,18 @@ static int hvc_opal_probe(struct platform_device *dev)
dev->dev.of_node->full_name,
boot ? " (boot console)" : "");
- /* We don't do IRQ yet */
+ /* We don't do IRQ ... */
hp = hvc_alloc(termno, 0, ops, MAX_VIO_PUT_CHARS);
if (IS_ERR(hp))
return PTR_ERR(hp);
dev_set_drvdata(&dev->dev, hp);
+ /* ... but we use OPAL event to kick the console */
+ if (!hvc_opal_event_registered) {
+ opal_notifier_register(&hvc_opal_console_nb);
+ hvc_opal_event_registered = true;
+ }
+
return 0;
}
diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c
index b0e540137e39..90ca082935f6 100644
--- a/drivers/tty/tty_audit.c
+++ b/drivers/tty/tty_audit.c
@@ -65,6 +65,7 @@ static void tty_audit_log(const char *description, int major, int minor,
{
struct audit_buffer *ab;
struct task_struct *tsk = current;
+ pid_t pid = task_pid_nr(tsk);
uid_t uid = from_kuid(&init_user_ns, task_uid(tsk));
uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk));
unsigned int sessionid = audit_get_sessionid(tsk);
@@ -74,7 +75,7 @@ static void tty_audit_log(const char *description, int major, int minor,
char name[sizeof(tsk->comm)];
audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d"
- " minor=%d comm=", description, tsk->pid, uid,
+ " minor=%d comm=", description, pid, uid,
loginuid, sessionid, major, minor);
get_task_comm(name, tsk);
audit_log_untrustedstring(ab, name);
diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c
index 460c266b8e24..f058c0368d61 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/tcm_usb_gadget.c
@@ -1471,6 +1471,11 @@ static void usbg_queue_tm_rsp(struct se_cmd *se_cmd)
{
}
+static void usbg_aborted_task(struct se_cmd *se_cmd)
+{
+ return;
+}
+
static const char *usbg_check_wwn(const char *name)
{
const char *n;
@@ -1726,7 +1731,7 @@ static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name)
pr_err("Unable to allocate struct tcm_vhost_nexus\n");
goto err_unlock;
}
- tv_nexus->tvn_se_sess = transport_init_session();
+ tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL);
if (IS_ERR(tv_nexus->tvn_se_sess))
goto err_free;
@@ -1897,6 +1902,7 @@ static struct target_core_fabric_ops usbg_ops = {
.queue_data_in = usbg_send_read_response,
.queue_status = usbg_send_status_response,
.queue_tm_rsp = usbg_queue_tm_rsp,
+ .aborted_task = usbg_aborted_task,
.check_stop_free = usbg_check_stop_free,
.fabric_make_wwn = usbg_make_tport,
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index e1e22e0f01e8..be414d2b2b22 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -818,9 +818,9 @@ static int vhost_net_release(struct inode *inode, struct file *f)
vhost_dev_cleanup(&n->dev, false);
vhost_net_vq_reset(n);
if (tx_sock)
- fput(tx_sock->file);
+ sockfd_put(tx_sock);
if (rx_sock)
- fput(rx_sock->file);
+ sockfd_put(rx_sock);
/* Make sure no callbacks are outstanding */
synchronize_rcu_bh();
/* We do an extra flush before freeing memory,
@@ -860,7 +860,7 @@ static struct socket *get_raw_socket(int fd)
}
return sock;
err:
- fput(sock->file);
+ sockfd_put(sock);
return ERR_PTR(r);
}
@@ -966,7 +966,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
if (oldsock) {
vhost_net_flush_vq(n, index);
- fput(oldsock->file);
+ sockfd_put(oldsock);
}
mutex_unlock(&n->dev.mutex);
@@ -978,7 +978,7 @@ err_used:
if (ubufs)
vhost_net_ubuf_put_wait_and_free(ubufs);
err_ubufs:
- fput(sock->file);
+ sockfd_put(sock);
err_vq:
mutex_unlock(&vq->mutex);
err:
@@ -1009,9 +1009,9 @@ static long vhost_net_reset_owner(struct vhost_net *n)
done:
mutex_unlock(&n->dev.mutex);
if (tx_sock)
- fput(tx_sock->file);
+ sockfd_put(tx_sock);
if (rx_sock)
- fput(rx_sock->file);
+ sockfd_put(rx_sock);
return err;
}
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index e48d4a672580..cf50ce93975b 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -539,6 +539,11 @@ static void tcm_vhost_queue_tm_rsp(struct se_cmd *se_cmd)
return;
}
+static void tcm_vhost_aborted_task(struct se_cmd *se_cmd)
+{
+ return;
+}
+
static void tcm_vhost_free_evt(struct vhost_scsi *vs, struct tcm_vhost_evt *evt)
{
vs->vs_events_nr--;
@@ -1740,7 +1745,8 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg,
*/
tv_nexus->tvn_se_sess = transport_init_session_tags(
TCM_VHOST_DEFAULT_TAGS,
- sizeof(struct tcm_vhost_cmd));
+ sizeof(struct tcm_vhost_cmd),
+ TARGET_PROT_NORMAL);
if (IS_ERR(tv_nexus->tvn_se_sess)) {
mutex_unlock(&tpg->tv_tpg_mutex);
kfree(tv_nexus);
@@ -2131,6 +2137,7 @@ static struct target_core_fabric_ops tcm_vhost_ops = {
.queue_data_in = tcm_vhost_queue_data_in,
.queue_status = tcm_vhost_queue_status,
.queue_tm_rsp = tcm_vhost_queue_tm_rsp,
+ .aborted_task = tcm_vhost_aborted_task,
/*
* Setup callers for generic logic in target_core_fabric_configfs.c
*/
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index 27d3cf255e78..bd2172c2d650 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -347,7 +347,7 @@ struct backlight_device *backlight_device_register(const char *name,
rc = device_register(&new_bd->dev);
if (rc) {
- kfree(new_bd);
+ put_device(&new_bd->dev);
return ERR_PTR(rc);
}
diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c
index 81fb12770c2a..a2eba12e1cb7 100644
--- a/drivers/video/backlight/gpio_backlight.c
+++ b/drivers/video/backlight/gpio_backlight.c
@@ -13,6 +13,8 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <linux/platform_data/gpio_backlight.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -23,6 +25,7 @@ struct gpio_backlight {
int gpio;
int active;
+ int def_value;
};
static int gpio_backlight_update_status(struct backlight_device *bl)
@@ -60,6 +63,29 @@ static const struct backlight_ops gpio_backlight_ops = {
.check_fb = gpio_backlight_check_fb,
};
+static int gpio_backlight_probe_dt(struct platform_device *pdev,
+ struct gpio_backlight *gbl)
+{
+ struct device_node *np = pdev->dev.of_node;
+ enum of_gpio_flags gpio_flags;
+
+ gbl->gpio = of_get_gpio_flags(np, 0, &gpio_flags);
+
+ if (!gpio_is_valid(gbl->gpio)) {
+ if (gbl->gpio != -EPROBE_DEFER) {
+ dev_err(&pdev->dev,
+ "Error: The gpios parameter is missing or invalid.\n");
+ }
+ return gbl->gpio;
+ }
+
+ gbl->active = (gpio_flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+ gbl->def_value = of_property_read_bool(np, "default-on");
+
+ return 0;
+}
+
static int gpio_backlight_probe(struct platform_device *pdev)
{
struct gpio_backlight_platform_data *pdata =
@@ -67,10 +93,12 @@ static int gpio_backlight_probe(struct platform_device *pdev)
struct backlight_properties props;
struct backlight_device *bl;
struct gpio_backlight *gbl;
+ struct device_node *np = pdev->dev.of_node;
int ret;
- if (!pdata) {
- dev_err(&pdev->dev, "failed to find platform data\n");
+ if (!pdata && !np) {
+ dev_err(&pdev->dev,
+ "failed to find platform data or device tree node.\n");
return -ENODEV;
}
@@ -79,14 +107,22 @@ static int gpio_backlight_probe(struct platform_device *pdev)
return -ENOMEM;
gbl->dev = &pdev->dev;
- gbl->fbdev = pdata->fbdev;
- gbl->gpio = pdata->gpio;
- gbl->active = pdata->active_low ? 0 : 1;
+
+ if (np) {
+ ret = gpio_backlight_probe_dt(pdev, gbl);
+ if (ret)
+ return ret;
+ } else {
+ gbl->fbdev = pdata->fbdev;
+ gbl->gpio = pdata->gpio;
+ gbl->active = pdata->active_low ? 0 : 1;
+ gbl->def_value = pdata->def_value;
+ }
ret = devm_gpio_request_one(gbl->dev, gbl->gpio, GPIOF_DIR_OUT |
(gbl->active ? GPIOF_INIT_LOW
: GPIOF_INIT_HIGH),
- pdata->name);
+ pdata ? pdata->name : "backlight");
if (ret < 0) {
dev_err(&pdev->dev, "unable to request GPIO\n");
return ret;
@@ -103,17 +139,25 @@ static int gpio_backlight_probe(struct platform_device *pdev)
return PTR_ERR(bl);
}
- bl->props.brightness = pdata->def_value;
+ bl->props.brightness = gbl->def_value;
backlight_update_status(bl);
platform_set_drvdata(pdev, bl);
return 0;
}
+#ifdef CONFIG_OF
+static struct of_device_id gpio_backlight_of_match[] = {
+ { .compatible = "gpio-backlight" },
+ { /* sentinel */ }
+};
+#endif
+
static struct platform_driver gpio_backlight_driver = {
.driver = {
.name = "gpio-backlight",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(gpio_backlight_of_match),
},
.probe = gpio_backlight_probe,
};
diff --git a/drivers/video/backlight/lm3639_bl.c b/drivers/video/backlight/lm3639_bl.c
index 6fd60adf922e..5f36808d214f 100644
--- a/drivers/video/backlight/lm3639_bl.c
+++ b/drivers/video/backlight/lm3639_bl.c
@@ -349,8 +349,9 @@ static int lm3639_probe(struct i2c_client *client,
props.brightness = pdata->init_brt_led;
props.max_brightness = pdata->max_brt_led;
pchip->bled =
- backlight_device_register("lm3639_bled", pchip->dev, pchip,
- &lm3639_bled_ops, &props);
+ devm_backlight_device_register(pchip->dev, "lm3639_bled",
+ pchip->dev, pchip, &lm3639_bled_ops,
+ &props);
if (IS_ERR(pchip->bled)) {
dev_err(&client->dev, "fail : backlight register\n");
ret = PTR_ERR(pchip->bled);
@@ -360,7 +361,7 @@ static int lm3639_probe(struct i2c_client *client,
ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode);
if (ret < 0) {
dev_err(&client->dev, "failed : add sysfs entries\n");
- goto err_bled_mode;
+ goto err_out;
}
/* flash */
@@ -391,8 +392,6 @@ err_torch:
led_classdev_unregister(&pchip->cdev_flash);
err_flash:
device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
-err_bled_mode:
- backlight_device_unregister(pchip->bled);
err_out:
return ret;
}
@@ -407,10 +406,8 @@ static int lm3639_remove(struct i2c_client *client)
led_classdev_unregister(&pchip->cdev_torch);
if (&pchip->cdev_flash)
led_classdev_unregister(&pchip->cdev_flash);
- if (pchip->bled) {
+ if (pchip->bled)
device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
- backlight_device_unregister(pchip->bled);
- }
return 0;
}
@@ -432,6 +429,6 @@ static struct i2c_driver lm3639_i2c_driver = {
module_i2c_driver(lm3639_i2c_driver);
MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639");
-MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>");
-MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>");
+MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
+MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
MODULE_LICENSE("GPL v2");