summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/Kconfig4
-rw-r--r--drivers/acpi/bgrt.c76
-rw-r--r--drivers/acpi/pci_root.c101
-rw-r--r--drivers/acpi/pci_slot.c44
-rw-r--r--drivers/base/core.c105
-rw-r--r--drivers/base/devres.c42
-rw-r--r--drivers/base/firmware_class.c838
-rw-r--r--drivers/base/platform.c41
-rw-r--r--drivers/base/power/main.c22
-rw-r--r--drivers/base/regmap/regmap-irq.c92
-rw-r--r--drivers/base/regmap/regmap.c13
-rw-r--r--drivers/block/aoe/aoecmd.c1
-rw-r--r--drivers/block/cciss_scsi.c1
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c38
-rw-r--r--drivers/block/mtip32xx/mtip32xx.h10
-rw-r--r--drivers/block/nbd.c9
-rw-r--r--drivers/block/nvme.c155
-rw-r--r--drivers/block/rbd.c7
-rw-r--r--drivers/block/xen-blkback/blkback.c2
-rw-r--r--drivers/char/Kconfig6
-rw-r--r--drivers/char/agp/sgi-agp.c5
-rw-r--r--drivers/char/mmtimer.c3
-rw-r--r--drivers/char/nwbutton.c4
-rw-r--r--drivers/char/pcmcia/synclink_cs.c20
-rw-r--r--drivers/char/ppdev.c3
-rw-r--r--drivers/char/rtc.c2
-rw-r--r--drivers/char/tlclk.c4
-rw-r--r--drivers/char/virtio_console.c12
-rw-r--r--drivers/clk/Kconfig13
-rw-r--r--drivers/clk/Makefile10
-rw-r--r--drivers/clk/clk-devres.c55
-rw-r--r--drivers/clk/clk-ls1x.c111
-rw-r--r--drivers/clk/clk-max77686.c244
-rw-r--r--drivers/clk/clk-prima2.c1171
-rw-r--r--drivers/clk/clk.c57
-rw-r--r--drivers/clk/clkdev.c45
-rw-r--r--drivers/clk/mmp/Makefile9
-rw-r--r--drivers/clk/mmp/clk-apbc.c152
-rw-r--r--drivers/clk/mmp/clk-apmu.c97
-rw-r--r--drivers/clk/mmp/clk-frac.c153
-rw-r--r--drivers/clk/mmp/clk-mmp2.c449
-rw-r--r--drivers/clk/mmp/clk-pxa168.c346
-rw-r--r--drivers/clk/mmp/clk-pxa910.c320
-rw-r--r--drivers/clk/mmp/clk.h35
-rw-r--r--drivers/clk/ux500/Makefile12
-rw-r--r--drivers/clk/ux500/clk-prcc.c164
-rw-r--r--drivers/clk/ux500/clk-prcmu.c252
-rw-r--r--drivers/clk/ux500/clk.h48
-rw-r--r--drivers/clk/ux500/u8500_clk.c477
-rw-r--r--drivers/clk/ux500/u8540_clk.c21
-rw-r--r--drivers/clk/ux500/u9540_clk.c21
-rw-r--r--drivers/clk/versatile/Makefile1
-rw-r--r--drivers/clk/versatile/clk-realview.c114
-rw-r--r--drivers/clocksource/Kconfig5
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/arm_generic.c232
-rw-r--r--drivers/cpufreq/powernow-k8.c63
-rw-r--r--drivers/dma/at_hdmac.c21
-rw-r--r--drivers/dma/ep93xx_dma.c2
-rw-r--r--drivers/dma/fsldma.c2
-rw-r--r--drivers/dma/imx-dma.c4
-rw-r--r--drivers/dma/intel_mid_dma.c2
-rw-r--r--drivers/dma/intel_mid_dma_regs.h6
-rw-r--r--drivers/dma/ioat/hw.h4
-rw-r--r--drivers/dma/pl330.c23
-rw-r--r--drivers/dma/ppc4xx/adma.c2
-rw-r--r--drivers/dma/ste_dma40_ll.h2
-rw-r--r--drivers/edac/edac_mc.c57
-rw-r--r--drivers/edac/i3200_edac.c2
-rw-r--r--drivers/edac/i5000_edac.c4
-rw-r--r--drivers/edac/sb_edac.c7
-rw-r--r--drivers/extcon/Kconfig8
-rw-r--r--drivers/extcon/Makefile5
-rw-r--r--drivers/extcon/extcon-adc-jack.c198
-rw-r--r--drivers/extcon/extcon-arizona.c83
-rw-r--r--drivers/extcon/extcon-class.c (renamed from drivers/extcon/extcon_class.c)12
-rw-r--r--drivers/extcon/extcon-gpio.c (renamed from drivers/extcon/extcon_gpio.c)0
-rw-r--r--drivers/extcon/extcon-max77693.c8
-rw-r--r--drivers/firmware/efivars.c17
-rw-r--r--drivers/gpio/gpio-lpc32xx.c5
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c3
-rw-r--r--drivers/gpu/drm/i915/intel_display.c12
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv50_gpio.c15
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fb.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fifo.c3
-rw-r--r--drivers/gpu/drm/nouveau/nve0_fifo.c3
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c163
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c10
-rw-r--r--drivers/gpu/drm/radeon/r100.c3
-rw-r--r--drivers/gpu/drm/udl/udl_connector.c7
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c2
-rw-r--r--drivers/gpu/vga/vgaarb.c29
-rw-r--r--drivers/hid/Kconfig22
-rw-r--r--drivers/hid/Makefile21
-rw-r--r--drivers/hid/hid-a4tech.c1
-rw-r--r--drivers/hid/hid-apple.c1
-rw-r--r--drivers/hid/hid-aureal.c1
-rw-r--r--drivers/hid/hid-belkin.c1
-rw-r--r--drivers/hid/hid-cherry.c1
-rw-r--r--drivers/hid/hid-core.c38
-rw-r--r--drivers/hid/hid-cypress.c1
-rw-r--r--drivers/hid/hid-debug.c12
-rw-r--r--drivers/hid/hid-ezkey.c1
-rw-r--r--drivers/hid/hid-gyration.c1
-rw-r--r--drivers/hid/hid-holtekff.c3
-rw-r--r--drivers/hid/hid-ids.h16
-rw-r--r--drivers/hid/hid-input.c11
-rw-r--r--drivers/hid/hid-lcpower.c2
-rw-r--r--drivers/hid/hid-lenovo-tpkbd.c149
-rw-r--r--drivers/hid/hid-lg.c20
-rw-r--r--drivers/hid/hid-lg.h4
-rw-r--r--drivers/hid/hid-lg4ff.c198
-rw-r--r--drivers/hid/hid-logitech-dj.c45
-rw-r--r--drivers/hid/hid-logitech-dj.h1
-rw-r--r--drivers/hid/hid-magicmouse.c2
-rw-r--r--drivers/hid/hid-microsoft.c1
-rw-r--r--drivers/hid/hid-monterey.c1
-rw-r--r--drivers/hid/hid-multitouch.c252
-rw-r--r--drivers/hid/hid-ntrig.c8
-rw-r--r--drivers/hid/hid-petalynx.c1
-rw-r--r--drivers/hid/hid-picolcd.c2748
-rw-r--r--drivers/hid/hid-picolcd.h309
-rw-r--r--drivers/hid/hid-picolcd_backlight.c122
-rw-r--r--drivers/hid/hid-picolcd_cir.c152
-rw-r--r--drivers/hid/hid-picolcd_core.c689
-rw-r--r--drivers/hid/hid-picolcd_debugfs.c899
-rw-r--r--drivers/hid/hid-picolcd_fb.c615
-rw-r--r--drivers/hid/hid-picolcd_lcd.c107
-rw-r--r--drivers/hid/hid-picolcd_leds.c175
-rw-r--r--drivers/hid/hid-primax.c25
-rw-r--r--drivers/hid/hid-prodikeys.c18
-rw-r--r--drivers/hid/hid-ps3remote.c215
-rw-r--r--drivers/hid/hid-samsung.c1
-rw-r--r--drivers/hid/hid-sony.c1
-rw-r--r--drivers/hid/hid-sunplus.c1
-rw-r--r--drivers/hid/hid-uclogic.c98
-rw-r--r--drivers/hid/hid-wacom.c170
-rw-r--r--drivers/hid/hid-waltop.c29
-rw-r--r--drivers/hid/hid-wiimote-ext.c97
-rw-r--r--drivers/hid/hidraw.c84
-rw-r--r--drivers/hid/usbhid/hid-core.c6
-rw-r--r--drivers/hid/usbhid/hid-quirks.c3
-rw-r--r--drivers/hv/hv.c34
-rw-r--r--drivers/hv/hv_kvp.c253
-rw-r--r--drivers/hv/hv_util.c4
-rw-r--r--drivers/hv/hyperv_vmbus.h47
-rw-r--r--drivers/hv/vmbus_drv.c42
-rw-r--r--drivers/hwmon/ad7314.c8
-rw-r--r--drivers/hwmon/ads7871.c9
-rw-r--r--drivers/hwmon/applesmc.c4
-rw-r--r--drivers/hwmon/coretemp.c5
-rw-r--r--drivers/hwmon/fam15h_power.c15
-rw-r--r--drivers/hwmon/via-cputemp.c5
-rw-r--r--drivers/hwspinlock/hwspinlock_core.c3
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c2
-rw-r--r--drivers/infiniband/hw/ehca/ehca_irq.c250
-rw-r--r--drivers/infiniband/hw/ehca/ehca_irq.h6
-rw-r--r--drivers/infiniband/hw/mthca/mthca_reset.c8
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.c8
-rw-r--r--drivers/infiniband/hw/qib/qib.h2
-rw-r--r--drivers/infiniband/hw/qib/qib_mad.c3
-rw-r--r--drivers/infiniband/hw/qib/qib_pcie.c40
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h5
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c93
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c2
-rw-r--r--drivers/input/evdev.c78
-rw-r--r--drivers/input/input-mt.c305
-rw-r--r--drivers/input/input.c254
-rw-r--r--drivers/input/keyboard/imx_keypad.c4
-rw-r--r--drivers/input/misc/ab8500-ponkey.c4
-rw-r--r--drivers/input/misc/uinput.c2
-rw-r--r--drivers/input/mouse/alps.c2
-rw-r--r--drivers/input/mouse/bcm5974.c348
-rw-r--r--drivers/input/mouse/elantech.c4
-rw-r--r--drivers/input/mouse/sentelic.c13
-rw-r--r--drivers/input/mouse/synaptics.c4
-rw-r--r--drivers/input/serio/ambakmi.c6
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h6
-rw-r--r--drivers/input/tablet/wacom_wac.c6
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c2
-rw-r--r--drivers/input/touchscreen/cyttsp_core.c2
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c11
-rw-r--r--drivers/input/touchscreen/egalax_ts.c2
-rw-r--r--drivers/input/touchscreen/ili210x.c2
-rw-r--r--drivers/input/touchscreen/mms114.c2
-rw-r--r--drivers/input/touchscreen/penmount.c2
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c40
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c2
-rw-r--r--drivers/iommu/amd_iommu.c6
-rw-r--r--drivers/iommu/intel-iommu.c6
-rw-r--r--drivers/lguest/x86/core.c10
-rw-r--r--drivers/md/dm-mpath.c11
-rw-r--r--drivers/md/dm-table.c61
-rw-r--r--drivers/md/dm-thin.c135
-rw-r--r--drivers/md/dm-verity.c8
-rw-r--r--drivers/md/dm.c71
-rw-r--r--drivers/md/dm.h1
-rw-r--r--drivers/md/md.c6
-rw-r--r--drivers/md/raid10.c8
-rw-r--r--drivers/md/raid5.c7
-rw-r--r--drivers/media/dvb/ngene/ngene-cards.c2
-rw-r--r--drivers/memory/Makefile3
-rw-r--r--drivers/memory/emif.c205
-rw-r--r--drivers/memory/of_memory.c153
-rw-r--r--drivers/memory/of_memory.h36
-rw-r--r--drivers/memory/tegra20-mc.c10
-rw-r--r--drivers/memory/tegra30-mc.c22
-rw-r--r--drivers/mfd/ab8500-gpadc.c2
-rw-r--r--drivers/mfd/db8500-prcmu.c42
-rw-r--r--drivers/mfd/dbx500-prcmu-regs.h4
-rw-r--r--drivers/mfd/rc5t583.c2
-rw-r--r--drivers/mfd/rdc321x-southbridge.c2
-rw-r--r--drivers/mfd/tps6586x.c13
-rw-r--r--drivers/mfd/tps65911-comparator.c2
-rw-r--r--drivers/mfd/wm8994-irq.c1
-rw-r--r--drivers/misc/Kconfig14
-rw-r--r--drivers/misc/bmp085-i2c.c7
-rw-r--r--drivers/misc/bmp085-spi.c13
-rw-r--r--drivers/misc/c2port/Kconfig5
-rw-r--r--drivers/misc/carma/carma-fpga-program.c1
-rw-r--r--drivers/misc/carma/carma-fpga.c2
-rw-r--r--drivers/misc/eeprom/Kconfig2
-rw-r--r--drivers/misc/eeprom/at25.c83
-rw-r--r--drivers/misc/hpilo.c11
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.c192
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.h49
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_i2c.c32
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_spi.c21
-rw-r--r--drivers/misc/mei/Kconfig2
-rw-r--r--drivers/misc/mei/hw.h83
-rw-r--r--drivers/misc/mei/init.c93
-rw-r--r--drivers/misc/mei/interface.h10
-rw-r--r--drivers/misc/mei/interrupt.c44
-rw-r--r--drivers/misc/mei/iorw.c59
-rw-r--r--drivers/misc/mei/main.c107
-rw-r--r--drivers/misc/mei/mei_dev.h78
-rw-r--r--drivers/misc/mei/wd.c91
-rw-r--r--drivers/misc/pch_phub.c15
-rw-r--r--drivers/misc/ti-st/st_core.c12
-rw-r--r--drivers/misc/ti-st/st_kim.c109
-rw-r--r--drivers/misc/tifm_7xx1.c14
-rw-r--r--drivers/mmc/core/sdio.c2
-rw-r--r--drivers/mmc/host/at91_mci.c2
-rw-r--r--drivers/mmc/host/atmel-mci.c2
-rw-r--r--drivers/mmc/host/omap_hsmmc.c2
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c2
-rw-r--r--drivers/mmc/host/vub300.c2
-rw-r--r--drivers/mtd/mtdchar.c48
-rw-r--r--drivers/net/can/janz-ican3.c4
-rw-r--r--drivers/net/can/ti_hecc.c2
-rw-r--r--drivers/net/ethernet/3com/typhoon.c2
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_main.c4
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_main.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.c4
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c12
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c31
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h2
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c52
-rw-r--r--drivers/net/ethernet/cadence/at91_ether.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3_hw.c22
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c12
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c6
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c2
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ethtool.c1
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ptp.c4
-rw-r--r--drivers/net/ethernet/intel/e100.c2
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_main.c13
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c29
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c14
-rw-r--r--drivers/net/ethernet/intel/igbvf/netdev.c2
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_main.c2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c4
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.c30
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.h10
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/reset.c8
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge.c31
-rw-r--r--drivers/net/ethernet/neterion/s2io.c2
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-config.c4
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-main.c2
-rw-r--r--drivers/net/ethernet/octeon/octeon_mgmt.c4
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c2
-rw-r--r--drivers/net/ethernet/pasemi/pasemi_mac.c4
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c8
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c4
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c2
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_main.c2
-rw-r--r--drivers/net/ethernet/realtek/r8169.c44
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c10
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c8
-rw-r--r--drivers/net/ethernet/sun/niu.c19
-rw-r--r--drivers/net/irda/sh_sir.c2
-rw-r--r--drivers/net/phy/bcm87xx.c2
-rw-r--r--drivers/net/phy/micrel.c45
-rw-r--r--drivers/net/phy/smsc.c28
-rw-r--r--drivers/net/ppp/pppoe.c2
-rw-r--r--drivers/net/team/team.c44
-rw-r--r--drivers/net/usb/asix_devices.c4
-rw-r--r--drivers/net/usb/qmi_wwan.c11
-rw-r--r--drivers/net/usb/smsc75xx.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h3
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c21
-rw-r--r--drivers/net/wireless/b43/Kconfig4
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c26
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c6
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/channel.c2
-rw-r--r--drivers/net/wireless/iwlegacy/common.h4
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c8
-rw-r--r--drivers/net/wireless/rtlwifi/pci.c8
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c6
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/def.h1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/hw.c12
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/sw.c6
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/fw.c6
-rw-r--r--drivers/oprofile/cpu_buffer.c11
-rw-r--r--drivers/parisc/dino.c10
-rw-r--r--drivers/parisc/lba_pci.c7
-rw-r--r--drivers/pci/.gitignore4
-rw-r--r--drivers/pci/Kconfig1
-rw-r--r--drivers/pci/access.c202
-rw-r--r--drivers/pci/bus.c6
-rw-r--r--drivers/pci/hotplug/Kconfig24
-rw-r--r--drivers/pci/hotplug/Makefile3
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c99
-rw-r--r--drivers/pci/hotplug/cpcihp_generic.c8
-rw-r--r--drivers/pci/hotplug/cpqphp_ctrl.c21
-rw-r--r--drivers/pci/hotplug/fakephp.c164
-rw-r--r--drivers/pci/hotplug/pciehp_acpi.c6
-rw-r--r--drivers/pci/hotplug/pciehp_core.c28
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c12
-rw-r--r--drivers/pci/hotplug/pcihp_slot.c20
-rw-r--r--drivers/pci/iov.c6
-rw-r--r--drivers/pci/pci-driver.c57
-rw-r--r--drivers/pci/pci.c358
-rw-r--r--drivers/pci/pcie/aer/aer_inject.c2
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c26
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c2
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c67
-rw-r--r--drivers/pci/pcie/aspm.c119
-rw-r--r--drivers/pci/pcie/pme.c29
-rw-r--r--drivers/pci/pcie/portdrv_bus.c2
-rw-r--r--drivers/pci/pcie/portdrv_core.c34
-rw-r--r--drivers/pci/pcie/portdrv_pci.c27
-rw-r--r--drivers/pci/probe.c39
-rw-r--r--drivers/pci/proc.c19
-rw-r--r--drivers/pci/quirks.c31
-rw-r--r--drivers/pci/remove.c156
-rw-r--r--drivers/pci/rom.c59
-rw-r--r--drivers/pci/search.c63
-rw-r--r--drivers/pci/setup-bus.c81
-rw-r--r--drivers/pci/setup-irq.c9
-rw-r--r--drivers/pci/xen-pcifront.c10
-rw-r--r--drivers/pcmcia/cardbus.c15
-rw-r--r--drivers/rapidio/devices/tsi721.c18
-rw-r--r--drivers/regulator/Kconfig38
-rw-r--r--drivers/regulator/Makefile2
-rw-r--r--drivers/regulator/aat2870-regulator.c2
-rw-r--r--drivers/regulator/ab3100.c6
-rw-r--r--drivers/regulator/ab8500.c36
-rw-r--r--drivers/regulator/arizona-ldo1.c6
-rw-r--r--drivers/regulator/arizona-micsupp.c5
-rw-r--r--drivers/regulator/core.c153
-rw-r--r--drivers/regulator/da9052-regulator.c4
-rw-r--r--drivers/regulator/dummy.c2
-rw-r--r--drivers/regulator/fan53555.c322
-rw-r--r--drivers/regulator/isl6271a-regulator.c6
-rw-r--r--drivers/regulator/lp872x.c88
-rw-r--r--drivers/regulator/lp8788-buck.c80
-rw-r--r--drivers/regulator/lp8788-ldo.c8
-rw-r--r--drivers/regulator/max77686.c30
-rw-r--r--drivers/regulator/max8907-regulator.c408
-rw-r--r--drivers/regulator/mc13783-regulator.c89
-rw-r--r--drivers/regulator/mc13892-regulator.c77
-rw-r--r--drivers/regulator/mc13xxx-regulator-core.c17
-rw-r--r--drivers/regulator/mc13xxx.h1
-rw-r--r--drivers/regulator/of_regulator.c25
-rw-r--r--drivers/regulator/palmas-regulator.c45
-rw-r--r--drivers/regulator/s2mps11.c27
-rw-r--r--drivers/regulator/tps6524x-regulator.c10
-rw-r--r--drivers/regulator/tps6586x-regulator.c96
-rw-r--r--drivers/regulator/twl-regulator.c110
-rw-r--r--drivers/regulator/wm831x-dcdc.c11
-rw-r--r--drivers/regulator/wm831x-ldo.c12
-rw-r--r--drivers/regulator/wm8400-regulator.c7
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c6
-rw-r--r--drivers/rtc/rtc-twl.c5
-rw-r--r--drivers/s390/block/Kconfig18
-rw-r--r--drivers/s390/block/Makefile6
-rw-r--r--drivers/s390/block/dasd.c17
-rw-r--r--drivers/s390/block/dasd_alias.c27
-rw-r--r--drivers/s390/block/dasd_eckd.c53
-rw-r--r--drivers/s390/block/dasd_ioctl.c4
-rw-r--r--drivers/s390/block/scm_blk.c445
-rw-r--r--drivers/s390/block/scm_blk.h117
-rw-r--r--drivers/s390/block/scm_blk_cluster.c228
-rw-r--r--drivers/s390/block/scm_drv.c81
-rw-r--r--drivers/s390/char/con3270.c1
-rw-r--r--drivers/s390/char/monreader.c5
-rw-r--r--drivers/s390/char/sclp.c2
-rw-r--r--drivers/s390/char/sclp_rw.c2
-rw-r--r--drivers/s390/char/tape.h1
-rw-r--r--drivers/s390/char/tape_std.h4
-rw-r--r--drivers/s390/char/vmlogrdr.c2
-rw-r--r--drivers/s390/cio/Makefile2
-rw-r--r--drivers/s390/cio/chsc.c52
-rw-r--r--drivers/s390/cio/chsc.h43
-rw-r--r--drivers/s390/cio/cio.c2
-rw-r--r--drivers/s390/cio/css.c1
-rw-r--r--drivers/s390/cio/device.c7
-rw-r--r--drivers/s390/cio/eadm_sch.c401
-rw-r--r--drivers/s390/cio/eadm_sch.h20
-rw-r--r--drivers/s390/cio/orb.h24
-rw-r--r--drivers/s390/cio/qdio_debug.h38
-rw-r--r--drivers/s390/cio/scm.c317
-rw-r--r--drivers/s390/crypto/Makefile3
-rw-r--r--drivers/s390/crypto/ap_bus.c209
-rw-r--r--drivers/s390/crypto/ap_bus.h35
-rw-r--r--drivers/s390/crypto/zcrypt_api.c187
-rw-r--r--drivers/s390/crypto/zcrypt_api.h19
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c371
-rw-r--r--drivers/s390/crypto/zcrypt_cex4.c149
-rw-r--r--drivers/s390/crypto/zcrypt_cex4.h12
-rw-r--r--drivers/s390/crypto/zcrypt_debug.h59
-rw-r--r--drivers/s390/crypto/zcrypt_error.h13
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.c531
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.h39
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.c856
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.h169
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c781
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.h3
-rw-r--r--drivers/s390/net/qeth_core_main.c15
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c2
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c2
-rw-r--r--drivers/scsi/bfa/bfa_ioc.h2
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c4
-rw-r--r--drivers/scsi/bnx2i/bnx2i_hwi.c3
-rw-r--r--drivers/scsi/gdth.h9
-rw-r--r--drivers/scsi/hpsa.c3
-rw-r--r--drivers/scsi/ipr.c68
-rw-r--r--drivers/scsi/isci/host.c2
-rw-r--r--drivers/scsi/isci/init.c2
-rw-r--r--drivers/scsi/isci/port.c2
-rw-r--r--drivers/scsi/isci/request.c2
-rw-r--r--drivers/scsi/isci/task.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c4
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c4
-rw-r--r--drivers/scsi/megaraid.c5
-rw-r--r--drivers/scsi/megaraid.h35
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c7
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c2
-rw-r--r--drivers/scsi/mvumi.c6
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c8
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c2
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.c4
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c8
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.c2
-rw-r--r--drivers/scsi/virtio_scsi.c2
-rw-r--r--drivers/scsi/vmw_pvscsi.c4
-rw-r--r--drivers/sh/intc/core.c2
-rw-r--r--drivers/sh/pfc/pinctrl.c5
-rw-r--r--drivers/spi/spi-au1550.c2
-rw-r--r--drivers/spi/spi-bfin-sport.c2
-rw-r--r--drivers/spi/spi-oc-tiny.c2
-rw-r--r--drivers/spi/spi-ppc4xx.c4
-rw-r--r--drivers/spi/spi-topcliff-pch.c2
-rw-r--r--drivers/staging/et131x/et131x.c19
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pci.c8
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.c4
-rw-r--r--drivers/tty/serial/8250/8250_pci.c2
-rw-r--r--drivers/tty/serial/jsm/jsm_driver.c2
-rw-r--r--drivers/usb/core/devices.c2
-rw-r--r--drivers/usb/core/hcd.c6
-rw-r--r--drivers/usb/host/ohci-at91.c3
-rw-r--r--drivers/vfio/pci/vfio_pci_intrs.c76
-rw-r--r--drivers/video/backlight/88pm860x_bl.c1
-rw-r--r--drivers/video/efifb.c4
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi.c2
-rw-r--r--drivers/video/tmiofb.c4
-rw-r--r--drivers/vme/bridges/vme_ca91cx42.c20
-rw-r--r--drivers/vme/bridges/vme_tsi148.c10
-rw-r--r--drivers/w1/masters/Kconfig1
-rw-r--r--drivers/w1/masters/ds1wm.c2
-rw-r--r--drivers/w1/masters/omap_hdq.c57
-rw-r--r--drivers/w1/masters/w1-gpio.c66
-rw-r--r--drivers/watchdog/hpwdt.c3
-rw-r--r--drivers/watchdog/watchdog_core.c3
-rw-r--r--drivers/xen/gntdev.c5
-rw-r--r--drivers/xen/grant-table.c6
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c2
499 files changed, 19889 insertions, 8725 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 8099895..119d58d 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -385,8 +385,8 @@ config ACPI_CUSTOM_METHOD
to override that restriction).
config ACPI_BGRT
- tristate "Boottime Graphics Resource Table support"
- default n
+ bool "Boottime Graphics Resource Table support"
+ depends on EFI
help
This driver adds support for exposing the ACPI Boottime Graphics
Resource Table, which allows the operating system to obtain
diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c
index 6680df3..be60399 100644
--- a/drivers/acpi/bgrt.c
+++ b/drivers/acpi/bgrt.c
@@ -1,5 +1,6 @@
/*
* Copyright 2012 Red Hat, Inc <mjg@redhat.com>
+ * Copyright 2012 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -11,20 +12,10 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/sysfs.h>
-#include <linux/io.h>
-#include <acpi/acpi.h>
-#include <acpi/acpi_bus.h>
+#include <linux/efi-bgrt.h>
-static struct acpi_table_bgrt *bgrt_tab;
static struct kobject *bgrt_kobj;
-struct bmp_header {
- u16 id;
- u32 size;
-} __attribute ((packed));
-
-static struct bmp_header bmp_header;
-
static ssize_t show_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -63,18 +54,7 @@ static DEVICE_ATTR(yoffset, S_IRUGO, show_yoffset, NULL);
static ssize_t show_image(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off, size_t count)
{
- int size = attr->size;
- void __iomem *image = attr->private;
-
- if (off >= size) {
- count = 0;
- } else {
- if (off + count > size)
- count = size - off;
-
- memcpy_fromio(buf, image+off, count);
- }
-
+ memcpy(buf, attr->private + off, count);
return count;
}
@@ -101,45 +81,18 @@ static struct attribute_group bgrt_attribute_group = {
static int __init bgrt_init(void)
{
- acpi_status status;
int ret;
- void __iomem *bgrt;
- if (acpi_disabled)
- return -ENODEV;
-
- status = acpi_get_table("BGRT", 0,
- (struct acpi_table_header **)&bgrt_tab);
-
- if (ACPI_FAILURE(status))
+ if (!bgrt_image)
return -ENODEV;
sysfs_bin_attr_init(&image_attr);
-
- bgrt = ioremap(bgrt_tab->image_address, sizeof(struct bmp_header));
-
- if (!bgrt) {
- ret = -EINVAL;
- goto out_err;
- }
-
- memcpy_fromio(&bmp_header, bgrt, sizeof(bmp_header));
- image_attr.size = bmp_header.size;
- iounmap(bgrt);
-
- image_attr.private = ioremap(bgrt_tab->image_address, image_attr.size);
-
- if (!image_attr.private) {
- ret = -EINVAL;
- goto out_err;
- }
-
+ image_attr.private = bgrt_image;
+ image_attr.size = bgrt_image_size;
bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj);
- if (!bgrt_kobj) {
- ret = -EINVAL;
- goto out_iounmap;
- }
+ if (!bgrt_kobj)
+ return -EINVAL;
ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group);
if (ret)
@@ -155,22 +108,11 @@ out_group:
sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
out_kobject:
kobject_put(bgrt_kobj);
-out_iounmap:
- iounmap(image_attr.private);
-out_err:
return ret;
}
-static void __exit bgrt_exit(void)
-{
- iounmap(image_attr.private);
- sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
- sysfs_remove_bin_file(bgrt_kobj, &image_attr);
-}
-
module_init(bgrt_init);
-module_exit(bgrt_exit);
-MODULE_AUTHOR("Matthew Garrett");
+MODULE_AUTHOR("Matthew Garrett, Josh Triplett <josh@joshtriplett.org>");
MODULE_DESCRIPTION("BGRT boot graphic support");
MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 72a2c98..bce469c 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -27,7 +27,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/pci.h>
@@ -71,9 +71,11 @@ static struct acpi_driver acpi_pci_root_driver = {
},
};
+/* Lock to protect both acpi_pci_roots and acpi_pci_drivers lists */
+static DEFINE_MUTEX(acpi_pci_root_lock);
static LIST_HEAD(acpi_pci_roots);
+static LIST_HEAD(acpi_pci_drivers);
-static struct acpi_pci_driver *sub_driver;
static DEFINE_MUTEX(osc_lock);
int acpi_pci_register_driver(struct acpi_pci_driver *driver)
@@ -81,55 +83,46 @@ int acpi_pci_register_driver(struct acpi_pci_driver *driver)
int n = 0;
struct acpi_pci_root *root;
- struct acpi_pci_driver **pptr = &sub_driver;
- while (*pptr)
- pptr = &(*pptr)->next;
- *pptr = driver;
-
- if (!driver->add)
- return 0;
-
- list_for_each_entry(root, &acpi_pci_roots, node) {
- driver->add(root->device->handle);
- n++;
- }
+ mutex_lock(&acpi_pci_root_lock);
+ list_add_tail(&driver->node, &acpi_pci_drivers);
+ if (driver->add)
+ list_for_each_entry(root, &acpi_pci_roots, node) {
+ driver->add(root);
+ n++;
+ }
+ mutex_unlock(&acpi_pci_root_lock);
return n;
}
-
EXPORT_SYMBOL(acpi_pci_register_driver);
void acpi_pci_unregister_driver(struct acpi_pci_driver *driver)
{
struct acpi_pci_root *root;
- struct acpi_pci_driver **pptr = &sub_driver;
- while (*pptr) {
- if (*pptr == driver)
- break;
- pptr = &(*pptr)->next;
- }
- BUG_ON(!*pptr);
- *pptr = (*pptr)->next;
-
- if (!driver->remove)
- return;
-
- list_for_each_entry(root, &acpi_pci_roots, node)
- driver->remove(root->device->handle);
+ mutex_lock(&acpi_pci_root_lock);
+ list_del(&driver->node);
+ if (driver->remove)
+ list_for_each_entry(root, &acpi_pci_roots, node)
+ driver->remove(root);
+ mutex_unlock(&acpi_pci_root_lock);
}
-
EXPORT_SYMBOL(acpi_pci_unregister_driver);
acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus)
{
struct acpi_pci_root *root;
+ acpi_handle handle = NULL;
+ mutex_lock(&acpi_pci_root_lock);
list_for_each_entry(root, &acpi_pci_roots, node)
if ((root->segment == (u16) seg) &&
- (root->secondary.start == (u16) bus))
- return root->device->handle;
- return NULL;
+ (root->secondary.start == (u16) bus)) {
+ handle = root->device->handle;
+ break;
+ }
+ mutex_unlock(&acpi_pci_root_lock);
+ return handle;
}
EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle);
@@ -277,12 +270,15 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags)
struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
{
struct acpi_pci_root *root;
+ struct acpi_device *device;
- list_for_each_entry(root, &acpi_pci_roots, node) {
- if (root->device->handle == handle)
- return root;
- }
- return NULL;
+ if (acpi_bus_get_device(handle, &device) ||
+ acpi_match_device_ids(device, root_device_ids))
+ return NULL;
+
+ root = acpi_driver_data(device);
+
+ return root;
}
EXPORT_SYMBOL_GPL(acpi_pci_find_root);
@@ -518,8 +514,9 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
* TBD: Need PCI interface for enumeration/configuration of roots.
*/
- /* TBD: Locking */
+ mutex_lock(&acpi_pci_root_lock);
list_add_tail(&root->node, &acpi_pci_roots);
+ mutex_unlock(&acpi_pci_root_lock);
printk(KERN_INFO PREFIX "%s [%s] (domain %04x %pR)\n",
acpi_device_name(device), acpi_device_bid(device),
@@ -538,7 +535,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
"Bus %04x:%02x not present in PCI namespace\n",
root->segment, (unsigned int)root->secondary.start);
result = -ENODEV;
- goto end;
+ goto out_del_root;
}
/*
@@ -548,7 +545,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
*/
result = acpi_pci_bind_root(device);
if (result)
- goto end;
+ goto out_del_root;
/*
* PCI Routing Table
@@ -633,9 +630,11 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
return 0;
+out_del_root:
+ mutex_lock(&acpi_pci_root_lock);
+ list_del(&root->node);
+ mutex_unlock(&acpi_pci_root_lock);
end:
- if (!list_empty(&root->node))
- list_del(&root->node);
kfree(root);
return result;
}
@@ -643,18 +642,34 @@ end:
static int acpi_pci_root_start(struct acpi_device *device)
{
struct acpi_pci_root *root = acpi_driver_data(device);
+ struct acpi_pci_driver *driver;
+
+ mutex_lock(&acpi_pci_root_lock);
+ list_for_each_entry(driver, &acpi_pci_drivers, node)
+ if (driver->add)
+ driver->add(root);
+ mutex_unlock(&acpi_pci_root_lock);
pci_bus_add_devices(root->bus);
+
return 0;
}
static int acpi_pci_root_remove(struct acpi_device *device, int type)
{
struct acpi_pci_root *root = acpi_driver_data(device);
+ struct acpi_pci_driver *driver;
+
+ mutex_lock(&acpi_pci_root_lock);
+ list_for_each_entry(driver, &acpi_pci_drivers, node)
+ if (driver->remove)
+ driver->remove(root);
device_set_run_wake(root->bus->bridge, false);
pci_acpi_remove_bus_pm_notifier(device);
+ list_del(&root->node);
+ mutex_unlock(&acpi_pci_root_lock);
kfree(root);
return 0;
}
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
index e50e31a..d22585f 100644
--- a/drivers/acpi/pci_slot.c
+++ b/drivers/acpi/pci_slot.c
@@ -67,8 +67,8 @@ struct acpi_pci_slot {
struct list_head list; /* node in the list of slots */
};
-static int acpi_pci_slot_add(acpi_handle handle);
-static void acpi_pci_slot_remove(acpi_handle handle);
+static int acpi_pci_slot_add(struct acpi_pci_root *root);
+static void acpi_pci_slot_remove(struct acpi_pci_root *root);
static LIST_HEAD(slot_list);
static DEFINE_MUTEX(slot_list_lock);
@@ -233,45 +233,20 @@ out:
/*
* walk_root_bridge - generic root bridge walker
- * @handle: points to an acpi_pci_root
+ * @root: poiner of an acpi_pci_root
* @user_function: user callback for slot objects
*
* Call user_function for all objects underneath this root bridge.
* Walk p2p bridges underneath us and call user_function on those too.
*/
static int
-walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function)
+walk_root_bridge(struct acpi_pci_root *root, acpi_walk_callback user_function)
{
- int seg, bus;
- unsigned long long tmp;
acpi_status status;
- acpi_handle dummy_handle;
- struct pci_bus *pci_bus;
+ acpi_handle handle = root->device->handle;
+ struct pci_bus *pci_bus = root->bus;
struct callback_args context;
- /* If the bridge doesn't have _STA, we assume it is always there */
- status = acpi_get_handle(handle, "_STA", &dummy_handle);
- if (ACPI_SUCCESS(status)) {
- status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp);
- if (ACPI_FAILURE(status)) {
- info("%s: _STA evaluation failure\n", __func__);
- return 0;
- }
- if ((tmp & ACPI_STA_DEVICE_FUNCTIONING) == 0)
- /* don't register this object */
- return 0;
- }
-
- status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp);
- seg = ACPI_SUCCESS(status) ? tmp : 0;
-
- status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp);
- bus = ACPI_SUCCESS(status) ? tmp : 0;
-
- pci_bus = pci_find_bus(seg, bus);
- if (!pci_bus)
- return 0;
-
context.pci_bus = pci_bus;
context.user_function = user_function;
context.root_handle = handle;
@@ -295,11 +270,11 @@ walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function)
* @handle: points to an acpi_pci_root
*/
static int
-acpi_pci_slot_add(acpi_handle handle)
+acpi_pci_slot_add(struct acpi_pci_root *root)
{
acpi_status status;
- status = walk_root_bridge(handle, register_slot);
+ status = walk_root_bridge(root, register_slot);
if (ACPI_FAILURE(status))
err("%s: register_slot failure - %d\n", __func__, status);
@@ -311,10 +286,11 @@ acpi_pci_slot_add(acpi_handle handle)
* @handle: points to an acpi_pci_root
*/
static void
-acpi_pci_slot_remove(acpi_handle handle)
+acpi_pci_slot_remove(struct acpi_pci_root *root)
{
struct acpi_pci_slot *slot, *tmp;
struct pci_bus *pbus;
+ acpi_handle handle = root->device->handle;
mutex_lock(&slot_list_lock);
list_for_each_entry_safe(slot, tmp, &slot_list, list) {
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 5e6e00b..abea76c 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -184,6 +184,17 @@ static void device_release(struct kobject *kobj)
struct device *dev = kobj_to_dev(kobj);
struct device_private *p = dev->p;
+ /*
+ * Some platform devices are driven without driver attached
+ * and managed resources may have been acquired. Make sure
+ * all resources are released.
+ *
+ * Drivers still can add resources into device after device
+ * is deleted but alive, so release devres here to avoid
+ * possible memory leak.
+ */
+ devres_release_all(dev);
+
if (dev->release)
dev->release(dev);
else if (dev->type && dev->type->release)
@@ -1196,13 +1207,6 @@ void device_del(struct device *dev)
bus_remove_device(dev);
driver_deferred_probe_del(dev);
- /*
- * Some platform devices are driven without driver attached
- * and managed resources may have been acquired. Make sure
- * all resources are released.
- */
- devres_release_all(dev);
-
/* Notify the platform of the removal, in case they
* need to do anything...
*/
@@ -1861,26 +1865,20 @@ void device_shutdown(void)
*/
#ifdef CONFIG_PRINTK
-int __dev_printk(const char *level, const struct device *dev,
- struct va_format *vaf)
+static int
+create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen)
{
- char dict[128];
- const char *level_extra = "";
- size_t dictlen = 0;
const char *subsys;
-
- if (!dev)
- return printk("%s(NULL device *): %pV", level, vaf);
+ size_t pos = 0;
if (dev->class)
subsys = dev->class->name;
else if (dev->bus)
subsys = dev->bus->name;
else
- goto skip;
+ return 0;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "SUBSYSTEM=%s", subsys);
+ pos += snprintf(hdr + pos, hdrlen - pos, "SUBSYSTEM=%s", subsys);
/*
* Add device identifier DEVICE=:
@@ -1896,32 +1894,63 @@ int __dev_printk(const char *level, const struct device *dev,
c = 'b';
else
c = 'c';
- dictlen++;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "DEVICE=%c%u:%u",
- c, MAJOR(dev->devt), MINOR(dev->devt));
+ pos++;
+ pos += snprintf(hdr + pos, hdrlen - pos,
+ "DEVICE=%c%u:%u",
+ c, MAJOR(dev->devt), MINOR(dev->devt));
} else if (strcmp(subsys, "net") == 0) {
struct net_device *net = to_net_dev(dev);
- dictlen++;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "DEVICE=n%u", net->ifindex);
+ pos++;
+ pos += snprintf(hdr + pos, hdrlen - pos,
+ "DEVICE=n%u", net->ifindex);
} else {
- dictlen++;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "DEVICE=+%s:%s", subsys, dev_name(dev));
+ pos++;
+ pos += snprintf(hdr + pos, hdrlen - pos,
+ "DEVICE=+%s:%s", subsys, dev_name(dev));
}
-skip:
- if (level[2])
- level_extra = &level[2]; /* skip past KERN_SOH "L" */
- return printk_emit(0, level[1] - '0',
- dictlen ? dict : NULL, dictlen,
- "%s %s: %s%pV",
- dev_driver_string(dev), dev_name(dev),
- level_extra, vaf);
+ return pos;
+}
+EXPORT_SYMBOL(create_syslog_header);
+
+int dev_vprintk_emit(int level, const struct device *dev,
+ const char *fmt, va_list args)
+{
+ char hdr[128];
+ size_t hdrlen;
+
+ hdrlen = create_syslog_header(dev, hdr, sizeof(hdr));
+
+ return vprintk_emit(0, level, hdrlen ? hdr : NULL, hdrlen, fmt, args);
+}
+EXPORT_SYMBOL(dev_vprintk_emit);
+
+int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+
+ r = dev_vprintk_emit(level, dev, fmt, args);
+
+ va_end(args);
+
+ return r;
+}
+EXPORT_SYMBOL(dev_printk_emit);
+
+static int __dev_printk(const char *level, const struct device *dev,
+ struct va_format *vaf)
+{
+ if (!dev)
+ return printk("%s(NULL device *): %pV", level, vaf);
+
+ return dev_printk_emit(level[1] - '0', dev,
+ "%s %s: %pV",
+ dev_driver_string(dev), dev_name(dev), vaf);
}
-EXPORT_SYMBOL(__dev_printk);
int dev_printk(const char *level, const struct device *dev,
const char *fmt, ...)
@@ -1936,6 +1965,7 @@ int dev_printk(const char *level, const struct device *dev,
vaf.va = &args;
r = __dev_printk(level, dev, &vaf);
+
va_end(args);
return r;
@@ -1955,6 +1985,7 @@ int func(const struct device *dev, const char *fmt, ...) \
vaf.va = &args; \
\
r = __dev_printk(kern_level, dev, &vaf); \
+ \
va_end(args); \
\
return r; \
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 2360adb..8731979 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -144,6 +144,48 @@ EXPORT_SYMBOL_GPL(devres_alloc);
#endif
/**
+ * devres_for_each_res - Resource iterator
+ * @dev: Device to iterate resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ * @fn: Function to be called for each matched resource.
+ * @data: Data for @fn, the 3rd parameter of @fn
+ *
+ * Call @fn for each devres of @dev which is associated with @release
+ * and for which @match returns 1.
+ *
+ * RETURNS:
+ * void
+ */
+void devres_for_each_res(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data,
+ void (*fn)(struct device *, void *, void *),
+ void *data)
+{
+ struct devres_node *node;
+ struct devres_node *tmp;
+ unsigned long flags;
+
+ if (!fn)
+ return;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ list_for_each_entry_safe_reverse(node, tmp,
+ &dev->devres_head, entry) {
+ struct devres *dr = container_of(node, struct devres, node);
+
+ if (node->release != release)
+ continue;
+ if (match && !match(dev, dr->data, match_data))
+ continue;
+ fn(dev, dr->data, data);
+ }
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+}
+EXPORT_SYMBOL_GPL(devres_for_each_res);
+
+/**
* devres_free - Free device resource data
* @res: Pointer to devres data to free
*
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 803cfc1..6e21080 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -21,6 +21,13 @@
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/async.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/syscore_ops.h>
+
+#include "base.h"
MODULE_AUTHOR("Manuel Estrada Sainz");
MODULE_DESCRIPTION("Multi purpose firmware loading support");
@@ -85,23 +92,168 @@ static inline long firmware_loading_timeout(void)
return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT;
}
-/* fw_lock could be moved to 'struct firmware_priv' but since it is just
- * guarding for corner cases a global lock should be OK */
-static DEFINE_MUTEX(fw_lock);
+struct firmware_cache {
+ /* firmware_buf instance will be added into the below list */
+ spinlock_t lock;
+ struct list_head head;
+ int state;
+
+#ifdef CONFIG_PM_SLEEP
+ /*
+ * Names of firmware images which have been cached successfully
+ * will be added into the below list so that device uncache
+ * helper can trace which firmware images have been cached
+ * before.
+ */
+ spinlock_t name_lock;
+ struct list_head fw_names;
+
+ wait_queue_head_t wait_queue;
+ int cnt;
+ struct delayed_work work;
+
+ struct notifier_block pm_notify;
+#endif
+};
-struct firmware_priv {
+struct firmware_buf {
+ struct kref ref;
+ struct list_head list;
struct completion completion;
- struct firmware *fw;
+ struct firmware_cache *fwc;
unsigned long status;
+ void *data;
+ size_t size;
struct page **pages;
int nr_pages;
int page_array_size;
+ char fw_id[];
+};
+
+struct fw_cache_entry {
+ struct list_head list;
+ char name[];
+};
+
+struct firmware_priv {
struct timer_list timeout;
- struct device dev;
bool nowait;
- char fw_id[];
+ struct device dev;
+ struct firmware_buf *buf;
+ struct firmware *fw;
+};
+
+struct fw_name_devm {
+ unsigned long magic;
+ char name[];
};
+#define to_fwbuf(d) container_of(d, struct firmware_buf, ref)
+
+#define FW_LOADER_NO_CACHE 0
+#define FW_LOADER_START_CACHE 1
+
+static int fw_cache_piggyback_on_request(const char *name);
+
+/* fw_lock could be moved to 'struct firmware_priv' but since it is just
+ * guarding for corner cases a global lock should be OK */
+static DEFINE_MUTEX(fw_lock);
+
+static struct firmware_cache fw_cache;
+
+static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
+ struct firmware_cache *fwc)
+{
+ struct firmware_buf *buf;
+
+ buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1 , GFP_ATOMIC);
+
+ if (!buf)
+ return buf;
+
+ kref_init(&buf->ref);
+ strcpy(buf->fw_id, fw_name);
+ buf->fwc = fwc;
+ init_completion(&buf->completion);
+
+ pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf);
+
+ return buf;
+}
+
+static struct firmware_buf *__fw_lookup_buf(const char *fw_name)
+{
+ struct firmware_buf *tmp;
+ struct firmware_cache *fwc = &fw_cache;
+
+ list_for_each_entry(tmp, &fwc->head, list)
+ if (!strcmp(tmp->fw_id, fw_name))
+ return tmp;
+ return NULL;
+}
+
+static int fw_lookup_and_allocate_buf(const char *fw_name,
+ struct firmware_cache *fwc,
+ struct firmware_buf **buf)
+{
+ struct firmware_buf *tmp;
+
+ spin_lock(&fwc->lock);
+ tmp = __fw_lookup_buf(fw_name);
+ if (tmp) {
+ kref_get(&tmp->ref);
+ spin_unlock(&fwc->lock);
+ *buf = tmp;
+ return 1;
+ }
+ tmp = __allocate_fw_buf(fw_name, fwc);
+ if (tmp)
+ list_add(&tmp->list, &fwc->head);
+ spin_unlock(&fwc->lock);
+
+ *buf = tmp;
+
+ return tmp ? 0 : -ENOMEM;
+}
+
+static struct firmware_buf *fw_lookup_buf(const char *fw_name)
+{
+ struct firmware_buf *tmp;
+ struct firmware_cache *fwc = &fw_cache;
+
+ spin_lock(&fwc->lock);
+ tmp = __fw_lookup_buf(fw_name);
+ spin_unlock(&fwc->lock);
+
+ return tmp;
+}
+
+static void __fw_free_buf(struct kref *ref)
+{
+ struct firmware_buf *buf = to_fwbuf(ref);
+ struct firmware_cache *fwc = buf->fwc;
+ int i;
+
+ pr_debug("%s: fw-%s buf=%p data=%p size=%u\n",
+ __func__, buf->fw_id, buf, buf->data,
+ (unsigned int)buf->size);
+
+ spin_lock(&fwc->lock);
+ list_del(&buf->list);
+ spin_unlock(&fwc->lock);
+
+ vunmap(buf->data);
+ for (i = 0; i < buf->nr_pages; i++)
+ __free_page(buf->pages[i]);
+ kfree(buf->pages);
+ kfree(buf);
+}
+
+static void fw_free_buf(struct firmware_buf *buf)
+{
+ kref_put(&buf->ref, __fw_free_buf);
+}
+
static struct firmware_priv *to_firmware_priv(struct device *dev)
{
return container_of(dev, struct firmware_priv, dev);
@@ -109,9 +261,10 @@ static struct firmware_priv *to_firmware_priv(struct device *dev)
static void fw_load_abort(struct firmware_priv *fw_priv)
{
- set_bit(FW_STATUS_ABORT, &fw_priv->status);
- wmb();
- complete(&fw_priv->completion);
+ struct firmware_buf *buf = fw_priv->buf;
+
+ set_bit(FW_STATUS_ABORT, &buf->status);
+ complete_all(&buf->completion);
}
static ssize_t firmware_timeout_show(struct class *class,
@@ -154,11 +307,7 @@ static struct class_attribute firmware_class_attrs[] = {
static void fw_dev_release(struct device *dev)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- int i;
- for (i = 0; i < fw_priv->nr_pages; i++)
- __free_page(fw_priv->pages[i]);
- kfree(fw_priv->pages);
kfree(fw_priv);
module_put(THIS_MODULE);
@@ -168,7 +317,7 @@ static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
+ if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->buf->fw_id))
return -ENOMEM;
if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
return -ENOMEM;
@@ -189,20 +338,16 @@ static ssize_t firmware_loading_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
+ int loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status);
return sprintf(buf, "%d\n", loading);
}
+/* firmware holds the ownership of pages */
static void firmware_free_data(const struct firmware *fw)
{
- int i;
- vunmap(fw->data);
- if (fw->pages) {
- for (i = 0; i < PFN_UP(fw->size); i++)
- __free_page(fw->pages[i]);
- kfree(fw->pages);
- }
+ WARN_ON(!fw->priv);
+ fw_free_buf(fw->priv);
}
/* Some architectures don't have PAGE_KERNEL_RO */
@@ -227,45 +372,33 @@ static ssize_t firmware_loading_store(struct device *dev,
const char *buf, size_t count)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ struct firmware_buf *fw_buf = fw_priv->buf;
int loading = simple_strtol(buf, NULL, 10);
int i;
mutex_lock(&fw_lock);
- if (!fw_priv->fw)
+ if (!fw_buf)
goto out;
switch (loading) {
case 1:
- firmware_free_data(fw_priv->fw);
- memset(fw_priv->fw, 0, sizeof(struct firmware));
- /* If the pages are not owned by 'struct firmware' */
- for (i = 0; i < fw_priv->nr_pages; i++)
- __free_page(fw_priv->pages[i]);
- kfree(fw_priv->pages);
- fw_priv->pages = NULL;
- fw_priv->page_array_size = 0;
- fw_priv->nr_pages = 0;
- set_bit(FW_STATUS_LOADING, &fw_priv->status);
+ /* discarding any previous partial load */
+ if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) {
+ for (i = 0; i < fw_buf->nr_pages; i++)
+ __free_page(fw_buf->pages[i]);
+ kfree(fw_buf->pages);
+ fw_buf->pages = NULL;
+ fw_buf->page_array_size = 0;
+ fw_buf->nr_pages = 0;
+ set_bit(FW_STATUS_LOADING, &fw_buf->status);
+ }
break;
case 0:
- if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
- vunmap(fw_priv->fw->data);
- fw_priv->fw->data = vmap(fw_priv->pages,
- fw_priv->nr_pages,
- 0, PAGE_KERNEL_RO);
- if (!fw_priv->fw->data) {
- dev_err(dev, "%s: vmap() failed\n", __func__);
- goto err;
- }
- /* Pages are now owned by 'struct firmware' */
- fw_priv->fw->pages = fw_priv->pages;
- fw_priv->pages = NULL;
-
- fw_priv->page_array_size = 0;
- fw_priv->nr_pages = 0;
- complete(&fw_priv->completion);
- clear_bit(FW_STATUS_LOADING, &fw_priv->status);
+ if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
+ set_bit(FW_STATUS_DONE, &fw_buf->status);
+ clear_bit(FW_STATUS_LOADING, &fw_buf->status);
+ complete_all(&fw_buf->completion);
break;
}
/* fallthrough */
@@ -273,7 +406,6 @@ static ssize_t firmware_loading_store(struct device *dev,
dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
/* fallthrough */
case -1:
- err:
fw_load_abort(fw_priv);
break;
}
@@ -290,21 +422,21 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
{
struct device *dev = kobj_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- struct firmware *fw;
+ struct firmware_buf *buf;
ssize_t ret_count;
mutex_lock(&fw_lock);
- fw = fw_priv->fw;
- if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+ buf = fw_priv->buf;
+ if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
ret_count = -ENODEV;
goto out;
}
- if (offset > fw->size) {
+ if (offset > buf->size) {
ret_count = 0;
goto out;
}
- if (count > fw->size - offset)
- count = fw->size - offset;
+ if (count > buf->size - offset)
+ count = buf->size - offset;
ret_count = count;
@@ -314,11 +446,11 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
int page_ofs = offset & (PAGE_SIZE-1);
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
- page_data = kmap(fw_priv->pages[page_nr]);
+ page_data = kmap(buf->pages[page_nr]);
memcpy(buffer, page_data + page_ofs, page_cnt);
- kunmap(fw_priv->pages[page_nr]);
+ kunmap(buf->pages[page_nr]);
buffer += page_cnt;
offset += page_cnt;
count -= page_cnt;
@@ -330,12 +462,13 @@ out:
static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
{
+ struct firmware_buf *buf = fw_priv->buf;
int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
/* If the array of pages is too small, grow it... */
- if (fw_priv->page_array_size < pages_needed) {
+ if (buf->page_array_size < pages_needed) {
int new_array_size = max(pages_needed,
- fw_priv->page_array_size * 2);
+ buf->page_array_size * 2);
struct page **new_pages;
new_pages = kmalloc(new_array_size * sizeof(void *),
@@ -344,24 +477,24 @@ static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
fw_load_abort(fw_priv);
return -ENOMEM;
}
- memcpy(new_pages, fw_priv->pages,
- fw_priv->page_array_size * sizeof(void *));
- memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
- (new_array_size - fw_priv->page_array_size));
- kfree(fw_priv->pages);
- fw_priv->pages = new_pages;
- fw_priv->page_array_size = new_array_size;
+ memcpy(new_pages, buf->pages,
+ buf->page_array_size * sizeof(void *));
+ memset(&new_pages[buf->page_array_size], 0, sizeof(void *) *
+ (new_array_size - buf->page_array_size));
+ kfree(buf->pages);
+ buf->pages = new_pages;
+ buf->page_array_size = new_array_size;
}
- while (fw_priv->nr_pages < pages_needed) {
- fw_priv->pages[fw_priv->nr_pages] =
+ while (buf->nr_pages < pages_needed) {
+ buf->pages[buf->nr_pages] =
alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
- if (!fw_priv->pages[fw_priv->nr_pages]) {
+ if (!buf->pages[buf->nr_pages]) {
fw_load_abort(fw_priv);
return -ENOMEM;
}
- fw_priv->nr_pages++;
+ buf->nr_pages++;
}
return 0;
}
@@ -384,18 +517,19 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
{
struct device *dev = kobj_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- struct firmware *fw;
+ struct firmware_buf *buf;
ssize_t retval;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
mutex_lock(&fw_lock);
- fw = fw_priv->fw;
- if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+ buf = fw_priv->buf;
+ if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
retval = -ENODEV;
goto out;
}
+
retval = fw_realloc_buffer(fw_priv, offset + count);
if (retval)
goto out;
@@ -408,17 +542,17 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
int page_ofs = offset & (PAGE_SIZE - 1);
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
- page_data = kmap(fw_priv->pages[page_nr]);
+ page_data = kmap(buf->pages[page_nr]);
memcpy(page_data + page_ofs, buffer, page_cnt);
- kunmap(fw_priv->pages[page_nr]);
+ kunmap(buf->pages[page_nr]);
buffer += page_cnt;
offset += page_cnt;
count -= page_cnt;
}
- fw->size = max_t(size_t, offset, fw->size);
+ buf->size = max_t(size_t, offset, buf->size);
out:
mutex_unlock(&fw_lock);
return retval;
@@ -445,35 +579,120 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
struct firmware_priv *fw_priv;
struct device *f_dev;
- fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
+ fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL);
if (!fw_priv) {
dev_err(device, "%s: kmalloc failed\n", __func__);
- return ERR_PTR(-ENOMEM);
+ fw_priv = ERR_PTR(-ENOMEM);
+ goto exit;
}
- fw_priv->fw = firmware;
fw_priv->nowait = nowait;
- strcpy(fw_priv->fw_id, fw_name);
- init_completion(&fw_priv->completion);
+ fw_priv->fw = firmware;
setup_timer(&fw_priv->timeout,
firmware_class_timeout, (u_long) fw_priv);
f_dev = &fw_priv->dev;
device_initialize(f_dev);
- dev_set_name(f_dev, "%s", dev_name(device));
+ dev_set_name(f_dev, "%s", fw_name);
f_dev->parent = device;
f_dev->class = &firmware_class;
-
+exit:
return fw_priv;
}
+/* one pages buffer is mapped/unmapped only once */
+static int fw_map_pages_buf(struct firmware_buf *buf)
+{
+ buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO);
+ if (!buf->data)
+ return -ENOMEM;
+ return 0;
+}
+
+/* store the pages buffer info firmware from buf */
+static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw)
+{
+ fw->priv = buf;
+ fw->pages = buf->pages;
+ fw->size = buf->size;
+ fw->data = buf->data;
+
+ pr_debug("%s: fw-%s buf=%p data=%p size=%u\n",
+ __func__, buf->fw_id, buf, buf->data,
+ (unsigned int)buf->size);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void fw_name_devm_release(struct device *dev, void *res)
+{
+ struct fw_name_devm *fwn = res;
+
+ if (fwn->magic == (unsigned long)&fw_cache)
+ pr_debug("%s: fw_name-%s devm-%p released\n",
+ __func__, fwn->name, res);
+}
+
+static int fw_devm_match(struct device *dev, void *res,
+ void *match_data)
+{
+ struct fw_name_devm *fwn = res;
+
+ return (fwn->magic == (unsigned long)&fw_cache) &&
+ !strcmp(fwn->name, match_data);
+}
+
+static struct fw_name_devm *fw_find_devm_name(struct device *dev,
+ const char *name)
+{
+ struct fw_name_devm *fwn;
+
+ fwn = devres_find(dev, fw_name_devm_release,
+ fw_devm_match, (void *)name);
+ return fwn;
+}
+
+/* add firmware name into devres list */
+static int fw_add_devm_name(struct device *dev, const char *name)
+{
+ struct fw_name_devm *fwn;
+
+ fwn = fw_find_devm_name(dev, name);
+ if (fwn)
+ return 1;
+
+ fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) +
+ strlen(name) + 1, GFP_KERNEL);
+ if (!fwn)
+ return -ENOMEM;
+
+ fwn->magic = (unsigned long)&fw_cache;
+ strcpy(fwn->name, name);
+ devres_add(dev, fwn);
+
+ return 0;
+}
+#else
+static int fw_add_devm_name(struct device *dev, const char *name)
+{
+ return 0;
+}
+#endif
+
+static void _request_firmware_cleanup(const struct firmware **firmware_p)
+{
+ release_firmware(*firmware_p);
+ *firmware_p = NULL;
+}
+
static struct firmware_priv *
_request_firmware_prepare(const struct firmware **firmware_p, const char *name,
struct device *device, bool uevent, bool nowait)
{
struct firmware *firmware;
- struct firmware_priv *fw_priv;
+ struct firmware_priv *fw_priv = NULL;
+ struct firmware_buf *buf;
+ int ret;
if (!firmware_p)
return ERR_PTR(-EINVAL);
@@ -490,18 +709,46 @@ _request_firmware_prepare(const struct firmware **firmware_p, const char *name,
return NULL;
}
- fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
- if (IS_ERR(fw_priv)) {
- release_firmware(firmware);
+ ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf);
+ if (!ret)
+ fw_priv = fw_create_instance(firmware, name, device,
+ uevent, nowait);
+
+ if (IS_ERR(fw_priv) || ret < 0) {
+ kfree(firmware);
*firmware_p = NULL;
+ return ERR_PTR(-ENOMEM);
+ } else if (fw_priv) {
+ fw_priv->buf = buf;
+
+ /*
+ * bind with 'buf' now to avoid warning in failure path
+ * of requesting firmware.
+ */
+ firmware->priv = buf;
+ return fw_priv;
}
- return fw_priv;
-}
-static void _request_firmware_cleanup(const struct firmware **firmware_p)
-{
- release_firmware(*firmware_p);
- *firmware_p = NULL;
+ /* share the cached buf, which is inprogessing or completed */
+ check_status:
+ mutex_lock(&fw_lock);
+ if (test_bit(FW_STATUS_ABORT, &buf->status)) {
+ fw_priv = ERR_PTR(-ENOENT);
+ firmware->priv = buf;
+ _request_firmware_cleanup(firmware_p);
+ goto exit;
+ } else if (test_bit(FW_STATUS_DONE, &buf->status)) {
+ fw_priv = NULL;
+ fw_set_page_data(buf, firmware);
+ goto exit;
+ }
+ mutex_unlock(&fw_lock);
+ wait_for_completion(&buf->completion);
+ goto check_status;
+
+exit:
+ mutex_unlock(&fw_lock);
+ return fw_priv;
}
static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
@@ -509,6 +756,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
{
int retval = 0;
struct device *f_dev = &fw_priv->dev;
+ struct firmware_buf *buf = fw_priv->buf;
+ struct firmware_cache *fwc = &fw_cache;
dev_set_uevent_suppress(f_dev, true);
@@ -535,7 +784,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
if (uevent) {
dev_set_uevent_suppress(f_dev, false);
- dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id);
+ dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id);
if (timeout != MAX_SCHEDULE_TIMEOUT)
mod_timer(&fw_priv->timeout,
round_jiffies_up(jiffies + timeout));
@@ -543,15 +792,40 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
}
- wait_for_completion(&fw_priv->completion);
+ wait_for_completion(&buf->completion);
- set_bit(FW_STATUS_DONE, &fw_priv->status);
del_timer_sync(&fw_priv->timeout);
mutex_lock(&fw_lock);
- if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status))
+ if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status))
retval = -ENOENT;
- fw_priv->fw = NULL;
+
+ /*
+ * add firmware name into devres list so that we can auto cache
+ * and uncache firmware for device.
+ *
+ * f_dev->parent may has been deleted already, but the problem
+ * should be fixed in devres or driver core.
+ */
+ if (!retval && f_dev->parent)
+ fw_add_devm_name(f_dev->parent, buf->fw_id);
+
+ if (!retval)
+ retval = fw_map_pages_buf(buf);
+
+ /*
+ * After caching firmware image is started, let it piggyback
+ * on request firmware.
+ */
+ if (!retval && fwc->state == FW_LOADER_START_CACHE) {
+ if (fw_cache_piggyback_on_request(buf->fw_id))
+ kref_get(&buf->ref);
+ }
+
+ /* pass the pages buffer to driver at the last minute */
+ fw_set_page_data(buf, fw_priv->fw);
+
+ fw_priv->buf = NULL;
mutex_unlock(&fw_lock);
device_remove_file(f_dev, &dev_attr_loading);
@@ -578,6 +852,8 @@ err_put_dev:
* @name will be used as $FIRMWARE in the uevent environment and
* should be distinctive enough not to be confused with any other
* firmware image for this or any other device.
+ *
+ * Caller must hold the reference count of @device.
**/
int
request_firmware(const struct firmware **firmware_p, const char *name,
@@ -659,6 +935,7 @@ static void request_firmware_work_func(struct work_struct *work)
out:
fw_work->cont(fw, fw_work->context);
+ put_device(fw_work->device);
module_put(fw_work->module);
kfree(fw_work);
@@ -677,9 +954,15 @@ static void request_firmware_work_func(struct work_struct *work)
* @cont: function will be called asynchronously when the firmware
* request is over.
*
- * Asynchronous variant of request_firmware() for user contexts where
- * it is not possible to sleep for long time. It can't be called
- * in atomic contexts.
+ * Caller must hold the reference count of @device.
+ *
+ * Asynchronous variant of request_firmware() for user contexts:
+ * - sleep for as small periods as possible since it may
+ * increase kernel boot time of built-in device drivers
+ * requesting firmware in their ->probe() methods, if
+ * @gfp is GFP_KERNEL.
+ *
+ * - can't sleep at all if @gfp is GFP_ATOMIC.
**/
int
request_firmware_nowait(
@@ -705,18 +988,363 @@ request_firmware_nowait(
return -EFAULT;
}
+ get_device(fw_work->device);
INIT_WORK(&fw_work->work, request_firmware_work_func);
schedule_work(&fw_work->work);
return 0;
}
+/**
+ * cache_firmware - cache one firmware image in kernel memory space
+ * @fw_name: the firmware image name
+ *
+ * Cache firmware in kernel memory so that drivers can use it when
+ * system isn't ready for them to request firmware image from userspace.
+ * Once it returns successfully, driver can use request_firmware or its
+ * nowait version to get the cached firmware without any interacting
+ * with userspace
+ *
+ * Return 0 if the firmware image has been cached successfully
+ * Return !0 otherwise
+ *
+ */
+int cache_firmware(const char *fw_name)
+{
+ int ret;
+ const struct firmware *fw;
+
+ pr_debug("%s: %s\n", __func__, fw_name);
+
+ ret = request_firmware(&fw, fw_name, NULL);
+ if (!ret)
+ kfree(fw);
+
+ pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret);
+
+ return ret;
+}
+
+/**
+ * uncache_firmware - remove one cached firmware image
+ * @fw_name: the firmware image name
+ *
+ * Uncache one firmware image which has been cached successfully
+ * before.
+ *
+ * Return 0 if the firmware cache has been removed successfully
+ * Return !0 otherwise
+ *
+ */
+int uncache_firmware(const char *fw_name)
+{
+ struct firmware_buf *buf;
+ struct firmware fw;
+
+ pr_debug("%s: %s\n", __func__, fw_name);
+
+ if (fw_get_builtin_firmware(&fw, fw_name))
+ return 0;
+
+ buf = fw_lookup_buf(fw_name);
+ if (buf) {
+ fw_free_buf(buf);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static struct fw_cache_entry *alloc_fw_cache_entry(const char *name)
+{
+ struct fw_cache_entry *fce;
+
+ fce = kzalloc(sizeof(*fce) + strlen(name) + 1, GFP_ATOMIC);
+ if (!fce)
+ goto exit;
+
+ strcpy(fce->name, name);
+exit:
+ return fce;
+}
+
+static int fw_cache_piggyback_on_request(const char *name)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ struct fw_cache_entry *fce;
+ int ret = 0;
+
+ spin_lock(&fwc->name_lock);
+ list_for_each_entry(fce, &fwc->fw_names, list) {
+ if (!strcmp(fce->name, name))
+ goto found;
+ }
+
+ fce = alloc_fw_cache_entry(name);
+ if (fce) {
+ ret = 1;
+ list_add(&fce->list, &fwc->fw_names);
+ pr_debug("%s: fw: %s\n", __func__, name);
+ }
+found:
+ spin_unlock(&fwc->name_lock);
+ return ret;
+}
+
+static void free_fw_cache_entry(struct fw_cache_entry *fce)
+{
+ kfree(fce);
+}
+
+static void __async_dev_cache_fw_image(void *fw_entry,
+ async_cookie_t cookie)
+{
+ struct fw_cache_entry *fce = fw_entry;
+ struct firmware_cache *fwc = &fw_cache;
+ int ret;
+
+ ret = cache_firmware(fce->name);
+ if (ret) {
+ spin_lock(&fwc->name_lock);
+ list_del(&fce->list);
+ spin_unlock(&fwc->name_lock);
+
+ free_fw_cache_entry(fce);
+ }
+
+ spin_lock(&fwc->name_lock);
+ fwc->cnt--;
+ spin_unlock(&fwc->name_lock);
+
+ wake_up(&fwc->wait_queue);
+}
+
+/* called with dev->devres_lock held */
+static void dev_create_fw_entry(struct device *dev, void *res,
+ void *data)
+{
+ struct fw_name_devm *fwn = res;
+ const char *fw_name = fwn->name;
+ struct list_head *head = data;
+ struct fw_cache_entry *fce;
+
+ fce = alloc_fw_cache_entry(fw_name);
+ if (fce)
+ list_add(&fce->list, head);
+}
+
+static int devm_name_match(struct device *dev, void *res,
+ void *match_data)
+{
+ struct fw_name_devm *fwn = res;
+ return (fwn->magic == (unsigned long)match_data);
+}
+
+static void dev_cache_fw_image(struct device *dev, void *data)
+{
+ LIST_HEAD(todo);
+ struct fw_cache_entry *fce;
+ struct fw_cache_entry *fce_next;
+ struct firmware_cache *fwc = &fw_cache;
+
+ devres_for_each_res(dev, fw_name_devm_release,
+ devm_name_match, &fw_cache,
+ dev_create_fw_entry, &todo);
+
+ list_for_each_entry_safe(fce, fce_next, &todo, list) {
+ list_del(&fce->list);
+
+ spin_lock(&fwc->name_lock);
+ fwc->cnt++;
+ list_add(&fce->list, &fwc->fw_names);
+ spin_unlock(&fwc->name_lock);
+
+ async_schedule(__async_dev_cache_fw_image, (void *)fce);
+ }
+}
+
+static void __device_uncache_fw_images(void)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ struct fw_cache_entry *fce;
+
+ spin_lock(&fwc->name_lock);
+ while (!list_empty(&fwc->fw_names)) {
+ fce = list_entry(fwc->fw_names.next,
+ struct fw_cache_entry, list);
+ list_del(&fce->list);
+ spin_unlock(&fwc->name_lock);
+
+ uncache_firmware(fce->name);
+ free_fw_cache_entry(fce);
+
+ spin_lock(&fwc->name_lock);
+ }
+ spin_unlock(&fwc->name_lock);
+}
+
+/**
+ * device_cache_fw_images - cache devices' firmware
+ *
+ * If one device called request_firmware or its nowait version
+ * successfully before, the firmware names are recored into the
+ * device's devres link list, so device_cache_fw_images can call
+ * cache_firmware() to cache these firmwares for the device,
+ * then the device driver can load its firmwares easily at
+ * time when system is not ready to complete loading firmware.
+ */
+static void device_cache_fw_images(void)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ int old_timeout;
+ DEFINE_WAIT(wait);
+
+ pr_debug("%s\n", __func__);
+
+ /*
+ * use small loading timeout for caching devices' firmware
+ * because all these firmware images have been loaded
+ * successfully at lease once, also system is ready for
+ * completing firmware loading now. The maximum size of
+ * firmware in current distributions is about 2M bytes,
+ * so 10 secs should be enough.
+ */
+ old_timeout = loading_timeout;
+ loading_timeout = 10;
+
+ mutex_lock(&fw_lock);
+ fwc->state = FW_LOADER_START_CACHE;
+ dpm_for_each_dev(NULL, dev_cache_fw_image);
+ mutex_unlock(&fw_lock);
+
+ /* wait for completion of caching firmware for all devices */
+ spin_lock(&fwc->name_lock);
+ for (;;) {
+ prepare_to_wait(&fwc->wait_queue, &wait,
+ TASK_UNINTERRUPTIBLE);
+ if (!fwc->cnt)
+ break;
+
+ spin_unlock(&fwc->name_lock);
+
+ schedule();
+
+ spin_lock(&fwc->name_lock);
+ }
+ spin_unlock(&fwc->name_lock);
+ finish_wait(&fwc->wait_queue, &wait);
+
+ loading_timeout = old_timeout;
+}
+
+/**
+ * device_uncache_fw_images - uncache devices' firmware
+ *
+ * uncache all firmwares which have been cached successfully
+ * by device_uncache_fw_images earlier
+ */
+static void device_uncache_fw_images(void)
+{
+ pr_debug("%s\n", __func__);
+ __device_uncache_fw_images();
+}
+
+static void device_uncache_fw_images_work(struct work_struct *work)
+{
+ device_uncache_fw_images();
+}
+
+/**
+ * device_uncache_fw_images_delay - uncache devices firmwares
+ * @delay: number of milliseconds to delay uncache device firmwares
+ *
+ * uncache all devices's firmwares which has been cached successfully
+ * by device_cache_fw_images after @delay milliseconds.
+ */
+static void device_uncache_fw_images_delay(unsigned long delay)
+{
+ schedule_delayed_work(&fw_cache.work,
+ msecs_to_jiffies(delay));
+}
+
+static int fw_pm_notify(struct notifier_block *notify_block,
+ unsigned long mode, void *unused)
+{
+ switch (mode) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ device_cache_fw_images();
+ break;
+
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+ case PM_POST_RESTORE:
+ /*
+ * In case that system sleep failed and syscore_suspend is
+ * not called.
+ */
+ mutex_lock(&fw_lock);
+ fw_cache.state = FW_LOADER_NO_CACHE;
+ mutex_unlock(&fw_lock);
+
+ device_uncache_fw_images_delay(10 * MSEC_PER_SEC);
+ break;
+ }
+
+ return 0;
+}
+
+/* stop caching firmware once syscore_suspend is reached */
+static int fw_suspend(void)
+{
+ fw_cache.state = FW_LOADER_NO_CACHE;
+ return 0;
+}
+
+static struct syscore_ops fw_syscore_ops = {
+ .suspend = fw_suspend,
+};
+#else
+static int fw_cache_piggyback_on_request(const char *name)
+{
+ return 0;
+}
+#endif
+
+static void __init fw_cache_init(void)
+{
+ spin_lock_init(&fw_cache.lock);
+ INIT_LIST_HEAD(&fw_cache.head);
+ fw_cache.state = FW_LOADER_NO_CACHE;
+
+#ifdef CONFIG_PM_SLEEP
+ spin_lock_init(&fw_cache.name_lock);
+ INIT_LIST_HEAD(&fw_cache.fw_names);
+ fw_cache.cnt = 0;
+
+ init_waitqueue_head(&fw_cache.wait_queue);
+ INIT_DELAYED_WORK(&fw_cache.work,
+ device_uncache_fw_images_work);
+
+ fw_cache.pm_notify.notifier_call = fw_pm_notify;
+ register_pm_notifier(&fw_cache.pm_notify);
+
+ register_syscore_ops(&fw_syscore_ops);
+#endif
+}
+
static int __init firmware_class_init(void)
{
+ fw_cache_init();
return class_register(&firmware_class);
}
static void __exit firmware_class_exit(void)
{
+#ifdef CONFIG_PM_SLEEP
+ unregister_syscore_ops(&fw_syscore_ops);
+ unregister_pm_notifier(&fw_cache.pm_notify);
+#endif
class_unregister(&firmware_class);
}
@@ -726,3 +1354,5 @@ module_exit(firmware_class_exit);
EXPORT_SYMBOL(release_firmware);
EXPORT_SYMBOL(request_firmware);
EXPORT_SYMBOL(request_firmware_nowait);
+EXPORT_SYMBOL_GPL(cache_firmware);
+EXPORT_SYMBOL_GPL(uncache_firmware);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index a1a7225..ddeca14 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -20,9 +20,13 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/idr.h>
#include "base.h"
+/* For automatically allocated device IDs */
+static DEFINE_IDA(platform_devid_ida);
+
#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
driver))
@@ -99,6 +103,9 @@ struct resource *platform_get_resource_byname(struct platform_device *dev,
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
+ if (unlikely(!r->name))
+ continue;
+
if (type == resource_type(r) && !strcmp(r->name, name))
return r;
}
@@ -263,7 +270,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data);
*/
int platform_device_add(struct platform_device *pdev)
{
- int i, ret = 0;
+ int i, ret;
if (!pdev)
return -EINVAL;
@@ -273,10 +280,27 @@ int platform_device_add(struct platform_device *pdev)
pdev->dev.bus = &platform_bus_type;
- if (pdev->id != -1)
+ switch (pdev->id) {
+ default:
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
- else
+ break;
+ case PLATFORM_DEVID_NONE:
dev_set_name(&pdev->dev, "%s", pdev->name);
+ break;
+ case PLATFORM_DEVID_AUTO:
+ /*
+ * Automatically allocated device ID. We mark it as such so
+ * that we remember it must be freed, and we append a suffix
+ * to avoid namespace collision with explicit IDs.
+ */
+ ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
+ if (ret < 0)
+ goto err_out;
+ pdev->id = ret;
+ pdev->id_auto = true;
+ dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
+ break;
+ }
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
@@ -309,6 +333,11 @@ int platform_device_add(struct platform_device *pdev)
return ret;
failed:
+ if (pdev->id_auto) {
+ ida_simple_remove(&platform_devid_ida, pdev->id);
+ pdev->id = PLATFORM_DEVID_AUTO;
+ }
+
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
@@ -317,6 +346,7 @@ int platform_device_add(struct platform_device *pdev)
release_resource(r);
}
+ err_out:
return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);
@@ -336,6 +366,11 @@ void platform_device_del(struct platform_device *pdev)
if (pdev) {
device_del(&pdev->dev);
+ if (pdev->id_auto) {
+ ida_simple_remove(&platform_devid_ida, pdev->id);
+ pdev->id = PLATFORM_DEVID_AUTO;
+ }
+
for (i = 0; i < pdev->num_resources; i++) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 0113adc..b0b072a 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -1324,3 +1324,25 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
return async_error;
}
EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
+
+/**
+ * dpm_for_each_dev - device iterator.
+ * @data: data for the callback.
+ * @fn: function to be called for each device.
+ *
+ * Iterate over devices in dpm_list, and call @fn for each device,
+ * passing it @data.
+ */
+void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
+{
+ struct device *dev;
+
+ if (!fn)
+ return;
+
+ device_pm_lock();
+ list_for_each_entry(dev, &dpm_list, power.entry)
+ fn(dev, data);
+ device_pm_unlock();
+}
+EXPORT_SYMBOL_GPL(dpm_for_each_dev);
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index a897346..5b6b1d8e 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -16,12 +16,14 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "internal.h"
struct regmap_irq_chip_data {
struct mutex lock;
+ struct irq_chip irq_chip;
struct regmap *map;
const struct regmap_irq_chip *chip;
@@ -59,6 +61,14 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
struct regmap *map = d->map;
int i, ret;
+ u32 reg;
+
+ if (d->chip->runtime_pm) {
+ ret = pm_runtime_get_sync(map->dev);
+ if (ret < 0)
+ dev_err(map->dev, "IRQ sync failed to resume: %d\n",
+ ret);
+ }
/*
* If there's been a change in the mask write it back to the
@@ -66,15 +76,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* suppress pointless writes.
*/
for (i = 0; i < d->chip->num_regs; i++) {
- ret = regmap_update_bits(d->map, d->chip->mask_base +
- (i * map->reg_stride *
- d->irq_reg_stride),
+ reg = d->chip->mask_base +
+ (i * map->reg_stride * d->irq_reg_stride);
+ if (d->chip->mask_invert)
+ ret = regmap_update_bits(d->map, reg,
+ d->mask_buf_def[i], ~d->mask_buf[i]);
+ else
+ ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf[i]);
if (ret != 0)
dev_err(d->map->dev, "Failed to sync masks in %x\n",
- d->chip->mask_base + (i * map->reg_stride));
+ reg);
}
+ if (d->chip->runtime_pm)
+ pm_runtime_put(map->dev);
+
/* If we've changed our wakeup count propagate it to the parent */
if (d->wake_count < 0)
for (i = d->wake_count; i < 0; i++)
@@ -128,8 +145,7 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
return 0;
}
-static struct irq_chip regmap_irq_chip = {
- .name = "regmap",
+static const struct irq_chip regmap_irq_chip = {
.irq_bus_lock = regmap_irq_lock,
.irq_bus_sync_unlock = regmap_irq_sync_unlock,
.irq_disable = regmap_irq_disable,
@@ -144,6 +160,16 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
struct regmap *map = data->map;
int ret, i;
bool handled = false;
+ u32 reg;
+
+ if (chip->runtime_pm) {
+ ret = pm_runtime_get_sync(map->dev);
+ if (ret < 0) {
+ dev_err(map->dev, "IRQ thread failed to resume: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+ }
/*
* Ignore masked IRQs and ack if we need to; we ack early so
@@ -160,20 +186,20 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n",
ret);
+ if (chip->runtime_pm)
+ pm_runtime_put(map->dev);
return IRQ_NONE;
}
data->status_buf[i] &= ~data->mask_buf[i];
if (data->status_buf[i] && chip->ack_base) {
- ret = regmap_write(map, chip->ack_base +
- (i * map->reg_stride *
- data->irq_reg_stride),
- data->status_buf[i]);
+ reg = chip->ack_base +
+ (i * map->reg_stride * data->irq_reg_stride);
+ ret = regmap_write(map, reg, data->status_buf[i]);
if (ret != 0)
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
- chip->ack_base + (i * map->reg_stride),
- ret);
+ reg, ret);
}
}
@@ -185,6 +211,9 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
}
}
+ if (chip->runtime_pm)
+ pm_runtime_put(map->dev);
+
if (handled)
return IRQ_HANDLED;
else
@@ -197,7 +226,7 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
struct regmap_irq_chip_data *data = h->host_data;
irq_set_chip_data(virq, data);
- irq_set_chip_and_handler(virq, &regmap_irq_chip, handle_edge_irq);
+ irq_set_chip(virq, &data->irq_chip);
irq_set_nested_thread(virq, 1);
/* ARM needs us to explicitly flag the IRQ as valid
@@ -238,6 +267,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
struct regmap_irq_chip_data *d;
int i;
int ret = -ENOMEM;
+ u32 reg;
for (i = 0; i < chip->num_irqs; i++) {
if (chip->irqs[i].reg_offset % map->reg_stride)
@@ -284,6 +314,13 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
goto err_alloc;
}
+ d->irq_chip = regmap_irq_chip;
+ d->irq_chip.name = chip->name;
+ if (!chip->wake_base) {
+ d->irq_chip.irq_set_wake = NULL;
+ d->irq_chip.flags |= IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SKIP_SET_WAKE;
+ }
d->irq = irq;
d->map = map;
d->chip = chip;
@@ -303,16 +340,37 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
/* Mask all the interrupts by default */
for (i = 0; i < chip->num_regs; i++) {
d->mask_buf[i] = d->mask_buf_def[i];
- ret = regmap_write(map, chip->mask_base + (i * map->reg_stride
- * d->irq_reg_stride),
- d->mask_buf[i]);
+ reg = chip->mask_base +
+ (i * map->reg_stride * d->irq_reg_stride);
+ if (chip->mask_invert)
+ ret = regmap_update_bits(map, reg,
+ d->mask_buf[i], ~d->mask_buf[i]);
+ else
+ ret = regmap_update_bits(map, reg,
+ d->mask_buf[i], d->mask_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
- chip->mask_base + (i * map->reg_stride), ret);
+ reg, ret);
goto err_alloc;
}
}
+ /* Wake is disabled by default */
+ if (d->wake_buf) {
+ for (i = 0; i < chip->num_regs; i++) {
+ d->wake_buf[i] = d->mask_buf_def[i];
+ reg = chip->wake_base +
+ (i * map->reg_stride * d->irq_reg_stride);
+ ret = regmap_update_bits(map, reg, d->wake_buf[i],
+ d->wake_buf[i]);
+ if (ret != 0) {
+ dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
+ reg, ret);
+ goto err_alloc;
+ }
+ }
+ }
+
if (irq_base)
d->domain = irq_domain_add_legacy(map->dev->of_node,
chip->num_irqs, irq_base, 0,
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index c241ae2..52069d2 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -659,13 +659,12 @@ EXPORT_SYMBOL_GPL(devm_regmap_init);
* new cache. This can be used to restore the cache to defaults or to
* update the cache configuration to reflect runtime discovery of the
* hardware.
+ *
+ * No explicit locking is done here, the user needs to ensure that
+ * this function will not race with other calls to regmap.
*/
int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
{
- int ret;
-
- map->lock(map);
-
regcache_exit(map);
regmap_debugfs_exit(map);
@@ -681,11 +680,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
map->cache_bypass = false;
map->cache_only = false;
- ret = regcache_init(map, config);
-
- map->unlock(map);
-
- return ret;
+ return regcache_init(map, config);
}
EXPORT_SYMBOL_GPL(regmap_reinit_cache);
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index de0435e..887f68f 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -35,6 +35,7 @@ new_skb(ulong len)
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
skb->protocol = __constant_htons(ETH_P_AOE);
+ skb_checksum_none_assert(skb);
}
return skb;
}
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index 38aa6dd..da33111 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -795,6 +795,7 @@ static void complete_scsi_command(CommandList_struct *c, int timeout,
}
break;
case CMD_PROTOCOL_ERR:
+ cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev,
"%p has protocol error\n", c);
break;
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index a8fddeb..f946d31 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -1148,11 +1148,15 @@ static bool mtip_pause_ncq(struct mtip_port *port,
reply = port->rxfis + RX_FIS_D2H_REG;
task_file_data = readl(port->mmio+PORT_TFDATA);
- if ((task_file_data & 1) || (fis->command == ATA_CMD_SEC_ERASE_UNIT))
+ if (fis->command == ATA_CMD_SEC_ERASE_UNIT)
+ clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
+
+ if ((task_file_data & 1))
return false;
if (fis->command == ATA_CMD_SEC_ERASE_PREP) {
set_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
+ set_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
port->ic_pause_timer = jiffies;
return true;
} else if ((fis->command == ATA_CMD_DOWNLOAD_MICRO) &&
@@ -1900,7 +1904,7 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
int rv = 0, xfer_sz = command[3];
if (xfer_sz) {
- if (user_buffer)
+ if (!user_buffer)
return -EFAULT;
buf = dmam_alloc_coherent(&port->dd->pdev->dev,
@@ -2043,7 +2047,7 @@ static void mtip_set_timeout(struct host_to_dev_fis *fis, unsigned int *timeout)
*timeout = 240000; /* 4 minutes */
break;
case ATA_CMD_STANDBYNOW1:
- *timeout = 10000; /* 10 seconds */
+ *timeout = 120000; /* 2 minutes */
break;
case 0xF7:
case 0xFA:
@@ -2588,9 +2592,6 @@ static ssize_t mtip_hw_read_registers(struct file *f, char __user *ubuf,
if (!len || size)
return 0;
- if (size < 0)
- return -EINVAL;
-
size += sprintf(&buf[size], "H/ S ACTive : [ 0x");
for (n = dd->slot_groups-1; n >= 0; n--)
@@ -2660,9 +2661,6 @@ static ssize_t mtip_hw_read_flags(struct file *f, char __user *ubuf,
if (!len || size)
return 0;
- if (size < 0)
- return -EINVAL;
-
size += sprintf(&buf[size], "Flag-port : [ %08lX ]\n",
dd->port->flags);
size += sprintf(&buf[size], "Flag-dd : [ %08lX ]\n",
@@ -3214,8 +3212,8 @@ static int mtip_hw_init(struct driver_data *dd)
"Unable to check write protect progress\n");
else
dev_info(&dd->pdev->dev,
- "Write protect progress: %d%% (%d blocks)\n",
- attr242.cur, attr242.data);
+ "Write protect progress: %u%% (%u blocks)\n",
+ attr242.cur, le32_to_cpu(attr242.data));
return rv;
out3:
@@ -3619,6 +3617,10 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio)
bio_endio(bio, -ENODATA);
return;
}
+ if (unlikely(test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag))) {
+ bio_endio(bio, -ENODATA);
+ return;
+ }
}
if (unlikely(!bio_has_data(bio))) {
@@ -4168,7 +4170,13 @@ static void mtip_pci_shutdown(struct pci_dev *pdev)
/* Table of device ids supported by this driver. */
static DEFINE_PCI_DEVICE_TABLE(mtip_pci_tbl) = {
- { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320H_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320M_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320S_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P325M_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420H_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420M_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P425M_DEVICE_ID) },
{ 0 }
};
@@ -4199,12 +4207,12 @@ static int __init mtip_init(void)
{
int error;
- printk(KERN_INFO MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n");
+ pr_info(MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n");
/* Allocate a major block device number to use with this driver. */
error = register_blkdev(0, MTIP_DRV_NAME);
if (error <= 0) {
- printk(KERN_ERR "Unable to register block device (%d)\n",
+ pr_err("Unable to register block device (%d)\n",
error);
return -EBUSY;
}
@@ -4213,7 +4221,7 @@ static int __init mtip_init(void)
if (!dfs_parent) {
dfs_parent = debugfs_create_dir("rssd", NULL);
if (IS_ERR_OR_NULL(dfs_parent)) {
- printk(KERN_WARNING "Error creating debugfs parent\n");
+ pr_warn("Error creating debugfs parent\n");
dfs_parent = NULL;
}
}
diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h
index f51fc23..18627a1 100644
--- a/drivers/block/mtip32xx/mtip32xx.h
+++ b/drivers/block/mtip32xx/mtip32xx.h
@@ -76,7 +76,13 @@
/* Micron Vendor ID & P320x SSD Device ID */
#define PCI_VENDOR_ID_MICRON 0x1344
-#define P320_DEVICE_ID 0x5150
+#define P320H_DEVICE_ID 0x5150
+#define P320M_DEVICE_ID 0x5151
+#define P320S_DEVICE_ID 0x5152
+#define P325M_DEVICE_ID 0x5153
+#define P420H_DEVICE_ID 0x5160
+#define P420M_DEVICE_ID 0x5161
+#define P425M_DEVICE_ID 0x5163
/* Driver name and version strings */
#define MTIP_DRV_NAME "mtip32xx"
@@ -131,10 +137,12 @@ enum {
MTIP_PF_SVC_THD_STOP_BIT = 8,
/* below are bit numbers in 'dd_flag' defined in driver_data */
+ MTIP_DDF_SEC_LOCK_BIT = 0,
MTIP_DDF_REMOVE_PENDING_BIT = 1,
MTIP_DDF_OVER_TEMP_BIT = 2,
MTIP_DDF_WRITE_PROTECT_BIT = 3,
MTIP_DDF_STOP_IO = ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \
+ (1 << MTIP_DDF_SEC_LOCK_BIT) | \
(1 << MTIP_DDF_OVER_TEMP_BIT) | \
(1 << MTIP_DDF_WRITE_PROTECT_BIT)),
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index d07c9f7..0c03411 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -449,6 +449,14 @@ static void nbd_clear_que(struct nbd_device *nbd)
req->errors++;
nbd_end_request(req);
}
+
+ while (!list_empty(&nbd->waiting_queue)) {
+ req = list_entry(nbd->waiting_queue.next, struct request,
+ queuelist);
+ list_del_init(&req->queuelist);
+ req->errors++;
+ nbd_end_request(req);
+ }
}
@@ -598,6 +606,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
nbd->file = NULL;
nbd_clear_que(nbd);
BUG_ON(!list_empty(&nbd->queue_head));
+ BUG_ON(!list_empty(&nbd->waiting_queue));
if (file)
fput(file);
return 0;
diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c
index 38a2d06..931769e 100644
--- a/drivers/block/nvme.c
+++ b/drivers/block/nvme.c
@@ -79,6 +79,7 @@ struct nvme_dev {
char serial[20];
char model[40];
char firmware_rev[8];
+ u32 max_hw_sectors;
};
/*
@@ -835,15 +836,15 @@ static int nvme_identify(struct nvme_dev *dev, unsigned nsid, unsigned cns,
}
static int nvme_get_features(struct nvme_dev *dev, unsigned fid,
- unsigned dword11, dma_addr_t dma_addr)
+ unsigned nsid, dma_addr_t dma_addr)
{
struct nvme_command c;
memset(&c, 0, sizeof(c));
c.features.opcode = nvme_admin_get_features;
+ c.features.nsid = cpu_to_le32(nsid);
c.features.prp1 = cpu_to_le64(dma_addr);
c.features.fid = cpu_to_le32(fid);
- c.features.dword11 = cpu_to_le32(dword11);
return nvme_submit_admin_cmd(dev, &c, NULL);
}
@@ -862,11 +863,51 @@ static int nvme_set_features(struct nvme_dev *dev, unsigned fid,
return nvme_submit_admin_cmd(dev, &c, result);
}
+/**
+ * nvme_cancel_ios - Cancel outstanding I/Os
+ * @queue: The queue to cancel I/Os on
+ * @timeout: True to only cancel I/Os which have timed out
+ */
+static void nvme_cancel_ios(struct nvme_queue *nvmeq, bool timeout)
+{
+ int depth = nvmeq->q_depth - 1;
+ struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
+ unsigned long now = jiffies;
+ int cmdid;
+
+ for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) {
+ void *ctx;
+ nvme_completion_fn fn;
+ static struct nvme_completion cqe = {
+ .status = cpu_to_le16(NVME_SC_ABORT_REQ) << 1,
+ };
+
+ if (timeout && !time_after(now, info[cmdid].timeout))
+ continue;
+ dev_warn(nvmeq->q_dmadev, "Cancelling I/O %d\n", cmdid);
+ ctx = cancel_cmdid(nvmeq, cmdid, &fn);
+ fn(nvmeq->dev, ctx, &cqe);
+ }
+}
+
+static void nvme_free_queue_mem(struct nvme_queue *nvmeq)
+{
+ 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);
+ kfree(nvmeq);
+}
+
static void nvme_free_queue(struct nvme_dev *dev, int qid)
{
struct nvme_queue *nvmeq = dev->queues[qid];
int vector = dev->entry[nvmeq->cq_vector].vector;
+ spin_lock_irq(&nvmeq->q_lock);
+ nvme_cancel_ios(nvmeq, false);
+ spin_unlock_irq(&nvmeq->q_lock);
+
irq_set_affinity_hint(vector, NULL);
free_irq(vector, nvmeq);
@@ -876,18 +917,15 @@ static void nvme_free_queue(struct nvme_dev *dev, int qid)
adapter_delete_cq(dev, qid);
}
- 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);
- kfree(nvmeq);
+ nvme_free_queue_mem(nvmeq);
}
static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
int depth, int vector)
{
struct device *dmadev = &dev->pci_dev->dev;
- unsigned extra = (depth / 8) + (depth * sizeof(struct nvme_cmd_info));
+ unsigned extra = DIV_ROUND_UP(depth, 8) + (depth *
+ sizeof(struct nvme_cmd_info));
struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq) + extra, GFP_KERNEL);
if (!nvmeq)
return NULL;
@@ -975,7 +1013,7 @@ static __devinit struct nvme_queue *nvme_create_queue(struct nvme_dev *dev,
static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev)
{
- int result;
+ int result = 0;
u32 aqa;
u64 cap;
unsigned long timeout;
@@ -1005,17 +1043,22 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev)
timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies;
dev->db_stride = NVME_CAP_STRIDE(cap);
- while (!(readl(&dev->bar->csts) & NVME_CSTS_RDY)) {
+ while (!result && !(readl(&dev->bar->csts) & NVME_CSTS_RDY)) {
msleep(100);
if (fatal_signal_pending(current))
- return -EINTR;
+ result = -EINTR;
if (time_after(jiffies, timeout)) {
dev_err(&dev->pci_dev->dev,
"Device not ready; aborting initialisation\n");
- return -ENODEV;
+ result = -ENODEV;
}
}
+ if (result) {
+ nvme_free_queue_mem(nvmeq);
+ return result;
+ }
+
result = queue_request_irq(dev, nvmeq, "nvme admin");
dev->queues[0] = nvmeq;
return result;
@@ -1037,6 +1080,8 @@ static struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write,
offset = offset_in_page(addr);
count = DIV_ROUND_UP(offset + length, PAGE_SIZE);
pages = kcalloc(count, sizeof(*pages), GFP_KERNEL);
+ if (!pages)
+ return ERR_PTR(-ENOMEM);
err = get_user_pages_fast(addr, count, 1, pages);
if (err < count) {
@@ -1146,14 +1191,13 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
return status;
}
-static int nvme_user_admin_cmd(struct nvme_ns *ns,
+static int nvme_user_admin_cmd(struct nvme_dev *dev,
struct nvme_admin_cmd __user *ucmd)
{
- struct nvme_dev *dev = ns->dev;
struct nvme_admin_cmd cmd;
struct nvme_command c;
int status, length;
- struct nvme_iod *iod;
+ struct nvme_iod *uninitialized_var(iod);
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
@@ -1204,7 +1248,7 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
case NVME_IOCTL_ID:
return ns->ns_id;
case NVME_IOCTL_ADMIN_CMD:
- return nvme_user_admin_cmd(ns, (void __user *)arg);
+ return nvme_user_admin_cmd(ns->dev, (void __user *)arg);
case NVME_IOCTL_SUBMIT_IO:
return nvme_submit_io(ns, (void __user *)arg);
default:
@@ -1218,26 +1262,6 @@ static const struct block_device_operations nvme_fops = {
.compat_ioctl = nvme_ioctl,
};
-static void nvme_timeout_ios(struct nvme_queue *nvmeq)
-{
- int depth = nvmeq->q_depth - 1;
- struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
- unsigned long now = jiffies;
- int cmdid;
-
- for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) {
- void *ctx;
- nvme_completion_fn fn;
- static struct nvme_completion cqe = { .status = cpu_to_le16(NVME_SC_ABORT_REQ) << 1, };
-
- if (!time_after(now, info[cmdid].timeout))
- continue;
- dev_warn(nvmeq->q_dmadev, "Timing out I/O %d\n", cmdid);
- ctx = cancel_cmdid(nvmeq, cmdid, &fn);
- fn(nvmeq->dev, ctx, &cqe);
- }
-}
-
static void nvme_resubmit_bios(struct nvme_queue *nvmeq)
{
while (bio_list_peek(&nvmeq->sq_cong)) {
@@ -1269,7 +1293,7 @@ static int nvme_kthread(void *data)
spin_lock_irq(&nvmeq->q_lock);
if (nvme_process_cq(nvmeq))
printk("process_cq did something\n");
- nvme_timeout_ios(nvmeq);
+ nvme_cancel_ios(nvmeq, true);
nvme_resubmit_bios(nvmeq);
spin_unlock_irq(&nvmeq->q_lock);
}
@@ -1339,6 +1363,9 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int nsid,
ns->disk = disk;
lbaf = id->flbas & 0xf;
ns->lba_shift = id->lbaf[lbaf].ds;
+ blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift);
+ if (dev->max_hw_sectors)
+ blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors);
disk->major = nvme_major;
disk->minors = NVME_MINORS;
@@ -1383,7 +1410,7 @@ static int set_queue_count(struct nvme_dev *dev, int count)
static int __devinit nvme_setup_io_queues(struct nvme_dev *dev)
{
- int result, cpu, i, nr_io_queues, db_bar_size;
+ int result, cpu, i, nr_io_queues, db_bar_size, q_depth;
nr_io_queues = num_online_cpus();
result = set_queue_count(dev, nr_io_queues);
@@ -1429,9 +1456,10 @@ static int __devinit nvme_setup_io_queues(struct nvme_dev *dev)
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 = 0; i < nr_io_queues; i++) {
- dev->queues[i + 1] = nvme_create_queue(dev, i + 1,
- NVME_Q_DEPTH, i);
+ dev->queues[i + 1] = nvme_create_queue(dev, i + 1, q_depth, i);
if (IS_ERR(dev->queues[i + 1]))
return PTR_ERR(dev->queues[i + 1]);
dev->queue_count++;
@@ -1480,6 +1508,10 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev)
memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn));
memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn));
memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr));
+ if (ctrl->mdts) {
+ int shift = NVME_CAP_MPSMIN(readq(&dev->bar->cap)) + 12;
+ dev->max_hw_sectors = 1 << (ctrl->mdts + shift - 9);
+ }
id_ns = mem;
for (i = 1; i <= nn; i++) {
@@ -1523,8 +1555,6 @@ static int nvme_dev_remove(struct nvme_dev *dev)
list_del(&dev->node);
spin_unlock(&dev_list_lock);
- /* TODO: wait all I/O finished or cancel them */
-
list_for_each_entry_safe(ns, next, &dev->namespaces, list) {
list_del(&ns->list);
del_gendisk(ns->disk);
@@ -1560,15 +1590,33 @@ static void nvme_release_prp_pools(struct nvme_dev *dev)
dma_pool_destroy(dev->prp_small_pool);
}
-/* XXX: Use an ida or something to let remove / add work correctly */
-static void nvme_set_instance(struct nvme_dev *dev)
+static DEFINE_IDA(nvme_instance_ida);
+
+static int nvme_set_instance(struct nvme_dev *dev)
{
- static int instance;
- dev->instance = instance++;
+ int instance, error;
+
+ do {
+ if (!ida_pre_get(&nvme_instance_ida, GFP_KERNEL))
+ return -ENODEV;
+
+ spin_lock(&dev_list_lock);
+ error = ida_get_new(&nvme_instance_ida, &instance);
+ spin_unlock(&dev_list_lock);
+ } while (error == -EAGAIN);
+
+ if (error)
+ return -ENODEV;
+
+ dev->instance = instance;
+ return 0;
}
static void nvme_release_instance(struct nvme_dev *dev)
{
+ spin_lock(&dev_list_lock);
+ ida_remove(&nvme_instance_ida, dev->instance);
+ spin_unlock(&dev_list_lock);
}
static int __devinit nvme_probe(struct pci_dev *pdev,
@@ -1601,7 +1649,10 @@ static int __devinit nvme_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, dev);
dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
- nvme_set_instance(dev);
+ result = nvme_set_instance(dev);
+ if (result)
+ goto disable;
+
dev->entry[0].vector = pdev->irq;
result = nvme_setup_prp_pools(dev);
@@ -1675,7 +1726,7 @@ static void __devexit nvme_remove(struct pci_dev *pdev)
#define nvme_suspend NULL
#define nvme_resume NULL
-static struct pci_error_handlers nvme_err_handler = {
+static const struct pci_error_handlers nvme_err_handler = {
.error_detected = nvme_error_detected,
.mmio_enabled = nvme_dump_registers,
.link_reset = nvme_link_reset,
@@ -1704,15 +1755,17 @@ static struct pci_driver nvme_driver = {
static int __init nvme_init(void)
{
- int result = -EBUSY;
+ int result;
nvme_thread = kthread_run(nvme_kthread, NULL, "nvme");
if (IS_ERR(nvme_thread))
return PTR_ERR(nvme_thread);
- nvme_major = register_blkdev(nvme_major, "nvme");
- if (nvme_major <= 0)
+ result = register_blkdev(nvme_major, "nvme");
+ if (result < 0)
goto kill_kthread;
+ else if (result > 0)
+ nvme_major = result;
result = pci_register_driver(&nvme_driver);
if (result)
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 9917943..54a55f0 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -246,13 +246,12 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
{
struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
- rbd_get_dev(rbd_dev);
-
- set_device_ro(bdev, rbd_dev->read_only);
-
if ((mode & FMODE_WRITE) && rbd_dev->read_only)
return -EROFS;
+ rbd_get_dev(rbd_dev);
+ set_device_ro(bdev, rbd_dev->read_only);
+
return 0;
}
diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c
index 73f196c..c6decb9 100644
--- a/drivers/block/xen-blkback/blkback.c
+++ b/drivers/block/xen-blkback/blkback.c
@@ -337,7 +337,7 @@ static void xen_blkbk_unmap(struct pending_req *req)
invcount++;
}
- ret = gnttab_unmap_refs(unmap, pages, invcount, false);
+ ret = gnttab_unmap_refs(unmap, NULL, pages, invcount);
BUG_ON(ret);
}
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index ea6f632..72bedad 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -418,8 +418,8 @@ config APPLICOM
If unsure, say N.
config SONYPI
- tristate "Sony Vaio Programmable I/O Control Device support (EXPERIMENTAL)"
- depends on EXPERIMENTAL && X86 && PCI && INPUT && !64BIT
+ tristate "Sony Vaio Programmable I/O Control Device support"
+ depends on X86 && PCI && INPUT && !64BIT
---help---
This driver enables access to the Sony Programmable I/O Control
Device which can be found in many (all ?) Sony Vaio laptops.
@@ -566,7 +566,7 @@ source "drivers/char/tpm/Kconfig"
config TELCLOCK
tristate "Telecom clock driver for ATCA SBC"
- depends on EXPERIMENTAL && X86
+ depends on X86
default n
help
The telecom clock device is specific to the MPCBL0010 and MPCBL0050
diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c
index 1920003..3a5af2f 100644
--- a/drivers/char/agp/sgi-agp.c
+++ b/drivers/char/agp/sgi-agp.c
@@ -289,12 +289,11 @@ static int __devinit agp_sgi_init(void)
j = 0;
list_for_each_entry(info, &tioca_list, ca_list) {
- struct list_head *tmp;
if (list_empty(info->ca_devices))
continue;
- list_for_each(tmp, info->ca_devices) {
+ list_for_each_entry(pdev, info->ca_devices, bus_list) {
u8 cap_ptr;
- pdev = pci_dev_b(tmp);
+
if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8))
continue;
cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c
index 33dc229..3d6c067 100644
--- a/drivers/char/mmtimer.c
+++ b/drivers/char/mmtimer.c
@@ -826,7 +826,7 @@ static int __init mmtimer_init(void)
/* Allocate list of node ptrs to mmtimer_t's */
timers = kzalloc(sizeof(struct mmtimer_node)*maxn, GFP_KERNEL);
- if (timers == NULL) {
+ if (!timers) {
printk(KERN_ERR "%s: failed to allocate memory for device\n",
MMTIMER_NAME);
goto out3;
@@ -848,7 +848,6 @@ static int __init mmtimer_init(void)
return 0;
out3:
- kfree(timers);
misc_deregister(&mmtimer_miscdev);
out2:
free_irq(SGI_MMTIMER_VECTOR, NULL);
diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c
index 04a480f..cfdfe49 100644
--- a/drivers/char/nwbutton.c
+++ b/drivers/char/nwbutton.c
@@ -93,9 +93,9 @@ int button_del_callback (void (*callback) (void))
button_callback_list [lp].count = 0;
callback_count--;
return 0;
- };
+ }
lp--;
- };
+ }
return -EINVAL;
}
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index 0a484b4..a6b8dde 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -891,6 +891,14 @@ static void rx_ready_async(MGSLPC_INFO *info, int tcd, struct tty_struct *tty)
int work = 0;
struct mgsl_icount *icount = &info->icount;
+ if (!tty) {
+ /* tty is not available anymore */
+ issue_command(info, CHA, CMD_RXRESET);
+ if (debug_level >= DEBUG_LEVEL_ISR)
+ printk("%s(%d):rx_ready_async(tty=NULL)\n",__FILE__,__LINE__);
+ return;
+ }
+
if (tcd) {
/* early termination, get FIFO count from RBCL register */
fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f);
@@ -980,7 +988,7 @@ static void tx_done(MGSLPC_INFO *info, struct tty_struct *tty)
else
#endif
{
- if (tty->stopped || tty->hw_stopped) {
+ if (tty && (tty->stopped || tty->hw_stopped)) {
tx_stop(info);
return;
}
@@ -1000,7 +1008,7 @@ static void tx_ready(MGSLPC_INFO *info, struct tty_struct *tty)
if (!info->tx_active)
return;
} else {
- if (tty->stopped || tty->hw_stopped) {
+ if (tty && (tty->stopped || tty->hw_stopped)) {
tx_stop(info);
return;
}
@@ -1050,13 +1058,12 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty)
wake_up_interruptible(&info->status_event_wait_q);
wake_up_interruptible(&info->event_wait_q);
- if (info->port.flags & ASYNC_CTS_FLOW) {
+ if (tty && (info->port.flags & ASYNC_CTS_FLOW)) {
if (tty->hw_stopped) {
if (info->serial_signals & SerialSignal_CTS) {
if (debug_level >= DEBUG_LEVEL_ISR)
printk("CTS tx start...");
- if (tty)
- tty->hw_stopped = 0;
+ tty->hw_stopped = 0;
tx_start(info, tty);
info->pending_bh |= BH_TRANSMIT;
return;
@@ -1065,8 +1072,7 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty)
if (!(info->serial_signals & SerialSignal_CTS)) {
if (debug_level >= DEBUG_LEVEL_ISR)
printk("CTS tx stop...");
- if (tty)
- tty->hw_stopped = 1;
+ tty->hw_stopped = 1;
tx_stop(info);
}
}
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 3fcf80f..d0d824e 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -783,7 +783,8 @@ static int __init ppdev_init (void)
err = PTR_ERR(ppdev_class);
goto out_chrdev;
}
- if (parport_register_driver(&pp_driver)) {
+ err = parport_register_driver(&pp_driver);
+ if (err < 0) {
printk (KERN_WARNING CHRDEV ": unable to register with parport\n");
goto out_class;
}
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index af94374..91470fd 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -411,7 +411,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
case RTC_IRQP_READ:
case RTC_IRQP_SET:
return -EINVAL;
- };
+ }
}
#endif
diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c
index ce29e7c..e95e0ab 100644
--- a/drivers/char/tlclk.c
+++ b/drivers/char/tlclk.c
@@ -784,8 +784,10 @@ static int __init tlclk_init(void)
}
tlclk_major = ret;
alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL);
- if (!alarm_events)
+ if (!alarm_events) {
+ ret = -ENOMEM;
goto out1;
+ }
/* Read telecom clock IRQ number (Set by BIOS) */
if (!request_region(TLCLK_BASE, 8, "telco_clock")) {
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index cdf2f54..060a672 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1941,7 +1941,17 @@ static int __init init(void)
INIT_LIST_HEAD(&pdrvdata.consoles);
INIT_LIST_HEAD(&pdrvdata.portdevs);
- return register_virtio_driver(&virtio_console);
+ err = register_virtio_driver(&virtio_console);
+ if (err < 0) {
+ pr_err("Error %d registering virtio driver\n", err);
+ goto free;
+ }
+ return 0;
+free:
+ if (pdrvdata.debugfs_dir)
+ debugfs_remove_recursive(pdrvdata.debugfs_dir);
+ class_destroy(pdrvdata.class);
+ return err;
}
static void __exit fini(void)
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 7f0b5ca..bace9e9 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -40,4 +40,17 @@ config COMMON_CLK_WM831X
Supports the clocking subsystem of the WM831x/2x series of
PMICs from Wolfson Microlectronics.
+config COMMON_CLK_VERSATILE
+ bool "Clock driver for ARM Reference designs"
+ depends on ARCH_INTEGRATOR || ARCH_REALVIEW
+ ---help---
+ Supports clocking on ARM Reference designs Integrator/AP,
+ Integrator/CP, RealView PB1176, EB, PB11MP and PBX.
+
+config COMMON_CLK_MAX77686
+ tristate "Clock driver for Maxim 77686 MFD"
+ depends on MFD_MAX77686
+ ---help---
+ This driver supports Maxim 77686 crystal oscillator clock.
+
endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 5869ea3..b7b8620 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,4 +1,5 @@
# common clock types
+obj-$(CONFIG_HAVE_CLK) += clk-devres.o
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \
clk-mux.o clk-divider.o clk-fixed-factor.o
@@ -9,7 +10,14 @@ obj-$(CONFIG_ARCH_MXS) += mxs/
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-$(CONFIG_ARCH_U300) += clk-u300.o
-obj-$(CONFIG_ARCH_INTEGRATOR) += versatile/
+obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
+obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o
+ifeq ($(CONFIG_COMMON_CLK), y)
+obj-$(CONFIG_ARCH_MMP) += mmp/
+endif
+obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
+obj-$(CONFIG_ARCH_U8500) += ux500/
# Chip specific
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
+obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
new file mode 100644
index 0000000..8f57154
--- /dev/null
+++ b/drivers/clk/clk-devres.c
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+
+static void devm_clk_release(struct device *dev, void *res)
+{
+ clk_put(*(struct clk **)res);
+}
+
+struct clk *devm_clk_get(struct device *dev, const char *id)
+{
+ struct clk **ptr, *clk;
+
+ ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ clk = clk_get(dev, id);
+ if (!IS_ERR(clk)) {
+ *ptr = clk;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return clk;
+}
+EXPORT_SYMBOL(devm_clk_get);
+
+static int devm_clk_match(struct device *dev, void *res, void *data)
+{
+ struct clk **c = res;
+ if (!c || !*c) {
+ WARN_ON(!c || !*c);
+ return 0;
+ }
+ return *c == data;
+}
+
+void devm_clk_put(struct device *dev, struct clk *clk)
+{
+ int ret;
+
+ ret = devres_release(dev, devm_clk_release, devm_clk_match, clk);
+
+ WARN_ON(ret);
+}
+EXPORT_SYMBOL(devm_clk_put);
diff --git a/drivers/clk/clk-ls1x.c b/drivers/clk/clk-ls1x.c
new file mode 100644
index 0000000..f20b750
--- /dev/null
+++ b/drivers/clk/clk-ls1x.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <loongson1.h>
+
+#define OSC 33
+
+static DEFINE_SPINLOCK(_lock);
+
+static int ls1x_pll_clk_enable(struct clk_hw *hw)
+{
+ return 0;
+}
+
+static void ls1x_pll_clk_disable(struct clk_hw *hw)
+{
+}
+
+static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u32 pll, rate;
+
+ pll = __raw_readl(LS1X_CLK_PLL_FREQ);
+ rate = ((12 + (pll & 0x3f)) * 1000000) +
+ ((((pll >> 8) & 0x3ff) * 1000000) >> 10);
+ rate *= OSC;
+ rate >>= 1;
+
+ return rate;
+}
+
+static const struct clk_ops ls1x_pll_clk_ops = {
+ .enable = ls1x_pll_clk_enable,
+ .disable = ls1x_pll_clk_disable,
+ .recalc_rate = ls1x_pll_recalc_rate,
+};
+
+static struct clk * __init clk_register_pll(struct device *dev,
+ const char *name, const char *parent_name, unsigned long flags)
+{
+ struct clk_hw *hw;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /* allocate the divider */
+ hw = kzalloc(sizeof(struct clk_hw), GFP_KERNEL);
+ if (!hw) {
+ pr_err("%s: could not allocate clk_hw\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init.name = name;
+ init.ops = &ls1x_pll_clk_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+ hw->init = &init;
+
+ /* register the clock */
+ clk = clk_register(dev, hw);
+
+ if (IS_ERR(clk))
+ kfree(hw);
+
+ return clk;
+}
+
+void __init ls1x_clk_init(void)
+{
+ struct clk *clk;
+
+ clk = clk_register_pll(NULL, "pll_clk", NULL, CLK_IS_ROOT);
+ clk_prepare_enable(clk);
+
+ clk = clk_register_divider(NULL, "cpu_clk", "pll_clk",
+ CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_CPU_SHIFT,
+ DIV_CPU_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
+ clk_prepare_enable(clk);
+ clk_register_clkdev(clk, "cpu", NULL);
+
+ clk = clk_register_divider(NULL, "dc_clk", "pll_clk",
+ CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
+ DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
+ clk_prepare_enable(clk);
+ clk_register_clkdev(clk, "dc", NULL);
+
+ clk = clk_register_divider(NULL, "ahb_clk", "pll_clk",
+ CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT,
+ DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
+ clk_prepare_enable(clk);
+ clk_register_clkdev(clk, "ahb", NULL);
+ clk_register_clkdev(clk, "stmmaceth", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1, 2);
+ clk_prepare_enable(clk);
+ clk_register_clkdev(clk, "apb", NULL);
+ clk_register_clkdev(clk, "serial8250", NULL);
+}
diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c
new file mode 100644
index 0000000..ac5f543
--- /dev/null
+++ b/drivers/clk/clk-max77686.c
@@ -0,0 +1,244 @@
+/*
+ * clk-max77686.c - Clock driver for Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electornics
+ * Jonghwa Lee <jonghwa3.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/clkdev.h>
+
+enum {
+ MAX77686_CLK_AP = 0,
+ MAX77686_CLK_CP,
+ MAX77686_CLK_PMIC,
+ MAX77686_CLKS_NUM,
+};
+
+struct max77686_clk {
+ struct max77686_dev *iodev;
+ u32 mask;
+ struct clk_hw hw;
+ struct clk_lookup *lookup;
+};
+
+static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
+{
+ return container_of(hw, struct max77686_clk, hw);
+}
+
+static int max77686_clk_prepare(struct clk_hw *hw)
+{
+ struct max77686_clk *max77686;
+ int ret;
+
+ max77686 = get_max77686_clk(hw);
+ if (!max77686)
+ return -ENOMEM;
+
+ ret = regmap_update_bits(max77686->iodev->regmap,
+ MAX77686_REG_32KHZ, max77686->mask, max77686->mask);
+
+ return ret;
+}
+
+static void max77686_clk_unprepare(struct clk_hw *hw)
+{
+ struct max77686_clk *max77686;
+
+ max77686 = get_max77686_clk(hw);
+ if (!max77686)
+ return;
+
+ regmap_update_bits(max77686->iodev->regmap,
+ MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
+}
+
+static int max77686_clk_is_enabled(struct clk_hw *hw)
+{
+ struct max77686_clk *max77686;
+ int ret;
+ u32 val;
+
+ max77686 = get_max77686_clk(hw);
+ if (!max77686)
+ return -ENOMEM;
+
+ ret = regmap_read(max77686->iodev->regmap,
+ MAX77686_REG_32KHZ, &val);
+
+ if (ret < 0)
+ return -EINVAL;
+
+ return val & max77686->mask;
+}
+
+static struct clk_ops max77686_clk_ops = {
+ .prepare = max77686_clk_prepare,
+ .unprepare = max77686_clk_unprepare,
+ .is_enabled = max77686_clk_is_enabled,
+};
+
+static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
+ [MAX77686_CLK_AP] = {
+ .name = "32khz_ap",
+ .ops = &max77686_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+ [MAX77686_CLK_CP] = {
+ .name = "32khz_cp",
+ .ops = &max77686_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+ [MAX77686_CLK_PMIC] = {
+ .name = "32khz_pmic",
+ .ops = &max77686_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+};
+
+static int max77686_clk_register(struct device *dev,
+ struct max77686_clk *max77686)
+{
+ struct clk *clk;
+ struct clk_hw *hw = &max77686->hw;
+
+ clk = clk_register(dev, hw);
+
+ if (IS_ERR(clk))
+ return -ENOMEM;
+
+ max77686->lookup = devm_kzalloc(dev, sizeof(struct clk_lookup),
+ GFP_KERNEL);
+ if (IS_ERR(max77686->lookup))
+ return -ENOMEM;
+
+ max77686->lookup->con_id = hw->init->name;
+ max77686->lookup->clk = clk;
+
+ clkdev_add(max77686->lookup);
+
+ return 0;
+}
+
+static __devinit int max77686_clk_probe(struct platform_device *pdev)
+{
+ struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct max77686_clk **max77686_clks;
+ int i, ret;
+
+ max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *)
+ * MAX77686_CLKS_NUM, GFP_KERNEL);
+ if (IS_ERR(max77686_clks))
+ return -ENOMEM;
+
+ for (i = 0; i < MAX77686_CLKS_NUM; i++) {
+ max77686_clks[i] = devm_kzalloc(&pdev->dev,
+ sizeof(struct max77686_clk), GFP_KERNEL);
+ if (IS_ERR(max77686_clks[i]))
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < MAX77686_CLKS_NUM; i++) {
+ max77686_clks[i]->iodev = iodev;
+ max77686_clks[i]->mask = 1 << i;
+ max77686_clks[i]->hw.init = &max77686_clks_init[i];
+
+ ret = max77686_clk_register(&pdev->dev, max77686_clks[i]);
+ if (ret) {
+ switch (i) {
+ case MAX77686_CLK_AP:
+ dev_err(&pdev->dev, "Fail to register CLK_AP\n");
+ goto err_clk_ap;
+ break;
+ case MAX77686_CLK_CP:
+ dev_err(&pdev->dev, "Fail to register CLK_CP\n");
+ goto err_clk_cp;
+ break;
+ case MAX77686_CLK_PMIC:
+ dev_err(&pdev->dev, "Fail to register CLK_PMIC\n");
+ goto err_clk_pmic;
+ }
+ }
+ }
+
+ platform_set_drvdata(pdev, max77686_clks);
+
+ goto out;
+
+err_clk_pmic:
+ clkdev_drop(max77686_clks[MAX77686_CLK_CP]->lookup);
+ kfree(max77686_clks[MAX77686_CLK_CP]->hw.clk);
+err_clk_cp:
+ clkdev_drop(max77686_clks[MAX77686_CLK_AP]->lookup);
+ kfree(max77686_clks[MAX77686_CLK_AP]->hw.clk);
+err_clk_ap:
+out:
+ return ret;
+}
+
+static int __devexit max77686_clk_remove(struct platform_device *pdev)
+{
+ struct max77686_clk **max77686_clks = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < MAX77686_CLKS_NUM; i++) {
+ clkdev_drop(max77686_clks[i]->lookup);
+ kfree(max77686_clks[i]->hw.clk);
+ }
+ return 0;
+}
+
+static const struct platform_device_id max77686_clk_id[] = {
+ { "max77686-clk", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(platform, max77686_clk_id);
+
+static struct platform_driver max77686_clk_driver = {
+ .driver = {
+ .name = "max77686-clk",
+ .owner = THIS_MODULE,
+ },
+ .probe = max77686_clk_probe,
+ .remove = __devexit_p(max77686_clk_remove),
+ .id_table = max77686_clk_id,
+};
+
+static int __init max77686_clk_init(void)
+{
+ return platform_driver_register(&max77686_clk_driver);
+}
+subsys_initcall(max77686_clk_init);
+
+static void __init max77686_clk_cleanup(void)
+{
+ platform_driver_unregister(&max77686_clk_driver);
+}
+module_exit(max77686_clk_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
+MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c
new file mode 100644
index 0000000..517874f
--- /dev/null
+++ b/drivers/clk/clk-prima2.c
@@ -0,0 +1,1171 @@
+/*
+ * Clock tree for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
+
+#define SIRFSOC_CLKC_CLK_EN0 0x0000
+#define SIRFSOC_CLKC_CLK_EN1 0x0004
+#define SIRFSOC_CLKC_REF_CFG 0x0014
+#define SIRFSOC_CLKC_CPU_CFG 0x0018
+#define SIRFSOC_CLKC_MEM_CFG 0x001c
+#define SIRFSOC_CLKC_SYS_CFG 0x0020
+#define SIRFSOC_CLKC_IO_CFG 0x0024
+#define SIRFSOC_CLKC_DSP_CFG 0x0028
+#define SIRFSOC_CLKC_GFX_CFG 0x002c
+#define SIRFSOC_CLKC_MM_CFG 0x0030
+#define SIRFSOC_CLKC_LCD_CFG 0x0034
+#define SIRFSOC_CLKC_MMC_CFG 0x0038
+#define SIRFSOC_CLKC_PLL1_CFG0 0x0040
+#define SIRFSOC_CLKC_PLL2_CFG0 0x0044
+#define SIRFSOC_CLKC_PLL3_CFG0 0x0048
+#define SIRFSOC_CLKC_PLL1_CFG1 0x004c
+#define SIRFSOC_CLKC_PLL2_CFG1 0x0050
+#define SIRFSOC_CLKC_PLL3_CFG1 0x0054
+#define SIRFSOC_CLKC_PLL1_CFG2 0x0058
+#define SIRFSOC_CLKC_PLL2_CFG2 0x005c
+#define SIRFSOC_CLKC_PLL3_CFG2 0x0060
+#define SIRFSOC_USBPHY_PLL_CTRL 0x0008
+#define SIRFSOC_USBPHY_PLL_POWERDOWN BIT(1)
+#define SIRFSOC_USBPHY_PLL_BYPASS BIT(2)
+#define SIRFSOC_USBPHY_PLL_LOCK BIT(3)
+
+static void *sirfsoc_clk_vbase, *sirfsoc_rsc_vbase;
+
+#define KHZ 1000
+#define MHZ (KHZ * KHZ)
+
+/*
+ * SiRFprimaII clock controller
+ * - 2 oscillators: osc-26MHz, rtc-32.768KHz
+ * - 3 standard configurable plls: pll1, pll2 & pll3
+ * - 2 exclusive plls: usb phy pll and sata phy pll
+ * - 8 clock domains: cpu/cpudiv, mem/memdiv, sys/io, dsp, graphic, multimedia,
+ * display and sdphy.
+ * Each clock domain can select its own clock source from five clock sources,
+ * X_XIN, X_XINW, PLL1, PLL2 and PLL3. The domain clock is used as the source
+ * clock of the group clock.
+ * - dsp domain: gps, mf
+ * - io domain: dmac, nand, audio, uart, i2c, spi, usp, pwm, pulse
+ * - sys domain: security
+ */
+
+struct clk_pll {
+ struct clk_hw hw;
+ unsigned short regofs; /* register offset */
+};
+
+#define to_pllclk(_hw) container_of(_hw, struct clk_pll, hw)
+
+struct clk_dmn {
+ struct clk_hw hw;
+ signed char enable_bit; /* enable bit: 0 ~ 63 */
+ unsigned short regofs; /* register offset */
+};
+
+#define to_dmnclk(_hw) container_of(_hw, struct clk_dmn, hw)
+
+struct clk_std {
+ struct clk_hw hw;
+ signed char enable_bit; /* enable bit: 0 ~ 63 */
+};
+
+#define to_stdclk(_hw) container_of(_hw, struct clk_std, hw)
+
+static int std_clk_is_enabled(struct clk_hw *hw);
+static int std_clk_enable(struct clk_hw *hw);
+static void std_clk_disable(struct clk_hw *hw);
+
+static inline unsigned long clkc_readl(unsigned reg)
+{
+ return readl(sirfsoc_clk_vbase + reg);
+}
+
+static inline void clkc_writel(u32 val, unsigned reg)
+{
+ writel(val, sirfsoc_clk_vbase + reg);
+}
+
+/*
+ * std pll
+ */
+
+static unsigned long pll_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long fin = parent_rate;
+ struct clk_pll *clk = to_pllclk(hw);
+ u32 regcfg2 = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 -
+ SIRFSOC_CLKC_PLL1_CFG0;
+
+ if (clkc_readl(regcfg2) & BIT(2)) {
+ /* pll bypass mode */
+ return fin;
+ } else {
+ /* fout = fin * nf / nr / od */
+ u32 cfg0 = clkc_readl(clk->regofs);
+ u32 nf = (cfg0 & (BIT(13) - 1)) + 1;
+ u32 nr = ((cfg0 >> 13) & (BIT(6) - 1)) + 1;
+ u32 od = ((cfg0 >> 19) & (BIT(4) - 1)) + 1;
+ WARN_ON(fin % MHZ);
+ return fin / MHZ * nf / nr / od * MHZ;
+ }
+}
+
+static long pll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long fin, nf, nr, od;
+
+ /*
+ * fout = fin * nf / (nr * od);
+ * set od = 1, nr = fin/MHz, so fout = nf * MHz
+ */
+ rate = rate - rate % MHZ;
+
+ nf = rate / MHZ;
+ if (nf > BIT(13))
+ nf = BIT(13);
+ if (nf < 1)
+ nf = 1;
+
+ fin = *parent_rate;
+
+ nr = fin / MHZ;
+ if (nr > BIT(6))
+ nr = BIT(6);
+ od = 1;
+
+ return fin * nf / (nr * od);
+}
+
+static int pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *clk = to_pllclk(hw);
+ unsigned long fin, nf, nr, od, reg;
+
+ /*
+ * fout = fin * nf / (nr * od);
+ * set od = 1, nr = fin/MHz, so fout = nf * MHz
+ */
+
+ nf = rate / MHZ;
+ if (unlikely((rate % MHZ) || nf > BIT(13) || nf < 1))
+ return -EINVAL;
+
+ fin = parent_rate;
+ BUG_ON(fin < MHZ);
+
+ nr = fin / MHZ;
+ BUG_ON((fin % MHZ) || nr > BIT(6));
+
+ od = 1;
+
+ reg = (nf - 1) | ((nr - 1) << 13) | ((od - 1) << 19);
+ clkc_writel(reg, clk->regofs);
+
+ reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG1 - SIRFSOC_CLKC_PLL1_CFG0;
+ clkc_writel((nf >> 1) - 1, reg);
+
+ reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 - SIRFSOC_CLKC_PLL1_CFG0;
+ while (!(clkc_readl(reg) & BIT(6)))
+ cpu_relax();
+
+ return 0;
+}
+
+static struct clk_ops std_pll_ops = {
+ .recalc_rate = pll_clk_recalc_rate,
+ .round_rate = pll_clk_round_rate,
+ .set_rate = pll_clk_set_rate,
+};
+
+static const char *pll_clk_parents[] = {
+ "osc",
+};
+
+static struct clk_init_data clk_pll1_init = {
+ .name = "pll1",
+ .ops = &std_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_init_data clk_pll2_init = {
+ .name = "pll2",
+ .ops = &std_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_init_data clk_pll3_init = {
+ .name = "pll3",
+ .ops = &std_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_pll clk_pll1 = {
+ .regofs = SIRFSOC_CLKC_PLL1_CFG0,
+ .hw = {
+ .init = &clk_pll1_init,
+ },
+};
+
+static struct clk_pll clk_pll2 = {
+ .regofs = SIRFSOC_CLKC_PLL2_CFG0,
+ .hw = {
+ .init = &clk_pll2_init,
+ },
+};
+
+static struct clk_pll clk_pll3 = {
+ .regofs = SIRFSOC_CLKC_PLL3_CFG0,
+ .hw = {
+ .init = &clk_pll3_init,
+ },
+};
+
+/*
+ * usb uses specified pll
+ */
+
+static int usb_pll_clk_enable(struct clk_hw *hw)
+{
+ u32 reg = readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ reg &= ~(SIRFSOC_USBPHY_PLL_POWERDOWN | SIRFSOC_USBPHY_PLL_BYPASS);
+ writel(reg, sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ while (!(readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL) &
+ SIRFSOC_USBPHY_PLL_LOCK))
+ cpu_relax();
+
+ return 0;
+}
+
+static void usb_pll_clk_disable(struct clk_hw *clk)
+{
+ u32 reg = readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ reg |= (SIRFSOC_USBPHY_PLL_POWERDOWN | SIRFSOC_USBPHY_PLL_BYPASS);
+ writel(reg, sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+}
+
+static unsigned long usb_pll_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ u32 reg = readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ return (reg & SIRFSOC_USBPHY_PLL_BYPASS) ? parent_rate : 48*MHZ;
+}
+
+static struct clk_ops usb_pll_ops = {
+ .enable = usb_pll_clk_enable,
+ .disable = usb_pll_clk_disable,
+ .recalc_rate = usb_pll_clk_recalc_rate,
+};
+
+static struct clk_init_data clk_usb_pll_init = {
+ .name = "usb_pll",
+ .ops = &usb_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_hw usb_pll_clk_hw = {
+ .init = &clk_usb_pll_init,
+};
+
+/*
+ * clock domains - cpu, mem, sys/io, dsp, gfx
+ */
+
+static const char *dmn_clk_parents[] = {
+ "rtc",
+ "osc",
+ "pll1",
+ "pll2",
+ "pll3",
+};
+
+static u8 dmn_clk_get_parent(struct clk_hw *hw)
+{
+ struct clk_dmn *clk = to_dmnclk(hw);
+ u32 cfg = clkc_readl(clk->regofs);
+
+ /* parent of io domain can only be pll3 */
+ if (strcmp(hw->init->name, "io") == 0)
+ return 4;
+
+ WARN_ON((cfg & (BIT(3) - 1)) > 4);
+
+ return cfg & (BIT(3) - 1);
+}
+
+static int dmn_clk_set_parent(struct clk_hw *hw, u8 parent)
+{
+ struct clk_dmn *clk = to_dmnclk(hw);
+ u32 cfg = clkc_readl(clk->regofs);
+
+ /* parent of io domain can only be pll3 */
+ if (strcmp(hw->init->name, "io") == 0)
+ return -EINVAL;
+
+ cfg &= ~(BIT(3) - 1);
+ clkc_writel(cfg | parent, clk->regofs);
+ /* BIT(3) - switching status: 1 - busy, 0 - done */
+ while (clkc_readl(clk->regofs) & BIT(3))
+ cpu_relax();
+
+ return 0;
+}
+
+static unsigned long dmn_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+
+{
+ unsigned long fin = parent_rate;
+ struct clk_dmn *clk = to_dmnclk(hw);
+
+ u32 cfg = clkc_readl(clk->regofs);
+
+ if (cfg & BIT(24)) {
+ /* fcd bypass mode */
+ return fin;
+ } else {
+ /*
+ * wait count: bit[19:16], hold count: bit[23:20]
+ */
+ u32 wait = (cfg >> 16) & (BIT(4) - 1);
+ u32 hold = (cfg >> 20) & (BIT(4) - 1);
+
+ return fin / (wait + hold + 2);
+ }
+}
+
+static long dmn_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long fin;
+ unsigned ratio, wait, hold;
+ unsigned bits = (strcmp(hw->init->name, "mem") == 0) ? 3 : 4;
+
+ fin = *parent_rate;
+ ratio = fin / rate;
+
+ if (ratio < 2)
+ ratio = 2;
+ if (ratio > BIT(bits + 1))
+ ratio = BIT(bits + 1);
+
+ wait = (ratio >> 1) - 1;
+ hold = ratio - wait - 2;
+
+ return fin / (wait + hold + 2);
+}
+
+static int dmn_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_dmn *clk = to_dmnclk(hw);
+ unsigned long fin;
+ unsigned ratio, wait, hold, reg;
+ unsigned bits = (strcmp(hw->init->name, "mem") == 0) ? 3 : 4;
+
+ fin = parent_rate;
+ ratio = fin / rate;
+
+ if (unlikely(ratio < 2 || ratio > BIT(bits + 1)))
+ return -EINVAL;
+
+ WARN_ON(fin % rate);
+
+ wait = (ratio >> 1) - 1;
+ hold = ratio - wait - 2;
+
+ reg = clkc_readl(clk->regofs);
+ reg &= ~(((BIT(bits) - 1) << 16) | ((BIT(bits) - 1) << 20));
+ reg |= (wait << 16) | (hold << 20) | BIT(25);
+ clkc_writel(reg, clk->regofs);
+
+ /* waiting FCD been effective */
+ while (clkc_readl(clk->regofs) & BIT(25))
+ cpu_relax();
+
+ return 0;
+}
+
+static struct clk_ops msi_ops = {
+ .set_rate = dmn_clk_set_rate,
+ .round_rate = dmn_clk_round_rate,
+ .recalc_rate = dmn_clk_recalc_rate,
+ .set_parent = dmn_clk_set_parent,
+ .get_parent = dmn_clk_get_parent,
+};
+
+static struct clk_init_data clk_mem_init = {
+ .name = "mem",
+ .ops = &msi_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mem = {
+ .regofs = SIRFSOC_CLKC_MEM_CFG,
+ .hw = {
+ .init = &clk_mem_init,
+ },
+};
+
+static struct clk_init_data clk_sys_init = {
+ .name = "sys",
+ .ops = &msi_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+ .flags = CLK_SET_RATE_GATE,
+};
+
+static struct clk_dmn clk_sys = {
+ .regofs = SIRFSOC_CLKC_SYS_CFG,
+ .hw = {
+ .init = &clk_sys_init,
+ },
+};
+
+static struct clk_init_data clk_io_init = {
+ .name = "io",
+ .ops = &msi_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_io = {
+ .regofs = SIRFSOC_CLKC_IO_CFG,
+ .hw = {
+ .init = &clk_io_init,
+ },
+};
+
+static struct clk_ops cpu_ops = {
+ .set_parent = dmn_clk_set_parent,
+ .get_parent = dmn_clk_get_parent,
+};
+
+static struct clk_init_data clk_cpu_init = {
+ .name = "cpu",
+ .ops = &cpu_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+ .flags = CLK_SET_RATE_PARENT,
+};
+
+static struct clk_dmn clk_cpu = {
+ .regofs = SIRFSOC_CLKC_CPU_CFG,
+ .hw = {
+ .init = &clk_cpu_init,
+ },
+};
+
+static struct clk_ops dmn_ops = {
+ .is_enabled = std_clk_is_enabled,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .set_rate = dmn_clk_set_rate,
+ .round_rate = dmn_clk_round_rate,
+ .recalc_rate = dmn_clk_recalc_rate,
+ .set_parent = dmn_clk_set_parent,
+ .get_parent = dmn_clk_get_parent,
+};
+
+/* dsp, gfx, mm, lcd and vpp domain */
+
+static struct clk_init_data clk_dsp_init = {
+ .name = "dsp",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_dsp = {
+ .regofs = SIRFSOC_CLKC_DSP_CFG,
+ .enable_bit = 0,
+ .hw = {
+ .init = &clk_dsp_init,
+ },
+};
+
+static struct clk_init_data clk_gfx_init = {
+ .name = "gfx",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_gfx = {
+ .regofs = SIRFSOC_CLKC_GFX_CFG,
+ .enable_bit = 8,
+ .hw = {
+ .init = &clk_gfx_init,
+ },
+};
+
+static struct clk_init_data clk_mm_init = {
+ .name = "mm",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mm = {
+ .regofs = SIRFSOC_CLKC_MM_CFG,
+ .enable_bit = 9,
+ .hw = {
+ .init = &clk_mm_init,
+ },
+};
+
+static struct clk_init_data clk_lcd_init = {
+ .name = "lcd",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_lcd = {
+ .regofs = SIRFSOC_CLKC_LCD_CFG,
+ .enable_bit = 10,
+ .hw = {
+ .init = &clk_lcd_init,
+ },
+};
+
+static struct clk_init_data clk_vpp_init = {
+ .name = "vpp",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_vpp = {
+ .regofs = SIRFSOC_CLKC_LCD_CFG,
+ .enable_bit = 11,
+ .hw = {
+ .init = &clk_vpp_init,
+ },
+};
+
+static struct clk_init_data clk_mmc01_init = {
+ .name = "mmc01",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mmc01 = {
+ .regofs = SIRFSOC_CLKC_MMC_CFG,
+ .enable_bit = 59,
+ .hw = {
+ .init = &clk_mmc01_init,
+ },
+};
+
+static struct clk_init_data clk_mmc23_init = {
+ .name = "mmc23",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mmc23 = {
+ .regofs = SIRFSOC_CLKC_MMC_CFG,
+ .enable_bit = 60,
+ .hw = {
+ .init = &clk_mmc23_init,
+ },
+};
+
+static struct clk_init_data clk_mmc45_init = {
+ .name = "mmc45",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mmc45 = {
+ .regofs = SIRFSOC_CLKC_MMC_CFG,
+ .enable_bit = 61,
+ .hw = {
+ .init = &clk_mmc45_init,
+ },
+};
+
+/*
+ * peripheral controllers in io domain
+ */
+
+static int std_clk_is_enabled(struct clk_hw *hw)
+{
+ u32 reg;
+ int bit;
+ struct clk_std *clk = to_stdclk(hw);
+
+ bit = clk->enable_bit % 32;
+ reg = clk->enable_bit / 32;
+ reg = SIRFSOC_CLKC_CLK_EN0 + reg * sizeof(reg);
+
+ return !!(clkc_readl(reg) & BIT(bit));
+}
+
+static int std_clk_enable(struct clk_hw *hw)
+{
+ u32 val, reg;
+ int bit;
+ struct clk_std *clk = to_stdclk(hw);
+
+ BUG_ON(clk->enable_bit < 0 || clk->enable_bit > 63);
+
+ bit = clk->enable_bit % 32;
+ reg = clk->enable_bit / 32;
+ reg = SIRFSOC_CLKC_CLK_EN0 + reg * sizeof(reg);
+
+ val = clkc_readl(reg) | BIT(bit);
+ clkc_writel(val, reg);
+ return 0;
+}
+
+static void std_clk_disable(struct clk_hw *hw)
+{
+ u32 val, reg;
+ int bit;
+ struct clk_std *clk = to_stdclk(hw);
+
+ BUG_ON(clk->enable_bit < 0 || clk->enable_bit > 63);
+
+ bit = clk->enable_bit % 32;
+ reg = clk->enable_bit / 32;
+ reg = SIRFSOC_CLKC_CLK_EN0 + reg * sizeof(reg);
+
+ val = clkc_readl(reg) & ~BIT(bit);
+ clkc_writel(val, reg);
+}
+
+static const char *std_clk_io_parents[] = {
+ "io",
+};
+
+static struct clk_ops ios_ops = {
+ .is_enabled = std_clk_is_enabled,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+};
+
+static struct clk_init_data clk_dmac0_init = {
+ .name = "dmac0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_dmac0 = {
+ .enable_bit = 32,
+ .hw = {
+ .init = &clk_dmac0_init,
+ },
+};
+
+static struct clk_init_data clk_dmac1_init = {
+ .name = "dmac1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_dmac1 = {
+ .enable_bit = 33,
+ .hw = {
+ .init = &clk_dmac1_init,
+ },
+};
+
+static struct clk_init_data clk_nand_init = {
+ .name = "nand",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_nand = {
+ .enable_bit = 34,
+ .hw = {
+ .init = &clk_nand_init,
+ },
+};
+
+static struct clk_init_data clk_audio_init = {
+ .name = "audio",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_audio = {
+ .enable_bit = 35,
+ .hw = {
+ .init = &clk_audio_init,
+ },
+};
+
+static struct clk_init_data clk_uart0_init = {
+ .name = "uart0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_uart0 = {
+ .enable_bit = 36,
+ .hw = {
+ .init = &clk_uart0_init,
+ },
+};
+
+static struct clk_init_data clk_uart1_init = {
+ .name = "uart1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_uart1 = {
+ .enable_bit = 37,
+ .hw = {
+ .init = &clk_uart1_init,
+ },
+};
+
+static struct clk_init_data clk_uart2_init = {
+ .name = "uart2",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_uart2 = {
+ .enable_bit = 38,
+ .hw = {
+ .init = &clk_uart2_init,
+ },
+};
+
+static struct clk_init_data clk_usp0_init = {
+ .name = "usp0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_usp0 = {
+ .enable_bit = 39,
+ .hw = {
+ .init = &clk_usp0_init,
+ },
+};
+
+static struct clk_init_data clk_usp1_init = {
+ .name = "usp1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_usp1 = {
+ .enable_bit = 40,
+ .hw = {
+ .init = &clk_usp1_init,
+ },
+};
+
+static struct clk_init_data clk_usp2_init = {
+ .name = "usp2",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_usp2 = {
+ .enable_bit = 41,
+ .hw = {
+ .init = &clk_usp2_init,
+ },
+};
+
+static struct clk_init_data clk_vip_init = {
+ .name = "vip",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_vip = {
+ .enable_bit = 42,
+ .hw = {
+ .init = &clk_vip_init,
+ },
+};
+
+static struct clk_init_data clk_spi0_init = {
+ .name = "spi0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_spi0 = {
+ .enable_bit = 43,
+ .hw = {
+ .init = &clk_spi0_init,
+ },
+};
+
+static struct clk_init_data clk_spi1_init = {
+ .name = "spi1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_spi1 = {
+ .enable_bit = 44,
+ .hw = {
+ .init = &clk_spi1_init,
+ },
+};
+
+static struct clk_init_data clk_tsc_init = {
+ .name = "tsc",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_tsc = {
+ .enable_bit = 45,
+ .hw = {
+ .init = &clk_tsc_init,
+ },
+};
+
+static struct clk_init_data clk_i2c0_init = {
+ .name = "i2c0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_i2c0 = {
+ .enable_bit = 46,
+ .hw = {
+ .init = &clk_i2c0_init,
+ },
+};
+
+static struct clk_init_data clk_i2c1_init = {
+ .name = "i2c1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_i2c1 = {
+ .enable_bit = 47,
+ .hw = {
+ .init = &clk_i2c1_init,
+ },
+};
+
+static struct clk_init_data clk_pwmc_init = {
+ .name = "pwmc",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_pwmc = {
+ .enable_bit = 48,
+ .hw = {
+ .init = &clk_pwmc_init,
+ },
+};
+
+static struct clk_init_data clk_efuse_init = {
+ .name = "efuse",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_efuse = {
+ .enable_bit = 49,
+ .hw = {
+ .init = &clk_efuse_init,
+ },
+};
+
+static struct clk_init_data clk_pulse_init = {
+ .name = "pulse",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_pulse = {
+ .enable_bit = 50,
+ .hw = {
+ .init = &clk_pulse_init,
+ },
+};
+
+static const char *std_clk_dsp_parents[] = {
+ "dsp",
+};
+
+static struct clk_init_data clk_gps_init = {
+ .name = "gps",
+ .ops = &ios_ops,
+ .parent_names = std_clk_dsp_parents,
+ .num_parents = ARRAY_SIZE(std_clk_dsp_parents),
+};
+
+static struct clk_std clk_gps = {
+ .enable_bit = 1,
+ .hw = {
+ .init = &clk_gps_init,
+ },
+};
+
+static struct clk_init_data clk_mf_init = {
+ .name = "mf",
+ .ops = &ios_ops,
+ .parent_names = std_clk_io_parents,
+ .num_parents = ARRAY_SIZE(std_clk_io_parents),
+};
+
+static struct clk_std clk_mf = {
+ .enable_bit = 2,
+ .hw = {
+ .init = &clk_mf_init,
+ },
+};
+
+static const char *std_clk_sys_parents[] = {
+ "sys",
+};
+
+static struct clk_init_data clk_security_init = {
+ .name = "mf",
+ .ops = &ios_ops,
+ .parent_names = std_clk_sys_parents,
+ .num_parents = ARRAY_SIZE(std_clk_sys_parents),
+};
+
+static struct clk_std clk_security = {
+ .enable_bit = 19,
+ .hw = {
+ .init = &clk_security_init,
+ },
+};
+
+static const char *std_clk_usb_parents[] = {
+ "usb_pll",
+};
+
+static struct clk_init_data clk_usb0_init = {
+ .name = "usb0",
+ .ops = &ios_ops,
+ .parent_names = std_clk_usb_parents,
+ .num_parents = ARRAY_SIZE(std_clk_usb_parents),
+};
+
+static struct clk_std clk_usb0 = {
+ .enable_bit = 16,
+ .hw = {
+ .init = &clk_usb0_init,
+ },
+};
+
+static struct clk_init_data clk_usb1_init = {
+ .name = "usb1",
+ .ops = &ios_ops,
+ .parent_names = std_clk_usb_parents,
+ .num_parents = ARRAY_SIZE(std_clk_usb_parents),
+};
+
+static struct clk_std clk_usb1 = {
+ .enable_bit = 17,
+ .hw = {
+ .init = &clk_usb1_init,
+ },
+};
+
+static struct of_device_id clkc_ids[] = {
+ { .compatible = "sirf,prima2-clkc" },
+ {},
+};
+
+static struct of_device_id rsc_ids[] = {
+ { .compatible = "sirf,prima2-rsc" },
+ {},
+};
+
+void __init sirfsoc_of_clk_init(void)
+{
+ struct clk *clk;
+ struct device_node *np;
+
+ np = of_find_matching_node(NULL, clkc_ids);
+ if (!np)
+ panic("unable to find compatible clkc node in dtb\n");
+
+ sirfsoc_clk_vbase = of_iomap(np, 0);
+ if (!sirfsoc_clk_vbase)
+ panic("unable to map clkc registers\n");
+
+ of_node_put(np);
+
+ np = of_find_matching_node(NULL, rsc_ids);
+ if (!np)
+ panic("unable to find compatible rsc node in dtb\n");
+
+ sirfsoc_rsc_vbase = of_iomap(np, 0);
+ if (!sirfsoc_rsc_vbase)
+ panic("unable to map rsc registers\n");
+
+ of_node_put(np);
+
+
+ /* These are always available (RTC and 26MHz OSC)*/
+ clk = clk_register_fixed_rate(NULL, "rtc", NULL,
+ CLK_IS_ROOT, 32768);
+ BUG_ON(!clk);
+ clk = clk_register_fixed_rate(NULL, "osc", NULL,
+ CLK_IS_ROOT, 26000000);
+ BUG_ON(!clk);
+
+ clk = clk_register(NULL, &clk_pll1.hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &clk_pll2.hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &clk_pll3.hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &clk_mem.hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &clk_sys.hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &clk_security.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b8030000.security");
+ clk = clk_register(NULL, &clk_dsp.hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &clk_gps.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "a8010000.gps");
+ clk = clk_register(NULL, &clk_mf.hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &clk_io.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "io");
+ clk = clk_register(NULL, &clk_cpu.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "cpu");
+ clk = clk_register(NULL, &clk_uart0.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0050000.uart");
+ clk = clk_register(NULL, &clk_uart1.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0060000.uart");
+ clk = clk_register(NULL, &clk_uart2.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0070000.uart");
+ clk = clk_register(NULL, &clk_tsc.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0110000.tsc");
+ clk = clk_register(NULL, &clk_i2c0.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b00e0000.i2c");
+ clk = clk_register(NULL, &clk_i2c1.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b00f0000.i2c");
+ clk = clk_register(NULL, &clk_spi0.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b00d0000.spi");
+ clk = clk_register(NULL, &clk_spi1.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0170000.spi");
+ clk = clk_register(NULL, &clk_pwmc.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0130000.pwm");
+ clk = clk_register(NULL, &clk_efuse.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0140000.efusesys");
+ clk = clk_register(NULL, &clk_pulse.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0150000.pulsec");
+ clk = clk_register(NULL, &clk_dmac0.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b00b0000.dma-controller");
+ clk = clk_register(NULL, &clk_dmac1.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0160000.dma-controller");
+ clk = clk_register(NULL, &clk_nand.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0030000.nand");
+ clk = clk_register(NULL, &clk_audio.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0040000.audio");
+ clk = clk_register(NULL, &clk_usp0.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0080000.usp");
+ clk = clk_register(NULL, &clk_usp1.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b0090000.usp");
+ clk = clk_register(NULL, &clk_usp2.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b00a0000.usp");
+ clk = clk_register(NULL, &clk_vip.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b00c0000.vip");
+ clk = clk_register(NULL, &clk_gfx.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "98000000.graphics");
+ clk = clk_register(NULL, &clk_mm.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "a0000000.multimedia");
+ clk = clk_register(NULL, &clk_lcd.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "90010000.display");
+ clk = clk_register(NULL, &clk_vpp.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "90020000.vpp");
+ clk = clk_register(NULL, &clk_mmc01.hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &clk_mmc23.hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &clk_mmc45.hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &usb_pll_clk_hw);
+ BUG_ON(!clk);
+ clk = clk_register(NULL, &clk_usb0.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b00e0000.usb");
+ clk = clk_register(NULL, &clk_usb1.hw);
+ BUG_ON(!clk);
+ clk_register_clkdev(clk, NULL, "b00f0000.usb");
+}
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index efdfd00..56e4495e 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -558,25 +558,6 @@ int clk_enable(struct clk *clk)
EXPORT_SYMBOL_GPL(clk_enable);
/**
- * clk_get_rate - return the rate of clk
- * @clk: the clk whose rate is being returned
- *
- * Simply returns the cached rate of the clk. Does not query the hardware. If
- * clk is NULL then returns 0.
- */
-unsigned long clk_get_rate(struct clk *clk)
-{
- unsigned long rate;
-
- mutex_lock(&prepare_lock);
- rate = __clk_get_rate(clk);
- mutex_unlock(&prepare_lock);
-
- return rate;
-}
-EXPORT_SYMBOL_GPL(clk_get_rate);
-
-/**
* __clk_round_rate - round the given rate for a clk
* @clk: round the rate of this clock
*
@@ -702,6 +683,30 @@ static void __clk_recalc_rates(struct clk *clk, unsigned long msg)
}
/**
+ * clk_get_rate - return the rate of clk
+ * @clk: the clk whose rate is being returned
+ *
+ * Simply returns the cached rate of the clk, unless CLK_GET_RATE_NOCACHE flag
+ * is set, which means a recalc_rate will be issued.
+ * If clk is NULL then returns 0.
+ */
+unsigned long clk_get_rate(struct clk *clk)
+{
+ unsigned long rate;
+
+ mutex_lock(&prepare_lock);
+
+ if (clk && (clk->flags & CLK_GET_RATE_NOCACHE))
+ __clk_recalc_rates(clk, 0);
+
+ rate = __clk_get_rate(clk);
+ mutex_unlock(&prepare_lock);
+
+ return rate;
+}
+EXPORT_SYMBOL_GPL(clk_get_rate);
+
+/**
* __clk_speculate_rates
* @clk: first clk in the subtree
* @parent_rate: the "future" rate of clk's parent
@@ -1582,6 +1587,20 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
}
EXPORT_SYMBOL_GPL(of_clk_src_simple_get);
+struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct clk_onecell_data *clk_data = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= clk_data->clk_num) {
+ pr_err("%s: invalid clock index %d\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return clk_data->clks[idx];
+}
+EXPORT_SYMBOL_GPL(of_clk_src_onecell_get);
+
/**
* of_clk_add_provider() - Register a clock provider for a node
* @np: Device node pointer associated with clock provider
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index d423c9b..442a313 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -171,51 +171,6 @@ void clk_put(struct clk *clk)
}
EXPORT_SYMBOL(clk_put);
-static void devm_clk_release(struct device *dev, void *res)
-{
- clk_put(*(struct clk **)res);
-}
-
-struct clk *devm_clk_get(struct device *dev, const char *id)
-{
- struct clk **ptr, *clk;
-
- ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- clk = clk_get(dev, id);
- if (!IS_ERR(clk)) {
- *ptr = clk;
- devres_add(dev, ptr);
- } else {
- devres_free(ptr);
- }
-
- return clk;
-}
-EXPORT_SYMBOL(devm_clk_get);
-
-static int devm_clk_match(struct device *dev, void *res, void *data)
-{
- struct clk **c = res;
- if (!c || !*c) {
- WARN_ON(!c || !*c);
- return 0;
- }
- return *c == data;
-}
-
-void devm_clk_put(struct device *dev, struct clk *clk)
-{
- int ret;
-
- ret = devres_destroy(dev, devm_clk_release, devm_clk_match, clk);
-
- WARN_ON(ret);
-}
-EXPORT_SYMBOL(devm_clk_put);
-
void clkdev_add(struct clk_lookup *cl)
{
mutex_lock(&clocks_mutex);
diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile
new file mode 100644
index 0000000..392d780
--- /dev/null
+++ b/drivers/clk/mmp/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for mmp specific clk
+#
+
+obj-y += clk-apbc.o clk-apmu.o clk-frac.o
+
+obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o
+obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
+obj-$(CONFIG_CPU_MMP2) += clk-mmp2.o
diff --git a/drivers/clk/mmp/clk-apbc.c b/drivers/clk/mmp/clk-apbc.c
new file mode 100644
index 0000000..d14120e
--- /dev/null
+++ b/drivers/clk/mmp/clk-apbc.c
@@ -0,0 +1,152 @@
+/*
+ * mmp APB clock operation source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+/* Common APB clock register bit definitions */
+#define APBC_APBCLK (1 << 0) /* APB Bus Clock Enable */
+#define APBC_FNCLK (1 << 1) /* Functional Clock Enable */
+#define APBC_RST (1 << 2) /* Reset Generation */
+#define APBC_POWER (1 << 7) /* Reset Generation */
+
+#define to_clk_apbc(hw) container_of(hw, struct clk_apbc, hw)
+struct clk_apbc {
+ struct clk_hw hw;
+ void __iomem *base;
+ unsigned int delay;
+ unsigned int flags;
+ spinlock_t *lock;
+};
+
+static int clk_apbc_prepare(struct clk_hw *hw)
+{
+ struct clk_apbc *apbc = to_clk_apbc(hw);
+ unsigned int data;
+ unsigned long flags = 0;
+
+ /*
+ * It may share same register as MUX clock,
+ * and it will impact FNCLK enable. Spinlock is needed
+ */
+ if (apbc->lock)
+ spin_lock_irqsave(apbc->lock, flags);
+
+ data = readl_relaxed(apbc->base);
+ if (apbc->flags & APBC_POWER_CTRL)
+ data |= APBC_POWER;
+ data |= APBC_FNCLK;
+ writel_relaxed(data, apbc->base);
+
+ if (apbc->lock)
+ spin_unlock_irqrestore(apbc->lock, flags);
+
+ udelay(apbc->delay);
+
+ if (apbc->lock)
+ spin_lock_irqsave(apbc->lock, flags);
+
+ data = readl_relaxed(apbc->base);
+ data |= APBC_APBCLK;
+ writel_relaxed(data, apbc->base);
+
+ if (apbc->lock)
+ spin_unlock_irqrestore(apbc->lock, flags);
+
+ udelay(apbc->delay);
+
+ if (!(apbc->flags & APBC_NO_BUS_CTRL)) {
+ if (apbc->lock)
+ spin_lock_irqsave(apbc->lock, flags);
+
+ data = readl_relaxed(apbc->base);
+ data &= ~APBC_RST;
+ writel_relaxed(data, apbc->base);
+
+ if (apbc->lock)
+ spin_unlock_irqrestore(apbc->lock, flags);
+ }
+
+ return 0;
+}
+
+static void clk_apbc_unprepare(struct clk_hw *hw)
+{
+ struct clk_apbc *apbc = to_clk_apbc(hw);
+ unsigned long data;
+ unsigned long flags = 0;
+
+ if (apbc->lock)
+ spin_lock_irqsave(apbc->lock, flags);
+
+ data = readl_relaxed(apbc->base);
+ if (apbc->flags & APBC_POWER_CTRL)
+ data &= ~APBC_POWER;
+ data &= ~APBC_FNCLK;
+ writel_relaxed(data, apbc->base);
+
+ if (apbc->lock)
+ spin_unlock_irqrestore(apbc->lock, flags);
+
+ udelay(10);
+
+ if (apbc->lock)
+ spin_lock_irqsave(apbc->lock, flags);
+
+ data = readl_relaxed(apbc->base);
+ data &= ~APBC_APBCLK;
+ writel_relaxed(data, apbc->base);
+
+ if (apbc->lock)
+ spin_unlock_irqrestore(apbc->lock, flags);
+}
+
+struct clk_ops clk_apbc_ops = {
+ .prepare = clk_apbc_prepare,
+ .unprepare = clk_apbc_unprepare,
+};
+
+struct clk *mmp_clk_register_apbc(const char *name, const char *parent_name,
+ void __iomem *base, unsigned int delay,
+ unsigned int apbc_flags, spinlock_t *lock)
+{
+ struct clk_apbc *apbc;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ apbc = kzalloc(sizeof(*apbc), GFP_KERNEL);
+ if (!apbc)
+ return NULL;
+
+ init.name = name;
+ init.ops = &clk_apbc_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ apbc->base = base;
+ apbc->delay = delay;
+ apbc->flags = apbc_flags;
+ apbc->lock = lock;
+ apbc->hw.init = &init;
+
+ clk = clk_register(NULL, &apbc->hw);
+ if (IS_ERR(clk))
+ kfree(apbc);
+
+ return clk;
+}
diff --git a/drivers/clk/mmp/clk-apmu.c b/drivers/clk/mmp/clk-apmu.c
new file mode 100644
index 0000000..abe182b
--- /dev/null
+++ b/drivers/clk/mmp/clk-apmu.c
@@ -0,0 +1,97 @@
+/*
+ * mmp AXI peripharal clock operation source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+#define to_clk_apmu(clk) (container_of(clk, struct clk_apmu, clk))
+struct clk_apmu {
+ struct clk_hw hw;
+ void __iomem *base;
+ u32 rst_mask;
+ u32 enable_mask;
+ spinlock_t *lock;
+};
+
+static int clk_apmu_enable(struct clk_hw *hw)
+{
+ struct clk_apmu *apmu = to_clk_apmu(hw);
+ unsigned long data;
+ unsigned long flags = 0;
+
+ if (apmu->lock)
+ spin_lock_irqsave(apmu->lock, flags);
+
+ data = readl_relaxed(apmu->base) | apmu->enable_mask;
+ writel_relaxed(data, apmu->base);
+
+ if (apmu->lock)
+ spin_unlock_irqrestore(apmu->lock, flags);
+
+ return 0;
+}
+
+static void clk_apmu_disable(struct clk_hw *hw)
+{
+ struct clk_apmu *apmu = to_clk_apmu(hw);
+ unsigned long data;
+ unsigned long flags = 0;
+
+ if (apmu->lock)
+ spin_lock_irqsave(apmu->lock, flags);
+
+ data = readl_relaxed(apmu->base) & ~apmu->enable_mask;
+ writel_relaxed(data, apmu->base);
+
+ if (apmu->lock)
+ spin_unlock_irqrestore(apmu->lock, flags);
+}
+
+struct clk_ops clk_apmu_ops = {
+ .enable = clk_apmu_enable,
+ .disable = clk_apmu_disable,
+};
+
+struct clk *mmp_clk_register_apmu(const char *name, const char *parent_name,
+ void __iomem *base, u32 enable_mask, spinlock_t *lock)
+{
+ struct clk_apmu *apmu;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ apmu = kzalloc(sizeof(*apmu), GFP_KERNEL);
+ if (!apmu)
+ return NULL;
+
+ init.name = name;
+ init.ops = &clk_apmu_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ apmu->base = base;
+ apmu->enable_mask = enable_mask;
+ apmu->lock = lock;
+ apmu->hw.init = &init;
+
+ clk = clk_register(NULL, &apmu->hw);
+
+ if (IS_ERR(clk))
+ kfree(apmu);
+
+ return clk;
+}
diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c
new file mode 100644
index 0000000..80c1dd1
--- /dev/null
+++ b/drivers/clk/mmp/clk-frac.c
@@ -0,0 +1,153 @@
+/*
+ * mmp factor clock operation source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include "clk.h"
+/*
+ * It is M/N clock
+ *
+ * Fout from synthesizer can be given from two equations:
+ * numerator/denominator = Fin / (Fout * factor)
+ */
+
+#define to_clk_factor(hw) container_of(hw, struct clk_factor, hw)
+struct clk_factor {
+ struct clk_hw hw;
+ void __iomem *base;
+ struct clk_factor_masks *masks;
+ struct clk_factor_tbl *ftbl;
+ unsigned int ftbl_cnt;
+};
+
+static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long *prate)
+{
+ struct clk_factor *factor = to_clk_factor(hw);
+ unsigned long rate = 0, prev_rate;
+ int i;
+
+ for (i = 0; i < factor->ftbl_cnt; i++) {
+ prev_rate = rate;
+ rate = (((*prate / 10000) * factor->ftbl[i].num) /
+ (factor->ftbl[i].den * factor->masks->factor)) * 10000;
+ if (rate > drate)
+ break;
+ }
+ if (i == 0)
+ return rate;
+ else
+ return prev_rate;
+}
+
+static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_factor *factor = to_clk_factor(hw);
+ struct clk_factor_masks *masks = factor->masks;
+ unsigned int val, num, den;
+
+ val = readl_relaxed(factor->base);
+
+ /* calculate numerator */
+ num = (val >> masks->num_shift) & masks->num_mask;
+
+ /* calculate denominator */
+ den = (val >> masks->den_shift) & masks->num_mask;
+
+ if (!den)
+ return 0;
+
+ return (((parent_rate / 10000) * den) /
+ (num * factor->masks->factor)) * 10000;
+}
+
+/* Configures new clock rate*/
+static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct clk_factor *factor = to_clk_factor(hw);
+ struct clk_factor_masks *masks = factor->masks;
+ int i;
+ unsigned long val;
+ unsigned long prev_rate, rate = 0;
+
+ for (i = 0; i < factor->ftbl_cnt; i++) {
+ prev_rate = rate;
+ rate = (((prate / 10000) * factor->ftbl[i].num) /
+ (factor->ftbl[i].den * factor->masks->factor)) * 10000;
+ if (rate > drate)
+ break;
+ }
+ if (i > 0)
+ i--;
+
+ val = readl_relaxed(factor->base);
+
+ val &= ~(masks->num_mask << masks->num_shift);
+ val |= (factor->ftbl[i].num & masks->num_mask) << masks->num_shift;
+
+ val &= ~(masks->den_mask << masks->den_shift);
+ val |= (factor->ftbl[i].den & masks->den_mask) << masks->den_shift;
+
+ writel_relaxed(val, factor->base);
+
+ return 0;
+}
+
+static struct clk_ops clk_factor_ops = {
+ .recalc_rate = clk_factor_recalc_rate,
+ .round_rate = clk_factor_round_rate,
+ .set_rate = clk_factor_set_rate,
+};
+
+struct clk *mmp_clk_register_factor(const char *name, const char *parent_name,
+ unsigned long flags, void __iomem *base,
+ struct clk_factor_masks *masks, struct clk_factor_tbl *ftbl,
+ unsigned int ftbl_cnt)
+{
+ struct clk_factor *factor;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ if (!masks) {
+ pr_err("%s: must pass a clk_factor_mask\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ factor = kzalloc(sizeof(*factor), GFP_KERNEL);
+ if (!factor) {
+ pr_err("%s: could not allocate factor clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* struct clk_aux assignments */
+ factor->base = base;
+ factor->masks = masks;
+ factor->ftbl = ftbl;
+ factor->ftbl_cnt = ftbl_cnt;
+ factor->hw.init = &init;
+
+ init.name = name;
+ init.ops = &clk_factor_ops;
+ init.flags = flags;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(NULL, &factor->hw);
+ if (IS_ERR_OR_NULL(clk))
+ kfree(factor);
+
+ return clk;
+}
diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c
new file mode 100644
index 0000000..ade4358
--- /dev/null
+++ b/drivers/clk/mmp/clk-mmp2.c
@@ -0,0 +1,449 @@
+/*
+ * mmp2 clock framework source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <mach/addr-map.h>
+
+#include "clk.h"
+
+#define APBC_RTC 0x0
+#define APBC_TWSI0 0x4
+#define APBC_TWSI1 0x8
+#define APBC_TWSI2 0xc
+#define APBC_TWSI3 0x10
+#define APBC_TWSI4 0x7c
+#define APBC_TWSI5 0x80
+#define APBC_KPC 0x18
+#define APBC_UART0 0x2c
+#define APBC_UART1 0x30
+#define APBC_UART2 0x34
+#define APBC_UART3 0x88
+#define APBC_GPIO 0x38
+#define APBC_PWM0 0x3c
+#define APBC_PWM1 0x40
+#define APBC_PWM2 0x44
+#define APBC_PWM3 0x48
+#define APBC_SSP0 0x50
+#define APBC_SSP1 0x54
+#define APBC_SSP2 0x58
+#define APBC_SSP3 0x5c
+#define APMU_SDH0 0x54
+#define APMU_SDH1 0x58
+#define APMU_SDH2 0xe8
+#define APMU_SDH3 0xec
+#define APMU_USB 0x5c
+#define APMU_DISP0 0x4c
+#define APMU_DISP1 0x110
+#define APMU_CCIC0 0x50
+#define APMU_CCIC1 0xf4
+#define MPMU_UART_PLL 0x14
+
+static DEFINE_SPINLOCK(clk_lock);
+
+static struct clk_factor_masks uart_factor_masks = {
+ .factor = 2,
+ .num_mask = 0x1fff,
+ .den_mask = 0x1fff,
+ .num_shift = 16,
+ .den_shift = 0,
+};
+
+static struct clk_factor_tbl uart_factor_tbl[] = {
+ {.num = 14634, .den = 2165}, /*14.745MHZ */
+ {.num = 3521, .den = 689}, /*19.23MHZ */
+ {.num = 9679, .den = 5728}, /*58.9824MHZ */
+ {.num = 15850, .den = 9451}, /*59.429MHZ */
+};
+
+static const char *uart_parent[] = {"uart_pll", "vctcxo"};
+static const char *ssp_parent[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"};
+static const char *sdh_parent[] = {"pll1_4", "pll2", "usb_pll", "pll1"};
+static const char *disp_parent[] = {"pll1", "pll1_16", "pll2", "vctcxo"};
+static const char *ccic_parent[] = {"pll1_2", "pll1_16", "vctcxo"};
+
+void __init mmp2_clk_init(void)
+{
+ struct clk *clk;
+ struct clk *vctcxo;
+ void __iomem *mpmu_base;
+ void __iomem *apmu_base;
+ void __iomem *apbc_base;
+
+ mpmu_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K);
+ if (mpmu_base == NULL) {
+ pr_err("error to ioremap MPMU base\n");
+ return;
+ }
+
+ apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
+ if (apmu_base == NULL) {
+ pr_err("error to ioremap APMU base\n");
+ return;
+ }
+
+ apbc_base = ioremap(APB_PHYS_BASE + 0x15000, SZ_4K);
+ if (apbc_base == NULL) {
+ pr_err("error to ioremap APBC base\n");
+ return;
+ }
+
+ clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200);
+ clk_register_clkdev(clk, "clk32", NULL);
+
+ vctcxo = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT,
+ 26000000);
+ clk_register_clkdev(vctcxo, "vctcxo", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT,
+ 800000000);
+ clk_register_clkdev(clk, "pll1", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "usb_pll", NULL, CLK_IS_ROOT,
+ 480000000);
+ clk_register_clkdev(clk, "usb_pll", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll2", NULL, CLK_IS_ROOT,
+ 960000000);
+ clk_register_clkdev(clk, "pll2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_4", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_8", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_16", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_20", "pll1_4",
+ CLK_SET_RATE_PARENT, 1, 5);
+ clk_register_clkdev(clk, "pll1_20", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_3", "pll1",
+ CLK_SET_RATE_PARENT, 1, 3);
+ clk_register_clkdev(clk, "pll1_3", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_3",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_6", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_12", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_2", "pll2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_4", "pll2_2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_4", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_8", "pll2_4",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_8", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_16", "pll2_8",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_16", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_3", "pll2",
+ CLK_SET_RATE_PARENT, 1, 3);
+ clk_register_clkdev(clk, "pll2_3", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_6", "pll2_3",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_6", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll2_12", "pll2_6",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll2_12", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "vctcxo_2", "vctcxo",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "vctcxo_2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "vctcxo_4", "vctcxo_2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "vctcxo_4", NULL);
+
+ clk = mmp_clk_register_factor("uart_pll", "pll1_4", 0,
+ mpmu_base + MPMU_UART_PLL,
+ &uart_factor_masks, uart_factor_tbl,
+ ARRAY_SIZE(uart_factor_tbl));
+ clk_set_rate(clk, 14745600);
+ clk_register_clkdev(clk, "uart_pll", NULL);
+
+ clk = mmp_clk_register_apbc("twsi0", "vctcxo",
+ apbc_base + APBC_TWSI0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0");
+
+ clk = mmp_clk_register_apbc("twsi1", "vctcxo",
+ apbc_base + APBC_TWSI1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1");
+
+ clk = mmp_clk_register_apbc("twsi2", "vctcxo",
+ apbc_base + APBC_TWSI2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.2");
+
+ clk = mmp_clk_register_apbc("twsi3", "vctcxo",
+ apbc_base + APBC_TWSI3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.3");
+
+ clk = mmp_clk_register_apbc("twsi4", "vctcxo",
+ apbc_base + APBC_TWSI4, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.4");
+
+ clk = mmp_clk_register_apbc("twsi5", "vctcxo",
+ apbc_base + APBC_TWSI5, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.5");
+
+ clk = mmp_clk_register_apbc("gpio", "vctcxo",
+ apbc_base + APBC_GPIO, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa-gpio");
+
+ clk = mmp_clk_register_apbc("kpc", "clk32",
+ apbc_base + APBC_KPC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa27x-keypad");
+
+ clk = mmp_clk_register_apbc("rtc", "clk32",
+ apbc_base + APBC_RTC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-rtc");
+
+ clk = mmp_clk_register_apbc("pwm0", "vctcxo",
+ apbc_base + APBC_PWM0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp2-pwm.0");
+
+ clk = mmp_clk_register_apbc("pwm1", "vctcxo",
+ apbc_base + APBC_PWM1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp2-pwm.1");
+
+ clk = mmp_clk_register_apbc("pwm2", "vctcxo",
+ apbc_base + APBC_PWM2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp2-pwm.2");
+
+ clk = mmp_clk_register_apbc("pwm3", "vctcxo",
+ apbc_base + APBC_PWM3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp2-pwm.3");
+
+ clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, vctcxo);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("uart0", "uart0_mux",
+ apbc_base + APBC_UART0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");
+
+ clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, vctcxo);
+ clk_register_clkdev(clk, "uart_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("uart1", "uart1_mux",
+ apbc_base + APBC_UART1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");
+
+ clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART2, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, vctcxo);
+ clk_register_clkdev(clk, "uart_mux.2", NULL);
+
+ clk = mmp_clk_register_apbc("uart2", "uart2_mux",
+ apbc_base + APBC_UART2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");
+
+ clk = clk_register_mux(NULL, "uart3_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART3, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, vctcxo);
+ clk_register_clkdev(clk, "uart_mux.3", NULL);
+
+ clk = mmp_clk_register_apbc("uart3", "uart3_mux",
+ apbc_base + APBC_UART3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.3");
+
+ clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("ssp0", "ssp0_mux",
+ apbc_base + APBC_SSP0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.0");
+
+ clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("ssp1", "ssp1_mux",
+ apbc_base + APBC_SSP1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.1");
+
+ clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.2", NULL);
+
+ clk = mmp_clk_register_apbc("ssp2", "ssp2_mux",
+ apbc_base + APBC_SSP2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.2");
+
+ clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.3", NULL);
+
+ clk = mmp_clk_register_apbc("ssp3", "ssp3_mux",
+ apbc_base + APBC_SSP3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.3");
+
+ clk = clk_register_mux(NULL, "sdh_mux", sdh_parent,
+ ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_SDH0, 8, 2, 0, &clk_lock);
+ clk_register_clkdev(clk, "sdh_mux", NULL);
+
+ clk = clk_register_divider(NULL, "sdh_div", "sdh_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_SDH0,
+ 10, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
+ clk_register_clkdev(clk, "sdh_div", NULL);
+
+ clk = mmp_clk_register_apmu("sdh0", "sdh_div", apmu_base + APMU_SDH0,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxav3.0");
+
+ clk = mmp_clk_register_apmu("sdh1", "sdh_div", apmu_base + APMU_SDH1,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxav3.1");
+
+ clk = mmp_clk_register_apmu("sdh2", "sdh_div", apmu_base + APMU_SDH2,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxav3.2");
+
+ clk = mmp_clk_register_apmu("sdh3", "sdh_div", apmu_base + APMU_SDH3,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxav3.3");
+
+ clk = mmp_clk_register_apmu("usb", "usb_pll", apmu_base + APMU_USB,
+ 0x9, &clk_lock);
+ clk_register_clkdev(clk, "usb_clk", NULL);
+
+ clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
+ ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_DISP0, 6, 2, 0, &clk_lock);
+ clk_register_clkdev(clk, "disp_mux.0", NULL);
+
+ clk = clk_register_divider(NULL, "disp0_div", "disp0_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_DISP0,
+ 8, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
+ clk_register_clkdev(clk, "disp_div.0", NULL);
+
+ clk = mmp_clk_register_apmu("disp0", "disp0_div",
+ apmu_base + APMU_DISP0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-disp.0");
+
+ clk = clk_register_divider(NULL, "disp0_sphy_div", "disp0_mux", 0,
+ apmu_base + APMU_DISP0, 15, 5, 0, &clk_lock);
+ clk_register_clkdev(clk, "disp_sphy_div.0", NULL);
+
+ clk = mmp_clk_register_apmu("disp0_sphy", "disp0_sphy_div",
+ apmu_base + APMU_DISP0, 0x1024, &clk_lock);
+ clk_register_clkdev(clk, "disp_sphy.0", NULL);
+
+ clk = clk_register_mux(NULL, "disp1_mux", disp_parent,
+ ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_DISP1, 6, 2, 0, &clk_lock);
+ clk_register_clkdev(clk, "disp_mux.1", NULL);
+
+ clk = clk_register_divider(NULL, "disp1_div", "disp1_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_DISP1,
+ 8, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
+ clk_register_clkdev(clk, "disp_div.1", NULL);
+
+ clk = mmp_clk_register_apmu("disp1", "disp1_div",
+ apmu_base + APMU_DISP1, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-disp.1");
+
+ clk = mmp_clk_register_apmu("ccic_arbiter", "vctcxo",
+ apmu_base + APMU_CCIC0, 0x1800, &clk_lock);
+ clk_register_clkdev(clk, "ccic_arbiter", NULL);
+
+ clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
+ ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_CCIC0, 6, 2, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_mux.0", NULL);
+
+ clk = clk_register_divider(NULL, "ccic0_div", "ccic0_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 17, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
+ clk_register_clkdev(clk, "ccic_div.0", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0", "ccic0_div",
+ apmu_base + APMU_CCIC0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, "fnclk", "mmp-ccic.0");
+
+ clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_div",
+ apmu_base + APMU_CCIC0, 0x24, &clk_lock);
+ clk_register_clkdev(clk, "phyclk", "mmp-ccic.0");
+
+ clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_div",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 10, 5, 0, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk_div", "mmp-ccic.0");
+
+ clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div",
+ apmu_base + APMU_CCIC0, 0x300, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0");
+
+ clk = clk_register_mux(NULL, "ccic1_mux", ccic_parent,
+ ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_CCIC1, 6, 2, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_mux.1", NULL);
+
+ clk = clk_register_divider(NULL, "ccic1_div", "ccic1_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC1,
+ 16, 4, CLK_DIVIDER_ONE_BASED, &clk_lock);
+ clk_register_clkdev(clk, "ccic_div.1", NULL);
+
+ clk = mmp_clk_register_apmu("ccic1", "ccic1_div",
+ apmu_base + APMU_CCIC1, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, "fnclk", "mmp-ccic.1");
+
+ clk = mmp_clk_register_apmu("ccic1_phy", "ccic1_div",
+ apmu_base + APMU_CCIC1, 0x24, &clk_lock);
+ clk_register_clkdev(clk, "phyclk", "mmp-ccic.1");
+
+ clk = clk_register_divider(NULL, "ccic1_sphy_div", "ccic1_div",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC1,
+ 10, 5, 0, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk_div", "mmp-ccic.1");
+
+ clk = mmp_clk_register_apmu("ccic1_sphy", "ccic1_sphy_div",
+ apmu_base + APMU_CCIC1, 0x300, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk", "mmp-ccic.1");
+}
diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c
new file mode 100644
index 0000000..e8d036c
--- /dev/null
+++ b/drivers/clk/mmp/clk-pxa168.c
@@ -0,0 +1,346 @@
+/*
+ * pxa168 clock framework source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <mach/addr-map.h>
+
+#include "clk.h"
+
+#define APBC_RTC 0x28
+#define APBC_TWSI0 0x2c
+#define APBC_KPC 0x30
+#define APBC_UART0 0x0
+#define APBC_UART1 0x4
+#define APBC_GPIO 0x8
+#define APBC_PWM0 0xc
+#define APBC_PWM1 0x10
+#define APBC_PWM2 0x14
+#define APBC_PWM3 0x18
+#define APBC_SSP0 0x81c
+#define APBC_SSP1 0x820
+#define APBC_SSP2 0x84c
+#define APBC_SSP3 0x858
+#define APBC_SSP4 0x85c
+#define APBC_TWSI1 0x6c
+#define APBC_UART2 0x70
+#define APMU_SDH0 0x54
+#define APMU_SDH1 0x58
+#define APMU_USB 0x5c
+#define APMU_DISP0 0x4c
+#define APMU_CCIC0 0x50
+#define APMU_DFC 0x60
+#define MPMU_UART_PLL 0x14
+
+static DEFINE_SPINLOCK(clk_lock);
+
+static struct clk_factor_masks uart_factor_masks = {
+ .factor = 2,
+ .num_mask = 0x1fff,
+ .den_mask = 0x1fff,
+ .num_shift = 16,
+ .den_shift = 0,
+};
+
+static struct clk_factor_tbl uart_factor_tbl[] = {
+ {.num = 8125, .den = 1536}, /*14.745MHZ */
+};
+
+static const char *uart_parent[] = {"pll1_3_16", "uart_pll"};
+static const char *ssp_parent[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"};
+static const char *sdh_parent[] = {"pll1_12", "pll1_13"};
+static const char *disp_parent[] = {"pll1_2", "pll1_12"};
+static const char *ccic_parent[] = {"pll1_2", "pll1_12"};
+static const char *ccic_phy_parent[] = {"pll1_6", "pll1_12"};
+
+void __init pxa168_clk_init(void)
+{
+ struct clk *clk;
+ struct clk *uart_pll;
+ void __iomem *mpmu_base;
+ void __iomem *apmu_base;
+ void __iomem *apbc_base;
+
+ mpmu_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K);
+ if (mpmu_base == NULL) {
+ pr_err("error to ioremap MPMU base\n");
+ return;
+ }
+
+ apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
+ if (apmu_base == NULL) {
+ pr_err("error to ioremap APMU base\n");
+ return;
+ }
+
+ apbc_base = ioremap(APB_PHYS_BASE + 0x15000, SZ_4K);
+ if (apbc_base == NULL) {
+ pr_err("error to ioremap APBC base\n");
+ return;
+ }
+
+ clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200);
+ clk_register_clkdev(clk, "clk32", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT,
+ 26000000);
+ clk_register_clkdev(clk, "vctcxo", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT,
+ 624000000);
+ clk_register_clkdev(clk, "pll1", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_4", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_8", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_16", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_2",
+ CLK_SET_RATE_PARENT, 1, 3);
+ clk_register_clkdev(clk, "pll1_6", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_12", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_24", "pll1_12",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_24", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_48", "pll1_24",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_48", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_96", "pll1_48",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_96", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_13", "pll1",
+ CLK_SET_RATE_PARENT, 1, 13);
+ clk_register_clkdev(clk, "pll1_13", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_13_1_5", "pll1",
+ CLK_SET_RATE_PARENT, 2, 3);
+ clk_register_clkdev(clk, "pll1_13_1_5", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_2_1_5", "pll1",
+ CLK_SET_RATE_PARENT, 2, 3);
+ clk_register_clkdev(clk, "pll1_2_1_5", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_3_16", "pll1",
+ CLK_SET_RATE_PARENT, 3, 16);
+ clk_register_clkdev(clk, "pll1_3_16", NULL);
+
+ uart_pll = mmp_clk_register_factor("uart_pll", "pll1_4", 0,
+ mpmu_base + MPMU_UART_PLL,
+ &uart_factor_masks, uart_factor_tbl,
+ ARRAY_SIZE(uart_factor_tbl));
+ clk_set_rate(uart_pll, 14745600);
+ clk_register_clkdev(uart_pll, "uart_pll", NULL);
+
+ clk = mmp_clk_register_apbc("twsi0", "pll1_13_1_5",
+ apbc_base + APBC_TWSI0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0");
+
+ clk = mmp_clk_register_apbc("twsi1", "pll1_13_1_5",
+ apbc_base + APBC_TWSI1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1");
+
+ clk = mmp_clk_register_apbc("gpio", "vctcxo",
+ apbc_base + APBC_GPIO, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa-gpio");
+
+ clk = mmp_clk_register_apbc("kpc", "clk32",
+ apbc_base + APBC_KPC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa27x-keypad");
+
+ clk = mmp_clk_register_apbc("rtc", "clk32",
+ apbc_base + APBC_RTC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sa1100-rtc");
+
+ clk = mmp_clk_register_apbc("pwm0", "pll1_48",
+ apbc_base + APBC_PWM0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa168-pwm.0");
+
+ clk = mmp_clk_register_apbc("pwm1", "pll1_48",
+ apbc_base + APBC_PWM1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa168-pwm.1");
+
+ clk = mmp_clk_register_apbc("pwm2", "pll1_48",
+ apbc_base + APBC_PWM2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa168-pwm.2");
+
+ clk = mmp_clk_register_apbc("pwm3", "pll1_48",
+ apbc_base + APBC_PWM3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa168-pwm.3");
+
+ clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("uart0", "uart0_mux",
+ apbc_base + APBC_UART0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");
+
+ clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("uart1", "uart1_mux",
+ apbc_base + APBC_UART1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");
+
+ clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART2, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.2", NULL);
+
+ clk = mmp_clk_register_apbc("uart2", "uart2_mux",
+ apbc_base + APBC_UART2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");
+
+ clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("ssp0", "ssp0_mux", apbc_base + APBC_SSP0,
+ 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.0");
+
+ clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("ssp1", "ssp1_mux", apbc_base + APBC_SSP1,
+ 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.1");
+
+ clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.2", NULL);
+
+ clk = mmp_clk_register_apbc("ssp2", "ssp1_mux", apbc_base + APBC_SSP2,
+ 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.2");
+
+ clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.3", NULL);
+
+ clk = mmp_clk_register_apbc("ssp3", "ssp1_mux", apbc_base + APBC_SSP3,
+ 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.3");
+
+ clk = clk_register_mux(NULL, "ssp4_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP4, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.4", NULL);
+
+ clk = mmp_clk_register_apbc("ssp4", "ssp1_mux", apbc_base + APBC_SSP4,
+ 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.4");
+
+ clk = mmp_clk_register_apmu("dfc", "pll1_4", apmu_base + APMU_DFC,
+ 0x19b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa3xx-nand.0");
+
+ clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent,
+ ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "sdh0_mux", NULL);
+
+ clk = mmp_clk_register_apmu("sdh0", "sdh_mux", apmu_base + APMU_SDH0,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxa.0");
+
+ clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent,
+ ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "sdh1_mux", NULL);
+
+ clk = mmp_clk_register_apmu("sdh1", "sdh1_mux", apmu_base + APMU_SDH1,
+ 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxa.1");
+
+ clk = mmp_clk_register_apmu("usb", "usb_pll", apmu_base + APMU_USB,
+ 0x9, &clk_lock);
+ clk_register_clkdev(clk, "usb_clk", NULL);
+
+ clk = mmp_clk_register_apmu("sph", "usb_pll", apmu_base + APMU_USB,
+ 0x12, &clk_lock);
+ clk_register_clkdev(clk, "sph_clk", NULL);
+
+ clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
+ ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "disp_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("disp0", "disp0_mux",
+ apmu_base + APMU_DISP0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, "fnclk", "mmp-disp.0");
+
+ clk = mmp_clk_register_apmu("disp0_hclk", "disp0_mux",
+ apmu_base + APMU_DISP0, 0x24, &clk_lock);
+ clk_register_clkdev(clk, "hclk", "mmp-disp.0");
+
+ clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
+ ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0", "ccic0_mux",
+ apmu_base + APMU_CCIC0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, "fnclk", "mmp-ccic.0");
+
+ clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent,
+ ARRAY_SIZE(ccic_phy_parent),
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 7, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_phy_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux",
+ apmu_base + APMU_CCIC0, 0x24, &clk_lock);
+ clk_register_clkdev(clk, "phyclk", "mmp-ccic.0");
+
+ clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 10, 5, 0, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk_div", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div",
+ apmu_base + APMU_CCIC0, 0x300, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0");
+}
diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c
new file mode 100644
index 0000000..7048c31
--- /dev/null
+++ b/drivers/clk/mmp/clk-pxa910.c
@@ -0,0 +1,320 @@
+/*
+ * pxa910 clock framework source file
+ *
+ * Copyright (C) 2012 Marvell
+ * Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <mach/addr-map.h>
+
+#include "clk.h"
+
+#define APBC_RTC 0x28
+#define APBC_TWSI0 0x2c
+#define APBC_KPC 0x18
+#define APBC_UART0 0x0
+#define APBC_UART1 0x4
+#define APBC_GPIO 0x8
+#define APBC_PWM0 0xc
+#define APBC_PWM1 0x10
+#define APBC_PWM2 0x14
+#define APBC_PWM3 0x18
+#define APBC_SSP0 0x1c
+#define APBC_SSP1 0x20
+#define APBC_SSP2 0x4c
+#define APBCP_TWSI1 0x28
+#define APBCP_UART2 0x1c
+#define APMU_SDH0 0x54
+#define APMU_SDH1 0x58
+#define APMU_USB 0x5c
+#define APMU_DISP0 0x4c
+#define APMU_CCIC0 0x50
+#define APMU_DFC 0x60
+#define MPMU_UART_PLL 0x14
+
+static DEFINE_SPINLOCK(clk_lock);
+
+static struct clk_factor_masks uart_factor_masks = {
+ .factor = 2,
+ .num_mask = 0x1fff,
+ .den_mask = 0x1fff,
+ .num_shift = 16,
+ .den_shift = 0,
+};
+
+static struct clk_factor_tbl uart_factor_tbl[] = {
+ {.num = 8125, .den = 1536}, /*14.745MHZ */
+};
+
+static const char *uart_parent[] = {"pll1_3_16", "uart_pll"};
+static const char *ssp_parent[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"};
+static const char *sdh_parent[] = {"pll1_12", "pll1_13"};
+static const char *disp_parent[] = {"pll1_2", "pll1_12"};
+static const char *ccic_parent[] = {"pll1_2", "pll1_12"};
+static const char *ccic_phy_parent[] = {"pll1_6", "pll1_12"};
+
+void __init pxa910_clk_init(void)
+{
+ struct clk *clk;
+ struct clk *uart_pll;
+ void __iomem *mpmu_base;
+ void __iomem *apmu_base;
+ void __iomem *apbcp_base;
+ void __iomem *apbc_base;
+
+ mpmu_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K);
+ if (mpmu_base == NULL) {
+ pr_err("error to ioremap MPMU base\n");
+ return;
+ }
+
+ apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
+ if (apmu_base == NULL) {
+ pr_err("error to ioremap APMU base\n");
+ return;
+ }
+
+ apbcp_base = ioremap(APB_PHYS_BASE + 0x3b000, SZ_4K);
+ if (apbcp_base == NULL) {
+ pr_err("error to ioremap APBC extension base\n");
+ return;
+ }
+
+ apbc_base = ioremap(APB_PHYS_BASE + 0x15000, SZ_4K);
+ if (apbc_base == NULL) {
+ pr_err("error to ioremap APBC base\n");
+ return;
+ }
+
+ clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200);
+ clk_register_clkdev(clk, "clk32", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT,
+ 26000000);
+ clk_register_clkdev(clk, "vctcxo", NULL);
+
+ clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT,
+ 624000000);
+ clk_register_clkdev(clk, "pll1", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_2", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_4", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_8", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_16", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_2",
+ CLK_SET_RATE_PARENT, 1, 3);
+ clk_register_clkdev(clk, "pll1_6", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_12", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_24", "pll1_12",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_24", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_48", "pll1_24",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_48", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_96", "pll1_48",
+ CLK_SET_RATE_PARENT, 1, 2);
+ clk_register_clkdev(clk, "pll1_96", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_13", "pll1",
+ CLK_SET_RATE_PARENT, 1, 13);
+ clk_register_clkdev(clk, "pll1_13", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_13_1_5", "pll1",
+ CLK_SET_RATE_PARENT, 2, 3);
+ clk_register_clkdev(clk, "pll1_13_1_5", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_2_1_5", "pll1",
+ CLK_SET_RATE_PARENT, 2, 3);
+ clk_register_clkdev(clk, "pll1_2_1_5", NULL);
+
+ clk = clk_register_fixed_factor(NULL, "pll1_3_16", "pll1",
+ CLK_SET_RATE_PARENT, 3, 16);
+ clk_register_clkdev(clk, "pll1_3_16", NULL);
+
+ uart_pll = mmp_clk_register_factor("uart_pll", "pll1_4", 0,
+ mpmu_base + MPMU_UART_PLL,
+ &uart_factor_masks, uart_factor_tbl,
+ ARRAY_SIZE(uart_factor_tbl));
+ clk_set_rate(uart_pll, 14745600);
+ clk_register_clkdev(uart_pll, "uart_pll", NULL);
+
+ clk = mmp_clk_register_apbc("twsi0", "pll1_13_1_5",
+ apbc_base + APBC_TWSI0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0");
+
+ clk = mmp_clk_register_apbc("twsi1", "pll1_13_1_5",
+ apbcp_base + APBCP_TWSI1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1");
+
+ clk = mmp_clk_register_apbc("gpio", "vctcxo",
+ apbc_base + APBC_GPIO, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa-gpio");
+
+ clk = mmp_clk_register_apbc("kpc", "clk32",
+ apbc_base + APBC_KPC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa27x-keypad");
+
+ clk = mmp_clk_register_apbc("rtc", "clk32",
+ apbc_base + APBC_RTC, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sa1100-rtc");
+
+ clk = mmp_clk_register_apbc("pwm0", "pll1_48",
+ apbc_base + APBC_PWM0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa910-pwm.0");
+
+ clk = mmp_clk_register_apbc("pwm1", "pll1_48",
+ apbc_base + APBC_PWM1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa910-pwm.1");
+
+ clk = mmp_clk_register_apbc("pwm2", "pll1_48",
+ apbc_base + APBC_PWM2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa910-pwm.2");
+
+ clk = mmp_clk_register_apbc("pwm3", "pll1_48",
+ apbc_base + APBC_PWM3, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa910-pwm.3");
+
+ clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("uart0", "uart0_mux",
+ apbc_base + APBC_UART0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");
+
+ clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("uart1", "uart1_mux",
+ apbc_base + APBC_UART1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");
+
+ clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
+ ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ apbcp_base + APBCP_UART2, 4, 3, 0, &clk_lock);
+ clk_set_parent(clk, uart_pll);
+ clk_register_clkdev(clk, "uart_mux.2", NULL);
+
+ clk = mmp_clk_register_apbc("uart2", "uart2_mux",
+ apbcp_base + APBCP_UART2, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");
+
+ clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "uart_mux.0", NULL);
+
+ clk = mmp_clk_register_apbc("ssp0", "ssp0_mux",
+ apbc_base + APBC_SSP0, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.0");
+
+ clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
+ ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
+ clk_register_clkdev(clk, "ssp_mux.1", NULL);
+
+ clk = mmp_clk_register_apbc("ssp1", "ssp1_mux",
+ apbc_base + APBC_SSP1, 10, 0, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-ssp.1");
+
+ clk = mmp_clk_register_apmu("dfc", "pll1_4",
+ apmu_base + APMU_DFC, 0x19b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "pxa3xx-nand.0");
+
+ clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent,
+ ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "sdh0_mux", NULL);
+
+ clk = mmp_clk_register_apmu("sdh0", "sdh_mux",
+ apmu_base + APMU_SDH0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxa.0");
+
+ clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent,
+ ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "sdh1_mux", NULL);
+
+ clk = mmp_clk_register_apmu("sdh1", "sdh1_mux",
+ apmu_base + APMU_SDH1, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "sdhci-pxa.1");
+
+ clk = mmp_clk_register_apmu("usb", "usb_pll",
+ apmu_base + APMU_USB, 0x9, &clk_lock);
+ clk_register_clkdev(clk, "usb_clk", NULL);
+
+ clk = mmp_clk_register_apmu("sph", "usb_pll",
+ apmu_base + APMU_USB, 0x12, &clk_lock);
+ clk_register_clkdev(clk, "sph_clk", NULL);
+
+ clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
+ ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "disp_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("disp0", "disp0_mux",
+ apmu_base + APMU_DISP0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, NULL, "mmp-disp.0");
+
+ clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
+ ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0", "ccic0_mux",
+ apmu_base + APMU_CCIC0, 0x1b, &clk_lock);
+ clk_register_clkdev(clk, "fnclk", "mmp-ccic.0");
+
+ clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent,
+ ARRAY_SIZE(ccic_phy_parent),
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 7, 1, 0, &clk_lock);
+ clk_register_clkdev(clk, "ccic_phy_mux.0", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux",
+ apmu_base + APMU_CCIC0, 0x24, &clk_lock);
+ clk_register_clkdev(clk, "phyclk", "mmp-ccic.0");
+
+ clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_mux",
+ CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
+ 10, 5, 0, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk_div", NULL);
+
+ clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div",
+ apmu_base + APMU_CCIC0, 0x300, &clk_lock);
+ clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0");
+}
diff --git a/drivers/clk/mmp/clk.h b/drivers/clk/mmp/clk.h
new file mode 100644
index 0000000..ab86dd4
--- /dev/null
+++ b/drivers/clk/mmp/clk.h
@@ -0,0 +1,35 @@
+#ifndef __MACH_MMP_CLK_H
+#define __MACH_MMP_CLK_H
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#define APBC_NO_BUS_CTRL BIT(0)
+#define APBC_POWER_CTRL BIT(1)
+
+struct clk_factor_masks {
+ unsigned int factor;
+ unsigned int num_mask;
+ unsigned int den_mask;
+ unsigned int num_shift;
+ unsigned int den_shift;
+};
+
+struct clk_factor_tbl {
+ unsigned int num;
+ unsigned int den;
+};
+
+extern struct clk *mmp_clk_register_pll2(const char *name,
+ const char *parent_name, unsigned long flags);
+extern struct clk *mmp_clk_register_apbc(const char *name,
+ const char *parent_name, void __iomem *base,
+ unsigned int delay, unsigned int apbc_flags, spinlock_t *lock);
+extern struct clk *mmp_clk_register_apmu(const char *name,
+ const char *parent_name, void __iomem *base, u32 enable_mask,
+ spinlock_t *lock);
+extern struct clk *mmp_clk_register_factor(const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *base, struct clk_factor_masks *masks,
+ struct clk_factor_tbl *ftbl, unsigned int ftbl_cnt);
+#endif
diff --git a/drivers/clk/ux500/Makefile b/drivers/clk/ux500/Makefile
new file mode 100644
index 0000000..858fbfe
--- /dev/null
+++ b/drivers/clk/ux500/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for ux500 clocks
+#
+
+# Clock types
+obj-y += clk-prcc.o
+obj-y += clk-prcmu.o
+
+# Clock definitions
+obj-y += u8500_clk.o
+obj-y += u9540_clk.o
+obj-y += u8540_clk.o
diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c
new file mode 100644
index 0000000..7eee7f7
--- /dev/null
+++ b/drivers/clk/ux500/clk-prcc.c
@@ -0,0 +1,164 @@
+/*
+ * PRCC clock implementation for ux500 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk-private.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <mach/hardware.h>
+
+#include "clk.h"
+
+#define PRCC_PCKEN 0x000
+#define PRCC_PCKDIS 0x004
+#define PRCC_KCKEN 0x008
+#define PRCC_KCKDIS 0x00C
+#define PRCC_PCKSR 0x010
+#define PRCC_KCKSR 0x014
+
+#define to_clk_prcc(_hw) container_of(_hw, struct clk_prcc, hw)
+
+struct clk_prcc {
+ struct clk_hw hw;
+ void __iomem *base;
+ u32 cg_sel;
+ int is_enabled;
+};
+
+/* PRCC clock operations. */
+
+static int clk_prcc_pclk_enable(struct clk_hw *hw)
+{
+ struct clk_prcc *clk = to_clk_prcc(hw);
+
+ writel(clk->cg_sel, (clk->base + PRCC_PCKEN));
+ while (!(readl(clk->base + PRCC_PCKSR) & clk->cg_sel))
+ cpu_relax();
+
+ clk->is_enabled = 1;
+ return 0;
+}
+
+static void clk_prcc_pclk_disable(struct clk_hw *hw)
+{
+ struct clk_prcc *clk = to_clk_prcc(hw);
+
+ writel(clk->cg_sel, (clk->base + PRCC_PCKDIS));
+ clk->is_enabled = 0;
+}
+
+static int clk_prcc_kclk_enable(struct clk_hw *hw)
+{
+ struct clk_prcc *clk = to_clk_prcc(hw);
+
+ writel(clk->cg_sel, (clk->base + PRCC_KCKEN));
+ while (!(readl(clk->base + PRCC_KCKSR) & clk->cg_sel))
+ cpu_relax();
+
+ clk->is_enabled = 1;
+ return 0;
+}
+
+static void clk_prcc_kclk_disable(struct clk_hw *hw)
+{
+ struct clk_prcc *clk = to_clk_prcc(hw);
+
+ writel(clk->cg_sel, (clk->base + PRCC_KCKDIS));
+ clk->is_enabled = 0;
+}
+
+static int clk_prcc_is_enabled(struct clk_hw *hw)
+{
+ struct clk_prcc *clk = to_clk_prcc(hw);
+ return clk->is_enabled;
+}
+
+static struct clk_ops clk_prcc_pclk_ops = {
+ .enable = clk_prcc_pclk_enable,
+ .disable = clk_prcc_pclk_disable,
+ .is_enabled = clk_prcc_is_enabled,
+};
+
+static struct clk_ops clk_prcc_kclk_ops = {
+ .enable = clk_prcc_kclk_enable,
+ .disable = clk_prcc_kclk_disable,
+ .is_enabled = clk_prcc_is_enabled,
+};
+
+static struct clk *clk_reg_prcc(const char *name,
+ const char *parent_name,
+ resource_size_t phy_base,
+ u32 cg_sel,
+ unsigned long flags,
+ struct clk_ops *clk_prcc_ops)
+{
+ struct clk_prcc *clk;
+ struct clk_init_data clk_prcc_init;
+ struct clk *clk_reg;
+
+ if (!name) {
+ pr_err("clk_prcc: %s invalid arguments passed\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ clk = kzalloc(sizeof(struct clk_prcc), GFP_KERNEL);
+ if (!clk) {
+ pr_err("clk_prcc: %s could not allocate clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ clk->base = ioremap(phy_base, SZ_4K);
+ if (!clk->base)
+ goto free_clk;
+
+ clk->cg_sel = cg_sel;
+ clk->is_enabled = 1;
+
+ clk_prcc_init.name = name;
+ clk_prcc_init.ops = clk_prcc_ops;
+ clk_prcc_init.flags = flags;
+ clk_prcc_init.parent_names = (parent_name ? &parent_name : NULL);
+ clk_prcc_init.num_parents = (parent_name ? 1 : 0);
+ clk->hw.init = &clk_prcc_init;
+
+ clk_reg = clk_register(NULL, &clk->hw);
+ if (IS_ERR_OR_NULL(clk_reg))
+ goto unmap_clk;
+
+ return clk_reg;
+
+unmap_clk:
+ iounmap(clk->base);
+free_clk:
+ kfree(clk);
+ pr_err("clk_prcc: %s failed to register clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+}
+
+struct clk *clk_reg_prcc_pclk(const char *name,
+ const char *parent_name,
+ resource_size_t phy_base,
+ u32 cg_sel,
+ unsigned long flags)
+{
+ return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags,
+ &clk_prcc_pclk_ops);
+}
+
+struct clk *clk_reg_prcc_kclk(const char *name,
+ const char *parent_name,
+ resource_size_t phy_base,
+ u32 cg_sel,
+ unsigned long flags)
+{
+ return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags,
+ &clk_prcc_kclk_ops);
+}
diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c
new file mode 100644
index 0000000..930cdfe
--- /dev/null
+++ b/drivers/clk/ux500/clk-prcmu.c
@@ -0,0 +1,252 @@
+/*
+ * PRCMU clock implementation for ux500 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk-private.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk.h"
+
+#define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw)
+
+struct clk_prcmu {
+ struct clk_hw hw;
+ u8 cg_sel;
+ int is_enabled;
+};
+
+/* PRCMU clock operations. */
+
+static int clk_prcmu_prepare(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ return prcmu_request_clock(clk->cg_sel, true);
+}
+
+static void clk_prcmu_unprepare(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ if (prcmu_request_clock(clk->cg_sel, false))
+ pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
+ hw->init->name);
+}
+
+static int clk_prcmu_enable(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ clk->is_enabled = 1;
+ return 0;
+}
+
+static void clk_prcmu_disable(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ clk->is_enabled = 0;
+}
+
+static int clk_prcmu_is_enabled(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ return clk->is_enabled;
+}
+
+static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ return prcmu_clock_rate(clk->cg_sel);
+}
+
+static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ return prcmu_round_clock_rate(clk->cg_sel, rate);
+}
+
+static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+ return prcmu_set_clock_rate(clk->cg_sel, rate);
+}
+
+static int request_ape_opp100(bool enable)
+{
+ static int reqs;
+ int err = 0;
+
+ if (enable) {
+ if (!reqs)
+ err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
+ "clock", 100);
+ if (!err)
+ reqs++;
+ } else {
+ reqs--;
+ if (!reqs)
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
+ "clock");
+ }
+ return err;
+}
+
+static int clk_prcmu_opp_prepare(struct clk_hw *hw)
+{
+ int err;
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+
+ err = request_ape_opp100(true);
+ if (err) {
+ pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n",
+ __func__, hw->init->name);
+ return err;
+ }
+
+ err = prcmu_request_clock(clk->cg_sel, true);
+ if (err)
+ request_ape_opp100(false);
+
+ return err;
+}
+
+static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
+{
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+
+ if (prcmu_request_clock(clk->cg_sel, false))
+ goto out_error;
+ if (request_ape_opp100(false))
+ goto out_error;
+ return;
+
+out_error:
+ pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
+ hw->init->name);
+}
+
+static struct clk_ops clk_prcmu_scalable_ops = {
+ .prepare = clk_prcmu_prepare,
+ .unprepare = clk_prcmu_unprepare,
+ .enable = clk_prcmu_enable,
+ .disable = clk_prcmu_disable,
+ .is_enabled = clk_prcmu_is_enabled,
+ .recalc_rate = clk_prcmu_recalc_rate,
+ .round_rate = clk_prcmu_round_rate,
+ .set_rate = clk_prcmu_set_rate,
+};
+
+static struct clk_ops clk_prcmu_gate_ops = {
+ .prepare = clk_prcmu_prepare,
+ .unprepare = clk_prcmu_unprepare,
+ .enable = clk_prcmu_enable,
+ .disable = clk_prcmu_disable,
+ .is_enabled = clk_prcmu_is_enabled,
+ .recalc_rate = clk_prcmu_recalc_rate,
+};
+
+static struct clk_ops clk_prcmu_rate_ops = {
+ .is_enabled = clk_prcmu_is_enabled,
+ .recalc_rate = clk_prcmu_recalc_rate,
+};
+
+static struct clk_ops clk_prcmu_opp_gate_ops = {
+ .prepare = clk_prcmu_opp_prepare,
+ .unprepare = clk_prcmu_opp_unprepare,
+ .enable = clk_prcmu_enable,
+ .disable = clk_prcmu_disable,
+ .is_enabled = clk_prcmu_is_enabled,
+ .recalc_rate = clk_prcmu_recalc_rate,
+};
+
+static struct clk *clk_reg_prcmu(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags,
+ struct clk_ops *clk_prcmu_ops)
+{
+ struct clk_prcmu *clk;
+ struct clk_init_data clk_prcmu_init;
+ struct clk *clk_reg;
+
+ if (!name) {
+ pr_err("clk_prcmu: %s invalid arguments passed\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ clk = kzalloc(sizeof(struct clk_prcmu), GFP_KERNEL);
+ if (!clk) {
+ pr_err("clk_prcmu: %s could not allocate clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ clk->cg_sel = cg_sel;
+ clk->is_enabled = 1;
+ /* "rate" can be used for changing the initial frequency */
+ if (rate)
+ prcmu_set_clock_rate(cg_sel, rate);
+
+ clk_prcmu_init.name = name;
+ clk_prcmu_init.ops = clk_prcmu_ops;
+ clk_prcmu_init.flags = flags;
+ clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL);
+ clk_prcmu_init.num_parents = (parent_name ? 1 : 0);
+ clk->hw.init = &clk_prcmu_init;
+
+ clk_reg = clk_register(NULL, &clk->hw);
+ if (IS_ERR_OR_NULL(clk_reg))
+ goto free_clk;
+
+ return clk_reg;
+
+free_clk:
+ kfree(clk);
+ pr_err("clk_prcmu: %s failed to register clk\n", __func__);
+ return ERR_PTR(-ENOMEM);
+}
+
+struct clk *clk_reg_prcmu_scalable(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags)
+{
+ return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags,
+ &clk_prcmu_scalable_ops);
+}
+
+struct clk *clk_reg_prcmu_gate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags)
+{
+ return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
+ &clk_prcmu_gate_ops);
+}
+
+struct clk *clk_reg_prcmu_rate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags)
+{
+ return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
+ &clk_prcmu_rate_ops);
+}
+
+struct clk *clk_reg_prcmu_opp_gate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags)
+{
+ return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
+ &clk_prcmu_opp_gate_ops);
+}
diff --git a/drivers/clk/ux500/clk.h b/drivers/clk/ux500/clk.h
new file mode 100644
index 0000000..836d7d1
--- /dev/null
+++ b/drivers/clk/ux500/clk.h
@@ -0,0 +1,48 @@
+/*
+ * Clocks for ux500 platforms
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef __UX500_CLK_H
+#define __UX500_CLK_H
+
+#include <linux/clk.h>
+
+struct clk *clk_reg_prcc_pclk(const char *name,
+ const char *parent_name,
+ unsigned int phy_base,
+ u32 cg_sel,
+ unsigned long flags);
+
+struct clk *clk_reg_prcc_kclk(const char *name,
+ const char *parent_name,
+ unsigned int phy_base,
+ u32 cg_sel,
+ unsigned long flags);
+
+struct clk *clk_reg_prcmu_scalable(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags);
+
+struct clk *clk_reg_prcmu_gate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags);
+
+struct clk *clk_reg_prcmu_rate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags);
+
+struct clk *clk_reg_prcmu_opp_gate(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long flags);
+
+#endif /* __UX500_CLK_H */
diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c
new file mode 100644
index 0000000..ca4a25e
--- /dev/null
+++ b/drivers/clk/ux500/u8500_clk.c
@@ -0,0 +1,477 @@
+/*
+ * Clock definitions for u8500 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/platform_data/clk-ux500.h>
+
+#include "clk.h"
+
+void u8500_clk_init(void)
+{
+ struct prcmu_fw_version *fw_version;
+ const char *sgaclk_parent = NULL;
+ struct clk *clk;
+
+ /* Clock sources */
+ clk = clk_reg_prcmu_gate("soc0_pll", NULL, PRCMU_PLLSOC0,
+ CLK_IS_ROOT|CLK_IGNORE_UNUSED);
+ clk_register_clkdev(clk, "soc0_pll", NULL);
+
+ clk = clk_reg_prcmu_gate("soc1_pll", NULL, PRCMU_PLLSOC1,
+ CLK_IS_ROOT|CLK_IGNORE_UNUSED);
+ clk_register_clkdev(clk, "soc1_pll", NULL);
+
+ clk = clk_reg_prcmu_gate("ddr_pll", NULL, PRCMU_PLLDDR,
+ CLK_IS_ROOT|CLK_IGNORE_UNUSED);
+ clk_register_clkdev(clk, "ddr_pll", NULL);
+
+ /* FIXME: Add sys, ulp and int clocks here. */
+
+ clk = clk_register_fixed_rate(NULL, "rtc32k", "NULL",
+ CLK_IS_ROOT|CLK_IGNORE_UNUSED,
+ 32768);
+ clk_register_clkdev(clk, "clk32k", NULL);
+ clk_register_clkdev(clk, NULL, "rtc-pl031");
+
+ /* PRCMU clocks */
+ fw_version = prcmu_get_fw_version();
+ if (fw_version != NULL) {
+ switch (fw_version->project) {
+ case PRCMU_FW_PROJECT_U8500_C2:
+ case PRCMU_FW_PROJECT_U8520:
+ case PRCMU_FW_PROJECT_U8420:
+ sgaclk_parent = "soc0_pll";
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (sgaclk_parent)
+ clk = clk_reg_prcmu_gate("sgclk", sgaclk_parent,
+ PRCMU_SGACLK, 0);
+ else
+ clk = clk_reg_prcmu_gate("sgclk", NULL,
+ PRCMU_SGACLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "mali");
+
+ clk = clk_reg_prcmu_gate("uartclk", NULL, PRCMU_UARTCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "UART");
+
+ clk = clk_reg_prcmu_gate("msp02clk", NULL, PRCMU_MSP02CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "MSP02");
+
+ clk = clk_reg_prcmu_gate("msp1clk", NULL, PRCMU_MSP1CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "MSP1");
+
+ clk = clk_reg_prcmu_gate("i2cclk", NULL, PRCMU_I2CCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "I2C");
+
+ clk = clk_reg_prcmu_gate("slimclk", NULL, PRCMU_SLIMCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "slim");
+
+ clk = clk_reg_prcmu_gate("per1clk", NULL, PRCMU_PER1CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH1");
+
+ clk = clk_reg_prcmu_gate("per2clk", NULL, PRCMU_PER2CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH2");
+
+ clk = clk_reg_prcmu_gate("per3clk", NULL, PRCMU_PER3CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH3");
+
+ clk = clk_reg_prcmu_gate("per5clk", NULL, PRCMU_PER5CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH5");
+
+ clk = clk_reg_prcmu_gate("per6clk", NULL, PRCMU_PER6CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH6");
+
+ clk = clk_reg_prcmu_gate("per7clk", NULL, PRCMU_PER7CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "PERIPH7");
+
+ clk = clk_reg_prcmu_scalable("lcdclk", NULL, PRCMU_LCDCLK, 0,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "lcd");
+ clk_register_clkdev(clk, "lcd", "mcde");
+
+ clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BMLCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "bml");
+
+ clk = clk_reg_prcmu_scalable("hsitxclk", NULL, PRCMU_HSITXCLK, 0,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+
+ clk = clk_reg_prcmu_scalable("hsirxclk", NULL, PRCMU_HSIRXCLK, 0,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+
+ clk = clk_reg_prcmu_scalable("hdmiclk", NULL, PRCMU_HDMICLK, 0,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "hdmi");
+ clk_register_clkdev(clk, "hdmi", "mcde");
+
+ clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "apeat");
+
+ clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK,
+ CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "apetrace");
+
+ clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "mcde");
+ clk_register_clkdev(clk, "mcde", "mcde");
+ clk_register_clkdev(clk, "dsisys", "dsilink.0");
+ clk_register_clkdev(clk, "dsisys", "dsilink.1");
+ clk_register_clkdev(clk, "dsisys", "dsilink.2");
+
+ clk = clk_reg_prcmu_opp_gate("ipi2cclk", NULL, PRCMU_IPI2CCLK,
+ CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "ipi2");
+
+ clk = clk_reg_prcmu_gate("dsialtclk", NULL, PRCMU_DSIALTCLK,
+ CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "dsialt");
+
+ clk = clk_reg_prcmu_gate("dmaclk", NULL, PRCMU_DMACLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "dma40.0");
+
+ clk = clk_reg_prcmu_gate("b2r2clk", NULL, PRCMU_B2R2CLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "b2r2");
+ clk_register_clkdev(clk, NULL, "b2r2_core");
+ clk_register_clkdev(clk, NULL, "U8500-B2R2.0");
+
+ clk = clk_reg_prcmu_scalable("tvclk", NULL, PRCMU_TVCLK, 0,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "tv");
+ clk_register_clkdev(clk, "tv", "mcde");
+
+ clk = clk_reg_prcmu_gate("sspclk", NULL, PRCMU_SSPCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "SSP");
+
+ clk = clk_reg_prcmu_gate("rngclk", NULL, PRCMU_RNGCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "rngclk");
+
+ clk = clk_reg_prcmu_gate("uiccclk", NULL, PRCMU_UICCCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "uicc");
+
+ /*
+ * FIXME: The MTU clocks might need some kind of "parent muxed join"
+ * and these have no K-clocks. For now, we ignore the missing
+ * connection to the corresponding P-clocks, p6_mtu0_clk and
+ * p6_mtu1_clk. Instead timclk is used which is the valid parent.
+ */
+ clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "mtu0");
+ clk_register_clkdev(clk, NULL, "mtu1");
+
+ clk = clk_reg_prcmu_gate("sdmmcclk", NULL, PRCMU_SDMMCCLK, CLK_IS_ROOT);
+ clk_register_clkdev(clk, NULL, "sdmmc");
+
+
+ clk = clk_reg_prcmu_scalable("dsi_pll", "hdmiclk",
+ PRCMU_PLLDSI, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsihs2", "mcde");
+ clk_register_clkdev(clk, "dsihs2", "dsilink.2");
+
+
+ clk = clk_reg_prcmu_scalable("dsi0clk", "dsi_pll",
+ PRCMU_DSI0CLK, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsihs0", "mcde");
+ clk_register_clkdev(clk, "dsihs0", "dsilink.0");
+
+ clk = clk_reg_prcmu_scalable("dsi1clk", "dsi_pll",
+ PRCMU_DSI1CLK, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsihs1", "mcde");
+ clk_register_clkdev(clk, "dsihs1", "dsilink.1");
+
+ clk = clk_reg_prcmu_scalable("dsi0escclk", "tvclk",
+ PRCMU_DSI0ESCCLK, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsilp0", "dsilink.0");
+ clk_register_clkdev(clk, "dsilp0", "mcde");
+
+ clk = clk_reg_prcmu_scalable("dsi1escclk", "tvclk",
+ PRCMU_DSI1ESCCLK, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsilp1", "dsilink.1");
+ clk_register_clkdev(clk, "dsilp1", "mcde");
+
+ clk = clk_reg_prcmu_scalable("dsi2escclk", "tvclk",
+ PRCMU_DSI2ESCCLK, 0, CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, "dsilp2", "dsilink.2");
+ clk_register_clkdev(clk, "dsilp2", "mcde");
+
+ clk = clk_reg_prcmu_rate("smp_twd", NULL, PRCMU_ARMSS,
+ CLK_IS_ROOT|CLK_GET_RATE_NOCACHE|
+ CLK_IGNORE_UNUSED);
+ clk_register_clkdev(clk, NULL, "smp_twd");
+
+ /*
+ * FIXME: Add special handled PRCMU clocks here:
+ * 1. clk_arm, use PRCMU_ARMCLK.
+ * 2. clkout0yuv, use PRCMU as parent + need regulator + pinctrl.
+ * 3. ab9540_clkout1yuv, see clkout0yuv
+ */
+
+ /* PRCC P-clocks */
+ clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", U8500_CLKRST1_BASE,
+ BIT(0), 0);
+ clk_register_clkdev(clk, "apb_pclk", "uart0");
+
+ clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", U8500_CLKRST1_BASE,
+ BIT(1), 0);
+ clk_register_clkdev(clk, "apb_pclk", "uart1");
+
+ clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", U8500_CLKRST1_BASE,
+ BIT(2), 0);
+ clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", U8500_CLKRST1_BASE,
+ BIT(3), 0);
+ clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", U8500_CLKRST1_BASE,
+ BIT(4), 0);
+
+ clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", U8500_CLKRST1_BASE,
+ BIT(5), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi0");
+
+ clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", U8500_CLKRST1_BASE,
+ BIT(6), 0);
+
+ clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", U8500_CLKRST1_BASE,
+ BIT(7), 0);
+ clk_register_clkdev(clk, NULL, "spi3");
+
+ clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", U8500_CLKRST1_BASE,
+ BIT(8), 0);
+
+ clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", U8500_CLKRST1_BASE,
+ BIT(9), 0);
+ clk_register_clkdev(clk, NULL, "gpio.0");
+ clk_register_clkdev(clk, NULL, "gpio.1");
+ clk_register_clkdev(clk, NULL, "gpioblock0");
+
+ clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", U8500_CLKRST1_BASE,
+ BIT(10), 0);
+ clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", U8500_CLKRST1_BASE,
+ BIT(11), 0);
+
+ clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", U8500_CLKRST2_BASE,
+ BIT(0), 0);
+
+ clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", U8500_CLKRST2_BASE,
+ BIT(1), 0);
+ clk_register_clkdev(clk, NULL, "spi2");
+
+ clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", U8500_CLKRST2_BASE,
+ BIT(2), 0);
+ clk_register_clkdev(clk, NULL, "spi1");
+
+ clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", U8500_CLKRST2_BASE,
+ BIT(3), 0);
+ clk_register_clkdev(clk, NULL, "pwl");
+
+ clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", U8500_CLKRST2_BASE,
+ BIT(4), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi4");
+
+ clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", U8500_CLKRST2_BASE,
+ BIT(5), 0);
+
+ clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", U8500_CLKRST2_BASE,
+ BIT(6), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi1");
+
+
+ clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", U8500_CLKRST2_BASE,
+ BIT(7), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi3");
+
+ clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", U8500_CLKRST2_BASE,
+ BIT(8), 0);
+ clk_register_clkdev(clk, NULL, "spi0");
+
+ clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", U8500_CLKRST2_BASE,
+ BIT(9), 0);
+ clk_register_clkdev(clk, "hsir_hclk", "ste_hsi.0");
+
+ clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", U8500_CLKRST2_BASE,
+ BIT(10), 0);
+ clk_register_clkdev(clk, "hsit_hclk", "ste_hsi.0");
+
+ clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", U8500_CLKRST2_BASE,
+ BIT(11), 0);
+ clk_register_clkdev(clk, NULL, "gpio.6");
+ clk_register_clkdev(clk, NULL, "gpio.7");
+ clk_register_clkdev(clk, NULL, "gpioblock1");
+
+ clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", U8500_CLKRST2_BASE,
+ BIT(11), 0);
+
+ clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", U8500_CLKRST3_BASE,
+ BIT(0), 0);
+ clk_register_clkdev(clk, NULL, "fsmc");
+
+ clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", U8500_CLKRST3_BASE,
+ BIT(1), 0);
+ clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", U8500_CLKRST3_BASE,
+ BIT(2), 0);
+ clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", U8500_CLKRST3_BASE,
+ BIT(3), 0);
+
+ clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", U8500_CLKRST3_BASE,
+ BIT(4), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi2");
+
+ clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", U8500_CLKRST3_BASE,
+ BIT(5), 0);
+
+ clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", U8500_CLKRST3_BASE,
+ BIT(6), 0);
+ clk_register_clkdev(clk, "apb_pclk", "uart2");
+
+ clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", U8500_CLKRST3_BASE,
+ BIT(7), 0);
+ clk_register_clkdev(clk, "apb_pclk", "sdi5");
+
+ clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", U8500_CLKRST3_BASE,
+ BIT(8), 0);
+ clk_register_clkdev(clk, NULL, "gpio.2");
+ clk_register_clkdev(clk, NULL, "gpio.3");
+ clk_register_clkdev(clk, NULL, "gpio.4");
+ clk_register_clkdev(clk, NULL, "gpio.5");
+ clk_register_clkdev(clk, NULL, "gpioblock2");
+
+ clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", U8500_CLKRST5_BASE,
+ BIT(0), 0);
+ clk_register_clkdev(clk, "usb", "musb-ux500.0");
+
+ clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", U8500_CLKRST5_BASE,
+ BIT(1), 0);
+ clk_register_clkdev(clk, NULL, "gpio.8");
+ clk_register_clkdev(clk, NULL, "gpioblock3");
+
+ clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", U8500_CLKRST6_BASE,
+ BIT(0), 0);
+
+ clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", U8500_CLKRST6_BASE,
+ BIT(1), 0);
+ clk_register_clkdev(clk, NULL, "cryp0");
+ clk_register_clkdev(clk, NULL, "cryp1");
+
+ clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", U8500_CLKRST6_BASE,
+ BIT(2), 0);
+ clk_register_clkdev(clk, NULL, "hash0");
+
+ clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", U8500_CLKRST6_BASE,
+ BIT(3), 0);
+ clk_register_clkdev(clk, NULL, "pka");
+
+ clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", U8500_CLKRST6_BASE,
+ BIT(4), 0);
+ clk_register_clkdev(clk, NULL, "hash1");
+
+ clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", U8500_CLKRST6_BASE,
+ BIT(5), 0);
+ clk_register_clkdev(clk, NULL, "cfgreg");
+
+ clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", U8500_CLKRST6_BASE,
+ BIT(6), 0);
+ clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", U8500_CLKRST6_BASE,
+ BIT(7), 0);
+
+ /* PRCC K-clocks
+ *
+ * FIXME: Some drivers requires PERPIH[n| to be automatically enabled
+ * by enabling just the K-clock, even if it is not a valid parent to
+ * the K-clock. Until drivers get fixed we might need some kind of
+ * "parent muxed join".
+ */
+
+ /* Periph1 */
+ clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk",
+ U8500_CLKRST1_BASE, BIT(0), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "uart0");
+
+ clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk",
+ U8500_CLKRST1_BASE, BIT(1), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "uart1");
+
+ clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk",
+ U8500_CLKRST1_BASE, BIT(2), CLK_SET_RATE_GATE);
+ clk = clk_reg_prcc_kclk("p1_msp0_kclk", "msp02clk",
+ U8500_CLKRST1_BASE, BIT(3), CLK_SET_RATE_GATE);
+ clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk",
+ U8500_CLKRST1_BASE, BIT(4), CLK_SET_RATE_GATE);
+
+ clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmcclk",
+ U8500_CLKRST1_BASE, BIT(5), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi0");
+
+ clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk",
+ U8500_CLKRST1_BASE, BIT(6), CLK_SET_RATE_GATE);
+ clk = clk_reg_prcc_kclk("p1_slimbus0_kclk", "slimclk",
+ U8500_CLKRST1_BASE, BIT(3), CLK_SET_RATE_GATE);
+ /* FIXME: Redefinition of BIT(3). */
+ clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk",
+ U8500_CLKRST1_BASE, BIT(9), CLK_SET_RATE_GATE);
+ clk = clk_reg_prcc_kclk("p1_msp3_kclk", "msp1clk",
+ U8500_CLKRST1_BASE, BIT(10), CLK_SET_RATE_GATE);
+
+ /* Periph2 */
+ clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk",
+ U8500_CLKRST2_BASE, BIT(0), CLK_SET_RATE_GATE);
+
+ clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmcclk",
+ U8500_CLKRST2_BASE, BIT(2), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi4");
+
+ clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk",
+ U8500_CLKRST2_BASE, BIT(3), CLK_SET_RATE_GATE);
+
+ clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmcclk",
+ U8500_CLKRST2_BASE, BIT(4), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi1");
+
+ clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk",
+ U8500_CLKRST2_BASE, BIT(5), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi3");
+
+ /* Note that rate is received from parent. */
+ clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk",
+ U8500_CLKRST2_BASE, BIT(6),
+ CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT);
+ clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk",
+ U8500_CLKRST2_BASE, BIT(7),
+ CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT);
+
+ /* Periph3 */
+ clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk",
+ U8500_CLKRST3_BASE, BIT(1), CLK_SET_RATE_GATE);
+ clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk",
+ U8500_CLKRST3_BASE, BIT(2), CLK_SET_RATE_GATE);
+ clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk",
+ U8500_CLKRST3_BASE, BIT(3), CLK_SET_RATE_GATE);
+
+ clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmcclk",
+ U8500_CLKRST3_BASE, BIT(4), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi2");
+
+ clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k",
+ U8500_CLKRST3_BASE, BIT(5), CLK_SET_RATE_GATE);
+
+ clk = clk_reg_prcc_kclk("p3_uart2_kclk", "uartclk",
+ U8500_CLKRST3_BASE, BIT(6), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "uart2");
+
+ clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk",
+ U8500_CLKRST3_BASE, BIT(7), CLK_SET_RATE_GATE);
+ clk_register_clkdev(clk, NULL, "sdi5");
+
+ /* Periph6 */
+ clk = clk_reg_prcc_kclk("p3_rng_kclk", "rngclk",
+ U8500_CLKRST6_BASE, BIT(0), CLK_SET_RATE_GATE);
+
+}
diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c
new file mode 100644
index 0000000..10adfd2
--- /dev/null
+++ b/drivers/clk/ux500/u8540_clk.c
@@ -0,0 +1,21 @@
+/*
+ * Clock definitions for u8540 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/platform_data/clk-ux500.h>
+
+#include "clk.h"
+
+void u8540_clk_init(void)
+{
+ /* register clocks here */
+}
diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c
new file mode 100644
index 0000000..dbc0191
--- /dev/null
+++ b/drivers/clk/ux500/u9540_clk.c
@@ -0,0 +1,21 @@
+/*
+ * Clock definitions for u9540 platform.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/platform_data/clk-ux500.h>
+
+#include "clk.h"
+
+void u9540_clk_init(void)
+{
+ /* register clocks here */
+}
diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile
index 50cf6a2..c0a0f64 100644
--- a/drivers/clk/versatile/Makefile
+++ b/drivers/clk/versatile/Makefile
@@ -1,3 +1,4 @@
# Makefile for Versatile-specific clocks
obj-$(CONFIG_ICST) += clk-icst.o
obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o
+obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o
diff --git a/drivers/clk/versatile/clk-realview.c b/drivers/clk/versatile/clk-realview.c
new file mode 100644
index 0000000..e21a99c
--- /dev/null
+++ b/drivers/clk/versatile/clk-realview.c
@@ -0,0 +1,114 @@
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+
+#include <mach/hardware.h>
+#include <mach/platform.h>
+
+#include "clk-icst.h"
+
+/*
+ * Implementation of the ARM RealView clock trees.
+ */
+
+static void __iomem *sys_lock;
+static void __iomem *sys_vcoreg;
+
+/**
+ * realview_oscvco_get() - get ICST OSC settings for the RealView
+ */
+static struct icst_vco realview_oscvco_get(void)
+{
+ u32 val;
+ struct icst_vco vco;
+
+ val = readl(sys_vcoreg);
+ vco.v = val & 0x1ff;
+ vco.r = (val >> 9) & 0x7f;
+ vco.s = (val >> 16) & 03;
+ return vco;
+}
+
+static void realview_oscvco_set(struct icst_vco vco)
+{
+ u32 val;
+
+ val = readl(sys_vcoreg) & ~0x7ffff;
+ val |= vco.v | (vco.r << 9) | (vco.s << 16);
+
+ /* This magic unlocks the CM VCO so it can be controlled */
+ writel(0xa05f, sys_lock);
+ writel(val, sys_vcoreg);
+ /* This locks the CM again */
+ writel(0, sys_lock);
+}
+
+static const struct icst_params realview_oscvco_params = {
+ .ref = 24000000,
+ .vco_max = ICST307_VCO_MAX,
+ .vco_min = ICST307_VCO_MIN,
+ .vd_min = 4 + 8,
+ .vd_max = 511 + 8,
+ .rd_min = 1 + 2,
+ .rd_max = 127 + 2,
+ .s2div = icst307_s2div,
+ .idx2s = icst307_idx2s,
+};
+
+static const struct clk_icst_desc __initdata realview_icst_desc = {
+ .params = &realview_oscvco_params,
+ .getvco = realview_oscvco_get,
+ .setvco = realview_oscvco_set,
+};
+
+/*
+ * realview_clk_init() - set up the RealView clock tree
+ */
+void __init realview_clk_init(void __iomem *sysbase, bool is_pb1176)
+{
+ struct clk *clk;
+
+ sys_lock = sysbase + REALVIEW_SYS_LOCK_OFFSET;
+ if (is_pb1176)
+ sys_vcoreg = sysbase + REALVIEW_SYS_OSC0_OFFSET;
+ else
+ sys_vcoreg = sysbase + REALVIEW_SYS_OSC4_OFFSET;
+
+
+ /* APB clock dummy */
+ clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0);
+ clk_register_clkdev(clk, "apb_pclk", NULL);
+
+ /* 24 MHz clock */
+ clk = clk_register_fixed_rate(NULL, "clk24mhz", NULL, CLK_IS_ROOT,
+ 24000000);
+ clk_register_clkdev(clk, NULL, "dev:uart0");
+ clk_register_clkdev(clk, NULL, "dev:uart1");
+ clk_register_clkdev(clk, NULL, "dev:uart2");
+ clk_register_clkdev(clk, NULL, "fpga:kmi0");
+ clk_register_clkdev(clk, NULL, "fpga:kmi1");
+ clk_register_clkdev(clk, NULL, "fpga:mmc0");
+ clk_register_clkdev(clk, NULL, "dev:ssp0");
+ if (is_pb1176) {
+ /*
+ * UART3 is on the dev chip in PB1176
+ * UART4 only exists in PB1176
+ */
+ clk_register_clkdev(clk, NULL, "dev:uart3");
+ clk_register_clkdev(clk, NULL, "dev:uart4");
+ } else
+ clk_register_clkdev(clk, NULL, "fpga:uart3");
+
+
+ /* 1 MHz clock */
+ clk = clk_register_fixed_rate(NULL, "clk1mhz", NULL, CLK_IS_ROOT,
+ 1000000);
+ clk_register_clkdev(clk, NULL, "sp804");
+
+ /* ICST VCO clock */
+ clk = icst_clk_register(NULL, &realview_icst_desc);
+ clk_register_clkdev(clk, NULL, "dev:clcd");
+ clk_register_clkdev(clk, NULL, "issp:clcd");
+}
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index d53cd0a..6a78073 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -35,3 +35,8 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
default y
help
Use the always on PRCMU Timer as sched_clock
+
+config CLKSRC_ARM_GENERIC
+ def_bool y if ARM64
+ help
+ This option enables support for the ARM generic timer.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index b65d0c5..6591990 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
+obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o
diff --git a/drivers/clocksource/arm_generic.c b/drivers/clocksource/arm_generic.c
new file mode 100644
index 0000000..c4d9f95
--- /dev/null
+++ b/drivers/clocksource/arm_generic.c
@@ -0,0 +1,232 @@
+/*
+ * Generic timers support
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+
+#include <clocksource/arm_generic.h>
+
+#include <asm/arm_generic.h>
+
+static u32 arch_timer_rate;
+static u64 sched_clock_mult __read_mostly;
+static DEFINE_PER_CPU(struct clock_event_device, arch_timer_evt);
+static int arch_timer_ppi;
+
+static irqreturn_t arch_timer_handle_irq(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ if (ctrl & ARCH_TIMER_CTRL_ISTATUS) {
+ ctrl |= ARCH_TIMER_CTRL_IMASK;
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+ evt->event_handler(evt);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void arch_timer_stop(void)
+{
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+}
+
+static void arch_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ arch_timer_stop();
+ break;
+ default:
+ break;
+ }
+}
+
+static int arch_timer_set_next_event(unsigned long evt,
+ struct clock_event_device *unused)
+{
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ ctrl |= ARCH_TIMER_CTRL_ENABLE;
+ ctrl &= ~ARCH_TIMER_CTRL_IMASK;
+
+ arch_timer_reg_write(ARCH_TIMER_REG_TVAL, evt);
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+
+ return 0;
+}
+
+static void __cpuinit arch_timer_setup(struct clock_event_device *clk)
+{
+ /* Let's make sure the timer is off before doing anything else */
+ arch_timer_stop();
+
+ clk->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP;
+ clk->name = "arch_sys_timer";
+ clk->rating = 400;
+ clk->set_mode = arch_timer_set_mode;
+ clk->set_next_event = arch_timer_set_next_event;
+ clk->irq = arch_timer_ppi;
+ clk->cpumask = cpumask_of(smp_processor_id());
+
+ clockevents_config_and_register(clk, arch_timer_rate,
+ 0xf, 0x7fffffff);
+
+ enable_percpu_irq(clk->irq, 0);
+
+ /* Ensure the physical counter is visible to userspace for the vDSO. */
+ arch_counter_enable_user_access();
+}
+
+static void __init arch_timer_calibrate(void)
+{
+ if (arch_timer_rate == 0) {
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, 0);
+ arch_timer_rate = arch_timer_reg_read(ARCH_TIMER_REG_FREQ);
+
+ /* Check the timer frequency. */
+ if (arch_timer_rate == 0)
+ panic("Architected timer frequency is set to zero.\n"
+ "You must set this in your .dts file\n");
+ }
+
+ /* Cache the sched_clock multiplier to save a divide in the hot path. */
+
+ sched_clock_mult = NSEC_PER_SEC / arch_timer_rate;
+
+ pr_info("Architected local timer running at %u.%02uMHz.\n",
+ arch_timer_rate / 1000000, (arch_timer_rate / 10000) % 100);
+}
+
+static cycle_t arch_counter_read(struct clocksource *cs)
+{
+ return arch_counter_get_cntpct();
+}
+
+static struct clocksource clocksource_counter = {
+ .name = "arch_sys_counter",
+ .rating = 400,
+ .read = arch_counter_read,
+ .mask = CLOCKSOURCE_MASK(56),
+ .flags = (CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_VALID_FOR_HRES),
+};
+
+int read_current_timer(unsigned long *timer_value)
+{
+ *timer_value = arch_counter_get_cntpct();
+ return 0;
+}
+
+unsigned long long notrace sched_clock(void)
+{
+ return arch_counter_get_cntvct() * sched_clock_mult;
+}
+
+static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ int cpu = (long)hcpu;
+ struct clock_event_device *clk = per_cpu_ptr(&arch_timer_evt, cpu);
+
+ switch(action) {
+ case CPU_STARTING:
+ case CPU_STARTING_FROZEN:
+ arch_timer_setup(clk);
+ break;
+
+ case CPU_DYING:
+ case CPU_DYING_FROZEN:
+ pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
+ clk->irq, cpu);
+ disable_percpu_irq(clk->irq);
+ arch_timer_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata arch_timer_cpu_nb = {
+ .notifier_call = arch_timer_cpu_notify,
+};
+
+static const struct of_device_id arch_timer_of_match[] __initconst = {
+ { .compatible = "arm,armv8-timer" },
+ {},
+};
+
+int __init arm_generic_timer_init(void)
+{
+ struct device_node *np;
+ int err;
+ u32 freq;
+
+ np = of_find_matching_node(NULL, arch_timer_of_match);
+ if (!np) {
+ pr_err("arch_timer: can't find DT node\n");
+ return -ENODEV;
+ }
+
+ /* Try to determine the frequency from the device tree or CNTFRQ */
+ if (!of_property_read_u32(np, "clock-frequency", &freq))
+ arch_timer_rate = freq;
+ arch_timer_calibrate();
+
+ arch_timer_ppi = irq_of_parse_and_map(np, 0);
+ pr_info("arch_timer: found %s irq %d\n", np->name, arch_timer_ppi);
+
+ err = request_percpu_irq(arch_timer_ppi, arch_timer_handle_irq,
+ np->name, &arch_timer_evt);
+ if (err) {
+ pr_err("arch_timer: can't register interrupt %d (%d)\n",
+ arch_timer_ppi, err);
+ return err;
+ }
+
+ clocksource_register_hz(&clocksource_counter, arch_timer_rate);
+
+ /* Calibrate the delay loop directly */
+ lpj_fine = arch_timer_rate / HZ;
+
+ /* Immediately configure the timer on the boot CPU */
+ arch_timer_setup(per_cpu_ptr(&arch_timer_evt, smp_processor_id()));
+
+ register_cpu_notifier(&arch_timer_cpu_nb);
+
+ return 0;
+}
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c
index c0e8164..1a40935 100644
--- a/drivers/cpufreq/powernow-k8.c
+++ b/drivers/cpufreq/powernow-k8.c
@@ -35,7 +35,6 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/cpumask.h>
-#include <linux/sched.h> /* for current / set_cpus_allowed() */
#include <linux/io.h>
#include <linux/delay.h>
@@ -1139,16 +1138,23 @@ static int transition_frequency_pstate(struct powernow_k8_data *data,
return res;
}
-/* Driver entry point to switch to the target frequency */
-static int powernowk8_target(struct cpufreq_policy *pol,
- unsigned targfreq, unsigned relation)
+struct powernowk8_target_arg {
+ struct cpufreq_policy *pol;
+ unsigned targfreq;
+ unsigned relation;
+};
+
+static long powernowk8_target_fn(void *arg)
{
- cpumask_var_t oldmask;
+ struct powernowk8_target_arg *pta = arg;
+ struct cpufreq_policy *pol = pta->pol;
+ unsigned targfreq = pta->targfreq;
+ unsigned relation = pta->relation;
struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
u32 checkfid;
u32 checkvid;
unsigned int newstate;
- int ret = -EIO;
+ int ret;
if (!data)
return -EINVAL;
@@ -1156,29 +1162,16 @@ static int powernowk8_target(struct cpufreq_policy *pol,
checkfid = data->currfid;
checkvid = data->currvid;
- /* only run on specific CPU from here on. */
- /* This is poor form: use a workqueue or smp_call_function_single */
- if (!alloc_cpumask_var(&oldmask, GFP_KERNEL))
- return -ENOMEM;
-
- cpumask_copy(oldmask, tsk_cpus_allowed(current));
- set_cpus_allowed_ptr(current, cpumask_of(pol->cpu));
-
- if (smp_processor_id() != pol->cpu) {
- printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
- goto err_out;
- }
-
if (pending_bit_stuck()) {
printk(KERN_ERR PFX "failing targ, change pending bit set\n");
- goto err_out;
+ return -EIO;
}
pr_debug("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
pol->cpu, targfreq, pol->min, pol->max, relation);
if (query_current_values_with_pending_wait(data))
- goto err_out;
+ return -EIO;
if (cpu_family != CPU_HW_PSTATE) {
pr_debug("targ: curr fid 0x%x, vid 0x%x\n",
@@ -1196,7 +1189,7 @@ static int powernowk8_target(struct cpufreq_policy *pol,
if (cpufreq_frequency_table_target(pol, data->powernow_table,
targfreq, relation, &newstate))
- goto err_out;
+ return -EIO;
mutex_lock(&fidvid_mutex);
@@ -1209,9 +1202,8 @@ static int powernowk8_target(struct cpufreq_policy *pol,
ret = transition_frequency_fidvid(data, newstate);
if (ret) {
printk(KERN_ERR PFX "transition frequency failed\n");
- ret = 1;
mutex_unlock(&fidvid_mutex);
- goto err_out;
+ return 1;
}
mutex_unlock(&fidvid_mutex);
@@ -1220,12 +1212,25 @@ static int powernowk8_target(struct cpufreq_policy *pol,
data->powernow_table[newstate].index);
else
pol->cur = find_khz_freq_from_fid(data->currfid);
- ret = 0;
-err_out:
- set_cpus_allowed_ptr(current, oldmask);
- free_cpumask_var(oldmask);
- return ret;
+ return 0;
+}
+
+/* Driver entry point to switch to the target frequency */
+static int powernowk8_target(struct cpufreq_policy *pol,
+ unsigned targfreq, unsigned relation)
+{
+ struct powernowk8_target_arg pta = { .pol = pol, .targfreq = targfreq,
+ .relation = relation };
+
+ /*
+ * Must run on @pol->cpu. cpufreq core is responsible for ensuring
+ * that we're bound to the current CPU and pol->cpu stays online.
+ */
+ if (smp_processor_id() == pol->cpu)
+ return powernowk8_target_fn(&pta);
+ else
+ return work_on_cpu(pol->cpu, powernowk8_target_fn, &pta);
}
/* Driver entry point to verify the policy and range of frequencies */
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 3934fcc..17d6958 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -168,9 +168,9 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc)
}
/**
- * atc_desc_chain - build chain adding a descripor
- * @first: address of first descripor of the chain
- * @prev: address of previous descripor of the chain
+ * atc_desc_chain - build chain adding a descriptor
+ * @first: address of first descriptor of the chain
+ * @prev: address of previous descriptor of the chain
* @desc: descriptor to queue
*
* Called from prep_* functions
@@ -661,7 +661,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
flags);
if (unlikely(!atslave || !sg_len)) {
- dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n");
+ dev_dbg(chan2dev(chan), "prep_slave_sg: sg length is zero!\n");
return NULL;
}
@@ -689,6 +689,11 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
mem = sg_dma_address(sg);
len = sg_dma_len(sg);
+ if (unlikely(!len)) {
+ dev_dbg(chan2dev(chan),
+ "prep_slave_sg: sg(%d) data length is zero\n", i);
+ goto err;
+ }
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
mem_width = 0;
@@ -724,6 +729,11 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
mem = sg_dma_address(sg);
len = sg_dma_len(sg);
+ if (unlikely(!len)) {
+ dev_dbg(chan2dev(chan),
+ "prep_slave_sg: sg(%d) data length is zero\n", i);
+ goto err;
+ }
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
mem_width = 0;
@@ -757,6 +767,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
err_desc_get:
dev_err(chan2dev(chan), "not enough descriptors available\n");
+err:
atc_desc_put(atchan, first);
return NULL;
}
@@ -785,7 +796,7 @@ err_out:
}
/**
- * atc_dma_cyclic_fill_desc - Fill one period decriptor
+ * atc_dma_cyclic_fill_desc - Fill one period descriptor
*/
static int
atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc,
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index c64917e..bb02fd9 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -1118,7 +1118,7 @@ fail:
* @chan: channel
* @dma_addr: DMA mapped address of the buffer
* @buf_len: length of the buffer (in bytes)
- * @period_len: lenght of a single period
+ * @period_len: length of a single period
* @dir: direction of the operation
* @context: operation context (ignored)
*
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 8f84761..094437b 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -1015,7 +1015,7 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data)
/*
* Programming Error
* The DMA_INTERRUPT async_tx is a NULL transfer, which will
- * triger a PE interrupt.
+ * trigger a PE interrupt.
*/
if (stat & FSL_DMA_SR_PE) {
chan_dbg(chan, "irq: Programming Error INT\n");
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index 5084975..54f580b 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -572,8 +572,8 @@ static void imxdma_tasklet(unsigned long data)
if (desc->desc.callback)
desc->desc.callback(desc->desc.callback_param);
- /* If we are dealing with a cyclic descriptor keep it on ld_active
- * and dont mark the descripor as complete.
+ /* If we are dealing with a cyclic descriptor, keep it on ld_active
+ * and dont mark the descriptor as complete.
* Only in non-cyclic cases it would be marked as complete
*/
if (imxdma_chan_is_doing_cyclic(imxdmac))
diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c
index 222e907..02b21d7 100644
--- a/drivers/dma/intel_mid_dma.c
+++ b/drivers/dma/intel_mid_dma.c
@@ -427,7 +427,7 @@ DMA engine callback Functions*/
* intel_mid_dma_tx_submit - callback to submit DMA transaction
* @tx: dma engine descriptor
*
- * Submit the DMA trasaction for this descriptor, start if ch idle
+ * Submit the DMA transaction for this descriptor, start if ch idle
*/
static dma_cookie_t intel_mid_dma_tx_submit(struct dma_async_tx_descriptor *tx)
{
diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h
index 1bfa926..17b4219 100644
--- a/drivers/dma/intel_mid_dma_regs.h
+++ b/drivers/dma/intel_mid_dma_regs.h
@@ -168,9 +168,9 @@ union intel_mid_dma_cfg_hi {
* @active_list: current active descriptors
* @queue: current queued up descriptors
* @free_list: current free descriptors
- * @slave: dma slave struture
- * @descs_allocated: total number of decsiptors allocated
- * @dma: dma device struture pointer
+ * @slave: dma slave structure
+ * @descs_allocated: total number of descriptors allocated
+ * @dma: dma device structure pointer
* @busy: bool representing if ch is busy (active txn) or not
* @in_use: bool representing if ch is in use or not
* @raw_tfr: raw trf interrupt received
diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h
index 60e6754..d2ff3fd 100644
--- a/drivers/dma/ioat/hw.h
+++ b/drivers/dma/ioat/hw.h
@@ -22,7 +22,6 @@
#define _IOAT_HW_H_
/* PCI Configuration Space Values */
-#define IOAT_PCI_VID 0x8086
#define IOAT_MMIO_BAR 0
/* CB device ID's */
@@ -31,9 +30,6 @@
#define IOAT_PCI_DID_SCNB 0x65FF
#define IOAT_PCI_DID_SNB 0x402F
-#define IOAT_PCI_RID 0x00
-#define IOAT_PCI_SVID 0x8086
-#define IOAT_PCI_SID 0x8086
#define IOAT_VER_1_2 0x12 /* Version 1.2 */
#define IOAT_VER_2_0 0x20 /* Version 2.0 */
#define IOAT_VER_3_0 0x30 /* Version 3.0 */
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index e4feba6..5d3bbcd 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -522,7 +522,7 @@ enum desc_status {
/* In the DMAC pool */
FREE,
/*
- * Allocted to some channel during prep_xxx
+ * Allocated to some channel during prep_xxx
* Also may be sitting on the work_list.
*/
PREP,
@@ -1567,17 +1567,19 @@ static int pl330_submit_req(void *ch_id, struct pl330_req *r)
goto xfer_exit;
}
- /* Prefer Secure Channel */
- if (!_manager_ns(thrd))
- r->cfg->nonsecure = 0;
- else
- r->cfg->nonsecure = 1;
/* Use last settings, if not provided */
- if (r->cfg)
+ if (r->cfg) {
+ /* Prefer Secure Channel */
+ if (!_manager_ns(thrd))
+ r->cfg->nonsecure = 0;
+ else
+ r->cfg->nonsecure = 1;
+
ccr = _prepare_ccr(r->cfg);
- else
+ } else {
ccr = readl(regs + CC(thrd->id));
+ }
/* If this req doesn't have valid xfer settings */
if (!_is_valid(ccr)) {
@@ -2928,6 +2930,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
num_chan = max_t(int, pi->pcfg.num_peri, pi->pcfg.num_chan);
pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
+ if (!pdmac->peripherals) {
+ ret = -ENOMEM;
+ dev_err(&adev->dev, "unable to allocate pdmac->peripherals\n");
+ goto probe_err5;
+ }
for (i = 0; i < num_chan; i++) {
pch = &pdmac->peripherals[i];
diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c
index ced9882..f72348d 100644
--- a/drivers/dma/ppc4xx/adma.c
+++ b/drivers/dma/ppc4xx/adma.c
@@ -4446,7 +4446,7 @@ static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev)
ret = -ENOMEM;
goto err_dma_alloc;
}
- dev_dbg(&ofdev->dev, "allocted descriptor pool virt 0x%p phys 0x%llx\n",
+ dev_dbg(&ofdev->dev, "allocated descriptor pool virt 0x%p phys 0x%llx\n",
adev->dma_desc_pool_virt, (u64)adev->dma_desc_pool);
regs = ioremap(res.start, resource_size(&res));
diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h
index 51e8e53..6d47373 100644
--- a/drivers/dma/ste_dma40_ll.h
+++ b/drivers/dma/ste_dma40_ll.h
@@ -202,7 +202,7 @@
/* LLI related structures */
/**
- * struct d40_phy_lli - The basic configration register for each physical
+ * struct d40_phy_lli - The basic configuration register for each physical
* channel.
*
* @reg_cfg: The configuration register.
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 616d90b..d5dc9da 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -199,6 +199,36 @@ void *edac_align_ptr(void **p, unsigned size, int n_elems)
return (void *)(((unsigned long)ptr) + align - r);
}
+static void _edac_mc_free(struct mem_ctl_info *mci)
+{
+ int i, chn, row;
+ struct csrow_info *csr;
+ const unsigned int tot_dimms = mci->tot_dimms;
+ const unsigned int tot_channels = mci->num_cschannel;
+ const unsigned int tot_csrows = mci->nr_csrows;
+
+ if (mci->dimms) {
+ for (i = 0; i < tot_dimms; i++)
+ kfree(mci->dimms[i]);
+ kfree(mci->dimms);
+ }
+ if (mci->csrows) {
+ for (row = 0; row < tot_csrows; row++) {
+ csr = mci->csrows[row];
+ if (csr) {
+ if (csr->channels) {
+ for (chn = 0; chn < tot_channels; chn++)
+ kfree(csr->channels[chn]);
+ kfree(csr->channels);
+ }
+ kfree(csr);
+ }
+ }
+ kfree(mci->csrows);
+ }
+ kfree(mci);
+}
+
/**
* edac_mc_alloc: Allocate and partially fill a struct mem_ctl_info structure
* @mc_num: Memory controller number
@@ -413,24 +443,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
return mci;
error:
- if (mci->dimms) {
- for (i = 0; i < tot_dimms; i++)
- kfree(mci->dimms[i]);
- kfree(mci->dimms);
- }
- if (mci->csrows) {
- for (chn = 0; chn < tot_channels; chn++) {
- csr = mci->csrows[chn];
- if (csr) {
- for (chn = 0; chn < tot_channels; chn++)
- kfree(csr->channels[chn]);
- kfree(csr);
- }
- kfree(mci->csrows[i]);
- }
- kfree(mci->csrows);
- }
- kfree(mci);
+ _edac_mc_free(mci);
return NULL;
}
@@ -445,6 +458,14 @@ void edac_mc_free(struct mem_ctl_info *mci)
{
edac_dbg(1, "\n");
+ /* If we're not yet registered with sysfs free only what was allocated
+ * in edac_mc_alloc().
+ */
+ if (!device_is_registered(&mci->dev)) {
+ _edac_mc_free(mci);
+ return;
+ }
+
/* the mci instance is freed here, when the sysfs object is dropped */
edac_unregister_sysfs(mci);
}
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index 47180a0..b6653a6 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -391,7 +391,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
for (j = 0; j < nr_channels; j++) {
struct dimm_info *dimm = csrow->channels[j]->dimm;
- dimm->nr_pages = nr_pages / nr_channels;
+ dimm->nr_pages = nr_pages;
dimm->grain = nr_pages << PAGE_SHIFT;
dimm->mtype = MEM_DDR2;
dimm->dtype = DEV_UNKNOWN;
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index 39c6375..6a49dd0 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -1012,6 +1012,10 @@ static void handle_channel(struct i5000_pvt *pvt, int slot, int channel,
/* add the number of COLUMN bits */
addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr);
+ /* Dual-rank memories have twice the size */
+ if (dinfo->dual_rank)
+ addrBits++;
+
addrBits += 6; /* add 64 bits per DIMM */
addrBits -= 20; /* divide by 2^^20 */
addrBits -= 3; /* 8 bits per bytes */
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index f3b1f9f..5715b7c 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -513,7 +513,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
{
struct sbridge_pvt *pvt = mci->pvt_info;
struct dimm_info *dimm;
- int i, j, banks, ranks, rows, cols, size, npages;
+ unsigned i, j, banks, ranks, rows, cols, npages;
+ u64 size;
u32 reg;
enum edac_type mode;
enum mem_type mtype;
@@ -585,10 +586,10 @@ static int get_dimm_config(struct mem_ctl_info *mci)
cols = numcol(mtr);
/* DDR3 has 8 I/O banks */
- size = (rows * cols * banks * ranks) >> (20 - 3);
+ size = ((u64)rows * cols * banks * ranks) >> (20 - 3);
npages = MiB_TO_PAGES(size);
- edac_dbg(0, "mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
+ edac_dbg(0, "mc#%d: channel %d, dimm %d, %Ld Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
pvt->sbridge_dev->mc, i, j,
size, npages,
banks, ranks, rows, cols);
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index e175c8e..07122a9 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -21,6 +21,12 @@ config EXTCON_GPIO
Say Y here to enable GPIO based extcon support. Note that GPIO
extcon supports single state per extcon instance.
+config EXTCON_ADC_JACK
+ tristate "ADC Jack extcon support"
+ depends on IIO
+ help
+ Say Y here to enable extcon device driver based on ADC values.
+
config EXTCON_MAX77693
tristate "MAX77693 EXTCON Support"
depends on MFD_MAX77693
@@ -41,7 +47,7 @@ config EXTCON_MAX8997
config EXTCON_ARIZONA
tristate "Wolfson Arizona EXTCON support"
- depends on MFD_ARIZONA
+ depends on MFD_ARIZONA && INPUT
help
Say Y here to enable support for external accessory detection
with Wolfson Arizona devices. These are audio CODECs with
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index 88961b3..f98a3c4 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -2,8 +2,9 @@
# Makefile for external connector class (extcon) devices
#
-obj-$(CONFIG_EXTCON) += extcon_class.o
-obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o
+obj-$(CONFIG_EXTCON) += extcon-class.o
+obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
+obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c
new file mode 100644
index 0000000..60ac3fb
--- /dev/null
+++ b/drivers/extcon/extcon-adc-jack.c
@@ -0,0 +1,198 @@
+/*
+ * drivers/extcon/extcon-adc-jack.c
+ *
+ * Analog Jack extcon driver with ADC-based detection capability.
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * Modified for calling to IIO to get adc by <anish.singh@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/iio/consumer.h>
+#include <linux/extcon/extcon-adc-jack.h>
+#include <linux/extcon.h>
+
+/**
+ * struct adc_jack_data - internal data for adc_jack device driver
+ * @edev - extcon device.
+ * @cable_names - list of supported cables.
+ * @num_cables - size of cable_names.
+ * @adc_conditions - list of adc value conditions.
+ * @num_conditions - size of adc_conditions.
+ * @irq - irq number of attach/detach event (0 if not exist).
+ * @handling_delay - interrupt handler will schedule extcon event
+ * handling at handling_delay jiffies.
+ * @handler - extcon event handler called by interrupt handler.
+ * @chan - iio channel being queried.
+ */
+struct adc_jack_data {
+ struct extcon_dev edev;
+
+ const char **cable_names;
+ int num_cables;
+ struct adc_jack_cond *adc_conditions;
+ int num_conditions;
+
+ int irq;
+ unsigned long handling_delay; /* in jiffies */
+ struct delayed_work handler;
+
+ struct iio_channel *chan;
+};
+
+static void adc_jack_handler(struct work_struct *work)
+{
+ struct adc_jack_data *data = container_of(to_delayed_work(work),
+ struct adc_jack_data,
+ handler);
+ u32 state = 0;
+ int ret, adc_val;
+ int i;
+
+ ret = iio_read_channel_raw(data->chan, &adc_val);
+ if (ret < 0) {
+ dev_err(data->edev.dev, "read channel() error: %d\n", ret);
+ return;
+ }
+
+ /* Get state from adc value with adc_conditions */
+ for (i = 0; i < data->num_conditions; i++) {
+ struct adc_jack_cond *def = &data->adc_conditions[i];
+ if (!def->state)
+ break;
+ if (def->min_adc <= adc_val && def->max_adc >= adc_val) {
+ state = def->state;
+ break;
+ }
+ }
+ /* if no def has met, it means state = 0 (no cables attached) */
+
+ extcon_set_state(&data->edev, state);
+}
+
+static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
+{
+ struct adc_jack_data *data = _data;
+
+ schedule_delayed_work(&data->handler, data->handling_delay);
+ return IRQ_HANDLED;
+}
+
+static int __devinit adc_jack_probe(struct platform_device *pdev)
+{
+ struct adc_jack_data *data;
+ struct adc_jack_pdata *pdata = pdev->dev.platform_data;
+ int i, err = 0;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->edev.name = pdata->name;
+
+ if (!pdata->cable_names) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "error: cable_names not defined.\n");
+ goto out;
+ }
+
+ data->edev.supported_cable = pdata->cable_names;
+
+ /* Check the length of array and set num_cables */
+ for (i = 0; data->edev.supported_cable[i]; i++)
+ ;
+ if (i == 0 || i > SUPPORTED_CABLE_MAX) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
+ i - 1);
+ goto out;
+ }
+ data->num_cables = i;
+
+ if (!pdata->adc_conditions ||
+ !pdata->adc_conditions[0].state) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
+ goto out;
+ }
+ data->adc_conditions = pdata->adc_conditions;
+
+ /* Check the length of array and set num_conditions */
+ for (i = 0; data->adc_conditions[i].state; i++)
+ ;
+ data->num_conditions = i;
+
+ data->chan = iio_channel_get(dev_name(&pdev->dev),
+ pdata->consumer_channel);
+ if (IS_ERR(data->chan)) {
+ err = PTR_ERR(data->chan);
+ goto out;
+ }
+
+ data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
+
+ INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler);
+
+ platform_set_drvdata(pdev, data);
+
+ err = extcon_dev_register(&data->edev, &pdev->dev);
+ if (err)
+ goto out;
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (!data->irq) {
+ dev_err(&pdev->dev, "platform_get_irq failed\n");
+ err = -ENODEV;
+ goto err_irq;
+ }
+
+ err = request_any_context_irq(data->irq, adc_jack_irq_thread,
+ pdata->irq_flags, pdata->name, data);
+
+ if (err) {
+ dev_err(&pdev->dev, "error: irq %d\n", data->irq);
+ err = -EINVAL;
+ goto err_irq;
+ }
+
+ goto out;
+
+err_irq:
+ extcon_dev_unregister(&data->edev);
+out:
+ return err;
+}
+
+static int __devexit adc_jack_remove(struct platform_device *pdev)
+{
+ struct adc_jack_data *data = platform_get_drvdata(pdev);
+
+ free_irq(data->irq, data);
+ cancel_work_sync(&data->handler.work);
+ extcon_dev_unregister(&data->edev);
+
+ return 0;
+}
+
+static struct platform_driver adc_jack_driver = {
+ .probe = adc_jack_probe,
+ .remove = __devexit_p(adc_jack_remove),
+ .driver = {
+ .name = "adc-jack",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(adc_jack_driver);
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c
index 427a289..cdab9e5 100644
--- a/drivers/extcon/extcon-arizona.c
+++ b/drivers/extcon/extcon-arizona.c
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/gpio.h>
+#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -30,11 +31,14 @@
#include <linux/mfd/arizona/pdata.h>
#include <linux/mfd/arizona/registers.h>
+#define ARIZONA_NUM_BUTTONS 6
+
struct arizona_extcon_info {
struct device *dev;
struct arizona *arizona;
struct mutex lock;
struct regulator *micvdd;
+ struct input_dev *input;
int micd_mode;
const struct arizona_micd_config *micd_modes;
@@ -54,6 +58,18 @@ static const struct arizona_micd_config micd_default_modes[] = {
{ 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
};
+static struct {
+ u16 status;
+ int report;
+} arizona_lvl_to_key[ARIZONA_NUM_BUTTONS] = {
+ { 0x1, BTN_0 },
+ { 0x2, BTN_1 },
+ { 0x4, BTN_2 },
+ { 0x8, BTN_3 },
+ { 0x10, BTN_4 },
+ { 0x20, BTN_5 },
+};
+
#define ARIZONA_CABLE_MECHANICAL 0
#define ARIZONA_CABLE_MICROPHONE 1
#define ARIZONA_CABLE_HEADPHONE 2
@@ -133,6 +149,7 @@ static void arizona_stop_mic(struct arizona_extcon_info *info)
if (change) {
regulator_disable(info->micvdd);
+ pm_runtime_mark_last_busy(info->dev);
pm_runtime_put_autosuspend(info->dev);
}
}
@@ -141,8 +158,8 @@ static irqreturn_t arizona_micdet(int irq, void *data)
{
struct arizona_extcon_info *info = data;
struct arizona *arizona = info->arizona;
- unsigned int val;
- int ret;
+ unsigned int val, lvl;
+ int ret, i;
mutex_lock(&info->lock);
@@ -219,13 +236,22 @@ static irqreturn_t arizona_micdet(int irq, void *data)
/*
* If we're still detecting and we detect a short then we've
- * got a headphone. Otherwise it's a button press, the
- * button reporting is stubbed out for now.
+ * got a headphone. Otherwise it's a button press.
*/
if (val & 0x3fc) {
if (info->mic) {
dev_dbg(arizona->dev, "Mic button detected\n");
+ lvl = val & ARIZONA_MICD_LVL_MASK;
+ lvl >>= ARIZONA_MICD_LVL_SHIFT;
+
+ for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
+ if (lvl & arizona_lvl_to_key[i].status)
+ input_report_key(info->input,
+ arizona_lvl_to_key[i].report,
+ 1);
+ input_sync(info->input);
+
} else if (info->detecting) {
dev_dbg(arizona->dev, "Headphone detected\n");
info->detecting = false;
@@ -244,6 +270,10 @@ static irqreturn_t arizona_micdet(int irq, void *data)
}
} else {
dev_dbg(arizona->dev, "Mic button released\n");
+ for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
+ input_report_key(info->input,
+ arizona_lvl_to_key[i].report, 0);
+ input_sync(info->input);
}
handled:
@@ -258,7 +288,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
struct arizona_extcon_info *info = data;
struct arizona *arizona = info->arizona;
unsigned int val;
- int ret;
+ int ret, i;
pm_runtime_get_sync(info->dev);
@@ -288,6 +318,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
arizona_stop_mic(info);
+ for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
+ input_report_key(info->input,
+ arizona_lvl_to_key[i].report, 0);
+ input_sync(info->input);
+
ret = extcon_update_state(&info->edev, 0xffffffff, 0);
if (ret != 0)
dev_err(arizona->dev, "Removal report failed: %d\n",
@@ -307,13 +342,13 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct arizona_pdata *pdata;
struct arizona_extcon_info *info;
- int ret, mode;
+ int ret, mode, i;
pdata = dev_get_platdata(arizona->dev);
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
+ dev_err(&pdev->dev, "Failed to allocate memory\n");
ret = -ENOMEM;
goto err;
}
@@ -350,7 +385,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
ret = extcon_dev_register(&info->edev, arizona->dev);
if (ret < 0) {
- dev_err(arizona->dev, "extcon_dev_regster() failed: %d\n",
+ dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
ret);
goto err;
}
@@ -382,6 +417,20 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
arizona_extcon_set_mode(info, 0);
+ info->input = input_allocate_device();
+ if (!info->input) {
+ dev_err(arizona->dev, "Can't allocate input dev\n");
+ ret = -ENOMEM;
+ goto err_register;
+ }
+
+ for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
+ input_set_capability(info->input, EV_KEY,
+ arizona_lvl_to_key[i].report);
+ info->input->name = "Headset";
+ info->input->phys = "arizona/extcon";
+ info->input->dev.parent = &pdev->dev;
+
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
@@ -391,7 +440,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
if (ret != 0) {
dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
ret);
- goto err_register;
+ goto err_input;
}
ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1);
@@ -434,10 +483,23 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
+ ret = regulator_allow_bypass(info->micvdd, true);
+ if (ret != 0)
+ dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n",
+ ret);
+
pm_runtime_put(&pdev->dev);
+ ret = input_register_device(info->input);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register input device: %d\n", ret);
+ goto err_micdet;
+ }
+
return 0;
+err_micdet:
+ arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
err_fall_wake:
arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
err_fall:
@@ -446,6 +508,8 @@ err_rise_wake:
arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
err_rise:
arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
+err_input:
+ input_free_device(info->input);
err_register:
pm_runtime_disable(&pdev->dev);
extcon_dev_unregister(&info->edev);
@@ -468,6 +532,7 @@ static int __devexit arizona_extcon_remove(struct platform_device *pdev)
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
ARIZONA_JD1_ENA, 0);
arizona_clk32k_disable(arizona);
+ input_unregister_device(info->input);
extcon_dev_unregister(&info->edev);
return 0;
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon-class.c
index f6419f9..946a318 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon-class.c
@@ -30,6 +30,7 @@
#include <linux/err.h>
#include <linux/extcon.h>
#include <linux/slab.h>
+#include <linux/sysfs.h>
/*
* extcon_cable_name suggests the standard cable names for commonly used
@@ -442,7 +443,7 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val,
/**
* extcon_register_interest() - Register a notifier for a state change of a
- * specific cable, not a entier set of cables of a
+ * specific cable, not an entier set of cables of a
* extcon device.
* @obj: an empty extcon_specific_cable_nb object to be returned.
* @extcon_name: the name of extcon device.
@@ -498,7 +499,7 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
}
/**
- * extcon_register_notifier() - Register a notifee to get notified by
+ * extcon_register_notifier() - Register a notifiee to get notified by
* any attach status changes from the extcon.
* @edev: the extcon device.
* @nb: a notifier block to be registered.
@@ -515,7 +516,7 @@ int extcon_register_notifier(struct extcon_dev *edev,
EXPORT_SYMBOL_GPL(extcon_register_notifier);
/**
- * extcon_unregister_notifier() - Unregister a notifee from the extcon device.
+ * extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
* @edev: the extcon device.
* @nb: a registered notifier block to be unregistered.
*/
@@ -673,10 +674,12 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
cable->attr_g.name = str;
cable->attr_g.attrs = cable->attrs;
+ sysfs_attr_init(&cable->attr_name.attr);
cable->attr_name.attr.name = "name";
cable->attr_name.attr.mode = 0444;
cable->attr_name.show = cable_name_show;
+ sysfs_attr_init(&cable->attr_state.attr);
cable->attr_state.attr.name = "state";
cable->attr_state.attr.mode = 0644;
cable->attr_state.show = cable_state_show;
@@ -722,6 +725,7 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
goto err_muex;
}
strcpy(name, buf);
+ sysfs_attr_init(&edev->d_attrs_muex[index].attr);
edev->d_attrs_muex[index].attr.name = name;
edev->d_attrs_muex[index].attr.mode = 0000;
edev->attrs_muex[index] = &edev->d_attrs_muex[index]
@@ -802,7 +806,7 @@ EXPORT_SYMBOL_GPL(extcon_dev_register);
/**
* extcon_dev_unregister() - Unregister the extcon device.
- * @edev: the extcon device instance to be unregitered.
+ * @edev: the extcon device instance to be unregistered.
*
* Note that this does not call kfree(edev) because edev was not allocated
* by this class.
diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon-gpio.c
index 3cc152e..3cc152e 100644
--- a/drivers/extcon/extcon_gpio.c
+++ b/drivers/extcon/extcon-gpio.c
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c
index 38f9e52..e21387e 100644
--- a/drivers/extcon/extcon-max77693.c
+++ b/drivers/extcon/extcon-max77693.c
@@ -356,7 +356,7 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info,
extcon_set_cable_state(info->edev, "MHL", attached);
break;
default:
- dev_err(info->dev, "faild to detect %s accessory\n",
+ dev_err(info->dev, "failed to detect %s accessory\n",
attached ? "attached" : "detached");
dev_err(info->dev, "- adc:0x%x, adclow:0x%x, adc1k:0x%x\n",
adc, adclow, adc1k);
@@ -548,7 +548,7 @@ static void max77693_muic_irq_work(struct work_struct *work)
curr_adc = info->status[0] & STATUS1_ADC_MASK;
curr_adc >>= STATUS1_ADC_SHIFT;
- /* Check accossory state which is either detached or attached */
+ /* Check accessory state which is either detached or attached */
if (curr_adc == MAX77693_MUIC_ADC_OPEN)
attached = false;
@@ -564,7 +564,7 @@ static void max77693_muic_irq_work(struct work_struct *work)
curr_chg_type = info->status[1] & STATUS2_CHGTYP_MASK;
curr_chg_type >>= STATUS2_CHGTYP_SHIFT;
- /* Check charger accossory state which
+ /* Check charger accessory state which
is either detached or attached */
if (curr_chg_type == MAX77693_CHARGER_TYPE_NONE)
attached = false;
@@ -699,7 +699,7 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
ret = request_threaded_irq(virq, NULL,
max77693_muic_irq_handler,
- 0, muic_irq->name, info);
+ IRQF_ONESHOT, muic_irq->name, info);
if (ret) {
dev_err(&pdev->dev,
"failed: irq request (IRQ: %d,"
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 47408e8..d10c987 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -435,12 +435,23 @@ efivar_attr_read(struct efivar_entry *entry, char *buf)
if (status != EFI_SUCCESS)
return -EIO;
- if (var->Attributes & 0x1)
+ if (var->Attributes & EFI_VARIABLE_NON_VOLATILE)
str += sprintf(str, "EFI_VARIABLE_NON_VOLATILE\n");
- if (var->Attributes & 0x2)
+ if (var->Attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)
str += sprintf(str, "EFI_VARIABLE_BOOTSERVICE_ACCESS\n");
- if (var->Attributes & 0x4)
+ if (var->Attributes & EFI_VARIABLE_RUNTIME_ACCESS)
str += sprintf(str, "EFI_VARIABLE_RUNTIME_ACCESS\n");
+ if (var->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD)
+ str += sprintf(str, "EFI_VARIABLE_HARDWARE_ERROR_RECORD\n");
+ if (var->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
+ str += sprintf(str,
+ "EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS\n");
+ if (var->Attributes &
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
+ str += sprintf(str,
+ "EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS\n");
+ if (var->Attributes & EFI_VARIABLE_APPEND_WRITE)
+ str += sprintf(str, "EFI_VARIABLE_APPEND_WRITE\n");
return str - buf;
}
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index 8a420f1..ed94b4e 100644
--- a/drivers/gpio/gpio-lpc32xx.c
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -308,6 +308,7 @@ static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
{
struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ __set_gpio_level_p012(group, pin, value);
__set_gpio_dir_p012(group, pin, 0);
return 0;
@@ -318,6 +319,7 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
{
struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ __set_gpio_level_p3(group, pin, value);
__set_gpio_dir_p3(group, pin, 0);
return 0;
@@ -326,6 +328,9 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin,
int value)
{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpo_level_p3(group, pin, value);
return 0;
}
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 489e2b1..274d25d 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -3242,7 +3242,8 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj,
{
int ret;
- BUG_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT);
+ if (WARN_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT))
+ return -EBUSY;
if (obj->gtt_space != NULL) {
if ((alignment && obj->gtt_offset & (alignment - 1)) ||
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index bc2ad34..c040aee 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -4191,12 +4191,6 @@ static void i8xx_update_pll(struct drm_crtc *crtc,
POSTING_READ(DPLL(pipe));
udelay(150);
- I915_WRITE(DPLL(pipe), dpll);
-
- /* Wait for the clocks to stabilize. */
- POSTING_READ(DPLL(pipe));
- udelay(150);
-
/* The LVDS pin pair needs to be on before the DPLLs are enabled.
* This is an exception to the general rule that mode_set doesn't turn
* things on.
@@ -4204,6 +4198,12 @@ static void i8xx_update_pll(struct drm_crtc *crtc,
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
intel_update_lvds(crtc, clock, adjusted_mode);
+ I915_WRITE(DPLL(pipe), dpll);
+
+ /* Wait for the clocks to stabilize. */
+ POSTING_READ(DPLL(pipe));
+ udelay(150);
+
/* The pixel multiplier can only be updated once the
* DPLL is enabled and the clocks are stable.
*
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 98f6024..12dc330 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -609,7 +609,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
u32 temp;
u32 enable_bits = SDVO_ENABLE;
- if (intel_hdmi->has_audio)
+ if (intel_hdmi->has_audio || mode != DRM_MODE_DPMS_ON)
enable_bits |= SDVO_AUDIO_ENABLE;
temp = I915_READ(intel_hdmi->sdvox_reg);
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index ff23d88..3ca240b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -179,7 +179,7 @@ nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
return 0;
} else
if (init->class == 0x906e) {
- NV_ERROR(dev, "906e not supported yet\n");
+ NV_DEBUG(dev, "906e not supported yet\n");
return -EINVAL;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c
index f034905..c399d51 100644
--- a/drivers/gpu/drm/nouveau/nv50_gpio.c
+++ b/drivers/gpu/drm/nouveau/nv50_gpio.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
+#include <linux/dmi.h>
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
@@ -110,13 +111,25 @@ nv50_gpio_isr(struct drm_device *dev)
nv_wr32(dev, 0xe074, intr1);
}
+static struct dmi_system_id gpio_reset_ids[] = {
+ {
+ .ident = "Apple Macbook 10,1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
+ }
+ },
+ { }
+};
+
int
nv50_gpio_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
/* initialise gpios and routing to vbios defaults */
- nouveau_gpio_reset(dev);
+ if (dmi_check_system(gpio_reset_ids))
+ nouveau_gpio_reset(dev);
/* disable, and ack any pending gpio interrupts */
nv_wr32(dev, 0xe050, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c
index f704e94..f376c39 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fb.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fb.c
@@ -124,6 +124,7 @@ nvc0_fb_init(struct drm_device *dev)
priv = dev_priv->engine.fb.priv;
nv_wr32(dev, 0x100c10, priv->r100c10 >> 8);
+ nv_mask(dev, 0x17e820, 0x00100000, 0x00000000); /* NV_PLTCG_INTR_EN */
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
index 7d85553..cd39eb9 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -373,7 +373,8 @@ nvc0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
static void
nvc0_fifo_isr(struct drm_device *dev)
{
- u32 stat = nv_rd32(dev, 0x002100);
+ u32 mask = nv_rd32(dev, 0x002140);
+ u32 stat = nv_rd32(dev, 0x002100) & mask;
if (stat & 0x00000100) {
NV_INFO(dev, "PFIFO: unknown status 0x00000100\n");
diff --git a/drivers/gpu/drm/nouveau/nve0_fifo.c b/drivers/gpu/drm/nouveau/nve0_fifo.c
index e98d144..281bece 100644
--- a/drivers/gpu/drm/nouveau/nve0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nve0_fifo.c
@@ -345,7 +345,8 @@ nve0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
static void
nve0_fifo_isr(struct drm_device *dev)
{
- u32 stat = nv_rd32(dev, 0x002100);
+ u32 mask = nv_rd32(dev, 0x002140);
+ u32 stat = nv_rd32(dev, 0x002100) & mask;
if (stat & 0x00000100) {
NV_INFO(dev, "PFIFO: unknown status 0x00000100\n");
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index e721e30..2817101 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -1479,98 +1479,14 @@ static void radeon_legacy_atom_fixup(struct drm_crtc *crtc)
}
}
-/**
- * radeon_get_pll_use_mask - look up a mask of which pplls are in use
- *
- * @crtc: drm crtc
- *
- * Returns the mask of which PPLLs (Pixel PLLs) are in use.
- */
-static u32 radeon_get_pll_use_mask(struct drm_crtc *crtc)
-{
- struct drm_device *dev = crtc->dev;
- struct drm_crtc *test_crtc;
- struct radeon_crtc *radeon_test_crtc;
- u32 pll_in_use = 0;
-
- list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
- if (crtc == test_crtc)
- continue;
-
- radeon_test_crtc = to_radeon_crtc(test_crtc);
- if (radeon_test_crtc->pll_id != ATOM_PPLL_INVALID)
- pll_in_use |= (1 << radeon_test_crtc->pll_id);
- }
- return pll_in_use;
-}
-
-/**
- * radeon_get_shared_dp_ppll - return the PPLL used by another crtc for DP
- *
- * @crtc: drm crtc
- *
- * Returns the PPLL (Pixel PLL) used by another crtc/encoder which is
- * also in DP mode. For DP, a single PPLL can be used for all DP
- * crtcs/encoders.
- */
-static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc)
-{
- struct drm_device *dev = crtc->dev;
- struct drm_encoder *test_encoder;
- struct radeon_crtc *radeon_test_crtc;
-
- list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
- if (test_encoder->crtc && (test_encoder->crtc != crtc)) {
- if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
- /* for DP use the same PLL for all */
- radeon_test_crtc = to_radeon_crtc(test_encoder->crtc);
- if (radeon_test_crtc->pll_id != ATOM_PPLL_INVALID)
- return radeon_test_crtc->pll_id;
- }
- }
- }
- return ATOM_PPLL_INVALID;
-}
-
-/**
- * radeon_atom_pick_pll - Allocate a PPLL for use by the crtc.
- *
- * @crtc: drm crtc
- *
- * Returns the PPLL (Pixel PLL) to be used by the crtc. For DP monitors
- * a single PPLL can be used for all DP crtcs/encoders. For non-DP
- * monitors a dedicated PPLL must be used. If a particular board has
- * an external DP PLL, return ATOM_PPLL_INVALID to skip PLL programming
- * as there is no need to program the PLL itself. If we are not able to
- * allocate a PLL, return ATOM_PPLL_INVALID to skip PLL programming to
- * avoid messing up an existing monitor.
- *
- * Asic specific PLL information
- *
- * DCE 6.1
- * - PPLL2 is only available to UNIPHYA (both DP and non-DP)
- * - PPLL0, PPLL1 are available for UNIPHYB/C/D/E/F (both DP and non-DP)
- *
- * DCE 6.0
- * - PPLL0 is available to all UNIPHY (DP only)
- * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC
- *
- * DCE 5.0
- * - DCPLL is available to all UNIPHY (DP only)
- * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC
- *
- * DCE 3.0/4.0/4.1
- * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC
- *
- */
static int radeon_atom_pick_pll(struct drm_crtc *crtc)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct radeon_device *rdev = dev->dev_private;
struct drm_encoder *test_encoder;
- u32 pll_in_use;
- int pll;
+ struct drm_crtc *test_crtc;
+ uint32_t pll_in_use = 0;
if (ASIC_IS_DCE61(rdev)) {
list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
@@ -1582,40 +1498,32 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
if ((test_radeon_encoder->encoder_id ==
ENCODER_OBJECT_ID_INTERNAL_UNIPHY) &&
- (dig->linkb == false))
- /* UNIPHY A uses PPLL2 */
+ (dig->linkb == false)) /* UNIPHY A uses PPLL2 */
return ATOM_PPLL2;
- else if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
- /* UNIPHY B/C/D/E/F */
- if (rdev->clock.dp_extclk)
- /* skip PPLL programming if using ext clock */
- return ATOM_PPLL_INVALID;
- else {
- /* use the same PPLL for all DP monitors */
- pll = radeon_get_shared_dp_ppll(crtc);
- if (pll != ATOM_PPLL_INVALID)
- return pll;
- }
- }
- break;
}
}
/* UNIPHY B/C/D/E/F */
- pll_in_use = radeon_get_pll_use_mask(crtc);
- if (!(pll_in_use & (1 << ATOM_PPLL0)))
+ list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
+ struct radeon_crtc *radeon_test_crtc;
+
+ if (crtc == test_crtc)
+ continue;
+
+ radeon_test_crtc = to_radeon_crtc(test_crtc);
+ if ((radeon_test_crtc->pll_id == ATOM_PPLL0) ||
+ (radeon_test_crtc->pll_id == ATOM_PPLL1))
+ pll_in_use |= (1 << radeon_test_crtc->pll_id);
+ }
+ if (!(pll_in_use & 4))
return ATOM_PPLL0;
- if (!(pll_in_use & (1 << ATOM_PPLL1)))
- return ATOM_PPLL1;
- DRM_ERROR("unable to allocate a PPLL\n");
- return ATOM_PPLL_INVALID;
+ return ATOM_PPLL1;
} else if (ASIC_IS_DCE4(rdev)) {
list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
if (test_encoder->crtc && (test_encoder->crtc == crtc)) {
/* in DP mode, the DP ref clock can come from PPLL, DCPLL, or ext clock,
* depending on the asic:
* DCE4: PPLL or ext clock
- * DCE5: PPLL, DCPLL, or ext clock
- * DCE6: PPLL, PPLL0, or ext clock
+ * DCE5: DCPLL or ext clock
*
* Setting ATOM_PPLL_INVALID will cause SetPixelClock to skip
* PPLL/DCPLL programming and only program the DP DTO for the
@@ -1623,34 +1531,31 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
*/
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
if (rdev->clock.dp_extclk)
- /* skip PPLL programming if using ext clock */
return ATOM_PPLL_INVALID;
else if (ASIC_IS_DCE6(rdev))
- /* use PPLL0 for all DP */
return ATOM_PPLL0;
else if (ASIC_IS_DCE5(rdev))
- /* use DCPLL for all DP */
return ATOM_DCPLL;
- else {
- /* use the same PPLL for all DP monitors */
- pll = radeon_get_shared_dp_ppll(crtc);
- if (pll != ATOM_PPLL_INVALID)
- return pll;
- }
}
- break;
}
}
- /* all other cases */
- pll_in_use = radeon_get_pll_use_mask(crtc);
- if (!(pll_in_use & (1 << ATOM_PPLL2)))
- return ATOM_PPLL2;
- if (!(pll_in_use & (1 << ATOM_PPLL1)))
+
+ /* otherwise, pick one of the plls */
+ list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
+ struct radeon_crtc *radeon_test_crtc;
+
+ if (crtc == test_crtc)
+ continue;
+
+ radeon_test_crtc = to_radeon_crtc(test_crtc);
+ if ((radeon_test_crtc->pll_id >= ATOM_PPLL1) &&
+ (radeon_test_crtc->pll_id <= ATOM_PPLL2))
+ pll_in_use |= (1 << radeon_test_crtc->pll_id);
+ }
+ if (!(pll_in_use & 1))
return ATOM_PPLL1;
- DRM_ERROR("unable to allocate a PPLL\n");
- return ATOM_PPLL_INVALID;
+ return ATOM_PPLL2;
} else
- /* use PPLL1 or PPLL2 */
return radeon_crtc->crtc_id;
}
@@ -1792,7 +1697,7 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
break;
}
done:
- radeon_crtc->pll_id = ATOM_PPLL_INVALID;
+ radeon_crtc->pll_id = -1;
}
static const struct drm_crtc_helper_funcs atombios_helper_funcs = {
@@ -1841,6 +1746,6 @@ void radeon_atombios_init_crtc(struct drm_device *dev,
else
radeon_crtc->crtc_offset = 0;
}
- radeon_crtc->pll_id = ATOM_PPLL_INVALID;
+ radeon_crtc->pll_id = -1;
drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs);
}
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index e93b80a..ed3340a 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -77,13 +77,9 @@ void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw,
void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev)
{
u16 ctl, v;
- int cap, err;
+ int err;
- cap = pci_pcie_cap(rdev->pdev);
- if (!cap)
- return;
-
- err = pci_read_config_word(rdev->pdev, cap + PCI_EXP_DEVCTL, &ctl);
+ err = pcie_capability_read_word(rdev->pdev, PCI_EXP_DEVCTL, &ctl);
if (err)
return;
@@ -95,7 +91,7 @@ void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev)
if ((v == 0) || (v == 6) || (v == 7)) {
ctl &= ~PCI_EXP_DEVCTL_READRQ;
ctl |= (2 << 12);
- pci_write_config_word(rdev->pdev, cap + PCI_EXP_DEVCTL, ctl);
+ pcie_capability_write_word(rdev->pdev, PCI_EXP_DEVCTL, ctl);
}
}
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 8acb34f..8d7e33a 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -1182,7 +1182,8 @@ int r100_cp_init(struct radeon_device *rdev, unsigned ring_size)
ring->ready = true;
radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size);
- if (radeon_ring_supports_scratch_reg(rdev, ring)) {
+ if (!ring->rptr_save_reg /* not resuming from suspend */
+ && radeon_ring_supports_scratch_reg(rdev, ring)) {
r = radeon_scratch_get(rdev, &ring->rptr_save_reg);
if (r) {
DRM_ERROR("failed to get scratch reg for rptr save (%d).\n", r);
diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
index ba055e9..8d9dc44 100644
--- a/drivers/gpu/drm/udl/udl_connector.c
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -69,6 +69,13 @@ static int udl_get_modes(struct drm_connector *connector)
static int udl_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
+ struct udl_device *udl = connector->dev->dev_private;
+ if (!udl->sku_pixel_limit)
+ return 0;
+
+ if (mode->vdisplay * mode->hdisplay > udl->sku_pixel_limit)
+ return MODE_VIRTUAL_Y;
+
return 0;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index f2fb8f1..7e07433 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -1018,7 +1018,7 @@ int vmw_event_fence_action_create(struct drm_file *file_priv,
}
- event = kzalloc(sizeof(event->event), GFP_KERNEL);
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
if (unlikely(event == NULL)) {
DRM_ERROR("Failed to allocate an event.\n");
ret = -ENOMEM;
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c
index 3df8fc0..e893f6e 100644
--- a/drivers/gpu/vga/vgaarb.c
+++ b/drivers/gpu/vga/vgaarb.c
@@ -141,7 +141,11 @@ EXPORT_SYMBOL_GPL(vga_default_device);
void vga_set_default_device(struct pci_dev *pdev)
{
- vga_default = pdev;
+ if (vga_default == pdev)
+ return;
+
+ pci_dev_put(vga_default);
+ vga_default = pci_dev_get(pdev);
}
#endif
@@ -577,7 +581,7 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
if (vga_default == NULL &&
((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK))
- vga_default = pci_dev_get(pdev);
+ vga_set_default_device(pdev);
#endif
vga_arbiter_check_bridge_sharing(vgadev);
@@ -613,10 +617,8 @@ static bool vga_arbiter_del_pci_device(struct pci_dev *pdev)
}
#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
- if (vga_default == pdev) {
- pci_dev_put(vga_default);
- vga_default = NULL;
- }
+ if (vga_default == pdev)
+ vga_set_default_device(NULL);
#endif
if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM))
@@ -1066,7 +1068,6 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
}
} else if (strncmp(curr_pos, "target ", 7) == 0) {
- struct pci_bus *pbus;
unsigned int domain, bus, devfn;
struct vga_device *vgadev;
@@ -1085,19 +1086,11 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
pr_debug("vgaarb: %s ==> %x:%x:%x.%x\n", curr_pos,
domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
- pbus = pci_find_bus(domain, bus);
- pr_debug("vgaarb: pbus %p\n", pbus);
- if (pbus == NULL) {
- pr_err("vgaarb: invalid PCI domain and/or bus address %x:%x\n",
- domain, bus);
- ret_val = -ENODEV;
- goto done;
- }
- pdev = pci_get_slot(pbus, devfn);
+ pdev = pci_get_domain_bus_and_slot(domain, bus, devfn);
pr_debug("vgaarb: pdev %p\n", pdev);
if (!pdev) {
- pr_err("vgaarb: invalid PCI address %x:%x\n",
- bus, devfn);
+ pr_err("vgaarb: invalid PCI address %x:%x:%x\n",
+ domain, bus, devfn);
ret_val = -ENODEV;
goto done;
}
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index f8d3140..1630150 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -307,7 +307,6 @@ config HID_LOGITECH
config HID_LOGITECH_DJ
tristate "Logitech Unifying receivers full support"
depends on HID_LOGITECH
- default m
---help---
Say Y if you want support for Logitech Unifying receivers and devices.
Unifying receivers are capable of pairing up to 6 Logitech compliant
@@ -527,6 +526,14 @@ config HID_PICOLCD_LEDS
---help---
Provide access to PicoLCD's GPO pins via leds class.
+config HID_PICOLCD_CIR
+ bool "CIR via RC class" if EXPERT
+ default !EXPERT
+ depends on HID_PICOLCD
+ depends on HID_PICOLCD=RC_CORE || RC_CORE=y
+ ---help---
+ Provide access to PicoLCD's CIR interface via remote control (LIRC).
+
config HID_PRIMAX
tristate "Primax non-fully HID-compliant devices"
depends on USB_HID
@@ -534,6 +541,15 @@ config HID_PRIMAX
Support for Primax devices that are not fully compliant with the
HID standard.
+config HID_PS3REMOTE
+ tristate "Sony PS3 BD Remote Control"
+ depends on BT_HIDP
+ ---help---
+ Support for the Sony PS3 Blue-ray Disk Remote Control and Logitech
+ Harmony Adapter for PS3, which connect over Bluetooth.
+
+ Support for the 6-axis controllers is provided by HID_SONY.
+
config HID_ROCCAT
tristate "Roccat device support"
depends on USB_HID
@@ -561,7 +577,9 @@ config HID_SONY
tristate "Sony PS3 controller"
depends on USB_HID
---help---
- Support for Sony PS3 controller.
+ Support for Sony PS3 6-axis controllers.
+
+ Support for the Sony PS3 BD Remote is provided by HID_PS3REMOTE.
config HID_SPEEDLINK
tristate "Speedlink VAD Cezanne mouse support"
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 1173d7a..cef68ca 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -69,7 +69,28 @@ obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
+hid-picolcd-y += hid-picolcd_core.o
+ifdef CONFIG_HID_PICOLCD_FB
+hid-picolcd-y += hid-picolcd_fb.o
+endif
+ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+hid-picolcd-y += hid-picolcd_backlight.o
+endif
+ifdef CONFIG_HID_PICOLCD_LCD
+hid-picolcd-y += hid-picolcd_lcd.o
+endif
+ifdef CONFIG_HID_PICOLCD_LEDS
+hid-picolcd-y += hid-picolcd_leds.o
+endif
+ifdef CONFIG_HID_PICOLCD_CIR
+hid-picolcd-y += hid-picolcd_cir.o
+endif
+ifdef CONFIG_DEBUG_FS
+hid-picolcd-y += hid-picolcd_debugfs.o
+endif
+
obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
+obj-$(CONFIG_HID_PS3REMOTE) += hid-ps3remote.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \
diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c
index 902d1df..0a23988 100644
--- a/drivers/hid/hid-a4tech.c
+++ b/drivers/hid/hid-a4tech.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 585344b..06ebdbb 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
*/
diff --git a/drivers/hid/hid-aureal.c b/drivers/hid/hid-aureal.c
index ba64b04..7968187 100644
--- a/drivers/hid/hid-aureal.c
+++ b/drivers/hid/hid-aureal.c
@@ -9,7 +9,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
#include <linux/device.h>
diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c
index a1a765a..a1a5a12 100644
--- a/drivers/hid/hid-belkin.c
+++ b/drivers/hid/hid-belkin.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c
index 888ece6..af034d3 100644
--- a/drivers/hid/hid-cherry.c
+++ b/drivers/hid/hid-cherry.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 9b1dff1..bd3971b 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -126,7 +126,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
hid_err(parser->device, "collection stack overflow\n");
- return -1;
+ return -EINVAL;
}
if (parser->device->maxcollection == parser->device->collection_size) {
@@ -134,7 +134,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
parser->device->collection_size * 2, GFP_KERNEL);
if (collection == NULL) {
hid_err(parser->device, "failed to reallocate collection array\n");
- return -1;
+ return -ENOMEM;
}
memcpy(collection, parser->device->collection,
sizeof(struct hid_collection) *
@@ -170,7 +170,7 @@ static int close_collection(struct hid_parser *parser)
{
if (!parser->collection_stack_ptr) {
hid_err(parser->device, "collection stack underflow\n");
- return -1;
+ return -EINVAL;
}
parser->collection_stack_ptr--;
return 0;
@@ -374,7 +374,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
parser->global.report_size = item_udata(item);
- if (parser->global.report_size > 96) {
+ if (parser->global.report_size > 128) {
hid_err(parser->device, "invalid report_size %d\n",
parser->global.report_size);
return -1;
@@ -757,6 +757,7 @@ int hid_open_report(struct hid_device *device)
struct hid_item item;
unsigned int size;
__u8 *start;
+ __u8 *buf;
__u8 *end;
int ret;
static int (*dispatch_type[])(struct hid_parser *parser,
@@ -775,12 +776,21 @@ int hid_open_report(struct hid_device *device)
return -ENODEV;
size = device->dev_rsize;
+ buf = kmemdup(start, size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
if (device->driver->report_fixup)
- start = device->driver->report_fixup(device, start, &size);
+ start = device->driver->report_fixup(device, buf, &size);
+ else
+ start = buf;
- device->rdesc = kmemdup(start, size, GFP_KERNEL);
- if (device->rdesc == NULL)
+ start = kmemdup(start, size, GFP_KERNEL);
+ kfree(buf);
+ if (start == NULL)
return -ENOMEM;
+
+ device->rdesc = start;
device->rsize = size;
parser = vzalloc(sizeof(struct hid_parser));
@@ -1448,7 +1458,14 @@ void hid_disconnect(struct hid_device *hdev)
}
EXPORT_SYMBOL_GPL(hid_disconnect);
-/* a list of devices for which there is a specialized driver on HID bus */
+/*
+ * A list of devices for which there is a specialized driver on HID bus.
+ *
+ * Please note that for multitouch devices (driven by hid-multitouch driver),
+ * there is a proper autodetection and autoloading in place (based on presence
+ * of HID_DG_CONTACTID), so those devices don't need to be added to this list,
+ * as we are doing the right thing in hid_scan_usage().
+ */
static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
@@ -1570,6 +1587,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
@@ -1631,6 +1649,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
+#if IS_ENABLED(CONFIG_HID_ROCCAT)
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
@@ -1639,10 +1658,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
+#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
@@ -1668,6 +1689,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) },
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
index 9e43aac..3e159a5 100644
--- a/drivers/hid/hid-cypress.c
+++ b/drivers/hid/hid-cypress.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 01dd9a7..933fff0 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -911,15 +911,21 @@ static void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)
}
-
static int hid_debug_rdesc_show(struct seq_file *f, void *p)
{
struct hid_device *hdev = f->private;
+ const __u8 *rdesc = hdev->rdesc;
+ unsigned rsize = hdev->rsize;
int i;
+ if (!rdesc) {
+ rdesc = hdev->dev_rdesc;
+ rsize = hdev->dev_rsize;
+ }
+
/* dump HID report descriptor */
- for (i = 0; i < hdev->rsize; i++)
- seq_printf(f, "%02x ", hdev->rdesc[i]);
+ for (i = 0; i < rsize; i++)
+ seq_printf(f, "%02x ", rdesc[i]);
seq_printf(f, "\n\n");
/* dump parsed data and input mappings */
diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c
index ca1163e..6540af2 100644
--- a/drivers/hid/hid-ezkey.c
+++ b/drivers/hid/hid-ezkey.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c
index e88b951..4442c30 100644
--- a/drivers/hid/hid-gyration.c
+++ b/drivers/hid/hid-gyration.c
@@ -4,7 +4,6 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2006-2008 Jiri Kosina
*/
diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c
index 4e75421..ff295e6 100644
--- a/drivers/hid/hid-holtekff.c
+++ b/drivers/hid/hid-holtekff.c
@@ -100,8 +100,7 @@ static void holtekff_send(struct holtekff_device *holtekff,
holtekff->field->value[i] = data[i];
}
- dbg_hid("sending %02x %02x %02x %02x %02x %02x %02x\n", data[0],
- data[1], data[2], data[3], data[4], data[5], data[6]);
+ dbg_hid("sending %*ph\n", 7, data);
usbhid_submit_report(hid, holtekff->field->report, USB_DIR_OUT);
}
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 8a270a3..269b509 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
*/
/*
@@ -269,7 +268,11 @@
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4
#define USB_VENDOR_ID_ELECOM 0x056e
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
@@ -283,6 +286,9 @@
#define USB_VENDOR_ID_EMS 0x2006
#define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118
+#define USB_VENDOR_ID_FLATFROG 0x25b5
+#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002
+
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
@@ -296,6 +302,9 @@
#define USB_VENDOR_ID_EZKEY 0x0518
#define USB_DEVICE_ID_BTC_8193 0x0002
+#define USB_VENDOR_ID_FREESCALE 0x15A2
+#define USB_DEVICE_ID_FREESCALE_MX28 0x004F
+
#define USB_VENDOR_ID_FRUCTEL 0x25B6
#define USB_DEVICE_ID_GAMETEL_MT_MODE 0x0002
@@ -305,6 +314,7 @@
#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100
#define USB_VENDOR_ID_GLAB 0x06c2
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
@@ -501,6 +511,7 @@
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
+#define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211
#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215
@@ -657,7 +668,6 @@
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008
-#define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001
#define USB_VENDOR_ID_ROCCAT 0x1e7d
#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4
@@ -688,6 +698,7 @@
#define USB_VENDOR_ID_SONY 0x054c
#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b
+#define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f
@@ -764,6 +775,7 @@
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064
#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522
+#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781
#define USB_VENDOR_ID_UNITEC 0x227d
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 811bfad..d917c0d 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1154,6 +1154,7 @@ static void report_features(struct hid_device *hid)
int hidinput_connect(struct hid_device *hid, unsigned int force)
{
+ struct hid_driver *drv = hid->driver;
struct hid_report *report;
struct hid_input *hidinput = NULL;
struct input_dev *input_dev;
@@ -1228,6 +1229,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
* UGCI) cram a lot of unrelated inputs into the
* same interface. */
hidinput->report = report;
+ if (drv->input_configured)
+ drv->input_configured(hid, hidinput);
if (input_register_device(hidinput->input))
goto out_cleanup;
hidinput = NULL;
@@ -1235,8 +1238,12 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
}
- if (hidinput && input_register_device(hidinput->input))
- goto out_cleanup;
+ if (hidinput) {
+ if (drv->input_configured)
+ drv->input_configured(hid, hidinput);
+ if (input_register_device(hidinput->input))
+ goto out_cleanup;
+ }
return 0;
diff --git a/drivers/hid/hid-lcpower.c b/drivers/hid/hid-lcpower.c
index c4fe9bd0..22bc14a 100644
--- a/drivers/hid/hid-lcpower.c
+++ b/drivers/hid/hid-lcpower.c
@@ -24,7 +24,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- if ((usage->hid & HID_USAGE_PAGE) != 0x0ffbc0000)
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
return 0;
switch (usage->hid & HID_USAGE) {
diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c
index 77d2df0..cea016e 100644
--- a/drivers/hid/hid-lenovo-tpkbd.c
+++ b/drivers/hid/hid-lenovo-tpkbd.c
@@ -56,9 +56,8 @@ static int tpkbd_input_mapping(struct hid_device *hdev,
static int tpkbd_features_set(struct hid_device *hdev)
{
struct hid_report *report;
- struct tpkbd_data_pointer *data_pointer;
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02;
@@ -77,14 +76,8 @@ static ssize_t pointer_press_to_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
-
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
}
@@ -94,16 +87,10 @@ static ssize_t pointer_press_to_select_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
@@ -119,14 +106,8 @@ static ssize_t pointer_dragging_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
-
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
}
@@ -136,16 +117,10 @@ static ssize_t pointer_dragging_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
@@ -161,14 +136,8 @@ static ssize_t pointer_release_to_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
-
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
}
@@ -178,16 +147,10 @@ static ssize_t pointer_release_to_select_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
@@ -203,14 +166,8 @@ static ssize_t pointer_select_right_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
-
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
}
@@ -220,16 +177,10 @@ static ssize_t pointer_select_right_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
@@ -245,14 +196,8 @@ static ssize_t pointer_sensitivity_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
-
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
data_pointer->sensitivity);
@@ -263,16 +208,10 @@ static ssize_t pointer_sensitivity_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
return -EINVAL;
@@ -286,14 +225,10 @@ static ssize_t pointer_press_speed_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
data_pointer->press_speed);
@@ -304,16 +239,10 @@ static ssize_t pointer_press_speed_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
return -EINVAL;
@@ -370,15 +299,11 @@ static const struct attribute_group tpkbd_attr_group_pointer = {
static enum led_brightness tpkbd_led_brightness_get(
struct led_classdev *led_cdev)
{
- struct device *dev;
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int led_nr = 0;
- dev = led_cdev->dev->parent;
- hdev = container_of(dev, struct hid_device, dev);
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (led_cdev == &data_pointer->led_micmute)
led_nr = 1;
@@ -390,16 +315,12 @@ static enum led_brightness tpkbd_led_brightness_get(
static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- struct device *dev;
- struct hid_device *hdev;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
struct hid_report *report;
- struct tpkbd_data_pointer *data_pointer;
int led_nr = 0;
- dev = led_cdev->dev->parent;
- hdev = container_of(dev, struct hid_device, dev);
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (led_cdev == &data_pointer->led_micmute)
led_nr = 1;
@@ -508,17 +429,17 @@ err_free:
static void tpkbd_remove_tp(struct hid_device *hdev)
{
- struct tpkbd_data_pointer *data_pointer;
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
sysfs_remove_group(&hdev->dev.kobj,
&tpkbd_attr_group_pointer);
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
led_classdev_unregister(&data_pointer->led_micmute);
led_classdev_unregister(&data_pointer->led_mute);
hid_set_drvdata(hdev, NULL);
+ kfree(data_pointer->led_micmute.name);
+ kfree(data_pointer->led_mute.name);
kfree(data_pointer);
}
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index fc37ed6..a2f8e88 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2010 Hendrik Iben
*/
@@ -109,7 +108,7 @@ static __u8 dfp_rdesc_fixed[] = {
static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
rdesc[84] == 0x8c && rdesc[85] == 0x02) {
@@ -278,7 +277,7 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
0, 0, 0, 0, 0,183,184,185,186,187,
188,189,190,191,192,193,194, 0, 0, 0
};
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
unsigned int hid = usage->hid;
if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
@@ -319,7 +318,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
(field->flags & HID_MAIN_ITEM_RELATIVE))
@@ -335,13 +334,16 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
static int lg_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
input_event(field->hidinput->input, usage->type, usage->code,
-value);
return 1;
}
+ if (drv_data->quirks & LG_FF4) {
+ return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
+ }
return 0;
}
@@ -358,7 +360,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
return -ENOMEM;
}
drv_data->quirks = id->driver_data;
-
+
hid_set_drvdata(hdev, (void *)drv_data);
if (drv_data->quirks & LG_NOGET)
@@ -380,7 +382,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
/* Setup wireless link with Logitech Wii wheel */
- if(hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
+ if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
@@ -416,7 +418,7 @@ err_free:
static void lg_remove(struct hid_device *hdev)
{
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if (drv_data->quirks & LG_FF4)
lg4ff_deinit(hdev);
@@ -476,7 +478,7 @@ static const struct hid_device_id lg_devices[] = {
.driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
.driver_data = LG_FF4 },
- { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
.driver_data = LG_FF2 },
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
index d64cf8d..142ce3f 100644
--- a/drivers/hid/hid-lg.h
+++ b/drivers/hid/hid-lg.h
@@ -25,9 +25,13 @@ static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
#endif
#ifdef CONFIG_LOGIWHEELS_FF
+int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
int lg4ff_init(struct hid_device *hdev);
int lg4ff_deinit(struct hid_device *hdev);
#else
+static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
#endif
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index f3390ee..d7947c7 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -43,6 +43,11 @@
#define G27_REV_MAJ 0x12
#define G27_REV_MIN 0x38
+#define DFP_X_MIN 0
+#define DFP_X_MAX 16383
+#define DFP_PEDAL_MIN 0
+#define DFP_PEDAL_MAX 255
+
#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
@@ -53,6 +58,7 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
struct lg4ff_device_entry {
+ __u32 product_id;
__u16 range;
__u16 min_range;
__u16 max_range;
@@ -129,26 +135,77 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = {
{G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */
};
+/* Recalculates X axis value accordingly to currently selected range */
+static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
+{
+ __u16 max_range;
+ __s32 new_value;
+
+ if (range == 900)
+ return value;
+ else if (range == 200)
+ return value;
+ else if (range < 200)
+ max_range = 200;
+ else
+ max_range = 900;
+
+ new_value = 8192 + mult_frac(value - 8192, max_range, range);
+ if (new_value < 0)
+ return 0;
+ else if (new_value > 16383)
+ return 16383;
+ else
+ return new_value;
+}
+
+int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data)
+{
+ struct lg4ff_device_entry *entry = drv_data->device_props;
+ __s32 new_value = 0;
+
+ if (!entry) {
+ hid_err(hid, "Device properties not found");
+ return 0;
+ }
+
+ switch (entry->product_id) {
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+ switch (usage->code) {
+ case ABS_X:
+ new_value = lg4ff_adjust_dfp_x_axis(value, entry->range);
+ input_event(field->hidinput->input, usage->type, usage->code, new_value);
+ return 1;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ __s32 *value = report->field[0]->value;
int x;
-#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
+#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
switch (effect->type) {
case FF_CONSTANT:
x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
CLAMP(x);
- report->field[0]->value[0] = 0x11; /* Slot 1 */
- report->field[0]->value[1] = 0x08;
- report->field[0]->value[2] = x;
- report->field[0]->value[3] = 0x80;
- report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0x11; /* Slot 1 */
+ value[1] = 0x08;
+ value[2] = x;
+ value[3] = 0x80;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
break;
@@ -163,14 +220,15 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ __s32 *value = report->field[0]->value;
- report->field[0]->value[0] = 0xfe;
- report->field[0]->value[1] = 0x0d;
- report->field[0]->value[2] = magnitude >> 13;
- report->field[0]->value[3] = magnitude >> 13;
- report->field[0]->value[4] = magnitude >> 8;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0xfe;
+ value[1] = 0x0d;
+ value[2] = magnitude >> 13;
+ value[3] = magnitude >> 13;
+ value[4] = magnitude >> 8;
+ value[5] = 0x00;
+ value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
@@ -181,16 +239,16 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ __s32 *value = report->field[0]->value;
magnitude = magnitude * 90 / 65535;
-
- report->field[0]->value[0] = 0xfe;
- report->field[0]->value[1] = 0x03;
- report->field[0]->value[2] = magnitude >> 14;
- report->field[0]->value[3] = magnitude >> 14;
- report->field[0]->value[4] = magnitude;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0xfe;
+ value[1] = 0x03;
+ value[2] = magnitude >> 14;
+ value[3] = magnitude >> 14;
+ value[4] = magnitude;
+ value[5] = 0x00;
+ value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
@@ -200,15 +258,17 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
{
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ __s32 *value = report->field[0]->value;
+
dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
- report->field[0]->value[0] = 0xf8;
- report->field[0]->value[1] = 0x81;
- report->field[0]->value[2] = range & 0x00ff;
- report->field[0]->value[3] = (range & 0xff00) >> 8;
- report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0xf8;
+ value[1] = 0x81;
+ value[2] = range & 0x00ff;
+ value[3] = (range & 0xff00) >> 8;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
@@ -219,16 +279,18 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
int start_left, start_right, full_range;
+ __s32 *value = report->field[0]->value;
+
dbg_hid("Driving Force Pro: setting range to %u\n", range);
/* Prepare "coarse" limit command */
- report->field[0]->value[0] = 0xf8;
- report->field[0]->value[1] = 0x00; /* Set later */
- report->field[0]->value[2] = 0x00;
- report->field[0]->value[3] = 0x00;
- report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0xf8;
+ value[1] = 0x00; /* Set later */
+ value[2] = 0x00;
+ value[3] = 0x00;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
if (range > 200) {
report->field[0]->value[1] = 0x03;
@@ -240,13 +302,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
usbhid_submit_report(hid, report, USB_DIR_OUT);
/* Prepare "fine" limit command */
- report->field[0]->value[0] = 0x81;
- report->field[0]->value[1] = 0x0b;
- report->field[0]->value[2] = 0x00;
- report->field[0]->value[3] = 0x00;
- report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0x81;
+ value[1] = 0x0b;
+ value[2] = 0x00;
+ value[3] = 0x00;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
if (range == 200 || range == 900) { /* Do not apply any fine limit */
usbhid_submit_report(hid, report, USB_DIR_OUT);
@@ -257,11 +319,11 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
start_left = (((full_range - range + 1) * 2047) / full_range);
start_right = 0xfff - start_left;
- report->field[0]->value[2] = start_left >> 4;
- report->field[0]->value[3] = start_right >> 4;
- report->field[0]->value[4] = 0xff;
- report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
- report->field[0]->value[6] = 0xff;
+ value[2] = start_left >> 4;
+ value[3] = start_right >> 4;
+ value[4] = 0xff;
+ value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
+ value[6] = 0xff;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
@@ -344,14 +406,15 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
{
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-
- report->field[0]->value[0] = 0xf8;
- report->field[0]->value[1] = 0x12;
- report->field[0]->value[2] = leds;
- report->field[0]->value[3] = 0x00;
- report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ __s32 *value = report->field[0]->value;
+
+ value[0] = 0xf8;
+ value[1] = 0x12;
+ value[2] = leds;
+ value[3] = 0x00;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
@@ -360,7 +423,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
{
struct device *dev = led_cdev->dev->parent;
struct hid_device *hid = container_of(dev, struct hid_device, dev);
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hid);
struct lg4ff_device_entry *entry;
int i, state = 0;
@@ -395,7 +458,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
{
struct device *dev = led_cdev->dev->parent;
struct hid_device *hid = container_of(dev, struct hid_device, dev);
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hid);
struct lg4ff_device_entry *entry;
int i, value = 0;
@@ -501,7 +564,7 @@ int lg4ff_init(struct hid_device *hid)
/* Check if autocentering is available and
* set the centering force to zero by default */
if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
- if(rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
+ if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
else
dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
@@ -524,6 +587,7 @@ int lg4ff_init(struct hid_device *hid)
}
drv_data->device_props = entry;
+ entry->product_id = lg4ff_devices[i].product_id;
entry->min_range = lg4ff_devices[i].min_range;
entry->max_range = lg4ff_devices[i].max_range;
entry->set_range = lg4ff_devices[i].set_range;
@@ -534,6 +598,18 @@ int lg4ff_init(struct hid_device *hid)
return error;
dbg_hid("sysfs interface created\n");
+ /* Set default axes parameters */
+ switch (lg4ff_devices[i].product_id) {
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+ dbg_hid("Setting axes parameters for Driving Force Pro\n");
+ input_set_abs_params(dev, ABS_X, DFP_X_MIN, DFP_X_MAX, 0, 0);
+ input_set_abs_params(dev, ABS_Y, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0);
+ input_set_abs_params(dev, ABS_RZ, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0);
+ break;
+ default:
+ break;
+ }
+
/* Set the maximum range to start with */
entry->range = entry->max_range;
if (entry->set_range != NULL)
@@ -594,6 +670,8 @@ out:
return 0;
}
+
+
int lg4ff_deinit(struct hid_device *hid)
{
struct lg4ff_device_entry *entry;
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 4d524b5..9500f2f 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -193,6 +193,7 @@ static struct hid_ll_driver logi_dj_ll_driver;
static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
size_t count,
unsigned char report_type);
+static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
@@ -233,6 +234,7 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
SPFUNCTION_DEVICE_LIST_EMPTY) {
dbg_hid("%s: device list is empty\n", __func__);
+ djrcv_dev->querying_devices = false;
return;
}
@@ -243,6 +245,12 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
return;
}
+ if (djrcv_dev->paired_dj_devices[dj_report->device_index]) {
+ /* The device is already known. No need to reallocate it. */
+ dbg_hid("%s: device is already known\n", __func__);
+ return;
+ }
+
dj_hiddev = hid_allocate_device();
if (IS_ERR(dj_hiddev)) {
dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
@@ -306,6 +314,7 @@ static void delayedwork_callback(struct work_struct *work)
struct dj_report dj_report;
unsigned long flags;
int count;
+ int retval;
dbg_hid("%s\n", __func__);
@@ -338,6 +347,25 @@ static void delayedwork_callback(struct work_struct *work)
logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
break;
default:
+ /* A normal report (i. e. not belonging to a pair/unpair notification)
+ * arriving here, means that the report arrived but we did not have a
+ * paired dj_device associated to the report's device_index, this
+ * means that the original "device paired" notification corresponding
+ * to this dj_device never arrived to this driver. The reason is that
+ * hid-core discards all packets coming from a device while probe() is
+ * executing. */
+ if (!djrcv_dev->paired_dj_devices[dj_report.device_index]) {
+ /* ok, we don't know the device, just re-ask the
+ * receiver for the list of connected devices. */
+ retval = logi_dj_recv_query_paired_devices(djrcv_dev);
+ if (!retval) {
+ /* everything went fine, so just leave */
+ break;
+ }
+ dev_err(&djrcv_dev->hdev->dev,
+ "%s:logi_dj_recv_query_paired_devices "
+ "error:%d\n", __func__, retval);
+ }
dbg_hid("%s: unexpected report type\n", __func__);
}
}
@@ -368,6 +396,12 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
if (!djdev) {
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d\n", dj_report->device_index);
+ kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+ if (schedule_work(&djrcv_dev->work) == 0) {
+ dbg_hid("%s: did not schedule the work item, was already "
+ "queued\n", __func__);
+ }
return;
}
@@ -398,6 +432,12 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
if (dj_device == NULL) {
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d\n", dj_report->device_index);
+ kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+ if (schedule_work(&djrcv_dev->work) == 0) {
+ dbg_hid("%s: did not schedule the work item, was already "
+ "queued\n", __func__);
+ }
return;
}
@@ -439,6 +479,10 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
struct dj_report *dj_report;
int retval;
+ /* no need to protect djrcv_dev->querying_devices */
+ if (djrcv_dev->querying_devices)
+ return 0;
+
dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
if (!dj_report)
return -ENOMEM;
@@ -450,6 +494,7 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
return retval;
}
+
static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
unsigned timeout)
{
diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h
index fd28a5e..4a40003 100644
--- a/drivers/hid/hid-logitech-dj.h
+++ b/drivers/hid/hid-logitech-dj.h
@@ -101,6 +101,7 @@ struct dj_receiver_dev {
struct work_struct work;
struct kfifo notif_fifo;
spinlock_t lock;
+ bool querying_devices;
};
struct dj_device {
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 7364726..25ddf3e 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -392,7 +392,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
__set_bit(EV_ABS, input->evbit);
- error = input_mt_init_slots(input, 16);
+ error = input_mt_init_slots(input, 16, 0);
if (error)
return error;
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index e5c699b..3acdcfc 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c
index dedf757..cd3643e 100644
--- a/drivers/hid/hid-monterey.c
+++ b/drivers/hid/hid-monterey.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 59c8b5c..3eb02b9 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -51,12 +51,12 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_VALID_IS_INRANGE (1 << 5)
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8)
+#define MT_QUIRK_NO_AREA (1 << 9)
struct mt_slot {
__s32 x, y, p, w, h;
__s32 contactid; /* the device ContactID assigned to this slot */
bool touch_state; /* is the touch valid? */
- bool seen_in_this_frame;/* has this slot been updated */
};
struct mt_class {
@@ -92,8 +92,9 @@ struct mt_device {
__u8 touches_by_report; /* how many touches are present in one report:
* 1 means we should use a serial protocol
* > 1 means hybrid (multitouch) protocol */
+ bool serial_maybe; /* need to check for serial protocol */
bool curvalid; /* is the current contact valid? */
- struct mt_slot *slots;
+ unsigned mt_flags; /* flags to pass to input-mt */
};
/* classes of device behavior */
@@ -115,6 +116,9 @@ struct mt_device {
#define MT_CLS_EGALAX_SERIAL 0x0104
#define MT_CLS_TOPSEED 0x0105
#define MT_CLS_PANASONIC 0x0106
+#define MT_CLS_FLATFROG 0x0107
+#define MT_CLS_GENERALTOUCH_TWOFINGERS 0x0108
+#define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109
#define MT_DEFAULT_MAXCONTACT 10
@@ -134,25 +138,6 @@ static int cypress_compute_slot(struct mt_device *td)
return -1;
}
-static int find_slot_from_contactid(struct mt_device *td)
-{
- int i;
- for (i = 0; i < td->maxcontacts; ++i) {
- if (td->slots[i].contactid == td->curdata.contactid &&
- td->slots[i].touch_state)
- return i;
- }
- for (i = 0; i < td->maxcontacts; ++i) {
- if (!td->slots[i].seen_in_this_frame &&
- !td->slots[i].touch_state)
- return i;
- }
- /* should not occurs. If this happens that means
- * that the device sent more touches that it says
- * in the report descriptor. It is ignored then. */
- return -1;
-}
-
static struct mt_class mt_classes[] = {
{ .name = MT_CLS_DEFAULT,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP },
@@ -190,7 +175,9 @@ static struct mt_class mt_classes[] = {
MT_QUIRK_SLOT_IS_CONTACTID,
.sn_move = 2048,
.sn_width = 128,
- .sn_height = 128 },
+ .sn_height = 128,
+ .maxcontacts = 60,
+ },
{ .name = MT_CLS_CYPRESS,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
MT_QUIRK_CYPRESS,
@@ -215,7 +202,24 @@ static struct mt_class mt_classes[] = {
{ .name = MT_CLS_PANASONIC,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP,
.maxcontacts = 4 },
+ { .name = MT_CLS_GENERALTOUCH_TWOFINGERS,
+ .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
+ MT_QUIRK_VALID_IS_INRANGE |
+ MT_QUIRK_SLOT_IS_CONTACTNUMBER,
+ .maxcontacts = 2
+ },
+ { .name = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
+ .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
+ MT_QUIRK_SLOT_IS_CONTACTNUMBER,
+ .maxcontacts = 10
+ },
+ { .name = MT_CLS_FLATFROG,
+ .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
+ MT_QUIRK_NO_AREA,
+ .sn_move = 2048,
+ .maxcontacts = 40,
+ },
{ }
};
@@ -319,24 +323,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
* We need to ignore fields that belong to other collections
* such as Mouse that might have the same GenericDesktop usages. */
if (field->application == HID_DG_TOUCHSCREEN)
- set_bit(INPUT_PROP_DIRECT, hi->input->propbit);
+ td->mt_flags |= INPUT_MT_DIRECT;
else if (field->application != HID_DG_TOUCHPAD)
return 0;
- /* In case of an indirect device (touchpad), we need to add
- * specific BTN_TOOL_* to be handled by the synaptics xorg
- * driver.
- * We also consider that touchscreens providing buttons are touchpads.
+ /*
+ * Model touchscreens providing buttons as touchpads.
*/
if (field->application == HID_DG_TOUCHPAD ||
- (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON ||
- cls->is_indirect) {
- set_bit(INPUT_PROP_POINTER, hi->input->propbit);
- set_bit(BTN_TOOL_FINGER, hi->input->keybit);
- set_bit(BTN_TOOL_DOUBLETAP, hi->input->keybit);
- set_bit(BTN_TOOL_TRIPLETAP, hi->input->keybit);
- set_bit(BTN_TOOL_QUADTAP, hi->input->keybit);
- }
+ (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
+ td->mt_flags |= INPUT_MT_POINTER;
/* eGalax devices provide a Digitizer.Stylus input which overrides
* the correct Digitizers.Finger X/Y ranges.
@@ -353,8 +349,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
EV_ABS, ABS_MT_POSITION_X);
set_abs(hi->input, ABS_MT_POSITION_X, field,
cls->sn_move);
- /* touchscreen emulation */
- set_abs(hi->input, ABS_X, field, cls->sn_move);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
@@ -363,8 +357,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
EV_ABS, ABS_MT_POSITION_Y);
set_abs(hi->input, ABS_MT_POSITION_Y, field,
cls->sn_move);
- /* touchscreen emulation */
- set_abs(hi->input, ABS_Y, field, cls->sn_move);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
@@ -388,9 +380,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
td->last_field_index = field->index;
return 1;
case HID_DG_CONTACTID:
- if (!td->maxcontacts)
- td->maxcontacts = MT_DEFAULT_MAXCONTACT;
- input_mt_init_slots(hi->input, td->maxcontacts);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
td->touches_by_report++;
@@ -398,18 +387,21 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_DG_WIDTH:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TOUCH_MAJOR);
- set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
- cls->sn_width);
+ if (!(cls->quirks & MT_QUIRK_NO_AREA))
+ set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
+ cls->sn_width);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
case HID_DG_HEIGHT:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TOUCH_MINOR);
- set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
- cls->sn_height);
- input_set_abs_params(hi->input,
+ if (!(cls->quirks & MT_QUIRK_NO_AREA)) {
+ set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
+ cls->sn_height);
+ input_set_abs_params(hi->input,
ABS_MT_ORIENTATION, 0, 1, 0, 0);
+ }
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
@@ -418,9 +410,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
EV_ABS, ABS_MT_PRESSURE);
set_abs(hi->input, ABS_MT_PRESSURE, field,
cls->sn_pressure);
- /* touchscreen emulation */
- set_abs(hi->input, ABS_PRESSURE, field,
- cls->sn_pressure);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
@@ -464,7 +453,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
return -1;
}
-static int mt_compute_slot(struct mt_device *td)
+static int mt_compute_slot(struct mt_device *td, struct input_dev *input)
{
__s32 quirks = td->mtclass.quirks;
@@ -480,42 +469,23 @@ static int mt_compute_slot(struct mt_device *td)
if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE)
return td->curdata.contactid - 1;
- return find_slot_from_contactid(td);
+ return input_mt_get_slot_by_key(input, td->curdata.contactid);
}
/*
* this function is called when a whole contact has been processed,
* so that it can assign it to a slot and store the data there
*/
-static void mt_complete_slot(struct mt_device *td)
+static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
{
- td->curdata.seen_in_this_frame = true;
if (td->curvalid) {
- int slotnum = mt_compute_slot(td);
-
- if (slotnum >= 0 && slotnum < td->maxcontacts)
- td->slots[slotnum] = td->curdata;
- }
- td->num_received++;
-}
+ int slotnum = mt_compute_slot(td, input);
+ struct mt_slot *s = &td->curdata;
+ if (slotnum < 0 || slotnum >= td->maxcontacts)
+ return;
-/*
- * this function is called when a whole packet has been received and processed,
- * so that it can decide what to send to the input layer.
- */
-static void mt_emit_event(struct mt_device *td, struct input_dev *input)
-{
- int i;
-
- for (i = 0; i < td->maxcontacts; ++i) {
- struct mt_slot *s = &(td->slots[i]);
- if ((td->mtclass.quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) &&
- !s->seen_in_this_frame) {
- s->touch_state = false;
- }
-
- input_mt_slot(input, i);
+ input_mt_slot(input, slotnum);
input_mt_report_slot_state(input, MT_TOOL_FINGER,
s->touch_state);
if (s->touch_state) {
@@ -532,24 +502,29 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input)
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
}
- s->seen_in_this_frame = false;
-
}
- input_mt_report_pointer_emulation(input, true);
+ td->num_received++;
+}
+
+/*
+ * this function is called when a whole packet has been received and processed,
+ * so that it can decide what to send to the input layer.
+ */
+static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
+{
+ input_mt_sync_frame(input);
input_sync(input);
td->num_received = 0;
}
-
-
static int mt_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct mt_device *td = hid_get_drvdata(hid);
__s32 quirks = td->mtclass.quirks;
- if (hid->claimed & HID_CLAIMED_INPUT && td->slots) {
+ if (hid->claimed & HID_CLAIMED_INPUT) {
switch (usage->hid) {
case HID_DG_INRANGE:
if (quirks & MT_QUIRK_ALWAYS_VALID)
@@ -602,11 +577,11 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
}
if (usage->hid == td->last_slot_field)
- mt_complete_slot(td);
+ mt_complete_slot(td, field->hidinput->input);
if (field->index == td->last_field_index
&& td->num_received >= td->num_expected)
- mt_emit_event(td, field->hidinput->input);
+ mt_sync_frame(td, field->hidinput->input);
}
@@ -685,18 +660,45 @@ static void mt_post_parse(struct mt_device *td)
}
}
+static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+
+{
+ struct mt_device *td = hid_get_drvdata(hdev);
+ struct mt_class *cls = &td->mtclass;
+ struct input_dev *input = hi->input;
+
+ /* Only initialize slots for MT input devices */
+ if (!test_bit(ABS_MT_POSITION_X, input->absbit))
+ return;
+
+ if (!td->maxcontacts)
+ td->maxcontacts = MT_DEFAULT_MAXCONTACT;
+
+ mt_post_parse(td);
+ if (td->serial_maybe)
+ mt_post_parse_default_settings(td);
+
+ if (cls->is_indirect)
+ td->mt_flags |= INPUT_MT_POINTER;
+
+ if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
+ td->mt_flags |= INPUT_MT_DROP_UNUSED;
+
+ input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
+
+ td->mt_flags = 0;
+}
+
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret, i;
struct mt_device *td;
struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
- if (id) {
- for (i = 0; mt_classes[i].name ; i++) {
- if (id->driver_data == mt_classes[i].name) {
- mtclass = &(mt_classes[i]);
- break;
- }
+ for (i = 0; mt_classes[i].name ; i++) {
+ if (id->driver_data == mt_classes[i].name) {
+ mtclass = &(mt_classes[i]);
+ break;
}
}
@@ -722,6 +724,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto fail;
}
+ if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
+ td->serial_maybe = true;
+
ret = hid_parse(hdev);
if (ret != 0)
goto fail;
@@ -730,20 +735,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret)
goto fail;
- mt_post_parse(td);
-
- if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
- mt_post_parse_default_settings(td);
-
- td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot),
- GFP_KERNEL);
- if (!td->slots) {
- dev_err(&hdev->dev, "cannot allocate multitouch slots\n");
- hid_hw_stop(hdev);
- ret = -ENOMEM;
- goto fail;
- }
-
ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
mt_set_maxcontacts(hdev);
@@ -767,6 +758,32 @@ static int mt_reset_resume(struct hid_device *hdev)
mt_set_input_mode(hdev);
return 0;
}
+
+static int mt_resume(struct hid_device *hdev)
+{
+ struct usb_interface *intf;
+ struct usb_host_interface *interface;
+ struct usb_device *dev;
+
+ if (hdev->bus != BUS_USB)
+ return 0;
+
+ intf = to_usb_interface(hdev->dev.parent);
+ interface = intf->cur_altsetting;
+ dev = hid_to_usb_dev(hdev);
+
+ /* Some Elan legacy devices require SET_IDLE to be set on resume.
+ * It should be safe to send it to other devices too.
+ * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
+
+ usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ HID_REQ_SET_IDLE,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, interface->desc.bInterfaceNumber,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+ return 0;
+}
#endif
static void mt_remove(struct hid_device *hdev)
@@ -774,7 +791,6 @@ static void mt_remove(struct hid_device *hdev)
struct mt_device *td = hid_get_drvdata(hdev);
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
hid_hw_stop(hdev);
- kfree(td->slots);
kfree(td);
hid_set_drvdata(hdev, NULL);
}
@@ -885,17 +901,37 @@ static const struct hid_device_id mt_devices[] = {
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349) },
{ .driver_data = MT_CLS_EGALAX_SERIAL,
MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
+ USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7) },
+ { .driver_data = MT_CLS_EGALAX_SERIAL,
+ MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
+ { .driver_data = MT_CLS_EGALAX,
+ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+ USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) },
+ { .driver_data = MT_CLS_EGALAX,
+ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+ USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) },
+ { .driver_data = MT_CLS_EGALAX,
+ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+ USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) },
/* Elo TouchSystems IntelliTouch Plus panel */
{ .driver_data = MT_CLS_DUAL_NSMU_CONTACTID,
MT_USB_DEVICE(USB_VENDOR_ID_ELO,
USB_DEVICE_ID_ELO_TS2515) },
+ /* Flatfrog Panels */
+ { .driver_data = MT_CLS_FLATFROG,
+ MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG,
+ USB_DEVICE_ID_MULTITOUCH_3200) },
+
/* GeneralTouch panel */
- { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
+ { .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS,
MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) },
+ { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
+ MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
+ USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS) },
/* Gametel game controller */
{ .driver_data = MT_CLS_DEFAULT,
@@ -1087,11 +1123,13 @@ static struct hid_driver mt_driver = {
.remove = mt_remove,
.input_mapping = mt_input_mapping,
.input_mapped = mt_input_mapped,
+ .input_configured = mt_input_configured,
.feature_mapping = mt_feature_mapping,
.usage_table = mt_grabbed_usages,
.event = mt_event,
#ifdef CONFIG_PM
.reset_resume = mt_reset_resume,
+ .resume = mt_resume,
#endif
};
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 9fae2eb..86a969f 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -882,10 +882,10 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
nd->activate_slack = activate_slack;
nd->act_state = activate_slack;
nd->deactivate_slack = -deactivate_slack;
- nd->sensor_logical_width = 0;
- nd->sensor_logical_height = 0;
- nd->sensor_physical_width = 0;
- nd->sensor_physical_height = 0;
+ nd->sensor_logical_width = 1;
+ nd->sensor_logical_height = 1;
+ nd->sensor_physical_width = 1;
+ nd->sensor_physical_height = 1;
hid_set_drvdata(hdev, nd);
diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c
index f1ea3ff..4c521de 100644
--- a/drivers/hid/hid-petalynx.c
+++ b/drivers/hid/hid-petalynx.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c
deleted file mode 100644
index 27c8ebd..0000000
--- a/drivers/hid/hid-picolcd.c
+++ /dev/null
@@ -1,2748 +0,0 @@
-/***************************************************************************
- * Copyright (C) 2010 by Bruno Prémont <bonbons@linux-vserver.org> *
- * *
- * Based on Logitech G13 driver (v0.4) *
- * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
- * *
- * 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 of the License. *
- * *
- * This driver 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 software. If not see <http://www.gnu.org/licenses/>. *
- ***************************************************************************/
-
-#include <linux/hid.h>
-#include <linux/hid-debug.h>
-#include <linux/input.h>
-#include "hid-ids.h"
-#include "usbhid/usbhid.h"
-#include <linux/usb.h>
-
-#include <linux/fb.h>
-#include <linux/vmalloc.h>
-#include <linux/backlight.h>
-#include <linux/lcd.h>
-
-#include <linux/leds.h>
-
-#include <linux/seq_file.h>
-#include <linux/debugfs.h>
-
-#include <linux/completion.h>
-#include <linux/uaccess.h>
-#include <linux/module.h>
-
-#define PICOLCD_NAME "PicoLCD (graphic)"
-
-/* Report numbers */
-#define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */
-#define ERR_SUCCESS 0x00
-#define ERR_PARAMETER_MISSING 0x01
-#define ERR_DATA_MISSING 0x02
-#define ERR_BLOCK_READ_ONLY 0x03
-#define ERR_BLOCK_NOT_ERASABLE 0x04
-#define ERR_BLOCK_TOO_BIG 0x05
-#define ERR_SECTION_OVERFLOW 0x06
-#define ERR_INVALID_CMD_LEN 0x07
-#define ERR_INVALID_DATA_LEN 0x08
-#define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */
-#define REPORT_IR_DATA 0x21 /* LCD: IN[63] */
-#define REPORT_EE_DATA 0x32 /* LCD: IN[63] */
-#define REPORT_MEMORY 0x41 /* LCD: IN[63] */
-#define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */
-#define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */
-#define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */
-#define REPORT_RESET 0x93 /* LCD: OUT[2] */
-#define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */
-#define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */
-#define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */
-#define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */
-#define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */
-#define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */
-#define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */
-#define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */
-#define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */
-#define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */
-#define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */
-#define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */
-#define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */
-#define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */
-#define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */
-#define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */
-#define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */
-#define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */
-
-#ifdef CONFIG_HID_PICOLCD_FB
-/* Framebuffer
- *
- * The PicoLCD use a Topway LCD module of 256x64 pixel
- * This display area is tiled over 4 controllers with 8 tiles
- * each. Each tile has 8x64 pixel, each data byte representing
- * a 1-bit wide vertical line of the tile.
- *
- * The display can be updated at a tile granularity.
- *
- * Chip 1 Chip 2 Chip 3 Chip 4
- * +----------------+----------------+----------------+----------------+
- * | Tile 1 | Tile 1 | Tile 1 | Tile 1 |
- * +----------------+----------------+----------------+----------------+
- * | Tile 2 | Tile 2 | Tile 2 | Tile 2 |
- * +----------------+----------------+----------------+----------------+
- * ...
- * +----------------+----------------+----------------+----------------+
- * | Tile 8 | Tile 8 | Tile 8 | Tile 8 |
- * +----------------+----------------+----------------+----------------+
- */
-#define PICOLCDFB_NAME "picolcdfb"
-#define PICOLCDFB_WIDTH (256)
-#define PICOLCDFB_HEIGHT (64)
-#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
-
-#define PICOLCDFB_UPDATE_RATE_LIMIT 10
-#define PICOLCDFB_UPDATE_RATE_DEFAULT 2
-
-/* Framebuffer visual structures */
-static const struct fb_fix_screeninfo picolcdfb_fix = {
- .id = PICOLCDFB_NAME,
- .type = FB_TYPE_PACKED_PIXELS,
- .visual = FB_VISUAL_MONO01,
- .xpanstep = 0,
- .ypanstep = 0,
- .ywrapstep = 0,
- .line_length = PICOLCDFB_WIDTH / 8,
- .accel = FB_ACCEL_NONE,
-};
-
-static const struct fb_var_screeninfo picolcdfb_var = {
- .xres = PICOLCDFB_WIDTH,
- .yres = PICOLCDFB_HEIGHT,
- .xres_virtual = PICOLCDFB_WIDTH,
- .yres_virtual = PICOLCDFB_HEIGHT,
- .width = 103,
- .height = 26,
- .bits_per_pixel = 1,
- .grayscale = 1,
- .red = {
- .offset = 0,
- .length = 1,
- .msb_right = 0,
- },
- .green = {
- .offset = 0,
- .length = 1,
- .msb_right = 0,
- },
- .blue = {
- .offset = 0,
- .length = 1,
- .msb_right = 0,
- },
- .transp = {
- .offset = 0,
- .length = 0,
- .msb_right = 0,
- },
-};
-#endif /* CONFIG_HID_PICOLCD_FB */
-
-/* Input device
- *
- * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
- * and header for 4x4 key matrix. The built-in keys are part of the matrix.
- */
-static const unsigned short def_keymap[] = {
- KEY_RESERVED, /* none */
- KEY_BACK, /* col 4 + row 1 */
- KEY_HOMEPAGE, /* col 3 + row 1 */
- KEY_RESERVED, /* col 2 + row 1 */
- KEY_RESERVED, /* col 1 + row 1 */
- KEY_SCROLLUP, /* col 4 + row 2 */
- KEY_OK, /* col 3 + row 2 */
- KEY_SCROLLDOWN, /* col 2 + row 2 */
- KEY_RESERVED, /* col 1 + row 2 */
- KEY_RESERVED, /* col 4 + row 3 */
- KEY_RESERVED, /* col 3 + row 3 */
- KEY_RESERVED, /* col 2 + row 3 */
- KEY_RESERVED, /* col 1 + row 3 */
- KEY_RESERVED, /* col 4 + row 4 */
- KEY_RESERVED, /* col 3 + row 4 */
- KEY_RESERVED, /* col 2 + row 4 */
- KEY_RESERVED, /* col 1 + row 4 */
-};
-#define PICOLCD_KEYS ARRAY_SIZE(def_keymap)
-
-/* Description of in-progress IO operation, used for operations
- * that trigger response from device */
-struct picolcd_pending {
- struct hid_report *out_report;
- struct hid_report *in_report;
- struct completion ready;
- int raw_size;
- u8 raw_data[64];
-};
-
-/* Per device data structure */
-struct picolcd_data {
- struct hid_device *hdev;
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debug_reset;
- struct dentry *debug_eeprom;
- struct dentry *debug_flash;
- struct mutex mutex_flash;
- int addr_sz;
-#endif
- u8 version[2];
- unsigned short opmode_delay;
- /* input stuff */
- u8 pressed_keys[2];
- struct input_dev *input_keys;
- struct input_dev *input_cir;
- unsigned short keycode[PICOLCD_KEYS];
-
-#ifdef CONFIG_HID_PICOLCD_FB
- /* Framebuffer stuff */
- u8 fb_update_rate;
- u8 fb_bpp;
- u8 fb_force;
- u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */
- u8 *fb_bitmap; /* framebuffer */
- struct fb_info *fb_info;
- struct fb_deferred_io fb_defio;
-#endif /* CONFIG_HID_PICOLCD_FB */
-#ifdef CONFIG_HID_PICOLCD_LCD
- struct lcd_device *lcd;
- u8 lcd_contrast;
-#endif /* CONFIG_HID_PICOLCD_LCD */
-#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
- struct backlight_device *backlight;
- u8 lcd_brightness;
- u8 lcd_power;
-#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
-#ifdef CONFIG_HID_PICOLCD_LEDS
- /* LED stuff */
- u8 led_state;
- struct led_classdev *led[8];
-#endif /* CONFIG_HID_PICOLCD_LEDS */
-
- /* Housekeeping stuff */
- spinlock_t lock;
- struct mutex mutex;
- struct picolcd_pending *pending;
- int status;
-#define PICOLCD_BOOTLOADER 1
-#define PICOLCD_FAILED 2
-#define PICOLCD_READY_FB 4
-};
-
-
-/* Find a given report */
-#define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT)
-#define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT)
-
-static struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
-{
- struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;
- struct hid_report *report = NULL;
-
- list_for_each_entry(report, feature_report_list, list) {
- if (report->id == id)
- return report;
- }
- hid_warn(hdev, "No report with id 0x%x found\n", id);
- return NULL;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static void picolcd_debug_out_report(struct picolcd_data *data,
- struct hid_device *hdev, struct hid_report *report);
-#define usbhid_submit_report(a, b, c) \
- do { \
- picolcd_debug_out_report(hid_get_drvdata(a), a, b); \
- usbhid_submit_report(a, b, c); \
- } while (0)
-#endif
-
-/* Submit a report and wait for a reply from device - if device fades away
- * or does not respond in time, return NULL */
-static struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
- int report_id, const u8 *raw_data, int size)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- struct picolcd_pending *work;
- struct hid_report *report = picolcd_out_report(report_id, hdev);
- unsigned long flags;
- int i, j, k;
-
- if (!report || !data)
- return NULL;
- if (data->status & PICOLCD_FAILED)
- return NULL;
- work = kzalloc(sizeof(*work), GFP_KERNEL);
- if (!work)
- return NULL;
-
- init_completion(&work->ready);
- work->out_report = report;
- work->in_report = NULL;
- work->raw_size = 0;
-
- mutex_lock(&data->mutex);
- spin_lock_irqsave(&data->lock, flags);
- for (i = k = 0; i < report->maxfield; i++)
- for (j = 0; j < report->field[i]->report_count; j++) {
- hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
- k++;
- }
- data->pending = work;
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
- wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
- spin_lock_irqsave(&data->lock, flags);
- data->pending = NULL;
- spin_unlock_irqrestore(&data->lock, flags);
- mutex_unlock(&data->mutex);
- return work;
-}
-
-#ifdef CONFIG_HID_PICOLCD_FB
-/* Send a given tile to PicoLCD */
-static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- struct hid_report *report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, hdev);
- struct hid_report *report2 = picolcd_out_report(REPORT_LCD_DATA, hdev);
- unsigned long flags;
- u8 *tdata;
- int i;
-
- if (!report1 || report1->maxfield != 1 || !report2 || report2->maxfield != 1)
- return -ENODEV;
-
- spin_lock_irqsave(&data->lock, flags);
- hid_set_field(report1->field[0], 0, chip << 2);
- hid_set_field(report1->field[0], 1, 0x02);
- hid_set_field(report1->field[0], 2, 0x00);
- hid_set_field(report1->field[0], 3, 0x00);
- hid_set_field(report1->field[0], 4, 0xb8 | tile);
- hid_set_field(report1->field[0], 5, 0x00);
- hid_set_field(report1->field[0], 6, 0x00);
- hid_set_field(report1->field[0], 7, 0x40);
- hid_set_field(report1->field[0], 8, 0x00);
- hid_set_field(report1->field[0], 9, 0x00);
- hid_set_field(report1->field[0], 10, 32);
-
- hid_set_field(report2->field[0], 0, (chip << 2) | 0x01);
- hid_set_field(report2->field[0], 1, 0x00);
- hid_set_field(report2->field[0], 2, 0x00);
- hid_set_field(report2->field[0], 3, 32);
-
- tdata = data->fb_vbitmap + (tile * 4 + chip) * 64;
- for (i = 0; i < 64; i++)
- if (i < 32)
- hid_set_field(report1->field[0], 11 + i, tdata[i]);
- else
- hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
-
- usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);
- usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
- return 0;
-}
-
-/* Translate a single tile*/
-static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
- int chip, int tile)
-{
- int i, b, changed = 0;
- u8 tdata[64];
- u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
-
- if (bpp == 1) {
- for (b = 7; b >= 0; b--) {
- const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
- for (i = 0; i < 64; i++) {
- tdata[i] <<= 1;
- tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
- }
- }
- } else if (bpp == 8) {
- for (b = 7; b >= 0; b--) {
- const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
- for (i = 0; i < 64; i++) {
- tdata[i] <<= 1;
- tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
- }
- }
- } else {
- /* Oops, we should never get here! */
- WARN_ON(1);
- return 0;
- }
-
- for (i = 0; i < 64; i++)
- if (tdata[i] != vdata[i]) {
- changed = 1;
- vdata[i] = tdata[i];
- }
- return changed;
-}
-
-/* Reconfigure LCD display */
-static int picolcd_fb_reset(struct picolcd_data *data, int clear)
-{
- struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
- int i, j;
- unsigned long flags;
- static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
-
- if (!report || report->maxfield != 1)
- return -ENODEV;
-
- spin_lock_irqsave(&data->lock, flags);
- for (i = 0; i < 4; i++) {
- for (j = 0; j < report->field[0]->maxusage; j++)
- if (j == 0)
- hid_set_field(report->field[0], j, i << 2);
- else if (j < sizeof(mapcmd))
- hid_set_field(report->field[0], j, mapcmd[j]);
- else
- hid_set_field(report->field[0], j, 0);
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- }
-
- data->status |= PICOLCD_READY_FB;
- spin_unlock_irqrestore(&data->lock, flags);
-
- if (data->fb_bitmap) {
- if (clear) {
- memset(data->fb_vbitmap, 0, PICOLCDFB_SIZE);
- memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp);
- }
- data->fb_force = 1;
- }
-
- /* schedule first output of framebuffer */
- if (data->fb_info)
- schedule_delayed_work(&data->fb_info->deferred_work, 0);
-
- return 0;
-}
-
-/* Update fb_vbitmap from the screen_base and send changed tiles to device */
-static void picolcd_fb_update(struct picolcd_data *data)
-{
- int chip, tile, n;
- unsigned long flags;
-
- if (!data)
- return;
-
- spin_lock_irqsave(&data->lock, flags);
- if (!(data->status & PICOLCD_READY_FB)) {
- spin_unlock_irqrestore(&data->lock, flags);
- picolcd_fb_reset(data, 0);
- } else {
- spin_unlock_irqrestore(&data->lock, flags);
- }
-
- /*
- * Translate the framebuffer into the format needed by the PicoLCD.
- * See display layout above.
- * Do this one tile after the other and push those tiles that changed.
- *
- * Wait for our IO to complete as otherwise we might flood the queue!
- */
- n = 0;
- for (chip = 0; chip < 4; chip++)
- for (tile = 0; tile < 8; tile++)
- if (picolcd_fb_update_tile(data->fb_vbitmap,
- data->fb_bitmap, data->fb_bpp, chip, tile) ||
- data->fb_force) {
- n += 2;
- if (!data->fb_info->par)
- return; /* device lost! */
- if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
- usbhid_wait_io(data->hdev);
- n = 0;
- }
- picolcd_fb_send_tile(data->hdev, chip, tile);
- }
- data->fb_force = false;
- if (n)
- usbhid_wait_io(data->hdev);
-}
-
-/* Stub to call the system default and update the image on the picoLCD */
-static void picolcd_fb_fillrect(struct fb_info *info,
- const struct fb_fillrect *rect)
-{
- if (!info->par)
- return;
- sys_fillrect(info, rect);
-
- schedule_delayed_work(&info->deferred_work, 0);
-}
-
-/* Stub to call the system default and update the image on the picoLCD */
-static void picolcd_fb_copyarea(struct fb_info *info,
- const struct fb_copyarea *area)
-{
- if (!info->par)
- return;
- sys_copyarea(info, area);
-
- schedule_delayed_work(&info->deferred_work, 0);
-}
-
-/* Stub to call the system default and update the image on the picoLCD */
-static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
-{
- if (!info->par)
- return;
- sys_imageblit(info, image);
-
- schedule_delayed_work(&info->deferred_work, 0);
-}
-
-/*
- * this is the slow path from userspace. they can seek and write to
- * the fb. it's inefficient to do anything less than a full screen draw
- */
-static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- ssize_t ret;
- if (!info->par)
- return -ENODEV;
- ret = fb_sys_write(info, buf, count, ppos);
- if (ret >= 0)
- schedule_delayed_work(&info->deferred_work, 0);
- return ret;
-}
-
-static int picolcd_fb_blank(int blank, struct fb_info *info)
-{
- if (!info->par)
- return -ENODEV;
- /* We let fb notification do this for us via lcd/backlight device */
- return 0;
-}
-
-static void picolcd_fb_destroy(struct fb_info *info)
-{
- struct picolcd_data *data = info->par;
- u32 *ref_cnt = info->pseudo_palette;
- int may_release;
-
- info->par = NULL;
- if (data)
- data->fb_info = NULL;
- fb_deferred_io_cleanup(info);
-
- ref_cnt--;
- mutex_lock(&info->lock);
- (*ref_cnt)--;
- may_release = !*ref_cnt;
- mutex_unlock(&info->lock);
- if (may_release) {
- vfree((u8 *)info->fix.smem_start);
- framebuffer_release(info);
- }
-}
-
-static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
-{
- __u32 bpp = var->bits_per_pixel;
- __u32 activate = var->activate;
-
- /* only allow 1/8 bit depth (8-bit is grayscale) */
- *var = picolcdfb_var;
- var->activate = activate;
- if (bpp >= 8) {
- var->bits_per_pixel = 8;
- var->red.length = 8;
- var->green.length = 8;
- var->blue.length = 8;
- } else {
- var->bits_per_pixel = 1;
- var->red.length = 1;
- var->green.length = 1;
- var->blue.length = 1;
- }
- return 0;
-}
-
-static int picolcd_set_par(struct fb_info *info)
-{
- struct picolcd_data *data = info->par;
- u8 *tmp_fb, *o_fb;
- if (!data)
- return -ENODEV;
- if (info->var.bits_per_pixel == data->fb_bpp)
- return 0;
- /* switch between 1/8 bit depths */
- if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
- return -EINVAL;
-
- o_fb = data->fb_bitmap;
- tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
- if (!tmp_fb)
- return -ENOMEM;
-
- /* translate FB content to new bits-per-pixel */
- if (info->var.bits_per_pixel == 1) {
- int i, b;
- for (i = 0; i < PICOLCDFB_SIZE; i++) {
- u8 p = 0;
- for (b = 0; b < 8; b++) {
- p <<= 1;
- p |= o_fb[i*8+b] ? 0x01 : 0x00;
- }
- tmp_fb[i] = p;
- }
- memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
- info->fix.visual = FB_VISUAL_MONO01;
- info->fix.line_length = PICOLCDFB_WIDTH / 8;
- } else {
- int i;
- memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
- for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
- o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
- info->fix.visual = FB_VISUAL_DIRECTCOLOR;
- info->fix.line_length = PICOLCDFB_WIDTH;
- }
-
- kfree(tmp_fb);
- data->fb_bpp = info->var.bits_per_pixel;
- return 0;
-}
-
-/* Do refcounting on our FB and cleanup per worker if FB is
- * closed after unplug of our device
- * (fb_release holds info->lock and still touches info after
- * we return so we can't release it immediately.
- */
-struct picolcd_fb_cleanup_item {
- struct fb_info *info;
- struct picolcd_fb_cleanup_item *next;
-};
-static struct picolcd_fb_cleanup_item *fb_pending;
-static DEFINE_SPINLOCK(fb_pending_lock);
-
-static void picolcd_fb_do_cleanup(struct work_struct *data)
-{
- struct picolcd_fb_cleanup_item *item;
- unsigned long flags;
-
- do {
- spin_lock_irqsave(&fb_pending_lock, flags);
- item = fb_pending;
- fb_pending = item ? item->next : NULL;
- spin_unlock_irqrestore(&fb_pending_lock, flags);
-
- if (item) {
- u8 *fb = (u8 *)item->info->fix.smem_start;
- /* make sure we do not race against fb core when
- * releasing */
- mutex_lock(&item->info->lock);
- mutex_unlock(&item->info->lock);
- framebuffer_release(item->info);
- vfree(fb);
- }
- } while (item);
-}
-
-static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
-
-static int picolcd_fb_open(struct fb_info *info, int u)
-{
- u32 *ref_cnt = info->pseudo_palette;
- ref_cnt--;
-
- (*ref_cnt)++;
- return 0;
-}
-
-static int picolcd_fb_release(struct fb_info *info, int u)
-{
- u32 *ref_cnt = info->pseudo_palette;
- ref_cnt--;
-
- (*ref_cnt)++;
- if (!*ref_cnt) {
- unsigned long flags;
- struct picolcd_fb_cleanup_item *item = (struct picolcd_fb_cleanup_item *)ref_cnt;
- item--;
- spin_lock_irqsave(&fb_pending_lock, flags);
- item->next = fb_pending;
- fb_pending = item;
- spin_unlock_irqrestore(&fb_pending_lock, flags);
- schedule_work(&picolcd_fb_cleanup);
- }
- return 0;
-}
-
-/* Note this can't be const because of struct fb_info definition */
-static struct fb_ops picolcdfb_ops = {
- .owner = THIS_MODULE,
- .fb_destroy = picolcd_fb_destroy,
- .fb_open = picolcd_fb_open,
- .fb_release = picolcd_fb_release,
- .fb_read = fb_sys_read,
- .fb_write = picolcd_fb_write,
- .fb_blank = picolcd_fb_blank,
- .fb_fillrect = picolcd_fb_fillrect,
- .fb_copyarea = picolcd_fb_copyarea,
- .fb_imageblit = picolcd_fb_imageblit,
- .fb_check_var = picolcd_fb_check_var,
- .fb_set_par = picolcd_set_par,
-};
-
-
-/* Callback from deferred IO workqueue */
-static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
-{
- picolcd_fb_update(info->par);
-}
-
-static const struct fb_deferred_io picolcd_fb_defio = {
- .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
- .deferred_io = picolcd_fb_deferred_io,
-};
-
-
-/*
- * The "fb_update_rate" sysfs attribute
- */
-static ssize_t picolcd_fb_update_rate_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
- unsigned i, fb_update_rate = data->fb_update_rate;
- size_t ret = 0;
-
- for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
- if (ret >= PAGE_SIZE)
- break;
- else if (i == fb_update_rate)
- ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
- else
- ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
- if (ret > 0)
- buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
- return ret;
-}
-
-static ssize_t picolcd_fb_update_rate_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
- int i;
- unsigned u;
-
- if (count < 1 || count > 10)
- return -EINVAL;
-
- i = sscanf(buf, "%u", &u);
- if (i != 1)
- return -EINVAL;
-
- if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
- return -ERANGE;
- else if (u == 0)
- u = PICOLCDFB_UPDATE_RATE_DEFAULT;
-
- data->fb_update_rate = u;
- data->fb_defio.delay = HZ / data->fb_update_rate;
- return count;
-}
-
-static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
- picolcd_fb_update_rate_store);
-
-/* initialize Framebuffer device */
-static int picolcd_init_framebuffer(struct picolcd_data *data)
-{
- struct device *dev = &data->hdev->dev;
- struct fb_info *info = NULL;
- int i, error = -ENOMEM;
- u8 *fb_vbitmap = NULL;
- u8 *fb_bitmap = NULL;
- u32 *palette;
-
- fb_bitmap = vmalloc(PICOLCDFB_SIZE*8);
- if (fb_bitmap == NULL) {
- dev_err(dev, "can't get a free page for framebuffer\n");
- goto err_nomem;
- }
-
- fb_vbitmap = kmalloc(PICOLCDFB_SIZE, GFP_KERNEL);
- if (fb_vbitmap == NULL) {
- dev_err(dev, "can't alloc vbitmap image buffer\n");
- goto err_nomem;
- }
-
- data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
- data->fb_defio = picolcd_fb_defio;
- /* The extra memory is:
- * - struct picolcd_fb_cleanup_item
- * - u32 for ref_count
- * - 256*u32 for pseudo_palette
- */
- info = framebuffer_alloc(257 * sizeof(u32) + sizeof(struct picolcd_fb_cleanup_item), dev);
- if (info == NULL) {
- dev_err(dev, "failed to allocate a framebuffer\n");
- goto err_nomem;
- }
-
- palette = info->par + sizeof(struct picolcd_fb_cleanup_item);
- *palette = 1;
- palette++;
- for (i = 0; i < 256; i++)
- palette[i] = i > 0 && i < 16 ? 0xff : 0;
- info->pseudo_palette = palette;
- info->fbdefio = &data->fb_defio;
- info->screen_base = (char __force __iomem *)fb_bitmap;
- info->fbops = &picolcdfb_ops;
- info->var = picolcdfb_var;
- info->fix = picolcdfb_fix;
- info->fix.smem_len = PICOLCDFB_SIZE*8;
- info->fix.smem_start = (unsigned long)fb_bitmap;
- info->par = data;
- info->flags = FBINFO_FLAG_DEFAULT;
-
- data->fb_vbitmap = fb_vbitmap;
- data->fb_bitmap = fb_bitmap;
- data->fb_bpp = picolcdfb_var.bits_per_pixel;
- error = picolcd_fb_reset(data, 1);
- if (error) {
- dev_err(dev, "failed to configure display\n");
- goto err_cleanup;
- }
- error = device_create_file(dev, &dev_attr_fb_update_rate);
- if (error) {
- dev_err(dev, "failed to create sysfs attributes\n");
- goto err_cleanup;
- }
- fb_deferred_io_init(info);
- data->fb_info = info;
- error = register_framebuffer(info);
- if (error) {
- dev_err(dev, "failed to register framebuffer\n");
- goto err_sysfs;
- }
- /* schedule first output of framebuffer */
- data->fb_force = 1;
- schedule_delayed_work(&info->deferred_work, 0);
- return 0;
-
-err_sysfs:
- fb_deferred_io_cleanup(info);
- device_remove_file(dev, &dev_attr_fb_update_rate);
-err_cleanup:
- data->fb_vbitmap = NULL;
- data->fb_bitmap = NULL;
- data->fb_bpp = 0;
- data->fb_info = NULL;
-
-err_nomem:
- framebuffer_release(info);
- vfree(fb_bitmap);
- kfree(fb_vbitmap);
- return error;
-}
-
-static void picolcd_exit_framebuffer(struct picolcd_data *data)
-{
- struct fb_info *info = data->fb_info;
- u8 *fb_vbitmap = data->fb_vbitmap;
-
- if (!info)
- return;
-
- info->par = NULL;
- device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
- unregister_framebuffer(info);
- data->fb_vbitmap = NULL;
- data->fb_bitmap = NULL;
- data->fb_bpp = 0;
- data->fb_info = NULL;
- kfree(fb_vbitmap);
-}
-
-#define picolcd_fbinfo(d) ((d)->fb_info)
-#else
-static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
-{
- return 0;
-}
-static inline int picolcd_init_framebuffer(struct picolcd_data *data)
-{
- return 0;
-}
-static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
-{
-}
-#define picolcd_fbinfo(d) NULL
-#endif /* CONFIG_HID_PICOLCD_FB */
-
-#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
-/*
- * backlight class device
- */
-static int picolcd_get_brightness(struct backlight_device *bdev)
-{
- struct picolcd_data *data = bl_get_data(bdev);
- return data->lcd_brightness;
-}
-
-static int picolcd_set_brightness(struct backlight_device *bdev)
-{
- struct picolcd_data *data = bl_get_data(bdev);
- struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);
- unsigned long flags;
-
- if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
- return -ENODEV;
-
- data->lcd_brightness = bdev->props.brightness & 0x0ff;
- data->lcd_power = bdev->props.power;
- spin_lock_irqsave(&data->lock, flags);
- hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
- return 0;
-}
-
-static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)
-{
- return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));
-}
-
-static const struct backlight_ops picolcd_blops = {
- .update_status = picolcd_set_brightness,
- .get_brightness = picolcd_get_brightness,
- .check_fb = picolcd_check_bl_fb,
-};
-
-static int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)
-{
- struct device *dev = &data->hdev->dev;
- struct backlight_device *bdev;
- struct backlight_properties props;
- if (!report)
- return -ENODEV;
- if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
- report->field[0]->report_size != 8) {
- dev_err(dev, "unsupported BRIGHTNESS report");
- return -EINVAL;
- }
-
- memset(&props, 0, sizeof(props));
- props.type = BACKLIGHT_RAW;
- props.max_brightness = 0xff;
- bdev = backlight_device_register(dev_name(dev), dev, data,
- &picolcd_blops, &props);
- if (IS_ERR(bdev)) {
- dev_err(dev, "failed to register backlight\n");
- return PTR_ERR(bdev);
- }
- bdev->props.brightness = 0xff;
- data->lcd_brightness = 0xff;
- data->backlight = bdev;
- picolcd_set_brightness(bdev);
- return 0;
-}
-
-static void picolcd_exit_backlight(struct picolcd_data *data)
-{
- struct backlight_device *bdev = data->backlight;
-
- data->backlight = NULL;
- if (bdev)
- backlight_device_unregister(bdev);
-}
-
-static inline int picolcd_resume_backlight(struct picolcd_data *data)
-{
- if (!data->backlight)
- return 0;
- return picolcd_set_brightness(data->backlight);
-}
-
-#ifdef CONFIG_PM
-static void picolcd_suspend_backlight(struct picolcd_data *data)
-{
- int bl_power = data->lcd_power;
- if (!data->backlight)
- return;
-
- data->backlight->props.power = FB_BLANK_POWERDOWN;
- picolcd_set_brightness(data->backlight);
- data->lcd_power = data->backlight->props.power = bl_power;
-}
-#endif /* CONFIG_PM */
-#else
-static inline int picolcd_init_backlight(struct picolcd_data *data,
- struct hid_report *report)
-{
- return 0;
-}
-static inline void picolcd_exit_backlight(struct picolcd_data *data)
-{
-}
-static inline int picolcd_resume_backlight(struct picolcd_data *data)
-{
- return 0;
-}
-static inline void picolcd_suspend_backlight(struct picolcd_data *data)
-{
-}
-#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
-
-#ifdef CONFIG_HID_PICOLCD_LCD
-/*
- * lcd class device
- */
-static int picolcd_get_contrast(struct lcd_device *ldev)
-{
- struct picolcd_data *data = lcd_get_data(ldev);
- return data->lcd_contrast;
-}
-
-static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)
-{
- struct picolcd_data *data = lcd_get_data(ldev);
- struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev);
- unsigned long flags;
-
- if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
- return -ENODEV;
-
- data->lcd_contrast = contrast & 0x0ff;
- spin_lock_irqsave(&data->lock, flags);
- hid_set_field(report->field[0], 0, data->lcd_contrast);
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
- return 0;
-}
-
-static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb)
-{
- return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev));
-}
-
-static struct lcd_ops picolcd_lcdops = {
- .get_contrast = picolcd_get_contrast,
- .set_contrast = picolcd_set_contrast,
- .check_fb = picolcd_check_lcd_fb,
-};
-
-static int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report)
-{
- struct device *dev = &data->hdev->dev;
- struct lcd_device *ldev;
-
- if (!report)
- return -ENODEV;
- if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
- report->field[0]->report_size != 8) {
- dev_err(dev, "unsupported CONTRAST report");
- return -EINVAL;
- }
-
- ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops);
- if (IS_ERR(ldev)) {
- dev_err(dev, "failed to register LCD\n");
- return PTR_ERR(ldev);
- }
- ldev->props.max_contrast = 0x0ff;
- data->lcd_contrast = 0xe5;
- data->lcd = ldev;
- picolcd_set_contrast(ldev, 0xe5);
- return 0;
-}
-
-static void picolcd_exit_lcd(struct picolcd_data *data)
-{
- struct lcd_device *ldev = data->lcd;
-
- data->lcd = NULL;
- if (ldev)
- lcd_device_unregister(ldev);
-}
-
-static inline int picolcd_resume_lcd(struct picolcd_data *data)
-{
- if (!data->lcd)
- return 0;
- return picolcd_set_contrast(data->lcd, data->lcd_contrast);
-}
-#else
-static inline int picolcd_init_lcd(struct picolcd_data *data,
- struct hid_report *report)
-{
- return 0;
-}
-static inline void picolcd_exit_lcd(struct picolcd_data *data)
-{
-}
-static inline int picolcd_resume_lcd(struct picolcd_data *data)
-{
- return 0;
-}
-#endif /* CONFIG_HID_PICOLCD_LCD */
-
-#ifdef CONFIG_HID_PICOLCD_LEDS
-/**
- * LED class device
- */
-static void picolcd_leds_set(struct picolcd_data *data)
-{
- struct hid_report *report;
- unsigned long flags;
-
- if (!data->led[0])
- return;
- report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
- if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
- return;
-
- spin_lock_irqsave(&data->lock, flags);
- hid_set_field(report->field[0], 0, data->led_state);
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
-}
-
-static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct device *dev;
- struct hid_device *hdev;
- struct picolcd_data *data;
- int i, state = 0;
-
- dev = led_cdev->dev->parent;
- hdev = container_of(dev, struct hid_device, dev);
- data = hid_get_drvdata(hdev);
- for (i = 0; i < 8; i++) {
- if (led_cdev != data->led[i])
- continue;
- state = (data->led_state >> i) & 1;
- if (value == LED_OFF && state) {
- data->led_state &= ~(1 << i);
- picolcd_leds_set(data);
- } else if (value != LED_OFF && !state) {
- data->led_state |= 1 << i;
- picolcd_leds_set(data);
- }
- break;
- }
-}
-
-static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
-{
- struct device *dev;
- struct hid_device *hdev;
- struct picolcd_data *data;
- int i, value = 0;
-
- dev = led_cdev->dev->parent;
- hdev = container_of(dev, struct hid_device, dev);
- data = hid_get_drvdata(hdev);
- for (i = 0; i < 8; i++)
- if (led_cdev == data->led[i]) {
- value = (data->led_state >> i) & 1;
- break;
- }
- return value ? LED_FULL : LED_OFF;
-}
-
-static int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
-{
- struct device *dev = &data->hdev->dev;
- struct led_classdev *led;
- size_t name_sz = strlen(dev_name(dev)) + 8;
- char *name;
- int i, ret = 0;
-
- if (!report)
- return -ENODEV;
- if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
- report->field[0]->report_size != 8) {
- dev_err(dev, "unsupported LED_STATE report");
- return -EINVAL;
- }
-
- for (i = 0; i < 8; i++) {
- led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
- if (!led) {
- dev_err(dev, "can't allocate memory for LED %d\n", i);
- ret = -ENOMEM;
- goto err;
- }
- name = (void *)(&led[1]);
- snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
- led->name = name;
- led->brightness = 0;
- led->max_brightness = 1;
- led->brightness_get = picolcd_led_get_brightness;
- led->brightness_set = picolcd_led_set_brightness;
-
- data->led[i] = led;
- ret = led_classdev_register(dev, data->led[i]);
- if (ret) {
- data->led[i] = NULL;
- kfree(led);
- dev_err(dev, "can't register LED %d\n", i);
- goto err;
- }
- }
- return 0;
-err:
- for (i = 0; i < 8; i++)
- if (data->led[i]) {
- led = data->led[i];
- data->led[i] = NULL;
- led_classdev_unregister(led);
- kfree(led);
- }
- return ret;
-}
-
-static void picolcd_exit_leds(struct picolcd_data *data)
-{
- struct led_classdev *led;
- int i;
-
- for (i = 0; i < 8; i++) {
- led = data->led[i];
- data->led[i] = NULL;
- if (!led)
- continue;
- led_classdev_unregister(led);
- kfree(led);
- }
-}
-
-#else
-static inline int picolcd_init_leds(struct picolcd_data *data,
- struct hid_report *report)
-{
- return 0;
-}
-static inline void picolcd_exit_leds(struct picolcd_data *data)
-{
-}
-static inline int picolcd_leds_set(struct picolcd_data *data)
-{
- return 0;
-}
-#endif /* CONFIG_HID_PICOLCD_LEDS */
-
-/*
- * input class device
- */
-static int picolcd_raw_keypad(struct picolcd_data *data,
- struct hid_report *report, u8 *raw_data, int size)
-{
- /*
- * Keypad event
- * First and second data bytes list currently pressed keys,
- * 0x00 means no key and at most 2 keys may be pressed at same time
- */
- int i, j;
-
- /* determine newly pressed keys */
- for (i = 0; i < size; i++) {
- unsigned int key_code;
- if (raw_data[i] == 0)
- continue;
- for (j = 0; j < sizeof(data->pressed_keys); j++)
- if (data->pressed_keys[j] == raw_data[i])
- goto key_already_down;
- for (j = 0; j < sizeof(data->pressed_keys); j++)
- if (data->pressed_keys[j] == 0) {
- data->pressed_keys[j] = raw_data[i];
- break;
- }
- input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);
- if (raw_data[i] < PICOLCD_KEYS)
- key_code = data->keycode[raw_data[i]];
- else
- key_code = KEY_UNKNOWN;
- if (key_code != KEY_UNKNOWN) {
- dbg_hid(PICOLCD_NAME " got key press for %u:%d",
- raw_data[i], key_code);
- input_report_key(data->input_keys, key_code, 1);
- }
- input_sync(data->input_keys);
-key_already_down:
- continue;
- }
-
- /* determine newly released keys */
- for (j = 0; j < sizeof(data->pressed_keys); j++) {
- unsigned int key_code;
- if (data->pressed_keys[j] == 0)
- continue;
- for (i = 0; i < size; i++)
- if (data->pressed_keys[j] == raw_data[i])
- goto key_still_down;
- input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);
- if (data->pressed_keys[j] < PICOLCD_KEYS)
- key_code = data->keycode[data->pressed_keys[j]];
- else
- key_code = KEY_UNKNOWN;
- if (key_code != KEY_UNKNOWN) {
- dbg_hid(PICOLCD_NAME " got key release for %u:%d",
- data->pressed_keys[j], key_code);
- input_report_key(data->input_keys, key_code, 0);
- }
- input_sync(data->input_keys);
- data->pressed_keys[j] = 0;
-key_still_down:
- continue;
- }
- return 1;
-}
-
-static int picolcd_raw_cir(struct picolcd_data *data,
- struct hid_report *report, u8 *raw_data, int size)
-{
- /* Need understanding of CIR data format to implement ... */
- return 1;
-}
-
-static int picolcd_check_version(struct hid_device *hdev)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- struct picolcd_pending *verinfo;
- int ret = 0;
-
- if (!data)
- return -ENODEV;
-
- verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);
- if (!verinfo) {
- hid_err(hdev, "no version response from PicoLCD\n");
- return -ENODEV;
- }
-
- if (verinfo->raw_size == 2) {
- data->version[0] = verinfo->raw_data[1];
- data->version[1] = verinfo->raw_data[0];
- if (data->status & PICOLCD_BOOTLOADER) {
- hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
- verinfo->raw_data[1], verinfo->raw_data[0]);
- } else {
- hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
- verinfo->raw_data[1], verinfo->raw_data[0]);
- }
- } else {
- hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
- ret = -EINVAL;
- }
- kfree(verinfo);
- return ret;
-}
-
-/*
- * Reset our device and wait for answer to VERSION request
- */
-static int picolcd_reset(struct hid_device *hdev)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
- unsigned long flags;
- int error;
-
- if (!data || !report || report->maxfield != 1)
- return -ENODEV;
-
- spin_lock_irqsave(&data->lock, flags);
- if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
- data->status |= PICOLCD_BOOTLOADER;
-
- /* perform the reset */
- hid_set_field(report->field[0], 0, 1);
- usbhid_submit_report(hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
-
- error = picolcd_check_version(hdev);
- if (error)
- return error;
-
- picolcd_resume_lcd(data);
- picolcd_resume_backlight(data);
-#ifdef CONFIG_HID_PICOLCD_FB
- if (data->fb_info)
- schedule_delayed_work(&data->fb_info->deferred_work, 0);
-#endif /* CONFIG_HID_PICOLCD_FB */
-
- picolcd_leds_set(data);
- return 0;
-}
-
-/*
- * The "operation_mode" sysfs attribute
- */
-static ssize_t picolcd_operation_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
-
- if (data->status & PICOLCD_BOOTLOADER)
- return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
- else
- return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");
-}
-
-static ssize_t picolcd_operation_mode_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
- struct hid_report *report = NULL;
- size_t cnt = count;
- int timeout = data->opmode_delay;
- unsigned long flags;
-
- if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) {
- if (data->status & PICOLCD_BOOTLOADER)
- report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
- buf += 3;
- cnt -= 3;
- } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) {
- if (!(data->status & PICOLCD_BOOTLOADER))
- report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
- buf += 10;
- cnt -= 10;
- }
- if (!report)
- return -EINVAL;
-
- while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
- cnt--;
- if (cnt != 0)
- return -EINVAL;
-
- spin_lock_irqsave(&data->lock, flags);
- hid_set_field(report->field[0], 0, timeout & 0xff);
- hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
- return count;
-}
-
-static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
- picolcd_operation_mode_store);
-
-/*
- * The "operation_mode_delay" sysfs attribute
- */
-static ssize_t picolcd_operation_mode_delay_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);
-}
-
-static ssize_t picolcd_operation_mode_delay_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
- unsigned u;
- if (sscanf(buf, "%u", &u) != 1)
- return -EINVAL;
- if (u > 30000)
- return -EINVAL;
- else
- data->opmode_delay = u;
- return count;
-}
-
-static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,
- picolcd_operation_mode_delay_store);
-
-
-#ifdef CONFIG_DEBUG_FS
-/*
- * The "reset" file
- */
-static int picolcd_debug_reset_show(struct seq_file *f, void *p)
-{
- if (picolcd_fbinfo((struct picolcd_data *)f->private))
- seq_printf(f, "all fb\n");
- else
- seq_printf(f, "all\n");
- return 0;
-}
-
-static int picolcd_debug_reset_open(struct inode *inode, struct file *f)
-{
- return single_open(f, picolcd_debug_reset_show, inode->i_private);
-}
-
-static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
- char buf[32];
- size_t cnt = min(count, sizeof(buf)-1);
- if (copy_from_user(buf, user_buf, cnt))
- return -EFAULT;
-
- while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))
- cnt--;
- buf[cnt] = '\0';
- if (strcmp(buf, "all") == 0) {
- picolcd_reset(data->hdev);
- picolcd_fb_reset(data, 1);
- } else if (strcmp(buf, "fb") == 0) {
- picolcd_fb_reset(data, 1);
- } else {
- return -EINVAL;
- }
- return count;
-}
-
-static const struct file_operations picolcd_debug_reset_fops = {
- .owner = THIS_MODULE,
- .open = picolcd_debug_reset_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .write = picolcd_debug_reset_write,
- .release = single_release,
-};
-
-/*
- * The "eeprom" file
- */
-static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
- size_t s, loff_t *off)
-{
- struct picolcd_data *data = f->private_data;
- struct picolcd_pending *resp;
- u8 raw_data[3];
- ssize_t ret = -EIO;
-
- if (s == 0)
- return -EINVAL;
- if (*off > 0x0ff)
- return 0;
-
- /* prepare buffer with info about what we want to read (addr & len) */
- raw_data[0] = *off & 0xff;
- raw_data[1] = (*off >> 8) & 0xff;
- raw_data[2] = s < 20 ? s : 20;
- if (*off + raw_data[2] > 0xff)
- raw_data[2] = 0x100 - *off;
- resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
- sizeof(raw_data));
- if (!resp)
- return -EIO;
-
- if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
- /* successful read :) */
- ret = resp->raw_data[2];
- if (ret > s)
- ret = s;
- if (copy_to_user(u, resp->raw_data+3, ret))
- ret = -EFAULT;
- else
- *off += ret;
- } /* anything else is some kind of IO error */
-
- kfree(resp);
- return ret;
-}
-
-static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
- size_t s, loff_t *off)
-{
- struct picolcd_data *data = f->private_data;
- struct picolcd_pending *resp;
- ssize_t ret = -EIO;
- u8 raw_data[23];
-
- if (s == 0)
- return -EINVAL;
- if (*off > 0x0ff)
- return -ENOSPC;
-
- memset(raw_data, 0, sizeof(raw_data));
- raw_data[0] = *off & 0xff;
- raw_data[1] = (*off >> 8) & 0xff;
- raw_data[2] = min((size_t)20, s);
- if (*off + raw_data[2] > 0xff)
- raw_data[2] = 0x100 - *off;
-
- if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
- return -EFAULT;
- resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
- sizeof(raw_data));
-
- if (!resp)
- return -EIO;
-
- if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
- /* check if written data matches */
- if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {
- *off += raw_data[2];
- ret = raw_data[2];
- }
- }
- kfree(resp);
- return ret;
-}
-
-/*
- * Notes:
- * - read/write happens in chunks of at most 20 bytes, it's up to userspace
- * to loop in order to get more data.
- * - on write errors on otherwise correct write request the bytes
- * that should have been written are in undefined state.
- */
-static const struct file_operations picolcd_debug_eeprom_fops = {
- .owner = THIS_MODULE,
- .open = simple_open,
- .read = picolcd_debug_eeprom_read,
- .write = picolcd_debug_eeprom_write,
- .llseek = generic_file_llseek,
-};
-
-/*
- * The "flash" file
- */
-/* record a flash address to buf (bounds check to be done by caller) */
-static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
-{
- buf[0] = off & 0xff;
- buf[1] = (off >> 8) & 0xff;
- if (data->addr_sz == 3)
- buf[2] = (off >> 16) & 0xff;
- return data->addr_sz == 2 ? 2 : 3;
-}
-
-/* read a given size of data (bounds check to be done by caller) */
-static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
- char __user *u, size_t s, loff_t *off)
-{
- struct picolcd_pending *resp;
- u8 raw_data[4];
- ssize_t ret = 0;
- int len_off, err = -EIO;
-
- while (s > 0) {
- err = -EIO;
- len_off = _picolcd_flash_setaddr(data, raw_data, *off);
- raw_data[len_off] = s > 32 ? 32 : s;
- resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
- if (!resp || !resp->in_report)
- goto skip;
- if (resp->in_report->id == REPORT_MEMORY ||
- resp->in_report->id == REPORT_BL_READ_MEMORY) {
- if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
- goto skip;
- if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
- err = -EFAULT;
- goto skip;
- }
- *off += raw_data[len_off];
- s -= raw_data[len_off];
- ret += raw_data[len_off];
- err = 0;
- }
-skip:
- kfree(resp);
- if (err)
- return ret > 0 ? ret : err;
- }
- return ret;
-}
-
-static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
- size_t s, loff_t *off)
-{
- struct picolcd_data *data = f->private_data;
-
- if (s == 0)
- return -EINVAL;
- if (*off > 0x05fff)
- return 0;
- if (*off + s > 0x05fff)
- s = 0x06000 - *off;
-
- if (data->status & PICOLCD_BOOTLOADER)
- return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
- else
- return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
-}
-
-/* erase block aligned to 64bytes boundary */
-static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
- loff_t *off)
-{
- struct picolcd_pending *resp;
- u8 raw_data[3];
- int len_off;
- ssize_t ret = -EIO;
-
- if (*off & 0x3f)
- return -EINVAL;
-
- len_off = _picolcd_flash_setaddr(data, raw_data, *off);
- resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
- if (!resp || !resp->in_report)
- goto skip;
- if (resp->in_report->id == REPORT_MEMORY ||
- resp->in_report->id == REPORT_BL_ERASE_MEMORY) {
- if (memcmp(raw_data, resp->raw_data, len_off) != 0)
- goto skip;
- ret = 0;
- }
-skip:
- kfree(resp);
- return ret;
-}
-
-/* write a given size of data (bounds check to be done by caller) */
-static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
- const char __user *u, size_t s, loff_t *off)
-{
- struct picolcd_pending *resp;
- u8 raw_data[36];
- ssize_t ret = 0;
- int len_off, err = -EIO;
-
- while (s > 0) {
- err = -EIO;
- len_off = _picolcd_flash_setaddr(data, raw_data, *off);
- raw_data[len_off] = s > 32 ? 32 : s;
- if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
- err = -EFAULT;
- break;
- }
- resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
- len_off+1+raw_data[len_off]);
- if (!resp || !resp->in_report)
- goto skip;
- if (resp->in_report->id == REPORT_MEMORY ||
- resp->in_report->id == REPORT_BL_WRITE_MEMORY) {
- if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
- goto skip;
- *off += raw_data[len_off];
- s -= raw_data[len_off];
- ret += raw_data[len_off];
- err = 0;
- }
-skip:
- kfree(resp);
- if (err)
- break;
- }
- return ret > 0 ? ret : err;
-}
-
-static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
- size_t s, loff_t *off)
-{
- struct picolcd_data *data = f->private_data;
- ssize_t err, ret = 0;
- int report_erase, report_write;
-
- if (s == 0)
- return -EINVAL;
- if (*off > 0x5fff)
- return -ENOSPC;
- if (s & 0x3f)
- return -EINVAL;
- if (*off & 0x3f)
- return -EINVAL;
-
- if (data->status & PICOLCD_BOOTLOADER) {
- report_erase = REPORT_BL_ERASE_MEMORY;
- report_write = REPORT_BL_WRITE_MEMORY;
- } else {
- report_erase = REPORT_ERASE_MEMORY;
- report_write = REPORT_WRITE_MEMORY;
- }
- mutex_lock(&data->mutex_flash);
- while (s > 0) {
- err = _picolcd_flash_erase64(data, report_erase, off);
- if (err)
- break;
- err = _picolcd_flash_write(data, report_write, u, 64, off);
- if (err < 0)
- break;
- ret += err;
- *off += err;
- s -= err;
- if (err != 64)
- break;
- }
- mutex_unlock(&data->mutex_flash);
- return ret > 0 ? ret : err;
-}
-
-/*
- * Notes:
- * - concurrent writing is prevented by mutex and all writes must be
- * n*64 bytes and 64-byte aligned, each write being preceded by an
- * ERASE which erases a 64byte block.
- * If less than requested was written or an error is returned for an
- * otherwise correct write request the next 64-byte block which should
- * have been written is in undefined state (mostly: original, erased,
- * (half-)written with write error)
- * - reading can happen without special restriction
- */
-static const struct file_operations picolcd_debug_flash_fops = {
- .owner = THIS_MODULE,
- .open = simple_open,
- .read = picolcd_debug_flash_read,
- .write = picolcd_debug_flash_write,
- .llseek = generic_file_llseek,
-};
-
-
-/*
- * Helper code for HID report level dumping/debugging
- */
-static const char *error_codes[] = {
- "success", "parameter missing", "data_missing", "block readonly",
- "block not erasable", "block too big", "section overflow",
- "invalid command length", "invalid data length",
-};
-
-static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
- const size_t data_len)
-{
- int i, j;
- for (i = j = 0; i < data_len && j + 3 < dst_sz; i++) {
- dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
- dst[j++] = hex_asc[data[i] & 0x0f];
- dst[j++] = ' ';
- }
- if (j < dst_sz) {
- dst[j--] = '\0';
- dst[j] = '\n';
- } else
- dst[j] = '\0';
-}
-
-static void picolcd_debug_out_report(struct picolcd_data *data,
- struct hid_device *hdev, struct hid_report *report)
-{
- u8 raw_data[70];
- int raw_size = (report->size >> 3) + 1;
- char *buff;
-#define BUFF_SZ 256
-
- /* Avoid unnecessary overhead if debugfs is disabled */
- if (list_empty(&hdev->debug_list))
- return;
-
- buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
- if (!buff)
- return;
-
- snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ",
- report->id, raw_size);
- hid_debug_event(hdev, buff);
- if (raw_size + 5 > sizeof(raw_data)) {
- kfree(buff);
- hid_debug_event(hdev, " TOO BIG\n");
- return;
- } else {
- raw_data[0] = report->id;
- hid_output_report(report, raw_data);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
- hid_debug_event(hdev, buff);
- }
-
- switch (report->id) {
- case REPORT_LED_STATE:
- /* 1 data byte with GPO state */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_LED_STATE", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_BRIGHTNESS:
- /* 1 data byte with brightness */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_BRIGHTNESS", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_CONTRAST:
- /* 1 data byte with contrast */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_CONTRAST", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_RESET:
- /* 2 data bytes with reset duration in ms */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_RESET", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
- raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_LCD_CMD:
- /* 63 data bytes with LCD commands */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_LCD_CMD", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- /* TODO: format decoding */
- break;
- case REPORT_LCD_DATA:
- /* 63 data bytes with LCD data */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_LCD_CMD", report->id, raw_size-1);
- /* TODO: format decoding */
- hid_debug_event(hdev, buff);
- break;
- case REPORT_LCD_CMD_DATA:
- /* 63 data bytes with LCD commands and data */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_LCD_CMD", report->id, raw_size-1);
- /* TODO: format decoding */
- hid_debug_event(hdev, buff);
- break;
- case REPORT_EE_READ:
- /* 3 data bytes with read area description */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_EE_READ", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_EE_WRITE:
- /* 3+1..20 data bytes with write area description */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_EE_WRITE", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- hid_debug_event(hdev, buff);
- if (raw_data[3] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- } else if (raw_data[3] + 4 <= raw_size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- }
- hid_debug_event(hdev, buff);
- break;
- case REPORT_ERASE_MEMORY:
- case REPORT_BL_ERASE_MEMORY:
- /* 3 data bytes with pointer inside erase block */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_ERASE_MEMORY", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- switch (data->addr_sz) {
- case 2:
- snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- break;
- case 3:
- snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
- raw_data[3], raw_data[2], raw_data[1]);
- break;
- default:
- snprintf(buff, BUFF_SZ, "\tNot supported\n");
- }
- hid_debug_event(hdev, buff);
- break;
- case REPORT_READ_MEMORY:
- case REPORT_BL_READ_MEMORY:
- /* 4 data bytes with read area description */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_READ_MEMORY", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- switch (data->addr_sz) {
- case 2:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- break;
- case 3:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
- raw_data[3], raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
- break;
- default:
- snprintf(buff, BUFF_SZ, "\tNot supported\n");
- }
- hid_debug_event(hdev, buff);
- break;
- case REPORT_WRITE_MEMORY:
- case REPORT_BL_WRITE_MEMORY:
- /* 4+1..32 data bytes with write adrea description */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_WRITE_MEMORY", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- switch (data->addr_sz) {
- case 2:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- hid_debug_event(hdev, buff);
- if (raw_data[3] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- } else if (raw_data[3] + 4 <= raw_size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- }
- break;
- case 3:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
- raw_data[3], raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
- hid_debug_event(hdev, buff);
- if (raw_data[4] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- } else if (raw_data[4] + 5 <= raw_size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- }
- break;
- default:
- snprintf(buff, BUFF_SZ, "\tNot supported\n");
- }
- hid_debug_event(hdev, buff);
- break;
- case REPORT_SPLASH_RESTART:
- /* TODO */
- break;
- case REPORT_EXIT_KEYBOARD:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
- raw_data[1] | (raw_data[2] << 8),
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_VERSION:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_VERSION", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_DEVID:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_DEVID", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_SPLASH_SIZE:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_SPLASH_SIZE", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_HOOK_VERSION:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_HOOK_VERSION", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_EXIT_FLASHER:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_VERSION", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
- raw_data[1] | (raw_data[2] << 8),
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- default:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "<unknown>", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- break;
- }
- wake_up_interruptible(&hdev->debug_wait);
- kfree(buff);
-}
-
-static void picolcd_debug_raw_event(struct picolcd_data *data,
- struct hid_device *hdev, struct hid_report *report,
- u8 *raw_data, int size)
-{
- char *buff;
-
-#define BUFF_SZ 256
- /* Avoid unnecessary overhead if debugfs is disabled */
- if (!hdev->debug_events)
- return;
-
- buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
- if (!buff)
- return;
-
- switch (report->id) {
- case REPORT_ERROR_CODE:
- /* 2 data bytes with affected report and error code */
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_ERROR_CODE", report->id, size-1);
- hid_debug_event(hdev, buff);
- if (raw_data[2] < ARRAY_SIZE(error_codes))
- snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
- raw_data[2], error_codes[raw_data[2]], raw_data[1]);
- else
- snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_KEY_STATE:
- /* 2 data bytes with key state */
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_KEY_STATE", report->id, size-1);
- hid_debug_event(hdev, buff);
- if (raw_data[1] == 0)
- snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
- else if (raw_data[2] == 0)
- snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
- raw_data[1], raw_data[1]);
- else
- snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
- raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_IR_DATA:
- /* Up to 20 byes of IR scancode data */
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_IR_DATA", report->id, size-1);
- hid_debug_event(hdev, buff);
- if (raw_data[1] == 0) {
- snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
- hid_debug_event(hdev, buff);
- } else if (raw_data[1] + 1 <= size) {
- snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
- raw_data[1]-1);
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]-1);
- hid_debug_event(hdev, buff);
- } else {
- snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
- raw_data[1]-1);
- hid_debug_event(hdev, buff);
- }
- break;
- case REPORT_EE_DATA:
- /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_EE_DATA", report->id, size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- hid_debug_event(hdev, buff);
- if (raw_data[3] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- hid_debug_event(hdev, buff);
- } else if (raw_data[3] + 4 <= size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
- hid_debug_event(hdev, buff);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- hid_debug_event(hdev, buff);
- }
- break;
- case REPORT_MEMORY:
- /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_MEMORY", report->id, size-1);
- hid_debug_event(hdev, buff);
- switch (data->addr_sz) {
- case 2:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- hid_debug_event(hdev, buff);
- if (raw_data[3] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- } else if (raw_data[3] + 4 <= size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- }
- break;
- case 3:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
- raw_data[3], raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
- hid_debug_event(hdev, buff);
- if (raw_data[4] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- } else if (raw_data[4] + 5 <= size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- }
- break;
- default:
- snprintf(buff, BUFF_SZ, "\tNot supported\n");
- }
- hid_debug_event(hdev, buff);
- break;
- case REPORT_VERSION:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_VERSION", report->id, size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_BL_ERASE_MEMORY:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_BL_ERASE_MEMORY", report->id, size-1);
- hid_debug_event(hdev, buff);
- /* TODO */
- break;
- case REPORT_BL_READ_MEMORY:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_BL_READ_MEMORY", report->id, size-1);
- hid_debug_event(hdev, buff);
- /* TODO */
- break;
- case REPORT_BL_WRITE_MEMORY:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_BL_WRITE_MEMORY", report->id, size-1);
- hid_debug_event(hdev, buff);
- /* TODO */
- break;
- case REPORT_DEVID:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_DEVID", report->id, size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
- raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
- raw_data[5]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_SPLASH_SIZE:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_SPLASH_SIZE", report->id, size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
- (raw_data[2] << 8) | raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
- (raw_data[4] << 8) | raw_data[3]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_HOOK_VERSION:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_HOOK_VERSION", report->id, size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
- raw_data[1], raw_data[2]);
- hid_debug_event(hdev, buff);
- break;
- default:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "<unknown>", report->id, size-1);
- hid_debug_event(hdev, buff);
- break;
- }
- wake_up_interruptible(&hdev->debug_wait);
- kfree(buff);
-}
-
-static void picolcd_init_devfs(struct picolcd_data *data,
- struct hid_report *eeprom_r, struct hid_report *eeprom_w,
- struct hid_report *flash_r, struct hid_report *flash_w,
- struct hid_report *reset)
-{
- struct hid_device *hdev = data->hdev;
-
- mutex_init(&data->mutex_flash);
-
- /* reset */
- if (reset)
- data->debug_reset = debugfs_create_file("reset", 0600,
- hdev->debug_dir, data, &picolcd_debug_reset_fops);
-
- /* eeprom */
- if (eeprom_r || eeprom_w)
- data->debug_eeprom = debugfs_create_file("eeprom",
- (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
- hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
-
- /* flash */
- if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)
- data->addr_sz = flash_r->field[0]->report_count - 1;
- else
- data->addr_sz = -1;
- if (data->addr_sz == 2 || data->addr_sz == 3) {
- data->debug_flash = debugfs_create_file("flash",
- (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
- hdev->debug_dir, data, &picolcd_debug_flash_fops);
- } else if (flash_r || flash_w)
- hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
-}
-
-static void picolcd_exit_devfs(struct picolcd_data *data)
-{
- struct dentry *dent;
-
- dent = data->debug_reset;
- data->debug_reset = NULL;
- if (dent)
- debugfs_remove(dent);
- dent = data->debug_eeprom;
- data->debug_eeprom = NULL;
- if (dent)
- debugfs_remove(dent);
- dent = data->debug_flash;
- data->debug_flash = NULL;
- if (dent)
- debugfs_remove(dent);
- mutex_destroy(&data->mutex_flash);
-}
-#else
-static inline void picolcd_debug_raw_event(struct picolcd_data *data,
- struct hid_device *hdev, struct hid_report *report,
- u8 *raw_data, int size)
-{
-}
-static inline void picolcd_init_devfs(struct picolcd_data *data,
- struct hid_report *eeprom_r, struct hid_report *eeprom_w,
- struct hid_report *flash_r, struct hid_report *flash_w,
- struct hid_report *reset)
-{
-}
-static inline void picolcd_exit_devfs(struct picolcd_data *data)
-{
-}
-#endif /* CONFIG_DEBUG_FS */
-
-/*
- * Handle raw report as sent by device
- */
-static int picolcd_raw_event(struct hid_device *hdev,
- struct hid_report *report, u8 *raw_data, int size)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- unsigned long flags;
- int ret = 0;
-
- if (!data)
- return 1;
-
- if (report->id == REPORT_KEY_STATE) {
- if (data->input_keys)
- ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
- } else if (report->id == REPORT_IR_DATA) {
- if (data->input_cir)
- ret = picolcd_raw_cir(data, report, raw_data+1, size-1);
- } else {
- spin_lock_irqsave(&data->lock, flags);
- /*
- * We let the caller of picolcd_send_and_wait() check if the
- * report we got is one of the expected ones or not.
- */
- if (data->pending) {
- memcpy(data->pending->raw_data, raw_data+1, size-1);
- data->pending->raw_size = size-1;
- data->pending->in_report = report;
- complete(&data->pending->ready);
- }
- spin_unlock_irqrestore(&data->lock, flags);
- }
-
- picolcd_debug_raw_event(data, hdev, report, raw_data, size);
- return 1;
-}
-
-#ifdef CONFIG_PM
-static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
-{
- if (PMSG_IS_AUTO(message))
- return 0;
-
- picolcd_suspend_backlight(hid_get_drvdata(hdev));
- dbg_hid(PICOLCD_NAME " device ready for suspend\n");
- return 0;
-}
-
-static int picolcd_resume(struct hid_device *hdev)
-{
- int ret;
- ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
- if (ret)
- dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
- return 0;
-}
-
-static int picolcd_reset_resume(struct hid_device *hdev)
-{
- int ret;
- ret = picolcd_reset(hdev);
- if (ret)
- dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);
- ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
- if (ret)
- dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
- ret = picolcd_resume_lcd(hid_get_drvdata(hdev));
- if (ret)
- dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);
- ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
- if (ret)
- dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
- picolcd_leds_set(hid_get_drvdata(hdev));
- return 0;
-}
-#endif
-
-/* initialize keypad input device */
-static int picolcd_init_keys(struct picolcd_data *data,
- struct hid_report *report)
-{
- struct hid_device *hdev = data->hdev;
- struct input_dev *idev;
- int error, i;
-
- if (!report)
- return -ENODEV;
- if (report->maxfield != 1 || report->field[0]->report_count != 2 ||
- report->field[0]->report_size != 8) {
- hid_err(hdev, "unsupported KEY_STATE report\n");
- return -EINVAL;
- }
-
- idev = input_allocate_device();
- if (idev == NULL) {
- hid_err(hdev, "failed to allocate input device\n");
- return -ENOMEM;
- }
- input_set_drvdata(idev, hdev);
- memcpy(data->keycode, def_keymap, sizeof(def_keymap));
- idev->name = hdev->name;
- idev->phys = hdev->phys;
- idev->uniq = hdev->uniq;
- idev->id.bustype = hdev->bus;
- idev->id.vendor = hdev->vendor;
- idev->id.product = hdev->product;
- idev->id.version = hdev->version;
- idev->dev.parent = hdev->dev.parent;
- idev->keycode = &data->keycode;
- idev->keycodemax = PICOLCD_KEYS;
- idev->keycodesize = sizeof(data->keycode[0]);
- input_set_capability(idev, EV_MSC, MSC_SCAN);
- set_bit(EV_REP, idev->evbit);
- for (i = 0; i < PICOLCD_KEYS; i++)
- input_set_capability(idev, EV_KEY, data->keycode[i]);
- error = input_register_device(idev);
- if (error) {
- hid_err(hdev, "error registering the input device\n");
- input_free_device(idev);
- return error;
- }
- data->input_keys = idev;
- return 0;
-}
-
-static void picolcd_exit_keys(struct picolcd_data *data)
-{
- struct input_dev *idev = data->input_keys;
-
- data->input_keys = NULL;
- if (idev)
- input_unregister_device(idev);
-}
-
-/* initialize CIR input device */
-static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
-{
- /* support not implemented yet */
- return 0;
-}
-
-static inline void picolcd_exit_cir(struct picolcd_data *data)
-{
-}
-
-static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
-{
- int error;
-
- error = picolcd_check_version(hdev);
- if (error)
- return error;
-
- if (data->version[0] != 0 && data->version[1] != 3)
- hid_info(hdev, "Device with untested firmware revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
- dev_name(&hdev->dev));
-
- /* Setup keypad input device */
- error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
- if (error)
- goto err;
-
- /* Setup CIR input device */
- error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));
- if (error)
- goto err;
-
- /* Set up the framebuffer device */
- error = picolcd_init_framebuffer(data);
- if (error)
- goto err;
-
- /* Setup lcd class device */
- error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
- if (error)
- goto err;
-
- /* Setup backlight class device */
- error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
- if (error)
- goto err;
-
- /* Setup the LED class devices */
- error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
- if (error)
- goto err;
-
- picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
- picolcd_out_report(REPORT_EE_WRITE, hdev),
- picolcd_out_report(REPORT_READ_MEMORY, hdev),
- picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
- picolcd_out_report(REPORT_RESET, hdev));
- return 0;
-err:
- picolcd_exit_leds(data);
- picolcd_exit_backlight(data);
- picolcd_exit_lcd(data);
- picolcd_exit_framebuffer(data);
- picolcd_exit_cir(data);
- picolcd_exit_keys(data);
- return error;
-}
-
-static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
-{
- int error;
-
- error = picolcd_check_version(hdev);
- if (error)
- return error;
-
- if (data->version[0] != 1 && data->version[1] != 0)
- hid_info(hdev, "Device with untested bootloader revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
- dev_name(&hdev->dev));
-
- picolcd_init_devfs(data, NULL, NULL,
- picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
- picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
- return 0;
-}
-
-static int picolcd_probe(struct hid_device *hdev,
- const struct hid_device_id *id)
-{
- struct picolcd_data *data;
- int error = -ENOMEM;
-
- dbg_hid(PICOLCD_NAME " hardware probe...\n");
-
- /*
- * Let's allocate the picolcd data structure, set some reasonable
- * defaults, and associate it with the device
- */
- data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);
- if (data == NULL) {
- hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");
- error = -ENOMEM;
- goto err_no_cleanup;
- }
-
- spin_lock_init(&data->lock);
- mutex_init(&data->mutex);
- data->hdev = hdev;
- data->opmode_delay = 5000;
- if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
- data->status |= PICOLCD_BOOTLOADER;
- hid_set_drvdata(hdev, data);
-
- /* Parse the device reports and start it up */
- error = hid_parse(hdev);
- if (error) {
- hid_err(hdev, "device report parse failed\n");
- goto err_cleanup_data;
- }
-
- error = hid_hw_start(hdev, 0);
- if (error) {
- hid_err(hdev, "hardware start failed\n");
- goto err_cleanup_data;
- }
-
- error = hid_hw_open(hdev);
- if (error) {
- hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");
- goto err_cleanup_hid_hw;
- }
-
- error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);
- if (error) {
- hid_err(hdev, "failed to create sysfs attributes\n");
- goto err_cleanup_hid_ll;
- }
-
- error = device_create_file(&hdev->dev, &dev_attr_operation_mode);
- if (error) {
- hid_err(hdev, "failed to create sysfs attributes\n");
- goto err_cleanup_sysfs1;
- }
-
- if (data->status & PICOLCD_BOOTLOADER)
- error = picolcd_probe_bootloader(hdev, data);
- else
- error = picolcd_probe_lcd(hdev, data);
- if (error)
- goto err_cleanup_sysfs2;
-
- dbg_hid(PICOLCD_NAME " activated and initialized\n");
- return 0;
-
-err_cleanup_sysfs2:
- device_remove_file(&hdev->dev, &dev_attr_operation_mode);
-err_cleanup_sysfs1:
- device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
-err_cleanup_hid_ll:
- hid_hw_close(hdev);
-err_cleanup_hid_hw:
- hid_hw_stop(hdev);
-err_cleanup_data:
- kfree(data);
-err_no_cleanup:
- hid_set_drvdata(hdev, NULL);
-
- return error;
-}
-
-static void picolcd_remove(struct hid_device *hdev)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- unsigned long flags;
-
- dbg_hid(PICOLCD_NAME " hardware remove...\n");
- spin_lock_irqsave(&data->lock, flags);
- data->status |= PICOLCD_FAILED;
- spin_unlock_irqrestore(&data->lock, flags);
-#ifdef CONFIG_HID_PICOLCD_FB
- /* short-circuit FB as early as possible in order to
- * avoid long delays if we host console.
- */
- if (data->fb_info)
- data->fb_info->par = NULL;
-#endif
-
- picolcd_exit_devfs(data);
- device_remove_file(&hdev->dev, &dev_attr_operation_mode);
- device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
- hid_hw_close(hdev);
- hid_hw_stop(hdev);
- hid_set_drvdata(hdev, NULL);
-
- /* Shortcut potential pending reply that will never arrive */
- spin_lock_irqsave(&data->lock, flags);
- if (data->pending)
- complete(&data->pending->ready);
- spin_unlock_irqrestore(&data->lock, flags);
-
- /* Cleanup LED */
- picolcd_exit_leds(data);
- /* Clean up the framebuffer */
- picolcd_exit_backlight(data);
- picolcd_exit_lcd(data);
- picolcd_exit_framebuffer(data);
- /* Cleanup input */
- picolcd_exit_cir(data);
- picolcd_exit_keys(data);
-
- mutex_destroy(&data->mutex);
- /* Finally, clean up the picolcd data itself */
- kfree(data);
-}
-
-static const struct hid_device_id picolcd_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
- { }
-};
-MODULE_DEVICE_TABLE(hid, picolcd_devices);
-
-static struct hid_driver picolcd_driver = {
- .name = "hid-picolcd",
- .id_table = picolcd_devices,
- .probe = picolcd_probe,
- .remove = picolcd_remove,
- .raw_event = picolcd_raw_event,
-#ifdef CONFIG_PM
- .suspend = picolcd_suspend,
- .resume = picolcd_resume,
- .reset_resume = picolcd_reset_resume,
-#endif
-};
-
-static int __init picolcd_init(void)
-{
- return hid_register_driver(&picolcd_driver);
-}
-
-static void __exit picolcd_exit(void)
-{
- hid_unregister_driver(&picolcd_driver);
-#ifdef CONFIG_HID_PICOLCD_FB
- flush_work_sync(&picolcd_fb_cleanup);
- WARN_ON(fb_pending);
-#endif
-}
-
-module_init(picolcd_init);
-module_exit(picolcd_exit);
-MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-picolcd.h b/drivers/hid/hid-picolcd.h
new file mode 100644
index 0000000..020cef6
--- /dev/null
+++ b/drivers/hid/hid-picolcd.h
@@ -0,0 +1,309 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * 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 of the License. *
+ * *
+ * This driver 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#define PICOLCD_NAME "PicoLCD (graphic)"
+
+/* Report numbers */
+#define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */
+#define ERR_SUCCESS 0x00
+#define ERR_PARAMETER_MISSING 0x01
+#define ERR_DATA_MISSING 0x02
+#define ERR_BLOCK_READ_ONLY 0x03
+#define ERR_BLOCK_NOT_ERASABLE 0x04
+#define ERR_BLOCK_TOO_BIG 0x05
+#define ERR_SECTION_OVERFLOW 0x06
+#define ERR_INVALID_CMD_LEN 0x07
+#define ERR_INVALID_DATA_LEN 0x08
+#define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */
+#define REPORT_IR_DATA 0x21 /* LCD: IN[63] */
+#define REPORT_EE_DATA 0x32 /* LCD: IN[63] */
+#define REPORT_MEMORY 0x41 /* LCD: IN[63] */
+#define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */
+#define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */
+#define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */
+#define REPORT_RESET 0x93 /* LCD: OUT[2] */
+#define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */
+#define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */
+#define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */
+#define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */
+#define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */
+#define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */
+#define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */
+#define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */
+#define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */
+#define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */
+#define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */
+#define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */
+#define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */
+#define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */
+#define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */
+#define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */
+#define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */
+#define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */
+
+/* Description of in-progress IO operation, used for operations
+ * that trigger response from device */
+struct picolcd_pending {
+ struct hid_report *out_report;
+ struct hid_report *in_report;
+ struct completion ready;
+ int raw_size;
+ u8 raw_data[64];
+};
+
+
+#define PICOLCD_KEYS 17
+
+/* Per device data structure */
+struct picolcd_data {
+ struct hid_device *hdev;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debug_reset;
+ struct dentry *debug_eeprom;
+ struct dentry *debug_flash;
+ struct mutex mutex_flash;
+ int addr_sz;
+#endif
+ u8 version[2];
+ unsigned short opmode_delay;
+ /* input stuff */
+ u8 pressed_keys[2];
+ struct input_dev *input_keys;
+#ifdef CONFIG_HID_PICOLCD_CIR
+ struct rc_dev *rc_dev;
+#endif
+ unsigned short keycode[PICOLCD_KEYS];
+
+#ifdef CONFIG_HID_PICOLCD_FB
+ /* Framebuffer stuff */
+ struct fb_info *fb_info;
+#endif /* CONFIG_HID_PICOLCD_FB */
+#ifdef CONFIG_HID_PICOLCD_LCD
+ struct lcd_device *lcd;
+ u8 lcd_contrast;
+#endif /* CONFIG_HID_PICOLCD_LCD */
+#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+ struct backlight_device *backlight;
+ u8 lcd_brightness;
+ u8 lcd_power;
+#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
+#ifdef CONFIG_HID_PICOLCD_LEDS
+ /* LED stuff */
+ u8 led_state;
+ struct led_classdev *led[8];
+#endif /* CONFIG_HID_PICOLCD_LEDS */
+
+ /* Housekeeping stuff */
+ spinlock_t lock;
+ struct mutex mutex;
+ struct picolcd_pending *pending;
+ int status;
+#define PICOLCD_BOOTLOADER 1
+#define PICOLCD_FAILED 2
+#define PICOLCD_CIR_SHUN 4
+};
+
+#ifdef CONFIG_HID_PICOLCD_FB
+struct picolcd_fb_data {
+ /* Framebuffer stuff */
+ spinlock_t lock;
+ struct picolcd_data *picolcd;
+ u8 update_rate;
+ u8 bpp;
+ u8 force;
+ u8 ready;
+ u8 *vbitmap; /* local copy of what was sent to PicoLCD */
+ u8 *bitmap; /* framebuffer */
+};
+#endif /* CONFIG_HID_PICOLCD_FB */
+
+/* Find a given report */
+#define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT)
+#define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT)
+
+struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir);
+
+#ifdef CONFIG_DEBUG_FS
+void picolcd_debug_out_report(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report);
+#define usbhid_submit_report(a, b, c) \
+ do { \
+ picolcd_debug_out_report(hid_get_drvdata(a), a, b); \
+ usbhid_submit_report(a, b, c); \
+ } while (0)
+
+void picolcd_debug_raw_event(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report,
+ u8 *raw_data, int size);
+
+void picolcd_init_devfs(struct picolcd_data *data,
+ struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+ struct hid_report *flash_r, struct hid_report *flash_w,
+ struct hid_report *reset);
+
+void picolcd_exit_devfs(struct picolcd_data *data);
+#else
+static inline void picolcd_debug_out_report(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report)
+{
+}
+static inline void picolcd_debug_raw_event(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report,
+ u8 *raw_data, int size)
+{
+}
+static inline void picolcd_init_devfs(struct picolcd_data *data,
+ struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+ struct hid_report *flash_r, struct hid_report *flash_w,
+ struct hid_report *reset)
+{
+}
+static inline void picolcd_exit_devfs(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+
+#ifdef CONFIG_HID_PICOLCD_FB
+int picolcd_fb_reset(struct picolcd_data *data, int clear);
+
+int picolcd_init_framebuffer(struct picolcd_data *data);
+
+void picolcd_exit_framebuffer(struct picolcd_data *data);
+
+void picolcd_fb_refresh(struct picolcd_data *data);
+#define picolcd_fbinfo(d) ((d)->fb_info)
+#else
+static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
+{
+ return 0;
+}
+static inline int picolcd_init_framebuffer(struct picolcd_data *data)
+{
+ return 0;
+}
+static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
+{
+}
+static inline void picolcd_fb_refresh(struct picolcd_data *data)
+{
+}
+#define picolcd_fbinfo(d) NULL
+#endif /* CONFIG_HID_PICOLCD_FB */
+
+
+#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+int picolcd_init_backlight(struct picolcd_data *data,
+ struct hid_report *report);
+
+void picolcd_exit_backlight(struct picolcd_data *data);
+
+int picolcd_resume_backlight(struct picolcd_data *data);
+
+void picolcd_suspend_backlight(struct picolcd_data *data);
+#else
+static inline int picolcd_init_backlight(struct picolcd_data *data,
+ struct hid_report *report)
+{
+ return 0;
+}
+static inline void picolcd_exit_backlight(struct picolcd_data *data)
+{
+}
+static inline int picolcd_resume_backlight(struct picolcd_data *data)
+{
+ return 0;
+}
+static inline void picolcd_suspend_backlight(struct picolcd_data *data)
+{
+}
+
+#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
+
+
+#ifdef CONFIG_HID_PICOLCD_LCD
+int picolcd_init_lcd(struct picolcd_data *data,
+ struct hid_report *report);
+
+void picolcd_exit_lcd(struct picolcd_data *data);
+
+int picolcd_resume_lcd(struct picolcd_data *data);
+#else
+static inline int picolcd_init_lcd(struct picolcd_data *data,
+ struct hid_report *report)
+{
+ return 0;
+}
+static inline void picolcd_exit_lcd(struct picolcd_data *data)
+{
+}
+static inline int picolcd_resume_lcd(struct picolcd_data *data)
+{
+ return 0;
+}
+#endif /* CONFIG_HID_PICOLCD_LCD */
+
+
+#ifdef CONFIG_HID_PICOLCD_LEDS
+int picolcd_init_leds(struct picolcd_data *data,
+ struct hid_report *report);
+
+void picolcd_exit_leds(struct picolcd_data *data);
+
+void picolcd_leds_set(struct picolcd_data *data);
+#else
+static inline int picolcd_init_leds(struct picolcd_data *data,
+ struct hid_report *report)
+{
+ return 0;
+}
+static inline void picolcd_exit_leds(struct picolcd_data *data)
+{
+}
+static inline void picolcd_leds_set(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_HID_PICOLCD_LEDS */
+
+
+#ifdef CONFIG_HID_PICOLCD_CIR
+int picolcd_raw_cir(struct picolcd_data *data,
+ struct hid_report *report, u8 *raw_data, int size);
+
+int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report);
+
+void picolcd_exit_cir(struct picolcd_data *data);
+#else
+static inline int picolcd_raw_cir(struct picolcd_data *data,
+ struct hid_report *report, u8 *raw_data, int size)
+{
+ return 1;
+}
+static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
+{
+ return 0;
+}
+static inline void picolcd_exit_cir(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_HID_PICOLCD_LIRC */
+
+int picolcd_reset(struct hid_device *hdev);
+struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
+ int report_id, const u8 *raw_data, int size);
diff --git a/drivers/hid/hid-picolcd_backlight.c b/drivers/hid/hid-picolcd_backlight.c
new file mode 100644
index 0000000..b91f309
--- /dev/null
+++ b/drivers/hid/hid-picolcd_backlight.c
@@ -0,0 +1,122 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * 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 of the License. *
+ * *
+ * This driver 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
+#include "hid-picolcd.h"
+
+static int picolcd_get_brightness(struct backlight_device *bdev)
+{
+ struct picolcd_data *data = bl_get_data(bdev);
+ return data->lcd_brightness;
+}
+
+static int picolcd_set_brightness(struct backlight_device *bdev)
+{
+ struct picolcd_data *data = bl_get_data(bdev);
+ struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);
+ unsigned long flags;
+
+ if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+ return -ENODEV;
+
+ data->lcd_brightness = bdev->props.brightness & 0x0ff;
+ data->lcd_power = bdev->props.power;
+ spin_lock_irqsave(&data->lock, flags);
+ hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
+ if (!(data->status & PICOLCD_FAILED))
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+ return 0;
+}
+
+static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)
+{
+ return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));
+}
+
+static const struct backlight_ops picolcd_blops = {
+ .update_status = picolcd_set_brightness,
+ .get_brightness = picolcd_get_brightness,
+ .check_fb = picolcd_check_bl_fb,
+};
+
+int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)
+{
+ struct device *dev = &data->hdev->dev;
+ struct backlight_device *bdev;
+ struct backlight_properties props;
+ if (!report)
+ return -ENODEV;
+ if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+ report->field[0]->report_size != 8) {
+ dev_err(dev, "unsupported BRIGHTNESS report");
+ return -EINVAL;
+ }
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = 0xff;
+ bdev = backlight_device_register(dev_name(dev), dev, data,
+ &picolcd_blops, &props);
+ if (IS_ERR(bdev)) {
+ dev_err(dev, "failed to register backlight\n");
+ return PTR_ERR(bdev);
+ }
+ bdev->props.brightness = 0xff;
+ data->lcd_brightness = 0xff;
+ data->backlight = bdev;
+ picolcd_set_brightness(bdev);
+ return 0;
+}
+
+void picolcd_exit_backlight(struct picolcd_data *data)
+{
+ struct backlight_device *bdev = data->backlight;
+
+ data->backlight = NULL;
+ if (bdev)
+ backlight_device_unregister(bdev);
+}
+
+int picolcd_resume_backlight(struct picolcd_data *data)
+{
+ if (!data->backlight)
+ return 0;
+ return picolcd_set_brightness(data->backlight);
+}
+
+#ifdef CONFIG_PM
+void picolcd_suspend_backlight(struct picolcd_data *data)
+{
+ int bl_power = data->lcd_power;
+ if (!data->backlight)
+ return;
+
+ data->backlight->props.power = FB_BLANK_POWERDOWN;
+ picolcd_set_brightness(data->backlight);
+ data->lcd_power = data->backlight->props.power = bl_power;
+}
+#endif /* CONFIG_PM */
+
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
new file mode 100644
index 0000000..13ca919
--- /dev/null
+++ b/drivers/hid/hid-picolcd_cir.c
@@ -0,0 +1,152 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * 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 of the License. *
+ * *
+ * This driver 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+
+#include <linux/leds.h>
+
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <media/rc-core.h>
+
+#include "hid-picolcd.h"
+
+
+int picolcd_raw_cir(struct picolcd_data *data,
+ struct hid_report *report, u8 *raw_data, int size)
+{
+ unsigned long flags;
+ int i, w, sz;
+ DEFINE_IR_RAW_EVENT(rawir);
+
+ /* ignore if rc_dev is NULL or status is shunned */
+ spin_lock_irqsave(&data->lock, flags);
+ if (!data->rc_dev || (data->status & PICOLCD_CIR_SHUN)) {
+ spin_unlock_irqrestore(&data->lock, flags);
+ return 1;
+ }
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* PicoLCD USB packets contain 16-bit intervals in network order,
+ * with value negated for pulse. Intervals are in microseconds.
+ *
+ * Note: some userspace LIRC code for PicoLCD says negated values
+ * for space - is it a matter of IR chip? (pulse for my TSOP2236)
+ *
+ * In addition, the first interval seems to be around 15000 + base
+ * interval for non-first report of IR data - thus the quirk below
+ * to get RC_CODE to understand Sony and JVC remotes I have at hand
+ */
+ sz = size > 0 ? min((int)raw_data[0], size-1) : 0;
+ for (i = 0; i+1 < sz; i += 2) {
+ init_ir_raw_event(&rawir);
+ w = (raw_data[i] << 8) | (raw_data[i+1]);
+ rawir.pulse = !!(w & 0x8000);
+ rawir.duration = US_TO_NS(rawir.pulse ? (65536 - w) : w);
+ /* Quirk!! - see above */
+ if (i == 0 && rawir.duration > 15000000)
+ rawir.duration -= 15000000;
+ ir_raw_event_store(data->rc_dev, &rawir);
+ }
+ ir_raw_event_handle(data->rc_dev);
+
+ return 1;
+}
+
+static int picolcd_cir_open(struct rc_dev *dev)
+{
+ struct picolcd_data *data = dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+ data->status &= ~PICOLCD_CIR_SHUN;
+ spin_unlock_irqrestore(&data->lock, flags);
+ return 0;
+}
+
+static void picolcd_cir_close(struct rc_dev *dev)
+{
+ struct picolcd_data *data = dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+ data->status |= PICOLCD_CIR_SHUN;
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+/* initialize CIR input device */
+int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
+{
+ struct rc_dev *rdev;
+ int ret = 0;
+
+ rdev = rc_allocate_device();
+ if (!rdev)
+ return -ENOMEM;
+
+ rdev->priv = data;
+ rdev->driver_type = RC_DRIVER_IR_RAW;
+ rdev->allowed_protos = RC_TYPE_ALL;
+ rdev->open = picolcd_cir_open;
+ rdev->close = picolcd_cir_close;
+ rdev->input_name = data->hdev->name;
+ rdev->input_phys = data->hdev->phys;
+ rdev->input_id.bustype = data->hdev->bus;
+ rdev->input_id.vendor = data->hdev->vendor;
+ rdev->input_id.product = data->hdev->product;
+ rdev->input_id.version = data->hdev->version;
+ rdev->dev.parent = &data->hdev->dev;
+ rdev->driver_name = PICOLCD_NAME;
+ rdev->map_name = RC_MAP_RC6_MCE;
+ rdev->timeout = MS_TO_NS(100);
+ rdev->rx_resolution = US_TO_NS(1);
+
+ ret = rc_register_device(rdev);
+ if (ret)
+ goto err;
+ data->rc_dev = rdev;
+ return 0;
+
+err:
+ rc_free_device(rdev);
+ return ret;
+}
+
+void picolcd_exit_cir(struct picolcd_data *data)
+{
+ struct rc_dev *rdev = data->rc_dev;
+
+ data->rc_dev = NULL;
+ rc_unregister_device(rdev);
+}
+
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
new file mode 100644
index 0000000..86df26e
--- /dev/null
+++ b/drivers/hid/hid-picolcd_core.c
@@ -0,0 +1,689 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * 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 of the License. *
+ * *
+ * This driver 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+
+/* Input device
+ *
+ * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
+ * and header for 4x4 key matrix. The built-in keys are part of the matrix.
+ */
+static const unsigned short def_keymap[PICOLCD_KEYS] = {
+ KEY_RESERVED, /* none */
+ KEY_BACK, /* col 4 + row 1 */
+ KEY_HOMEPAGE, /* col 3 + row 1 */
+ KEY_RESERVED, /* col 2 + row 1 */
+ KEY_RESERVED, /* col 1 + row 1 */
+ KEY_SCROLLUP, /* col 4 + row 2 */
+ KEY_OK, /* col 3 + row 2 */
+ KEY_SCROLLDOWN, /* col 2 + row 2 */
+ KEY_RESERVED, /* col 1 + row 2 */
+ KEY_RESERVED, /* col 4 + row 3 */
+ KEY_RESERVED, /* col 3 + row 3 */
+ KEY_RESERVED, /* col 2 + row 3 */
+ KEY_RESERVED, /* col 1 + row 3 */
+ KEY_RESERVED, /* col 4 + row 4 */
+ KEY_RESERVED, /* col 3 + row 4 */
+ KEY_RESERVED, /* col 2 + row 4 */
+ KEY_RESERVED, /* col 1 + row 4 */
+};
+
+
+/* Find a given report */
+struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
+{
+ struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;
+ struct hid_report *report = NULL;
+
+ list_for_each_entry(report, feature_report_list, list) {
+ if (report->id == id)
+ return report;
+ }
+ hid_warn(hdev, "No report with id 0x%x found\n", id);
+ return NULL;
+}
+
+/* Submit a report and wait for a reply from device - if device fades away
+ * or does not respond in time, return NULL */
+struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
+ int report_id, const u8 *raw_data, int size)
+{
+ struct picolcd_data *data = hid_get_drvdata(hdev);
+ struct picolcd_pending *work;
+ struct hid_report *report = picolcd_out_report(report_id, hdev);
+ unsigned long flags;
+ int i, j, k;
+
+ if (!report || !data)
+ return NULL;
+ if (data->status & PICOLCD_FAILED)
+ return NULL;
+ work = kzalloc(sizeof(*work), GFP_KERNEL);
+ if (!work)
+ return NULL;
+
+ init_completion(&work->ready);
+ work->out_report = report;
+ work->in_report = NULL;
+ work->raw_size = 0;
+
+ mutex_lock(&data->mutex);
+ spin_lock_irqsave(&data->lock, flags);
+ for (i = k = 0; i < report->maxfield; i++)
+ for (j = 0; j < report->field[i]->report_count; j++) {
+ hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
+ k++;
+ }
+ if (data->status & PICOLCD_FAILED) {
+ kfree(work);
+ work = NULL;
+ } else {
+ data->pending = work;
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+ wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
+ spin_lock_irqsave(&data->lock, flags);
+ data->pending = NULL;
+ }
+ spin_unlock_irqrestore(&data->lock, flags);
+ mutex_unlock(&data->mutex);
+ return work;
+}
+
+/*
+ * input class device
+ */
+static int picolcd_raw_keypad(struct picolcd_data *data,
+ struct hid_report *report, u8 *raw_data, int size)
+{
+ /*
+ * Keypad event
+ * First and second data bytes list currently pressed keys,
+ * 0x00 means no key and at most 2 keys may be pressed at same time
+ */
+ int i, j;
+
+ /* determine newly pressed keys */
+ for (i = 0; i < size; i++) {
+ unsigned int key_code;
+ if (raw_data[i] == 0)
+ continue;
+ for (j = 0; j < sizeof(data->pressed_keys); j++)
+ if (data->pressed_keys[j] == raw_data[i])
+ goto key_already_down;
+ for (j = 0; j < sizeof(data->pressed_keys); j++)
+ if (data->pressed_keys[j] == 0) {
+ data->pressed_keys[j] = raw_data[i];
+ break;
+ }
+ input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);
+ if (raw_data[i] < PICOLCD_KEYS)
+ key_code = data->keycode[raw_data[i]];
+ else
+ key_code = KEY_UNKNOWN;
+ if (key_code != KEY_UNKNOWN) {
+ dbg_hid(PICOLCD_NAME " got key press for %u:%d",
+ raw_data[i], key_code);
+ input_report_key(data->input_keys, key_code, 1);
+ }
+ input_sync(data->input_keys);
+key_already_down:
+ continue;
+ }
+
+ /* determine newly released keys */
+ for (j = 0; j < sizeof(data->pressed_keys); j++) {
+ unsigned int key_code;
+ if (data->pressed_keys[j] == 0)
+ continue;
+ for (i = 0; i < size; i++)
+ if (data->pressed_keys[j] == raw_data[i])
+ goto key_still_down;
+ input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);
+ if (data->pressed_keys[j] < PICOLCD_KEYS)
+ key_code = data->keycode[data->pressed_keys[j]];
+ else
+ key_code = KEY_UNKNOWN;
+ if (key_code != KEY_UNKNOWN) {
+ dbg_hid(PICOLCD_NAME " got key release for %u:%d",
+ data->pressed_keys[j], key_code);
+ input_report_key(data->input_keys, key_code, 0);
+ }
+ input_sync(data->input_keys);
+ data->pressed_keys[j] = 0;
+key_still_down:
+ continue;
+ }
+ return 1;
+}
+
+static int picolcd_check_version(struct hid_device *hdev)
+{
+ struct picolcd_data *data = hid_get_drvdata(hdev);
+ struct picolcd_pending *verinfo;
+ int ret = 0;
+
+ if (!data)
+ return -ENODEV;
+
+ verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);
+ if (!verinfo) {
+ hid_err(hdev, "no version response from PicoLCD\n");
+ return -ENODEV;
+ }
+
+ if (verinfo->raw_size == 2) {
+ data->version[0] = verinfo->raw_data[1];
+ data->version[1] = verinfo->raw_data[0];
+ if (data->status & PICOLCD_BOOTLOADER) {
+ hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
+ verinfo->raw_data[1], verinfo->raw_data[0]);
+ } else {
+ hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
+ verinfo->raw_data[1], verinfo->raw_data[0]);
+ }
+ } else {
+ hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
+ ret = -EINVAL;
+ }
+ kfree(verinfo);
+ return ret;
+}
+
+/*
+ * Reset our device and wait for answer to VERSION request
+ */
+int picolcd_reset(struct hid_device *hdev)
+{
+ struct picolcd_data *data = hid_get_drvdata(hdev);
+ struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
+ unsigned long flags;
+ int error;
+
+ if (!data || !report || report->maxfield != 1)
+ return -ENODEV;
+
+ spin_lock_irqsave(&data->lock, flags);
+ if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
+ data->status |= PICOLCD_BOOTLOADER;
+
+ /* perform the reset */
+ hid_set_field(report->field[0], 0, 1);
+ if (data->status & PICOLCD_FAILED) {
+ spin_unlock_irqrestore(&data->lock, flags);
+ return -ENODEV;
+ }
+ usbhid_submit_report(hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ error = picolcd_check_version(hdev);
+ if (error)
+ return error;
+
+ picolcd_resume_lcd(data);
+ picolcd_resume_backlight(data);
+ picolcd_fb_refresh(data);
+ picolcd_leds_set(data);
+ return 0;
+}
+
+/*
+ * The "operation_mode" sysfs attribute
+ */
+static ssize_t picolcd_operation_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+
+ if (data->status & PICOLCD_BOOTLOADER)
+ return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");
+}
+
+static ssize_t picolcd_operation_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+ struct hid_report *report = NULL;
+ size_t cnt = count;
+ int timeout = data->opmode_delay;
+ unsigned long flags;
+
+ if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) {
+ if (data->status & PICOLCD_BOOTLOADER)
+ report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
+ buf += 3;
+ cnt -= 3;
+ } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) {
+ if (!(data->status & PICOLCD_BOOTLOADER))
+ report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
+ buf += 10;
+ cnt -= 10;
+ }
+ if (!report)
+ return -EINVAL;
+
+ while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
+ cnt--;
+ if (cnt != 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&data->lock, flags);
+ hid_set_field(report->field[0], 0, timeout & 0xff);
+ hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+ return count;
+}
+
+static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
+ picolcd_operation_mode_store);
+
+/*
+ * The "operation_mode_delay" sysfs attribute
+ */
+static ssize_t picolcd_operation_mode_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);
+}
+
+static ssize_t picolcd_operation_mode_delay_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+ unsigned u;
+ if (sscanf(buf, "%u", &u) != 1)
+ return -EINVAL;
+ if (u > 30000)
+ return -EINVAL;
+ else
+ data->opmode_delay = u;
+ return count;
+}
+
+static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,
+ picolcd_operation_mode_delay_store);
+
+/*
+ * Handle raw report as sent by device
+ */
+static int picolcd_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *raw_data, int size)
+{
+ struct picolcd_data *data = hid_get_drvdata(hdev);
+ unsigned long flags;
+ int ret = 0;
+
+ if (!data)
+ return 1;
+
+ if (report->id == REPORT_KEY_STATE) {
+ if (data->input_keys)
+ ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
+ } else if (report->id == REPORT_IR_DATA) {
+ ret = picolcd_raw_cir(data, report, raw_data+1, size-1);
+ } else {
+ spin_lock_irqsave(&data->lock, flags);
+ /*
+ * We let the caller of picolcd_send_and_wait() check if the
+ * report we got is one of the expected ones or not.
+ */
+ if (data->pending) {
+ memcpy(data->pending->raw_data, raw_data+1, size-1);
+ data->pending->raw_size = size-1;
+ data->pending->in_report = report;
+ complete(&data->pending->ready);
+ }
+ spin_unlock_irqrestore(&data->lock, flags);
+ }
+
+ picolcd_debug_raw_event(data, hdev, report, raw_data, size);
+ return 1;
+}
+
+#ifdef CONFIG_PM
+static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
+{
+ if (PMSG_IS_AUTO(message))
+ return 0;
+
+ picolcd_suspend_backlight(hid_get_drvdata(hdev));
+ dbg_hid(PICOLCD_NAME " device ready for suspend\n");
+ return 0;
+}
+
+static int picolcd_resume(struct hid_device *hdev)
+{
+ int ret;
+ ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
+ if (ret)
+ dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
+ return 0;
+}
+
+static int picolcd_reset_resume(struct hid_device *hdev)
+{
+ int ret;
+ ret = picolcd_reset(hdev);
+ if (ret)
+ dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);
+ ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
+ if (ret)
+ dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
+ ret = picolcd_resume_lcd(hid_get_drvdata(hdev));
+ if (ret)
+ dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);
+ ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
+ if (ret)
+ dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
+ picolcd_leds_set(hid_get_drvdata(hdev));
+ return 0;
+}
+#endif
+
+/* initialize keypad input device */
+static int picolcd_init_keys(struct picolcd_data *data,
+ struct hid_report *report)
+{
+ struct hid_device *hdev = data->hdev;
+ struct input_dev *idev;
+ int error, i;
+
+ if (!report)
+ return -ENODEV;
+ if (report->maxfield != 1 || report->field[0]->report_count != 2 ||
+ report->field[0]->report_size != 8) {
+ hid_err(hdev, "unsupported KEY_STATE report\n");
+ return -EINVAL;
+ }
+
+ idev = input_allocate_device();
+ if (idev == NULL) {
+ hid_err(hdev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+ input_set_drvdata(idev, hdev);
+ memcpy(data->keycode, def_keymap, sizeof(def_keymap));
+ idev->name = hdev->name;
+ idev->phys = hdev->phys;
+ idev->uniq = hdev->uniq;
+ idev->id.bustype = hdev->bus;
+ idev->id.vendor = hdev->vendor;
+ idev->id.product = hdev->product;
+ idev->id.version = hdev->version;
+ idev->dev.parent = &hdev->dev;
+ idev->keycode = &data->keycode;
+ idev->keycodemax = PICOLCD_KEYS;
+ idev->keycodesize = sizeof(data->keycode[0]);
+ input_set_capability(idev, EV_MSC, MSC_SCAN);
+ set_bit(EV_REP, idev->evbit);
+ for (i = 0; i < PICOLCD_KEYS; i++)
+ input_set_capability(idev, EV_KEY, data->keycode[i]);
+ error = input_register_device(idev);
+ if (error) {
+ hid_err(hdev, "error registering the input device\n");
+ input_free_device(idev);
+ return error;
+ }
+ data->input_keys = idev;
+ return 0;
+}
+
+static void picolcd_exit_keys(struct picolcd_data *data)
+{
+ struct input_dev *idev = data->input_keys;
+
+ data->input_keys = NULL;
+ if (idev)
+ input_unregister_device(idev);
+}
+
+static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
+{
+ int error;
+
+ /* Setup keypad input device */
+ error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
+ if (error)
+ goto err;
+
+ /* Setup CIR input device */
+ error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));
+ if (error)
+ goto err;
+
+ /* Set up the framebuffer device */
+ error = picolcd_init_framebuffer(data);
+ if (error)
+ goto err;
+
+ /* Setup lcd class device */
+ error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
+ if (error)
+ goto err;
+
+ /* Setup backlight class device */
+ error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
+ if (error)
+ goto err;
+
+ /* Setup the LED class devices */
+ error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
+ if (error)
+ goto err;
+
+ picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
+ picolcd_out_report(REPORT_EE_WRITE, hdev),
+ picolcd_out_report(REPORT_READ_MEMORY, hdev),
+ picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
+ picolcd_out_report(REPORT_RESET, hdev));
+ return 0;
+err:
+ picolcd_exit_leds(data);
+ picolcd_exit_backlight(data);
+ picolcd_exit_lcd(data);
+ picolcd_exit_framebuffer(data);
+ picolcd_exit_cir(data);
+ picolcd_exit_keys(data);
+ return error;
+}
+
+static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
+{
+ picolcd_init_devfs(data, NULL, NULL,
+ picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
+ picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
+ return 0;
+}
+
+static int picolcd_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct picolcd_data *data;
+ int error = -ENOMEM;
+
+ dbg_hid(PICOLCD_NAME " hardware probe...\n");
+
+ /*
+ * Let's allocate the picolcd data structure, set some reasonable
+ * defaults, and associate it with the device
+ */
+ data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);
+ if (data == NULL) {
+ hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");
+ error = -ENOMEM;
+ goto err_no_cleanup;
+ }
+
+ spin_lock_init(&data->lock);
+ mutex_init(&data->mutex);
+ data->hdev = hdev;
+ data->opmode_delay = 5000;
+ if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
+ data->status |= PICOLCD_BOOTLOADER;
+ hid_set_drvdata(hdev, data);
+
+ /* Parse the device reports and start it up */
+ error = hid_parse(hdev);
+ if (error) {
+ hid_err(hdev, "device report parse failed\n");
+ goto err_cleanup_data;
+ }
+
+ error = hid_hw_start(hdev, 0);
+ if (error) {
+ hid_err(hdev, "hardware start failed\n");
+ goto err_cleanup_data;
+ }
+
+ error = hid_hw_open(hdev);
+ if (error) {
+ hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");
+ goto err_cleanup_hid_hw;
+ }
+
+ error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);
+ if (error) {
+ hid_err(hdev, "failed to create sysfs attributes\n");
+ goto err_cleanup_hid_ll;
+ }
+
+ error = device_create_file(&hdev->dev, &dev_attr_operation_mode);
+ if (error) {
+ hid_err(hdev, "failed to create sysfs attributes\n");
+ goto err_cleanup_sysfs1;
+ }
+
+ if (data->status & PICOLCD_BOOTLOADER)
+ error = picolcd_probe_bootloader(hdev, data);
+ else
+ error = picolcd_probe_lcd(hdev, data);
+ if (error)
+ goto err_cleanup_sysfs2;
+
+ dbg_hid(PICOLCD_NAME " activated and initialized\n");
+ return 0;
+
+err_cleanup_sysfs2:
+ device_remove_file(&hdev->dev, &dev_attr_operation_mode);
+err_cleanup_sysfs1:
+ device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
+err_cleanup_hid_ll:
+ hid_hw_close(hdev);
+err_cleanup_hid_hw:
+ hid_hw_stop(hdev);
+err_cleanup_data:
+ kfree(data);
+err_no_cleanup:
+ hid_set_drvdata(hdev, NULL);
+
+ return error;
+}
+
+static void picolcd_remove(struct hid_device *hdev)
+{
+ struct picolcd_data *data = hid_get_drvdata(hdev);
+ unsigned long flags;
+
+ dbg_hid(PICOLCD_NAME " hardware remove...\n");
+ spin_lock_irqsave(&data->lock, flags);
+ data->status |= PICOLCD_FAILED;
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ picolcd_exit_devfs(data);
+ device_remove_file(&hdev->dev, &dev_attr_operation_mode);
+ device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
+
+ /* Shortcut potential pending reply that will never arrive */
+ spin_lock_irqsave(&data->lock, flags);
+ if (data->pending)
+ complete(&data->pending->ready);
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* Cleanup LED */
+ picolcd_exit_leds(data);
+ /* Clean up the framebuffer */
+ picolcd_exit_backlight(data);
+ picolcd_exit_lcd(data);
+ picolcd_exit_framebuffer(data);
+ /* Cleanup input */
+ picolcd_exit_cir(data);
+ picolcd_exit_keys(data);
+
+ hid_set_drvdata(hdev, NULL);
+ mutex_destroy(&data->mutex);
+ /* Finally, clean up the picolcd data itself */
+ kfree(data);
+}
+
+static const struct hid_device_id picolcd_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, picolcd_devices);
+
+static struct hid_driver picolcd_driver = {
+ .name = "hid-picolcd",
+ .id_table = picolcd_devices,
+ .probe = picolcd_probe,
+ .remove = picolcd_remove,
+ .raw_event = picolcd_raw_event,
+#ifdef CONFIG_PM
+ .suspend = picolcd_suspend,
+ .resume = picolcd_resume,
+ .reset_resume = picolcd_reset_resume,
+#endif
+};
+
+static int __init picolcd_init(void)
+{
+ return hid_register_driver(&picolcd_driver);
+}
+
+static void __exit picolcd_exit(void)
+{
+ hid_unregister_driver(&picolcd_driver);
+}
+
+module_init(picolcd_init);
+module_exit(picolcd_exit);
+MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c
new file mode 100644
index 0000000..4809aa1
--- /dev/null
+++ b/drivers/hid/hid-picolcd_debugfs.c
@@ -0,0 +1,899 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * 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 of the License. *
+ * *
+ * This driver 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+#include "hid-picolcd.h"
+
+
+static int picolcd_debug_reset_show(struct seq_file *f, void *p)
+{
+ if (picolcd_fbinfo((struct picolcd_data *)f->private))
+ seq_printf(f, "all fb\n");
+ else
+ seq_printf(f, "all\n");
+ return 0;
+}
+
+static int picolcd_debug_reset_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, picolcd_debug_reset_show, inode->i_private);
+}
+
+static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
+ char buf[32];
+ size_t cnt = min(count, sizeof(buf)-1);
+ if (copy_from_user(buf, user_buf, cnt))
+ return -EFAULT;
+
+ while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))
+ cnt--;
+ buf[cnt] = '\0';
+ if (strcmp(buf, "all") == 0) {
+ picolcd_reset(data->hdev);
+ picolcd_fb_reset(data, 1);
+ } else if (strcmp(buf, "fb") == 0) {
+ picolcd_fb_reset(data, 1);
+ } else {
+ return -EINVAL;
+ }
+ return count;
+}
+
+static const struct file_operations picolcd_debug_reset_fops = {
+ .owner = THIS_MODULE,
+ .open = picolcd_debug_reset_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = picolcd_debug_reset_write,
+ .release = single_release,
+};
+
+/*
+ * The "eeprom" file
+ */
+static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
+ size_t s, loff_t *off)
+{
+ struct picolcd_data *data = f->private_data;
+ struct picolcd_pending *resp;
+ u8 raw_data[3];
+ ssize_t ret = -EIO;
+
+ if (s == 0)
+ return -EINVAL;
+ if (*off > 0x0ff)
+ return 0;
+
+ /* prepare buffer with info about what we want to read (addr & len) */
+ raw_data[0] = *off & 0xff;
+ raw_data[1] = (*off >> 8) & 0xff;
+ raw_data[2] = s < 20 ? s : 20;
+ if (*off + raw_data[2] > 0xff)
+ raw_data[2] = 0x100 - *off;
+ resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
+ sizeof(raw_data));
+ if (!resp)
+ return -EIO;
+
+ if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
+ /* successful read :) */
+ ret = resp->raw_data[2];
+ if (ret > s)
+ ret = s;
+ if (copy_to_user(u, resp->raw_data+3, ret))
+ ret = -EFAULT;
+ else
+ *off += ret;
+ } /* anything else is some kind of IO error */
+
+ kfree(resp);
+ return ret;
+}
+
+static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
+ size_t s, loff_t *off)
+{
+ struct picolcd_data *data = f->private_data;
+ struct picolcd_pending *resp;
+ ssize_t ret = -EIO;
+ u8 raw_data[23];
+
+ if (s == 0)
+ return -EINVAL;
+ if (*off > 0x0ff)
+ return -ENOSPC;
+
+ memset(raw_data, 0, sizeof(raw_data));
+ raw_data[0] = *off & 0xff;
+ raw_data[1] = (*off >> 8) & 0xff;
+ raw_data[2] = min_t(size_t, 20, s);
+ if (*off + raw_data[2] > 0xff)
+ raw_data[2] = 0x100 - *off;
+
+ if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
+ return -EFAULT;
+ resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
+ sizeof(raw_data));
+
+ if (!resp)
+ return -EIO;
+
+ if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
+ /* check if written data matches */
+ if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {
+ *off += raw_data[2];
+ ret = raw_data[2];
+ }
+ }
+ kfree(resp);
+ return ret;
+}
+
+/*
+ * Notes:
+ * - read/write happens in chunks of at most 20 bytes, it's up to userspace
+ * to loop in order to get more data.
+ * - on write errors on otherwise correct write request the bytes
+ * that should have been written are in undefined state.
+ */
+static const struct file_operations picolcd_debug_eeprom_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = picolcd_debug_eeprom_read,
+ .write = picolcd_debug_eeprom_write,
+ .llseek = generic_file_llseek,
+};
+
+/*
+ * The "flash" file
+ */
+/* record a flash address to buf (bounds check to be done by caller) */
+static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
+{
+ buf[0] = off & 0xff;
+ buf[1] = (off >> 8) & 0xff;
+ if (data->addr_sz == 3)
+ buf[2] = (off >> 16) & 0xff;
+ return data->addr_sz == 2 ? 2 : 3;
+}
+
+/* read a given size of data (bounds check to be done by caller) */
+static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
+ char __user *u, size_t s, loff_t *off)
+{
+ struct picolcd_pending *resp;
+ u8 raw_data[4];
+ ssize_t ret = 0;
+ int len_off, err = -EIO;
+
+ while (s > 0) {
+ err = -EIO;
+ len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+ raw_data[len_off] = s > 32 ? 32 : s;
+ resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
+ if (!resp || !resp->in_report)
+ goto skip;
+ if (resp->in_report->id == REPORT_MEMORY ||
+ resp->in_report->id == REPORT_BL_READ_MEMORY) {
+ if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
+ goto skip;
+ if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
+ err = -EFAULT;
+ goto skip;
+ }
+ *off += raw_data[len_off];
+ s -= raw_data[len_off];
+ ret += raw_data[len_off];
+ err = 0;
+ }
+skip:
+ kfree(resp);
+ if (err)
+ return ret > 0 ? ret : err;
+ }
+ return ret;
+}
+
+static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
+ size_t s, loff_t *off)
+{
+ struct picolcd_data *data = f->private_data;
+
+ if (s == 0)
+ return -EINVAL;
+ if (*off > 0x05fff)
+ return 0;
+ if (*off + s > 0x05fff)
+ s = 0x06000 - *off;
+
+ if (data->status & PICOLCD_BOOTLOADER)
+ return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
+ else
+ return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
+}
+
+/* erase block aligned to 64bytes boundary */
+static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
+ loff_t *off)
+{
+ struct picolcd_pending *resp;
+ u8 raw_data[3];
+ int len_off;
+ ssize_t ret = -EIO;
+
+ if (*off & 0x3f)
+ return -EINVAL;
+
+ len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+ resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
+ if (!resp || !resp->in_report)
+ goto skip;
+ if (resp->in_report->id == REPORT_MEMORY ||
+ resp->in_report->id == REPORT_BL_ERASE_MEMORY) {
+ if (memcmp(raw_data, resp->raw_data, len_off) != 0)
+ goto skip;
+ ret = 0;
+ }
+skip:
+ kfree(resp);
+ return ret;
+}
+
+/* write a given size of data (bounds check to be done by caller) */
+static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
+ const char __user *u, size_t s, loff_t *off)
+{
+ struct picolcd_pending *resp;
+ u8 raw_data[36];
+ ssize_t ret = 0;
+ int len_off, err = -EIO;
+
+ while (s > 0) {
+ err = -EIO;
+ len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+ raw_data[len_off] = s > 32 ? 32 : s;
+ if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
+ err = -EFAULT;
+ break;
+ }
+ resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
+ len_off+1+raw_data[len_off]);
+ if (!resp || !resp->in_report)
+ goto skip;
+ if (resp->in_report->id == REPORT_MEMORY ||
+ resp->in_report->id == REPORT_BL_WRITE_MEMORY) {
+ if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
+ goto skip;
+ *off += raw_data[len_off];
+ s -= raw_data[len_off];
+ ret += raw_data[len_off];
+ err = 0;
+ }
+skip:
+ kfree(resp);
+ if (err)
+ break;
+ }
+ return ret > 0 ? ret : err;
+}
+
+static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
+ size_t s, loff_t *off)
+{
+ struct picolcd_data *data = f->private_data;
+ ssize_t err, ret = 0;
+ int report_erase, report_write;
+
+ if (s == 0)
+ return -EINVAL;
+ if (*off > 0x5fff)
+ return -ENOSPC;
+ if (s & 0x3f)
+ return -EINVAL;
+ if (*off & 0x3f)
+ return -EINVAL;
+
+ if (data->status & PICOLCD_BOOTLOADER) {
+ report_erase = REPORT_BL_ERASE_MEMORY;
+ report_write = REPORT_BL_WRITE_MEMORY;
+ } else {
+ report_erase = REPORT_ERASE_MEMORY;
+ report_write = REPORT_WRITE_MEMORY;
+ }
+ mutex_lock(&data->mutex_flash);
+ while (s > 0) {
+ err = _picolcd_flash_erase64(data, report_erase, off);
+ if (err)
+ break;
+ err = _picolcd_flash_write(data, report_write, u, 64, off);
+ if (err < 0)
+ break;
+ ret += err;
+ *off += err;
+ s -= err;
+ if (err != 64)
+ break;
+ }
+ mutex_unlock(&data->mutex_flash);
+ return ret > 0 ? ret : err;
+}
+
+/*
+ * Notes:
+ * - concurrent writing is prevented by mutex and all writes must be
+ * n*64 bytes and 64-byte aligned, each write being preceded by an
+ * ERASE which erases a 64byte block.
+ * If less than requested was written or an error is returned for an
+ * otherwise correct write request the next 64-byte block which should
+ * have been written is in undefined state (mostly: original, erased,
+ * (half-)written with write error)
+ * - reading can happen without special restriction
+ */
+static const struct file_operations picolcd_debug_flash_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = picolcd_debug_flash_read,
+ .write = picolcd_debug_flash_write,
+ .llseek = generic_file_llseek,
+};
+
+
+/*
+ * Helper code for HID report level dumping/debugging
+ */
+static const char * const error_codes[] = {
+ "success", "parameter missing", "data_missing", "block readonly",
+ "block not erasable", "block too big", "section overflow",
+ "invalid command length", "invalid data length",
+};
+
+static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
+ const size_t data_len)
+{
+ int i, j;
+ for (i = j = 0; i < data_len && j + 4 < dst_sz; i++) {
+ dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
+ dst[j++] = hex_asc[data[i] & 0x0f];
+ dst[j++] = ' ';
+ }
+ dst[j] = '\0';
+ if (j > 0)
+ dst[j-1] = '\n';
+ if (i < data_len && j > 2)
+ dst[j-2] = dst[j-3] = '.';
+}
+
+void picolcd_debug_out_report(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report)
+{
+ u8 raw_data[70];
+ int raw_size = (report->size >> 3) + 1;
+ char *buff;
+#define BUFF_SZ 256
+
+ /* Avoid unnecessary overhead if debugfs is disabled */
+ if (list_empty(&hdev->debug_list))
+ return;
+
+ buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
+ if (!buff)
+ return;
+
+ snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ",
+ report->id, raw_size);
+ hid_debug_event(hdev, buff);
+ if (raw_size + 5 > sizeof(raw_data)) {
+ kfree(buff);
+ hid_debug_event(hdev, " TOO BIG\n");
+ return;
+ } else {
+ raw_data[0] = report->id;
+ hid_output_report(report, raw_data);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
+ hid_debug_event(hdev, buff);
+ }
+
+ switch (report->id) {
+ case REPORT_LED_STATE:
+ /* 1 data byte with GPO state */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_LED_STATE", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_BRIGHTNESS:
+ /* 1 data byte with brightness */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_BRIGHTNESS", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_CONTRAST:
+ /* 1 data byte with contrast */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_CONTRAST", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_RESET:
+ /* 2 data bytes with reset duration in ms */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_RESET", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
+ raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_LCD_CMD:
+ /* 63 data bytes with LCD commands */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_LCD_CMD", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ /* TODO: format decoding */
+ break;
+ case REPORT_LCD_DATA:
+ /* 63 data bytes with LCD data */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_LCD_CMD", report->id, raw_size-1);
+ /* TODO: format decoding */
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_LCD_CMD_DATA:
+ /* 63 data bytes with LCD commands and data */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_LCD_CMD", report->id, raw_size-1);
+ /* TODO: format decoding */
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_EE_READ:
+ /* 3 data bytes with read area description */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_EE_READ", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_EE_WRITE:
+ /* 3+1..20 data bytes with write area description */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_EE_WRITE", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[3] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ } else if (raw_data[3] + 4 <= raw_size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ }
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_ERASE_MEMORY:
+ case REPORT_BL_ERASE_MEMORY:
+ /* 3 data bytes with pointer inside erase block */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_ERASE_MEMORY", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ switch (data->addr_sz) {
+ case 2:
+ snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ break;
+ case 3:
+ snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
+ raw_data[3], raw_data[2], raw_data[1]);
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "\tNot supported\n");
+ }
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_READ_MEMORY:
+ case REPORT_BL_READ_MEMORY:
+ /* 4 data bytes with read area description */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_READ_MEMORY", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ switch (data->addr_sz) {
+ case 2:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ break;
+ case 3:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+ raw_data[3], raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "\tNot supported\n");
+ }
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_WRITE_MEMORY:
+ case REPORT_BL_WRITE_MEMORY:
+ /* 4+1..32 data bytes with write adrea description */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_WRITE_MEMORY", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ switch (data->addr_sz) {
+ case 2:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[3] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ } else if (raw_data[3] + 4 <= raw_size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ }
+ break;
+ case 3:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+ raw_data[3], raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[4] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ } else if (raw_data[4] + 5 <= raw_size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ }
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "\tNot supported\n");
+ }
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_SPLASH_RESTART:
+ /* TODO */
+ break;
+ case REPORT_EXIT_KEYBOARD:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
+ raw_data[1] | (raw_data[2] << 8),
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_VERSION:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_VERSION", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_DEVID:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_DEVID", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_SPLASH_SIZE:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_SPLASH_SIZE", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_HOOK_VERSION:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_HOOK_VERSION", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_EXIT_FLASHER:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_VERSION", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
+ raw_data[1] | (raw_data[2] << 8),
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "<unknown>", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ }
+ wake_up_interruptible(&hdev->debug_wait);
+ kfree(buff);
+}
+
+void picolcd_debug_raw_event(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report,
+ u8 *raw_data, int size)
+{
+ char *buff;
+
+#define BUFF_SZ 256
+ /* Avoid unnecessary overhead if debugfs is disabled */
+ if (list_empty(&hdev->debug_list))
+ return;
+
+ buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
+ if (!buff)
+ return;
+
+ switch (report->id) {
+ case REPORT_ERROR_CODE:
+ /* 2 data bytes with affected report and error code */
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_ERROR_CODE", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ if (raw_data[2] < ARRAY_SIZE(error_codes))
+ snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
+ raw_data[2], error_codes[raw_data[2]], raw_data[1]);
+ else
+ snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_KEY_STATE:
+ /* 2 data bytes with key state */
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_KEY_STATE", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ if (raw_data[1] == 0)
+ snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
+ else if (raw_data[2] == 0)
+ snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
+ raw_data[1], raw_data[1]);
+ else
+ snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
+ raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_IR_DATA:
+ /* Up to 20 byes of IR scancode data */
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_IR_DATA", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ if (raw_data[1] == 0) {
+ snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
+ hid_debug_event(hdev, buff);
+ } else if (raw_data[1] + 1 <= size) {
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
+ raw_data[1]);
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]);
+ hid_debug_event(hdev, buff);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
+ raw_data[1]-1);
+ hid_debug_event(hdev, buff);
+ }
+ break;
+ case REPORT_EE_DATA:
+ /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_EE_DATA", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[3] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ hid_debug_event(hdev, buff);
+ } else if (raw_data[3] + 4 <= size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+ hid_debug_event(hdev, buff);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ hid_debug_event(hdev, buff);
+ }
+ break;
+ case REPORT_MEMORY:
+ /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_MEMORY", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ switch (data->addr_sz) {
+ case 2:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[3] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ } else if (raw_data[3] + 4 <= size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ }
+ break;
+ case 3:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+ raw_data[3], raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[4] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ } else if (raw_data[4] + 5 <= size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ }
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "\tNot supported\n");
+ }
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_VERSION:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_VERSION", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_BL_ERASE_MEMORY:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_BL_ERASE_MEMORY", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ /* TODO */
+ break;
+ case REPORT_BL_READ_MEMORY:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_BL_READ_MEMORY", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ /* TODO */
+ break;
+ case REPORT_BL_WRITE_MEMORY:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_BL_WRITE_MEMORY", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ /* TODO */
+ break;
+ case REPORT_DEVID:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_DEVID", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
+ raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
+ raw_data[5]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_SPLASH_SIZE:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_SPLASH_SIZE", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
+ (raw_data[2] << 8) | raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
+ (raw_data[4] << 8) | raw_data[3]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_HOOK_VERSION:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_HOOK_VERSION", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
+ raw_data[1], raw_data[2]);
+ hid_debug_event(hdev, buff);
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "<unknown>", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ }
+ wake_up_interruptible(&hdev->debug_wait);
+ kfree(buff);
+}
+
+void picolcd_init_devfs(struct picolcd_data *data,
+ struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+ struct hid_report *flash_r, struct hid_report *flash_w,
+ struct hid_report *reset)
+{
+ struct hid_device *hdev = data->hdev;
+
+ mutex_init(&data->mutex_flash);
+
+ /* reset */
+ if (reset)
+ data->debug_reset = debugfs_create_file("reset", 0600,
+ hdev->debug_dir, data, &picolcd_debug_reset_fops);
+
+ /* eeprom */
+ if (eeprom_r || eeprom_w)
+ data->debug_eeprom = debugfs_create_file("eeprom",
+ (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
+ hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
+
+ /* flash */
+ if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)
+ data->addr_sz = flash_r->field[0]->report_count - 1;
+ else
+ data->addr_sz = -1;
+ if (data->addr_sz == 2 || data->addr_sz == 3) {
+ data->debug_flash = debugfs_create_file("flash",
+ (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
+ hdev->debug_dir, data, &picolcd_debug_flash_fops);
+ } else if (flash_r || flash_w)
+ hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
+}
+
+void picolcd_exit_devfs(struct picolcd_data *data)
+{
+ struct dentry *dent;
+
+ dent = data->debug_reset;
+ data->debug_reset = NULL;
+ if (dent)
+ debugfs_remove(dent);
+ dent = data->debug_eeprom;
+ data->debug_eeprom = NULL;
+ if (dent)
+ debugfs_remove(dent);
+ dent = data->debug_flash;
+ data->debug_flash = NULL;
+ if (dent)
+ debugfs_remove(dent);
+ mutex_destroy(&data->mutex_flash);
+}
+
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
new file mode 100644
index 0000000..0008a51
--- /dev/null
+++ b/drivers/hid/hid-picolcd_fb.c
@@ -0,0 +1,615 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * 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 of the License. *
+ * *
+ * This driver 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/vmalloc.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+/* Framebuffer
+ *
+ * The PicoLCD use a Topway LCD module of 256x64 pixel
+ * This display area is tiled over 4 controllers with 8 tiles
+ * each. Each tile has 8x64 pixel, each data byte representing
+ * a 1-bit wide vertical line of the tile.
+ *
+ * The display can be updated at a tile granularity.
+ *
+ * Chip 1 Chip 2 Chip 3 Chip 4
+ * +----------------+----------------+----------------+----------------+
+ * | Tile 1 | Tile 1 | Tile 1 | Tile 1 |
+ * +----------------+----------------+----------------+----------------+
+ * | Tile 2 | Tile 2 | Tile 2 | Tile 2 |
+ * +----------------+----------------+----------------+----------------+
+ * ...
+ * +----------------+----------------+----------------+----------------+
+ * | Tile 8 | Tile 8 | Tile 8 | Tile 8 |
+ * +----------------+----------------+----------------+----------------+
+ */
+#define PICOLCDFB_NAME "picolcdfb"
+#define PICOLCDFB_WIDTH (256)
+#define PICOLCDFB_HEIGHT (64)
+#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
+
+#define PICOLCDFB_UPDATE_RATE_LIMIT 10
+#define PICOLCDFB_UPDATE_RATE_DEFAULT 2
+
+/* Framebuffer visual structures */
+static const struct fb_fix_screeninfo picolcdfb_fix = {
+ .id = PICOLCDFB_NAME,
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_MONO01,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+ .line_length = PICOLCDFB_WIDTH / 8,
+ .accel = FB_ACCEL_NONE,
+};
+
+static const struct fb_var_screeninfo picolcdfb_var = {
+ .xres = PICOLCDFB_WIDTH,
+ .yres = PICOLCDFB_HEIGHT,
+ .xres_virtual = PICOLCDFB_WIDTH,
+ .yres_virtual = PICOLCDFB_HEIGHT,
+ .width = 103,
+ .height = 26,
+ .bits_per_pixel = 1,
+ .grayscale = 1,
+ .red = {
+ .offset = 0,
+ .length = 1,
+ .msb_right = 0,
+ },
+ .green = {
+ .offset = 0,
+ .length = 1,
+ .msb_right = 0,
+ },
+ .blue = {
+ .offset = 0,
+ .length = 1,
+ .msb_right = 0,
+ },
+ .transp = {
+ .offset = 0,
+ .length = 0,
+ .msb_right = 0,
+ },
+};
+
+/* Send a given tile to PicoLCD */
+static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap,
+ int chip, int tile)
+{
+ struct hid_report *report1, *report2;
+ unsigned long flags;
+ u8 *tdata;
+ int i;
+
+ report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, data->hdev);
+ if (!report1 || report1->maxfield != 1)
+ return -ENODEV;
+ report2 = picolcd_out_report(REPORT_LCD_DATA, data->hdev);
+ if (!report2 || report2->maxfield != 1)
+ return -ENODEV;
+
+ spin_lock_irqsave(&data->lock, flags);
+ if ((data->status & PICOLCD_FAILED)) {
+ spin_unlock_irqrestore(&data->lock, flags);
+ return -ENODEV;
+ }
+ hid_set_field(report1->field[0], 0, chip << 2);
+ hid_set_field(report1->field[0], 1, 0x02);
+ hid_set_field(report1->field[0], 2, 0x00);
+ hid_set_field(report1->field[0], 3, 0x00);
+ hid_set_field(report1->field[0], 4, 0xb8 | tile);
+ hid_set_field(report1->field[0], 5, 0x00);
+ hid_set_field(report1->field[0], 6, 0x00);
+ hid_set_field(report1->field[0], 7, 0x40);
+ hid_set_field(report1->field[0], 8, 0x00);
+ hid_set_field(report1->field[0], 9, 0x00);
+ hid_set_field(report1->field[0], 10, 32);
+
+ hid_set_field(report2->field[0], 0, (chip << 2) | 0x01);
+ hid_set_field(report2->field[0], 1, 0x00);
+ hid_set_field(report2->field[0], 2, 0x00);
+ hid_set_field(report2->field[0], 3, 32);
+
+ tdata = vbitmap + (tile * 4 + chip) * 64;
+ for (i = 0; i < 64; i++)
+ if (i < 32)
+ hid_set_field(report1->field[0], 11 + i, tdata[i]);
+ else
+ hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
+
+ usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);
+ usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+ return 0;
+}
+
+/* Translate a single tile*/
+static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
+ int chip, int tile)
+{
+ int i, b, changed = 0;
+ u8 tdata[64];
+ u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
+
+ if (bpp == 1) {
+ for (b = 7; b >= 0; b--) {
+ const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
+ for (i = 0; i < 64; i++) {
+ tdata[i] <<= 1;
+ tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
+ }
+ }
+ } else if (bpp == 8) {
+ for (b = 7; b >= 0; b--) {
+ const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
+ for (i = 0; i < 64; i++) {
+ tdata[i] <<= 1;
+ tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
+ }
+ }
+ } else {
+ /* Oops, we should never get here! */
+ WARN_ON(1);
+ return 0;
+ }
+
+ for (i = 0; i < 64; i++)
+ if (tdata[i] != vdata[i]) {
+ changed = 1;
+ vdata[i] = tdata[i];
+ }
+ return changed;
+}
+
+void picolcd_fb_refresh(struct picolcd_data *data)
+{
+ if (data->fb_info)
+ schedule_delayed_work(&data->fb_info->deferred_work, 0);
+}
+
+/* Reconfigure LCD display */
+int picolcd_fb_reset(struct picolcd_data *data, int clear)
+{
+ struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
+ struct picolcd_fb_data *fbdata = data->fb_info->par;
+ int i, j;
+ unsigned long flags;
+ static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
+
+ if (!report || report->maxfield != 1)
+ return -ENODEV;
+
+ spin_lock_irqsave(&data->lock, flags);
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < report->field[0]->maxusage; j++)
+ if (j == 0)
+ hid_set_field(report->field[0], j, i << 2);
+ else if (j < sizeof(mapcmd))
+ hid_set_field(report->field[0], j, mapcmd[j]);
+ else
+ hid_set_field(report->field[0], j, 0);
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ }
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ if (clear) {
+ memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE);
+ memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp);
+ }
+ fbdata->force = 1;
+
+ /* schedule first output of framebuffer */
+ if (fbdata->ready)
+ schedule_delayed_work(&data->fb_info->deferred_work, 0);
+ else
+ fbdata->ready = 1;
+
+ return 0;
+}
+
+/* Update fb_vbitmap from the screen_base and send changed tiles to device */
+static void picolcd_fb_update(struct fb_info *info)
+{
+ int chip, tile, n;
+ unsigned long flags;
+ struct picolcd_fb_data *fbdata = info->par;
+ struct picolcd_data *data;
+
+ mutex_lock(&info->lock);
+
+ spin_lock_irqsave(&fbdata->lock, flags);
+ if (!fbdata->ready && fbdata->picolcd)
+ picolcd_fb_reset(fbdata->picolcd, 0);
+ spin_unlock_irqrestore(&fbdata->lock, flags);
+
+ /*
+ * Translate the framebuffer into the format needed by the PicoLCD.
+ * See display layout above.
+ * Do this one tile after the other and push those tiles that changed.
+ *
+ * Wait for our IO to complete as otherwise we might flood the queue!
+ */
+ n = 0;
+ for (chip = 0; chip < 4; chip++)
+ for (tile = 0; tile < 8; tile++) {
+ if (!fbdata->force && !picolcd_fb_update_tile(
+ fbdata->vbitmap, fbdata->bitmap,
+ fbdata->bpp, chip, tile))
+ continue;
+ n += 2;
+ if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
+ spin_lock_irqsave(&fbdata->lock, flags);
+ data = fbdata->picolcd;
+ spin_unlock_irqrestore(&fbdata->lock, flags);
+ mutex_unlock(&info->lock);
+ if (!data)
+ return;
+ usbhid_wait_io(data->hdev);
+ mutex_lock(&info->lock);
+ n = 0;
+ }
+ spin_lock_irqsave(&fbdata->lock, flags);
+ data = fbdata->picolcd;
+ spin_unlock_irqrestore(&fbdata->lock, flags);
+ if (!data || picolcd_fb_send_tile(data,
+ fbdata->vbitmap, chip, tile))
+ goto out;
+ }
+ fbdata->force = false;
+ if (n) {
+ spin_lock_irqsave(&fbdata->lock, flags);
+ data = fbdata->picolcd;
+ spin_unlock_irqrestore(&fbdata->lock, flags);
+ mutex_unlock(&info->lock);
+ if (data)
+ usbhid_wait_io(data->hdev);
+ return;
+ }
+out:
+ mutex_unlock(&info->lock);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect)
+{
+ if (!info->par)
+ return;
+ sys_fillrect(info, rect);
+
+ schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ if (!info->par)
+ return;
+ sys_copyarea(info, area);
+
+ schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ if (!info->par)
+ return;
+ sys_imageblit(info, image);
+
+ schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/*
+ * this is the slow path from userspace. they can seek and write to
+ * the fb. it's inefficient to do anything less than a full screen draw
+ */
+static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t ret;
+ if (!info->par)
+ return -ENODEV;
+ ret = fb_sys_write(info, buf, count, ppos);
+ if (ret >= 0)
+ schedule_delayed_work(&info->deferred_work, 0);
+ return ret;
+}
+
+static int picolcd_fb_blank(int blank, struct fb_info *info)
+{
+ /* We let fb notification do this for us via lcd/backlight device */
+ return 0;
+}
+
+static void picolcd_fb_destroy(struct fb_info *info)
+{
+ struct picolcd_fb_data *fbdata = info->par;
+
+ /* make sure no work is deferred */
+ fb_deferred_io_cleanup(info);
+
+ /* No thridparty should ever unregister our framebuffer! */
+ WARN_ON(fbdata->picolcd != NULL);
+
+ vfree((u8 *)info->fix.smem_start);
+ framebuffer_release(info);
+}
+
+static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ __u32 bpp = var->bits_per_pixel;
+ __u32 activate = var->activate;
+
+ /* only allow 1/8 bit depth (8-bit is grayscale) */
+ *var = picolcdfb_var;
+ var->activate = activate;
+ if (bpp >= 8) {
+ var->bits_per_pixel = 8;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ } else {
+ var->bits_per_pixel = 1;
+ var->red.length = 1;
+ var->green.length = 1;
+ var->blue.length = 1;
+ }
+ return 0;
+}
+
+static int picolcd_set_par(struct fb_info *info)
+{
+ struct picolcd_fb_data *fbdata = info->par;
+ u8 *tmp_fb, *o_fb;
+ if (info->var.bits_per_pixel == fbdata->bpp)
+ return 0;
+ /* switch between 1/8 bit depths */
+ if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
+ return -EINVAL;
+
+ o_fb = fbdata->bitmap;
+ tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
+ if (!tmp_fb)
+ return -ENOMEM;
+
+ /* translate FB content to new bits-per-pixel */
+ if (info->var.bits_per_pixel == 1) {
+ int i, b;
+ for (i = 0; i < PICOLCDFB_SIZE; i++) {
+ u8 p = 0;
+ for (b = 0; b < 8; b++) {
+ p <<= 1;
+ p |= o_fb[i*8+b] ? 0x01 : 0x00;
+ }
+ tmp_fb[i] = p;
+ }
+ memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
+ info->fix.visual = FB_VISUAL_MONO01;
+ info->fix.line_length = PICOLCDFB_WIDTH / 8;
+ } else {
+ int i;
+ memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
+ for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
+ o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
+ info->fix.visual = FB_VISUAL_DIRECTCOLOR;
+ info->fix.line_length = PICOLCDFB_WIDTH;
+ }
+
+ kfree(tmp_fb);
+ fbdata->bpp = info->var.bits_per_pixel;
+ return 0;
+}
+
+/* Note this can't be const because of struct fb_info definition */
+static struct fb_ops picolcdfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_destroy = picolcd_fb_destroy,
+ .fb_read = fb_sys_read,
+ .fb_write = picolcd_fb_write,
+ .fb_blank = picolcd_fb_blank,
+ .fb_fillrect = picolcd_fb_fillrect,
+ .fb_copyarea = picolcd_fb_copyarea,
+ .fb_imageblit = picolcd_fb_imageblit,
+ .fb_check_var = picolcd_fb_check_var,
+ .fb_set_par = picolcd_set_par,
+};
+
+
+/* Callback from deferred IO workqueue */
+static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
+{
+ picolcd_fb_update(info);
+}
+
+static const struct fb_deferred_io picolcd_fb_defio = {
+ .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
+ .deferred_io = picolcd_fb_deferred_io,
+};
+
+
+/*
+ * The "fb_update_rate" sysfs attribute
+ */
+static ssize_t picolcd_fb_update_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+ struct picolcd_fb_data *fbdata = data->fb_info->par;
+ unsigned i, fb_update_rate = fbdata->update_rate;
+ size_t ret = 0;
+
+ for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
+ if (ret >= PAGE_SIZE)
+ break;
+ else if (i == fb_update_rate)
+ ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
+ else
+ ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
+ if (ret > 0)
+ buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
+ return ret;
+}
+
+static ssize_t picolcd_fb_update_rate_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+ struct picolcd_fb_data *fbdata = data->fb_info->par;
+ int i;
+ unsigned u;
+
+ if (count < 1 || count > 10)
+ return -EINVAL;
+
+ i = sscanf(buf, "%u", &u);
+ if (i != 1)
+ return -EINVAL;
+
+ if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
+ return -ERANGE;
+ else if (u == 0)
+ u = PICOLCDFB_UPDATE_RATE_DEFAULT;
+
+ fbdata->update_rate = u;
+ data->fb_info->fbdefio->delay = HZ / fbdata->update_rate;
+ return count;
+}
+
+static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
+ picolcd_fb_update_rate_store);
+
+/* initialize Framebuffer device */
+int picolcd_init_framebuffer(struct picolcd_data *data)
+{
+ struct device *dev = &data->hdev->dev;
+ struct fb_info *info = NULL;
+ struct picolcd_fb_data *fbdata = NULL;
+ int i, error = -ENOMEM;
+ u32 *palette;
+
+ /* The extra memory is:
+ * - 256*u32 for pseudo_palette
+ * - struct fb_deferred_io
+ */
+ info = framebuffer_alloc(256 * sizeof(u32) +
+ sizeof(struct fb_deferred_io) +
+ sizeof(struct picolcd_fb_data) +
+ PICOLCDFB_SIZE, dev);
+ if (info == NULL) {
+ dev_err(dev, "failed to allocate a framebuffer\n");
+ goto err_nomem;
+ }
+
+ info->fbdefio = info->par;
+ *info->fbdefio = picolcd_fb_defio;
+ info->par += sizeof(struct fb_deferred_io);
+ palette = info->par;
+ info->par += 256 * sizeof(u32);
+ for (i = 0; i < 256; i++)
+ palette[i] = i > 0 && i < 16 ? 0xff : 0;
+ info->pseudo_palette = palette;
+ info->fbops = &picolcdfb_ops;
+ info->var = picolcdfb_var;
+ info->fix = picolcdfb_fix;
+ info->fix.smem_len = PICOLCDFB_SIZE*8;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ fbdata = info->par;
+ spin_lock_init(&fbdata->lock);
+ fbdata->picolcd = data;
+ fbdata->update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
+ fbdata->bpp = picolcdfb_var.bits_per_pixel;
+ fbdata->force = 1;
+ fbdata->vbitmap = info->par + sizeof(struct picolcd_fb_data);
+ fbdata->bitmap = vmalloc(PICOLCDFB_SIZE*8);
+ if (fbdata->bitmap == NULL) {
+ dev_err(dev, "can't get a free page for framebuffer\n");
+ goto err_nomem;
+ }
+ info->screen_base = (char __force __iomem *)fbdata->bitmap;
+ info->fix.smem_start = (unsigned long)fbdata->bitmap;
+ memset(fbdata->vbitmap, 0xff, PICOLCDFB_SIZE);
+ data->fb_info = info;
+
+ error = picolcd_fb_reset(data, 1);
+ if (error) {
+ dev_err(dev, "failed to configure display\n");
+ goto err_cleanup;
+ }
+
+ error = device_create_file(dev, &dev_attr_fb_update_rate);
+ if (error) {
+ dev_err(dev, "failed to create sysfs attributes\n");
+ goto err_cleanup;
+ }
+
+ fb_deferred_io_init(info);
+ error = register_framebuffer(info);
+ if (error) {
+ dev_err(dev, "failed to register framebuffer\n");
+ goto err_sysfs;
+ }
+ return 0;
+
+err_sysfs:
+ device_remove_file(dev, &dev_attr_fb_update_rate);
+ fb_deferred_io_cleanup(info);
+err_cleanup:
+ data->fb_info = NULL;
+
+err_nomem:
+ if (fbdata)
+ vfree(fbdata->bitmap);
+ framebuffer_release(info);
+ return error;
+}
+
+void picolcd_exit_framebuffer(struct picolcd_data *data)
+{
+ struct fb_info *info = data->fb_info;
+ struct picolcd_fb_data *fbdata = info->par;
+ unsigned long flags;
+
+ device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
+
+ /* disconnect framebuffer from HID dev */
+ spin_lock_irqsave(&fbdata->lock, flags);
+ fbdata->picolcd = NULL;
+ spin_unlock_irqrestore(&fbdata->lock, flags);
+
+ /* make sure there is no running update - thus that fbdata->picolcd
+ * once obtained under lock is guaranteed not to get free() under
+ * the feet of the deferred work */
+ flush_delayed_work_sync(&info->deferred_work);
+
+ data->fb_info = NULL;
+ unregister_framebuffer(info);
+}
diff --git a/drivers/hid/hid-picolcd_lcd.c b/drivers/hid/hid-picolcd_lcd.c
new file mode 100644
index 0000000..2d0ddc5
--- /dev/null
+++ b/drivers/hid/hid-picolcd_lcd.c
@@ -0,0 +1,107 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * 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 of the License. *
+ * *
+ * This driver 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/lcd.h>
+
+#include "hid-picolcd.h"
+
+/*
+ * lcd class device
+ */
+static int picolcd_get_contrast(struct lcd_device *ldev)
+{
+ struct picolcd_data *data = lcd_get_data(ldev);
+ return data->lcd_contrast;
+}
+
+static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)
+{
+ struct picolcd_data *data = lcd_get_data(ldev);
+ struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev);
+ unsigned long flags;
+
+ if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+ return -ENODEV;
+
+ data->lcd_contrast = contrast & 0x0ff;
+ spin_lock_irqsave(&data->lock, flags);
+ hid_set_field(report->field[0], 0, data->lcd_contrast);
+ if (!(data->status & PICOLCD_FAILED))
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+ return 0;
+}
+
+static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb)
+{
+ return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev));
+}
+
+static struct lcd_ops picolcd_lcdops = {
+ .get_contrast = picolcd_get_contrast,
+ .set_contrast = picolcd_set_contrast,
+ .check_fb = picolcd_check_lcd_fb,
+};
+
+int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report)
+{
+ struct device *dev = &data->hdev->dev;
+ struct lcd_device *ldev;
+
+ if (!report)
+ return -ENODEV;
+ if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+ report->field[0]->report_size != 8) {
+ dev_err(dev, "unsupported CONTRAST report");
+ return -EINVAL;
+ }
+
+ ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops);
+ if (IS_ERR(ldev)) {
+ dev_err(dev, "failed to register LCD\n");
+ return PTR_ERR(ldev);
+ }
+ ldev->props.max_contrast = 0x0ff;
+ data->lcd_contrast = 0xe5;
+ data->lcd = ldev;
+ picolcd_set_contrast(ldev, 0xe5);
+ return 0;
+}
+
+void picolcd_exit_lcd(struct picolcd_data *data)
+{
+ struct lcd_device *ldev = data->lcd;
+
+ data->lcd = NULL;
+ if (ldev)
+ lcd_device_unregister(ldev);
+}
+
+int picolcd_resume_lcd(struct picolcd_data *data)
+{
+ if (!data->lcd)
+ return 0;
+ return picolcd_set_contrast(data->lcd, data->lcd_contrast);
+}
+
diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c
new file mode 100644
index 0000000..28cb6a4
--- /dev/null
+++ b/drivers/hid/hid-picolcd_leds.c
@@ -0,0 +1,175 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * 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 of the License. *
+ * *
+ * This driver 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+
+#include <linux/leds.h>
+
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+
+void picolcd_leds_set(struct picolcd_data *data)
+{
+ struct hid_report *report;
+ unsigned long flags;
+
+ if (!data->led[0])
+ return;
+ report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
+ if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+ return;
+
+ spin_lock_irqsave(&data->lock, flags);
+ hid_set_field(report->field[0], 0, data->led_state);
+ if (!(data->status & PICOLCD_FAILED))
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct device *dev;
+ struct hid_device *hdev;
+ struct picolcd_data *data;
+ int i, state = 0;
+
+ dev = led_cdev->dev->parent;
+ hdev = container_of(dev, struct hid_device, dev);
+ data = hid_get_drvdata(hdev);
+ if (!data)
+ return;
+ for (i = 0; i < 8; i++) {
+ if (led_cdev != data->led[i])
+ continue;
+ state = (data->led_state >> i) & 1;
+ if (value == LED_OFF && state) {
+ data->led_state &= ~(1 << i);
+ picolcd_leds_set(data);
+ } else if (value != LED_OFF && !state) {
+ data->led_state |= 1 << i;
+ picolcd_leds_set(data);
+ }
+ break;
+ }
+}
+
+static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
+{
+ struct device *dev;
+ struct hid_device *hdev;
+ struct picolcd_data *data;
+ int i, value = 0;
+
+ dev = led_cdev->dev->parent;
+ hdev = container_of(dev, struct hid_device, dev);
+ data = hid_get_drvdata(hdev);
+ for (i = 0; i < 8; i++)
+ if (led_cdev == data->led[i]) {
+ value = (data->led_state >> i) & 1;
+ break;
+ }
+ return value ? LED_FULL : LED_OFF;
+}
+
+int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
+{
+ struct device *dev = &data->hdev->dev;
+ struct led_classdev *led;
+ size_t name_sz = strlen(dev_name(dev)) + 8;
+ char *name;
+ int i, ret = 0;
+
+ if (!report)
+ return -ENODEV;
+ if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+ report->field[0]->report_size != 8) {
+ dev_err(dev, "unsupported LED_STATE report");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 8; i++) {
+ led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
+ if (!led) {
+ dev_err(dev, "can't allocate memory for LED %d\n", i);
+ ret = -ENOMEM;
+ goto err;
+ }
+ name = (void *)(&led[1]);
+ snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
+ led->name = name;
+ led->brightness = 0;
+ led->max_brightness = 1;
+ led->brightness_get = picolcd_led_get_brightness;
+ led->brightness_set = picolcd_led_set_brightness;
+
+ data->led[i] = led;
+ ret = led_classdev_register(dev, data->led[i]);
+ if (ret) {
+ data->led[i] = NULL;
+ kfree(led);
+ dev_err(dev, "can't register LED %d\n", i);
+ goto err;
+ }
+ }
+ return 0;
+err:
+ for (i = 0; i < 8; i++)
+ if (data->led[i]) {
+ led = data->led[i];
+ data->led[i] = NULL;
+ led_classdev_unregister(led);
+ kfree(led);
+ }
+ return ret;
+}
+
+void picolcd_exit_leds(struct picolcd_data *data)
+{
+ struct led_classdev *led;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ led = data->led[i];
+ data->led[i] = NULL;
+ if (!led)
+ continue;
+ led_classdev_unregister(led);
+ kfree(led);
+ }
+}
+
+
diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c
index 4d3c60d..c15adb0 100644
--- a/drivers/hid/hid-primax.c
+++ b/drivers/hid/hid-primax.c
@@ -64,29 +64,6 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report,
return 0;
}
-static int px_probe(struct hid_device *hid, const struct hid_device_id *id)
-{
- int ret;
-
- ret = hid_parse(hid);
- if (ret) {
- hid_err(hid, "parse failed\n");
- goto fail;
- }
-
- ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
- if (ret)
- hid_err(hid, "hw start failed\n");
-
-fail:
- return ret;
-}
-
-static void px_remove(struct hid_device *hid)
-{
- hid_hw_stop(hid);
-}
-
static const struct hid_device_id px_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
{ }
@@ -97,8 +74,6 @@ static struct hid_driver px_driver = {
.name = "primax",
.id_table = px_devices,
.raw_event = px_raw_event,
- .probe = px_probe,
- .remove = px_remove,
};
static int __init px_init(void)
diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c
index b71b77a..ec8ca33 100644
--- a/drivers/hid/hid-prodikeys.c
+++ b/drivers/hid/hid-prodikeys.c
@@ -105,7 +105,7 @@ static ssize_t show_channel(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel);
@@ -118,7 +118,7 @@ static ssize_t store_channel(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
unsigned channel = 0;
@@ -142,7 +142,7 @@ static ssize_t show_sustain(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain);
@@ -155,7 +155,7 @@ static ssize_t store_sustain(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
unsigned sustain = 0;
@@ -181,7 +181,7 @@ static ssize_t show_octave(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave);
@@ -194,7 +194,7 @@ static ssize_t store_octave(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
int octave = 0;
@@ -759,7 +759,7 @@ static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
struct pcmidi_snd *pm;
pm = pk->pm;
@@ -777,7 +777,7 @@ static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
static int pk_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
int ret = 0;
if (1 == pk->pm->ifnum) {
@@ -858,7 +858,7 @@ err_free_pk:
static void pk_remove(struct hid_device *hdev)
{
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
struct pcmidi_snd *pm;
pm = pk->pm;
diff --git a/drivers/hid/hid-ps3remote.c b/drivers/hid/hid-ps3remote.c
new file mode 100644
index 0000000..03811e53
--- /dev/null
+++ b/drivers/hid/hid-ps3remote.c
@@ -0,0 +1,215 @@
+/*
+ * HID driver for Sony PS3 BD Remote Control
+ *
+ * Copyright (c) 2012 David Dillow <dave@thedillows.org>
+ * Based on a blend of the bluez fakehid user-space code by Marcel Holtmann
+ * and other kernel HID drivers.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/* NOTE: in order for the Sony PS3 BD Remote Control to be found by
+ * a Bluetooth host, the key combination Start+Enter has to be kept pressed
+ * for about 7 seconds with the Bluetooth Host Controller in discovering mode.
+ *
+ * There will be no PIN request from the device.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static __u8 ps3remote_rdesc[] = {
+ 0x05, 0x01, /* GUsagePage Generic Desktop */
+ 0x09, 0x05, /* LUsage 0x05 [Game Pad] */
+ 0xA1, 0x01, /* MCollection Application (mouse, keyboard) */
+
+ /* Use collection 1 for joypad buttons */
+ 0xA1, 0x02, /* MCollection Logical (interrelated data) */
+
+ /* Ignore the 1st byte, maybe it is used for a controller
+ * number but it's not needed for correct operation */
+ 0x75, 0x08, /* GReportSize 0x08 [8] */
+ 0x95, 0x01, /* GReportCount 0x01 [1] */
+ 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
+
+ /* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these
+ * buttons multiple keypresses are allowed */
+ 0x05, 0x09, /* GUsagePage Button */
+ 0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */
+ 0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */
+ 0x14, /* GLogicalMinimum [0] */
+ 0x25, 0x01, /* GLogicalMaximum 0x01 [1] */
+ 0x75, 0x01, /* GReportSize 0x01 [1] */
+ 0x95, 0x18, /* GReportCount 0x18 [24] */
+ 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */
+
+ 0xC0, /* MEndCollection */
+
+ /* Use collection 2 for remote control buttons */
+ 0xA1, 0x02, /* MCollection Logical (interrelated data) */
+
+ /* 5th byte is used for remote control buttons */
+ 0x05, 0x09, /* GUsagePage Button */
+ 0x18, /* LUsageMinimum [No button pressed] */
+ 0x29, 0xFE, /* LUsageMaximum 0xFE [Button 254] */
+ 0x14, /* GLogicalMinimum [0] */
+ 0x26, 0xFE, 0x00, /* GLogicalMaximum 0x00FE [254] */
+ 0x75, 0x08, /* GReportSize 0x08 [8] */
+ 0x95, 0x01, /* GReportCount 0x01 [1] */
+ 0x80, /* MInput */
+
+ /* Ignore bytes from 6th to 11th, 6th to 10th are always constant at
+ * 0xff and 11th is for press indication */
+ 0x75, 0x08, /* GReportSize 0x08 [8] */
+ 0x95, 0x06, /* GReportCount 0x06 [6] */
+ 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
+
+ /* 12th byte is for battery strength */
+ 0x05, 0x06, /* GUsagePage Generic Device Controls */
+ 0x09, 0x20, /* LUsage 0x20 [Battery Strength] */
+ 0x14, /* GLogicalMinimum [0] */
+ 0x25, 0x05, /* GLogicalMaximum 0x05 [5] */
+ 0x75, 0x08, /* GReportSize 0x08 [8] */
+ 0x95, 0x01, /* GReportCount 0x01 [1] */
+ 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */
+
+ 0xC0, /* MEndCollection */
+
+ 0xC0 /* MEndCollection [Game Pad] */
+};
+
+static const unsigned int ps3remote_keymap_joypad_buttons[] = {
+ [0x01] = KEY_SELECT,
+ [0x02] = BTN_THUMBL, /* L3 */
+ [0x03] = BTN_THUMBR, /* R3 */
+ [0x04] = BTN_START,
+ [0x05] = KEY_UP,
+ [0x06] = KEY_RIGHT,
+ [0x07] = KEY_DOWN,
+ [0x08] = KEY_LEFT,
+ [0x09] = BTN_TL2, /* L2 */
+ [0x0a] = BTN_TR2, /* R2 */
+ [0x0b] = BTN_TL, /* L1 */
+ [0x0c] = BTN_TR, /* R1 */
+ [0x0d] = KEY_OPTION, /* options/triangle */
+ [0x0e] = KEY_BACK, /* back/circle */
+ [0x0f] = BTN_0, /* cross */
+ [0x10] = KEY_SCREEN, /* view/square */
+ [0x11] = KEY_HOMEPAGE, /* PS button */
+ [0x14] = KEY_ENTER,
+};
+static const unsigned int ps3remote_keymap_remote_buttons[] = {
+ [0x00] = KEY_1,
+ [0x01] = KEY_2,
+ [0x02] = KEY_3,
+ [0x03] = KEY_4,
+ [0x04] = KEY_5,
+ [0x05] = KEY_6,
+ [0x06] = KEY_7,
+ [0x07] = KEY_8,
+ [0x08] = KEY_9,
+ [0x09] = KEY_0,
+ [0x0e] = KEY_ESC, /* return */
+ [0x0f] = KEY_CLEAR,
+ [0x16] = KEY_EJECTCD,
+ [0x1a] = KEY_MENU, /* top menu */
+ [0x28] = KEY_TIME,
+ [0x30] = KEY_PREVIOUS,
+ [0x31] = KEY_NEXT,
+ [0x32] = KEY_PLAY,
+ [0x33] = KEY_REWIND, /* scan back */
+ [0x34] = KEY_FORWARD, /* scan forward */
+ [0x38] = KEY_STOP,
+ [0x39] = KEY_PAUSE,
+ [0x40] = KEY_CONTEXT_MENU, /* pop up/menu */
+ [0x60] = KEY_FRAMEBACK, /* slow/step back */
+ [0x61] = KEY_FRAMEFORWARD, /* slow/step forward */
+ [0x63] = KEY_SUBTITLE,
+ [0x64] = KEY_AUDIO,
+ [0x65] = KEY_ANGLE,
+ [0x70] = KEY_INFO, /* display */
+ [0x80] = KEY_BLUE,
+ [0x81] = KEY_RED,
+ [0x82] = KEY_GREEN,
+ [0x83] = KEY_YELLOW,
+};
+
+static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ *rsize = sizeof(ps3remote_rdesc);
+ return ps3remote_rdesc;
+}
+
+static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ unsigned int key = usage->hid & HID_USAGE;
+
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
+ return -1;
+
+ switch (usage->collection_index) {
+ case 1:
+ if (key >= ARRAY_SIZE(ps3remote_keymap_joypad_buttons))
+ return -1;
+
+ key = ps3remote_keymap_joypad_buttons[key];
+ if (!key)
+ return -1;
+ break;
+ case 2:
+ if (key >= ARRAY_SIZE(ps3remote_keymap_remote_buttons))
+ return -1;
+
+ key = ps3remote_keymap_remote_buttons[key];
+ if (!key)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+
+ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
+ return 1;
+}
+
+static const struct hid_device_id ps3remote_devices[] = {
+ /* PS3 BD Remote Control */
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
+ /* Logitech Harmony Adapter for PS3 */
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, ps3remote_devices);
+
+static struct hid_driver ps3remote_driver = {
+ .name = "ps3_remote",
+ .id_table = ps3remote_devices,
+ .report_fixup = ps3remote_fixup,
+ .input_mapping = ps3remote_mapping,
+};
+
+static int __init ps3remote_init(void)
+{
+ return hid_register_driver(&ps3remote_driver);
+}
+
+static void __exit ps3remote_exit(void)
+{
+ hid_unregister_driver(&ps3remote_driver);
+}
+
+module_init(ps3remote_init);
+module_exit(ps3remote_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Dillow <dave@thedillows.org>, Antonio Ospite <ospite@studenti.unina.it>");
diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c
index 3c1fd8a..a5821d3 100644
--- a/drivers/hid/hid-samsung.c
+++ b/drivers/hid/hid-samsung.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>
*
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 5cd25bd..7f33ebf 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -4,7 +4,6 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2006-2008 Jiri Kosina
*/
diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c
index d484a00..45b4b06 100644
--- a/drivers/hid/hid-sunplus.c
+++ b/drivers/hid/hid-sunplus.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c
index 3aba02b..2e56a1f 100644
--- a/drivers/hid/hid-uclogic.c
+++ b/drivers/hid/hid-uclogic.c
@@ -466,6 +466,86 @@ static __u8 twhl850_rdesc_fixed2[] = {
0xC0 /* End Collection */
};
+/*
+ * See TWHA60 description, device and HID report descriptors at
+ * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_TWHA60
+ */
+
+/* Size of the original descriptors of TWHA60 tablet */
+#define TWHA60_RDESC_ORIG_SIZE0 254
+#define TWHA60_RDESC_ORIG_SIZE1 139
+
+/* Fixed TWHA60 report descriptor, interface 0 (stylus) */
+static __u8 twha60_rdesc_fixed0[] = {
+ 0x05, 0x0D, /* Usage Page (Digitizer), */
+ 0x09, 0x02, /* Usage (Pen), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x09, /* Report ID (9), */
+ 0x09, 0x20, /* Usage (Stylus), */
+ 0xA0, /* Collection (Physical), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x09, 0x42, /* Usage (Tip Switch), */
+ 0x09, 0x44, /* Usage (Barrel Switch), */
+ 0x09, 0x46, /* Usage (Tablet Pick), */
+ 0x14, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x04, /* Report Count (4), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x09, 0x32, /* Usage (In Range), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x14, /* Logical Minimum (0), */
+ 0xA4, /* Push, */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x55, 0xFD, /* Unit Exponent (-3), */
+ 0x65, 0x13, /* Unit (Inch), */
+ 0x34, /* Physical Minimum (0), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x46, 0x10, 0x27, /* Physical Maximum (10000), */
+ 0x27, 0x3F, 0x9C,
+ 0x00, 0x00, /* Logical Maximum (39999), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x31, /* Usage (Y), */
+ 0x46, 0x6A, 0x18, /* Physical Maximum (6250), */
+ 0x26, 0xA7, 0x61, /* Logical Maximum (24999), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xB4, /* Pop, */
+ 0x09, 0x30, /* Usage (Tip Pressure), */
+ 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xC0, /* End Collection, */
+ 0xC0 /* End Collection */
+};
+
+/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
+static __u8 twha60_rdesc_fixed1[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x06, /* Usage (Keyboard), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x05, /* Report ID (5), */
+ 0x05, 0x07, /* Usage Page (Keyboard), */
+ 0x14, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x08, /* Report Count (8), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x95, 0x0C, /* Report Count (12), */
+ 0x19, 0x3A, /* Usage Minimum (KB F1), */
+ 0x29, 0x45, /* Usage Maximum (KB F12), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x0C, /* Report Count (12), */
+ 0x19, 0x68, /* Usage Minimum (KB F13), */
+ 0x29, 0x73, /* Usage Maximum (KB F24), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x08, /* Report Count (8), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0xC0 /* End Collection */
+};
+
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@@ -525,6 +605,22 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
break;
}
break;
+ case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
+ switch (iface_num) {
+ case 0:
+ if (*rsize == TWHA60_RDESC_ORIG_SIZE0) {
+ rdesc = twha60_rdesc_fixed0;
+ *rsize = sizeof(twha60_rdesc_fixed0);
+ }
+ break;
+ case 1:
+ if (*rsize == TWHA60_RDESC_ORIG_SIZE1) {
+ rdesc = twha60_rdesc_fixed1;
+ *rsize = sizeof(twha60_rdesc_fixed1);
+ }
+ break;
+ }
+ break;
}
return rdesc;
@@ -543,6 +639,8 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
+ USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index fe23a1e..2f60da9 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
* Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
* Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
@@ -33,6 +32,8 @@
#define PAD_DEVICE_ID 0x0F
#define WAC_CMD_LED_CONTROL 0x20
+#define WAC_CMD_ICON_START_STOP 0x21
+#define WAC_CMD_ICON_TRANSFER 0x26
struct wacom_data {
__u16 tool;
@@ -69,6 +70,91 @@ static enum power_supply_property wacom_ac_props[] = {
POWER_SUPPLY_PROP_SCOPE,
};
+static void wacom_scramble(__u8 *image)
+{
+ __u16 mask;
+ __u16 s1;
+ __u16 s2;
+ __u16 r1 ;
+ __u16 r2 ;
+ __u16 r;
+ __u8 buf[256];
+ int i, w, x, y, z;
+
+ for (x = 0; x < 32; x++) {
+ for (y = 0; y < 8; y++)
+ buf[(8 * x) + (7 - y)] = image[(8 * x) + y];
+ }
+
+ /* Change 76543210 into GECA6420 as required by Intuos4 WL
+ * HGFEDCBA HFDB7531
+ */
+ for (x = 0; x < 4; x++) {
+ for (y = 0; y < 4; y++) {
+ for (z = 0; z < 8; z++) {
+ mask = 0x0001;
+ r1 = 0;
+ r2 = 0;
+ i = (x << 6) + (y << 4) + z;
+ s1 = buf[i];
+ s2 = buf[i+8];
+ for (w = 0; w < 8; w++) {
+ r1 |= (s1 & mask);
+ r2 |= (s2 & mask);
+ s1 <<= 1;
+ s2 <<= 1;
+ mask <<= 2;
+ }
+ r = r1 | (r2 << 1);
+ i = (x << 6) + (y << 4) + (z << 1);
+ image[i] = 0xFF & r;
+ image[i+1] = (0xFF00 & r) >> 8;
+ }
+ }
+ }
+}
+
+static void wacom_set_image(struct hid_device *hdev, const char *image,
+ __u8 icon_no)
+{
+ __u8 rep_data[68];
+ __u8 p[256];
+ int ret, i, j;
+
+ for (i = 0; i < 256; i++)
+ p[i] = image[i];
+
+ rep_data[0] = WAC_CMD_ICON_START_STOP;
+ rep_data[1] = 0;
+ ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
+ HID_FEATURE_REPORT);
+ if (ret < 0)
+ goto err;
+
+ rep_data[0] = WAC_CMD_ICON_TRANSFER;
+ rep_data[1] = icon_no & 0x07;
+
+ wacom_scramble(p);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 64; j++)
+ rep_data[j + 3] = p[(i << 6) + j];
+
+ rep_data[2] = i;
+ ret = hdev->hid_output_raw_report(hdev, rep_data, 67,
+ HID_FEATURE_REPORT);
+ }
+
+ rep_data[0] = WAC_CMD_ICON_START_STOP;
+ rep_data[1] = 0;
+
+ ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
+ HID_FEATURE_REPORT);
+
+err:
+ return;
+}
+
static void wacom_leds_set_brightness(struct led_classdev *led_dev,
enum led_brightness value)
{
@@ -91,7 +177,10 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev,
if (buf) {
buf[0] = WAC_CMD_LED_CONTROL;
buf[1] = led;
- buf[2] = value;
+ buf[2] = value >> 2;
+ buf[3] = value;
+ /* use fixed brightness for OLEDs */
+ buf[4] = 0x08;
hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT);
kfree(buf);
}
@@ -317,6 +406,34 @@ static ssize_t wacom_store_speed(struct device *dev,
static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
wacom_show_speed, wacom_store_speed);
+#define WACOM_STORE(OLED_ID) \
+static ssize_t wacom_oled##OLED_ID##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hid_device *hdev = container_of(dev, struct hid_device, \
+ dev); \
+ \
+ if (count != 256) \
+ return -EINVAL; \
+ \
+ wacom_set_image(hdev, buf, OLED_ID); \
+ \
+ return count; \
+} \
+ \
+static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL, \
+ wacom_oled##OLED_ID##_store)
+
+WACOM_STORE(0);
+WACOM_STORE(1);
+WACOM_STORE(2);
+WACOM_STORE(3);
+WACOM_STORE(4);
+WACOM_STORE(5);
+WACOM_STORE(6);
+WACOM_STORE(7);
+
static int wacom_gr_parse_report(struct hid_device *hdev,
struct wacom_data *wdata,
struct input_dev *input, unsigned char *data)
@@ -717,17 +834,33 @@ static int wacom_probe(struct hid_device *hdev,
hid_warn(hdev,
"can't create sysfs speed attribute err: %d\n", ret);
+#define OLED_INIT(OLED_ID) \
+ do { \
+ ret = device_create_file(&hdev->dev, \
+ &dev_attr_oled##OLED_ID##_img); \
+ if (ret) \
+ hid_warn(hdev, \
+ "can't create sysfs oled attribute, err: %d\n", ret);\
+ } while (0)
+
+OLED_INIT(0);
+OLED_INIT(1);
+OLED_INIT(2);
+OLED_INIT(3);
+OLED_INIT(4);
+OLED_INIT(5);
+OLED_INIT(6);
+OLED_INIT(7);
+
wdata->features = 0;
wacom_set_features(hdev, 1);
if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) {
sprintf(hdev->name, "%s", "Wacom Intuos4 WL");
ret = wacom_initialize_leds(hdev);
- if (ret) {
+ if (ret)
hid_warn(hdev,
"can't create led attribute, err: %d\n", ret);
- goto destroy_leds;
- }
}
wdata->battery.properties = wacom_battery_props;
@@ -740,8 +873,8 @@ static int wacom_probe(struct hid_device *hdev,
ret = power_supply_register(&hdev->dev, &wdata->battery);
if (ret) {
- hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n",
- ret);
+ hid_err(hdev, "can't create sysfs battery attribute, err: %d\n",
+ ret);
goto err_battery;
}
@@ -756,8 +889,8 @@ static int wacom_probe(struct hid_device *hdev,
ret = power_supply_register(&hdev->dev, &wdata->ac);
if (ret) {
- hid_warn(hdev,
- "can't create ac battery attribute, err: %d\n", ret);
+ hid_err(hdev,
+ "can't create ac battery attribute, err: %d\n", ret);
goto err_ac;
}
@@ -767,10 +900,17 @@ static int wacom_probe(struct hid_device *hdev,
err_ac:
power_supply_unregister(&wdata->battery);
err_battery:
+ wacom_destroy_leds(hdev);
+ device_remove_file(&hdev->dev, &dev_attr_oled0_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled1_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled2_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled3_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled4_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled5_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled6_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled7_img);
device_remove_file(&hdev->dev, &dev_attr_speed);
hid_hw_stop(hdev);
-destroy_leds:
- wacom_destroy_leds(hdev);
err_free:
kfree(wdata);
return ret;
@@ -781,6 +921,14 @@ static void wacom_remove(struct hid_device *hdev)
struct wacom_data *wdata = hid_get_drvdata(hdev);
wacom_destroy_leds(hdev);
+ device_remove_file(&hdev->dev, &dev_attr_oled0_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled1_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled2_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled3_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled4_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled5_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled6_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled7_img);
device_remove_file(&hdev->dev, &dev_attr_speed);
hid_hw_stop(hdev);
diff --git a/drivers/hid/hid-waltop.c b/drivers/hid/hid-waltop.c
index 745e4e9..bb536ab 100644
--- a/drivers/hid/hid-waltop.c
+++ b/drivers/hid/hid-waltop.c
@@ -638,28 +638,6 @@ static __u8 sirius_battery_free_tablet_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-static int waltop_probe(struct hid_device *hdev,
- const struct hid_device_id *id)
-{
- int ret;
-
- ret = hid_parse(hdev);
- if (ret) {
- hid_err(hdev, "parse failed\n");
- goto err;
- }
-
- ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
- if (ret) {
- hid_err(hdev, "hw start failed\n");
- goto err;
- }
-
- return 0;
-err:
- return ret;
-}
-
static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@@ -776,11 +754,6 @@ static int waltop_raw_event(struct hid_device *hdev, struct hid_report *report,
return 0;
}
-static void waltop_remove(struct hid_device *hdev)
-{
- hid_hw_stop(hdev);
-}
-
static const struct hid_device_id waltop_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP,
USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
@@ -803,10 +776,8 @@ MODULE_DEVICE_TABLE(hid, waltop_devices);
static struct hid_driver waltop_driver = {
.name = "waltop",
.id_table = waltop_devices,
- .probe = waltop_probe,
.report_fixup = waltop_report_fixup,
.raw_event = waltop_raw_event,
- .remove = waltop_remove,
};
static int __init waltop_init(void)
diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c
index 0a1805c..bc85bf2 100644
--- a/drivers/hid/hid-wiimote-ext.c
+++ b/drivers/hid/hid-wiimote-ext.c
@@ -28,12 +28,14 @@ struct wiimote_ext {
bool mp_plugged;
bool motionp;
__u8 ext_type;
+ __u16 calib[4][3];
};
enum wiiext_type {
WIIEXT_NONE, /* placeholder */
WIIEXT_CLASSIC, /* Nintendo classic controller */
WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */
+ WIIEXT_BALANCE_BOARD, /* Nintendo balance board controller */
};
enum wiiext_keys {
@@ -126,6 +128,7 @@ error:
static __u8 ext_read(struct wiimote_ext *ext)
{
ssize_t ret;
+ __u8 buf[24], i, j, offs = 0;
__u8 rmem[2], wmem;
__u8 type = WIIEXT_NONE;
@@ -151,6 +154,28 @@ static __u8 ext_read(struct wiimote_ext *ext)
type = WIIEXT_NUNCHUCK;
else if (rmem[0] == 0x01 && rmem[1] == 0x01)
type = WIIEXT_CLASSIC;
+ else if (rmem[0] == 0x04 && rmem[1] == 0x02)
+ type = WIIEXT_BALANCE_BOARD;
+ }
+
+ /* get balance board calibration data */
+ if (type == WIIEXT_BALANCE_BOARD) {
+ ret = wiimote_cmd_read(ext->wdata, 0xa40024, buf, 12);
+ ret += wiimote_cmd_read(ext->wdata, 0xa40024 + 12,
+ buf + 12, 12);
+
+ if (ret != 24) {
+ type = WIIEXT_NONE;
+ } else {
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 4; j++) {
+ ext->calib[j][i] = buf[offs];
+ ext->calib[j][i] <<= 8;
+ ext->calib[j][i] |= buf[offs + 1];
+ offs += 2;
+ }
+ }
+ }
}
wiimote_cmd_release(ext->wdata);
@@ -509,6 +534,71 @@ static void handler_classic(struct wiimote_ext *ext, const __u8 *payload)
input_sync(ext->input);
}
+static void handler_balance_board(struct wiimote_ext *ext, const __u8 *payload)
+{
+ __s32 val[4], tmp;
+ unsigned int i;
+
+ /* Byte | 8 7 6 5 4 3 2 1 |
+ * -----+--------------------------+
+ * 1 | Top Right <15:8> |
+ * 2 | Top Right <7:0> |
+ * -----+--------------------------+
+ * 3 | Bottom Right <15:8> |
+ * 4 | Bottom Right <7:0> |
+ * -----+--------------------------+
+ * 5 | Top Left <15:8> |
+ * 6 | Top Left <7:0> |
+ * -----+--------------------------+
+ * 7 | Bottom Left <15:8> |
+ * 8 | Bottom Left <7:0> |
+ * -----+--------------------------+
+ *
+ * These values represent the weight-measurements of the Wii-balance
+ * board with 16bit precision.
+ *
+ * The balance-board is never reported interleaved with motionp.
+ */
+
+ val[0] = payload[0];
+ val[0] <<= 8;
+ val[0] |= payload[1];
+
+ val[1] = payload[2];
+ val[1] <<= 8;
+ val[1] |= payload[3];
+
+ val[2] = payload[4];
+ val[2] <<= 8;
+ val[2] |= payload[5];
+
+ val[3] = payload[6];
+ val[3] <<= 8;
+ val[3] |= payload[7];
+
+ /* apply calibration data */
+ for (i = 0; i < 4; i++) {
+ if (val[i] < ext->calib[i][1]) {
+ tmp = val[i] - ext->calib[i][0];
+ tmp *= 1700;
+ tmp /= ext->calib[i][1] - ext->calib[i][0];
+ } else {
+ tmp = val[i] - ext->calib[i][1];
+ tmp *= 1700;
+ tmp /= ext->calib[i][2] - ext->calib[i][1];
+ tmp += 1700;
+ }
+ val[i] = tmp;
+ }
+
+ input_report_abs(ext->input, ABS_HAT0X, val[0]);
+ input_report_abs(ext->input, ABS_HAT0Y, val[1]);
+ input_report_abs(ext->input, ABS_HAT1X, val[2]);
+ input_report_abs(ext->input, ABS_HAT1Y, val[3]);
+
+ input_sync(ext->input);
+}
+
/* call this with state.lock spinlock held */
void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
{
@@ -523,6 +613,8 @@ void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
handler_nunchuck(ext, payload);
} else if (ext->ext_type == WIIEXT_CLASSIC) {
handler_classic(ext, payload);
+ } else if (ext->ext_type == WIIEXT_BALANCE_BOARD) {
+ handler_balance_board(ext, payload);
}
}
@@ -551,6 +643,11 @@ static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "motionp+classic\n");
else
return sprintf(buf, "classic\n");
+ } else if (type == WIIEXT_BALANCE_BOARD) {
+ if (motionp)
+ return sprintf(buf, "motionp+balanceboard\n");
+ else
+ return sprintf(buf, "balanceboard\n");
} else {
if (motionp)
return sprintf(buf, "motionp\n");
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 3b6f7bf..17d15bb 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -42,6 +42,7 @@ static struct cdev hidraw_cdev;
static struct class *hidraw_class;
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
static DEFINE_MUTEX(minors_lock);
+static void drop_ref(struct hidraw *hid, int exists_bit);
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
@@ -113,7 +114,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
__u8 *buf;
int ret = 0;
- if (!hidraw_table[minor]) {
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
ret = -ENODEV;
goto out;
}
@@ -261,7 +262,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
}
mutex_lock(&minors_lock);
- if (!hidraw_table[minor]) {
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
err = -ENODEV;
goto out_unlock;
}
@@ -298,36 +299,12 @@ out:
static int hidraw_release(struct inode * inode, struct file * file)
{
unsigned int minor = iminor(inode);
- struct hidraw *dev;
struct hidraw_list *list = file->private_data;
- int ret;
- int i;
-
- mutex_lock(&minors_lock);
- if (!hidraw_table[minor]) {
- ret = -ENODEV;
- goto unlock;
- }
+ drop_ref(hidraw_table[minor], 0);
list_del(&list->node);
- dev = hidraw_table[minor];
- if (!--dev->open) {
- if (list->hidraw->exist) {
- hid_hw_power(dev->hid, PM_HINT_NORMAL);
- hid_hw_close(dev->hid);
- } else {
- kfree(list->hidraw);
- }
- }
-
- for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i)
- kfree(list->buffer[i].value);
kfree(list);
- ret = 0;
-unlock:
- mutex_unlock(&minors_lock);
-
- return ret;
+ return 0;
}
static long hidraw_ioctl(struct file *file, unsigned int cmd,
@@ -529,21 +506,7 @@ EXPORT_SYMBOL_GPL(hidraw_connect);
void hidraw_disconnect(struct hid_device *hid)
{
struct hidraw *hidraw = hid->hidraw;
-
- mutex_lock(&minors_lock);
- hidraw->exist = 0;
-
- device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
-
- hidraw_table[hidraw->minor] = NULL;
-
- if (hidraw->open) {
- hid_hw_close(hid);
- wake_up_interruptible(&hidraw->wait);
- } else {
- kfree(hidraw);
- }
- mutex_unlock(&minors_lock);
+ drop_ref(hidraw, 1);
}
EXPORT_SYMBOL_GPL(hidraw_disconnect);
@@ -559,21 +522,28 @@ int __init hidraw_init(void)
if (result < 0) {
pr_warn("can't get major number\n");
- result = 0;
goto out;
}
hidraw_class = class_create(THIS_MODULE, "hidraw");
if (IS_ERR(hidraw_class)) {
result = PTR_ERR(hidraw_class);
- unregister_chrdev(hidraw_major, "hidraw");
- goto out;
+ goto error_cdev;
}
cdev_init(&hidraw_cdev, &hidraw_ops);
- cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
+ result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
+ if (result < 0)
+ goto error_class;
+
out:
return result;
+
+error_class:
+ class_destroy(hidraw_class);
+error_cdev:
+ unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
+ goto out;
}
void hidraw_exit(void)
@@ -585,3 +555,23 @@ void hidraw_exit(void)
unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
}
+
+static void drop_ref(struct hidraw *hidraw, int exists_bit)
+{
+ mutex_lock(&minors_lock);
+ if (exists_bit) {
+ hid_hw_close(hidraw->hid);
+ hidraw->exist = 0;
+ if (hidraw->open)
+ wake_up_interruptible(&hidraw->wait);
+ } else {
+ --hidraw->open;
+ }
+
+ if (!hidraw->open && !hidraw->exist) {
+ device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
+ hidraw_table[hidraw->minor] = NULL;
+ kfree(hidraw);
+ }
+ mutex_unlock(&minors_lock);
+}
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index dedd8e4..8e0c4bf94 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -1415,20 +1415,20 @@ static int hid_post_reset(struct usb_interface *intf)
* configuration descriptors passed, we already know that
* the size of the HID report descriptor has not changed.
*/
- rdesc = kmalloc(hid->rsize, GFP_KERNEL);
+ rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL);
if (!rdesc) {
dbg_hid("couldn't allocate rdesc memory (post_reset)\n");
return 1;
}
status = hid_get_class_descriptor(dev,
interface->desc.bInterfaceNumber,
- HID_DT_REPORT, rdesc, hid->rsize);
+ HID_DT_REPORT, rdesc, hid->dev_rsize);
if (status < 0) {
dbg_hid("reading report descriptor failed (post_reset)\n");
kfree(rdesc);
return 1;
}
- status = memcmp(rdesc, hid->rdesc, hid->rsize);
+ status = memcmp(rdesc, hid->dev_rdesc, hid->dev_rsize);
kfree(rdesc);
if (status != 0) {
dbg_hid("report descriptor changed\n");
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 991e85c..11c7932 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -70,12 +70,13 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index 86f8885..3648f8f 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/hyperv.h>
+#include <linux/version.h>
#include <asm/hyperv.h>
#include "hyperv_vmbus.h"
@@ -38,28 +39,6 @@ struct hv_context hv_context = {
};
/*
- * query_hypervisor_presence
- * - Query the cpuid for presence of windows hypervisor
- */
-static int query_hypervisor_presence(void)
-{
- unsigned int eax;
- unsigned int ebx;
- unsigned int ecx;
- unsigned int edx;
- unsigned int op;
-
- eax = 0;
- ebx = 0;
- ecx = 0;
- edx = 0;
- op = HVCPUID_VERSION_FEATURES;
- cpuid(op, &eax, &ebx, &ecx, &edx);
-
- return ecx & HV_PRESENT_BIT;
-}
-
-/*
* query_hypervisor_info - Get version info of the windows hypervisor
*/
static int query_hypervisor_info(void)
@@ -159,14 +138,13 @@ int hv_init(void)
memset(hv_context.synic_message_page, 0,
sizeof(void *) * NR_CPUS);
- if (!query_hypervisor_presence())
- goto cleanup;
-
max_leaf = query_hypervisor_info();
- /* Write our OS info */
- wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
- hv_context.guestid = HV_LINUX_GUEST_ID;
+ /*
+ * Write our OS ID.
+ */
+ hv_context.guestid = generate_guest_id(0, LINUX_VERSION_CODE, 0);
+ wrmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid);
/* See if the hypercall page is already set */
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index 0012eed..ed50e9e 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -48,13 +48,24 @@ static struct {
void *kvp_context; /* for the channel callback */
} kvp_transaction;
+/*
+ * Before we can accept KVP messages from the host, we need
+ * to handshake with the user level daemon. This state tracks
+ * if we are in the handshake phase.
+ */
+static bool in_hand_shake = true;
+
+/*
+ * This state maintains the version number registered by the daemon.
+ */
+static int dm_reg_value;
+
static void kvp_send_key(struct work_struct *dummy);
-#define TIMEOUT_FIRED 1
-static void kvp_respond_to_host(char *key, char *value, int error);
+static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error);
static void kvp_work_func(struct work_struct *dummy);
-static void kvp_register(void);
+static void kvp_register(int);
static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func);
static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
@@ -68,7 +79,7 @@ static u8 *recv_buffer;
*/
static void
-kvp_register(void)
+kvp_register(int reg_value)
{
struct cn_msg *msg;
@@ -83,7 +94,7 @@ kvp_register(void)
msg->id.idx = CN_KVP_IDX;
msg->id.val = CN_KVP_VAL;
- kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER;
+ kvp_msg->kvp_hdr.operation = reg_value;
strcpy(version, HV_DRV_VERSION);
msg->len = sizeof(struct hv_kvp_msg);
cn_netlink_send(msg, 0, GFP_ATOMIC);
@@ -97,9 +108,43 @@ kvp_work_func(struct work_struct *dummy)
* If the timer fires, the user-mode component has not responded;
* process the pending transaction.
*/
- kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED);
+ kvp_respond_to_host(NULL, HV_E_FAIL);
+}
+
+static int kvp_handle_handshake(struct hv_kvp_msg *msg)
+{
+ int ret = 1;
+
+ switch (msg->kvp_hdr.operation) {
+ case KVP_OP_REGISTER:
+ dm_reg_value = KVP_OP_REGISTER;
+ pr_info("KVP: IP injection functionality not available\n");
+ pr_info("KVP: Upgrade the KVP daemon\n");
+ break;
+ case KVP_OP_REGISTER1:
+ dm_reg_value = KVP_OP_REGISTER1;
+ break;
+ default:
+ pr_info("KVP: incompatible daemon\n");
+ pr_info("KVP: KVP version: %d, Daemon version: %d\n",
+ KVP_OP_REGISTER1, msg->kvp_hdr.operation);
+ ret = 0;
+ }
+
+ if (ret) {
+ /*
+ * We have a compatible daemon; complete the handshake.
+ */
+ pr_info("KVP: user-mode registering done.\n");
+ kvp_register(dm_reg_value);
+ kvp_transaction.active = false;
+ if (kvp_transaction.kvp_context)
+ hv_kvp_onchannelcallback(kvp_transaction.kvp_context);
+ }
+ return ret;
}
+
/*
* Callback when data is received from user mode.
*/
@@ -109,29 +154,165 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
{
struct hv_kvp_msg *message;
struct hv_kvp_msg_enumerate *data;
+ int error = 0;
message = (struct hv_kvp_msg *)msg->data;
- switch (message->kvp_hdr.operation) {
+
+ /*
+ * If we are negotiating the version information
+ * with the daemon; handle that first.
+ */
+
+ if (in_hand_shake) {
+ if (kvp_handle_handshake(message))
+ in_hand_shake = false;
+ return;
+ }
+
+ /*
+ * Based on the version of the daemon, we propagate errors from the
+ * daemon differently.
+ */
+
+ data = &message->body.kvp_enum_data;
+
+ switch (dm_reg_value) {
case KVP_OP_REGISTER:
- pr_info("KVP: user-mode registering done.\n");
- kvp_register();
- kvp_transaction.active = false;
- hv_kvp_onchannelcallback(kvp_transaction.kvp_context);
+ /*
+ * Null string is used to pass back error condition.
+ */
+ if (data->data.key[0] == 0)
+ error = HV_S_CONT;
break;
- default:
- data = &message->body.kvp_enum_data;
+ case KVP_OP_REGISTER1:
/*
- * Complete the transaction by forwarding the key value
- * to the host. But first, cancel the timeout.
+ * We use the message header information from
+ * the user level daemon to transmit errors.
*/
- if (cancel_delayed_work_sync(&kvp_work))
- kvp_respond_to_host(data->data.key,
- data->data.value,
- !strlen(data->data.key));
+ error = message->error;
+ break;
+ }
+
+ /*
+ * Complete the transaction by forwarding the key value
+ * to the host. But first, cancel the timeout.
+ */
+ if (cancel_delayed_work_sync(&kvp_work))
+ kvp_respond_to_host(message, error);
+}
+
+
+static int process_ob_ipinfo(void *in_msg, void *out_msg, int op)
+{
+ struct hv_kvp_msg *in = in_msg;
+ struct hv_kvp_ip_msg *out = out_msg;
+ int len;
+
+ switch (op) {
+ case KVP_OP_GET_IP_INFO:
+ /*
+ * Transform all parameters into utf16 encoding.
+ */
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.ip_addr,
+ strlen((char *)in->body.kvp_ip_val.ip_addr),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.ip_addr,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.sub_net,
+ strlen((char *)in->body.kvp_ip_val.sub_net),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.sub_net,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.gate_way,
+ strlen((char *)in->body.kvp_ip_val.gate_way),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.gate_way,
+ MAX_GATEWAY_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.dns_addr,
+ strlen((char *)in->body.kvp_ip_val.dns_addr),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.dns_addr,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.adapter_id,
+ strlen((char *)in->body.kvp_ip_val.adapter_id),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.adapter_id,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ out->kvp_ip_val.dhcp_enabled =
+ in->body.kvp_ip_val.dhcp_enabled;
+ out->kvp_ip_val.addr_family =
+ in->body.kvp_ip_val.addr_family;
+ }
+
+ return 0;
+}
+
+static void process_ib_ipinfo(void *in_msg, void *out_msg, int op)
+{
+ struct hv_kvp_ip_msg *in = in_msg;
+ struct hv_kvp_msg *out = out_msg;
+
+ switch (op) {
+ case KVP_OP_SET_IP_INFO:
+ /*
+ * Transform all parameters into utf8 encoding.
+ */
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.ip_addr,
+ MAX_IP_ADDR_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.ip_addr,
+ MAX_IP_ADDR_SIZE);
+
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.sub_net,
+ MAX_IP_ADDR_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.sub_net,
+ MAX_IP_ADDR_SIZE);
+
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.gate_way,
+ MAX_GATEWAY_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.gate_way,
+ MAX_GATEWAY_SIZE);
+
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.dns_addr,
+ MAX_IP_ADDR_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.dns_addr,
+ MAX_IP_ADDR_SIZE);
+
+ out->body.kvp_ip_val.dhcp_enabled = in->kvp_ip_val.dhcp_enabled;
+
+ default:
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id,
+ MAX_ADAPTER_ID_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.adapter_id,
+ MAX_ADAPTER_ID_SIZE);
+
+ out->body.kvp_ip_val.addr_family = in->kvp_ip_val.addr_family;
}
}
+
+
+
static void
kvp_send_key(struct work_struct *dummy)
{
@@ -167,6 +348,12 @@ kvp_send_key(struct work_struct *dummy)
*/
switch (message->kvp_hdr.operation) {
+ case KVP_OP_SET_IP_INFO:
+ process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO);
+ break;
+ case KVP_OP_GET_IP_INFO:
+ process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO);
+ break;
case KVP_OP_SET:
switch (in_msg->body.kvp_set.data.value_type) {
case REG_SZ:
@@ -243,17 +430,19 @@ kvp_send_key(struct work_struct *dummy)
*/
static void
-kvp_respond_to_host(char *key, char *value, int error)
+kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
{
struct hv_kvp_msg *kvp_msg;
struct hv_kvp_exchg_msg_value *kvp_data;
char *key_name;
+ char *value;
struct icmsg_hdr *icmsghdrp;
int keylen = 0;
int valuelen = 0;
u32 buf_len;
struct vmbus_channel *channel;
u64 req_id;
+ int ret;
/*
* If a transaction is not active; log and return.
@@ -287,6 +476,7 @@ kvp_respond_to_host(char *key, char *value, int error)
*/
return;
+ icmsghdrp->status = error;
/*
* If the error parameter is set, terminate the host's enumeration
@@ -294,20 +484,27 @@ kvp_respond_to_host(char *key, char *value, int error)
*/
if (error) {
/*
- * Something failed or the we have timedout;
- * terminate the current host-side iteration.
+ * Something failed or we have timedout;
+ * terminate the current host-side iteration.
*/
- icmsghdrp->status = HV_S_CONT;
goto response_done;
}
- icmsghdrp->status = HV_S_OK;
-
kvp_msg = (struct hv_kvp_msg *)
&recv_buffer[sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
switch (kvp_transaction.kvp_msg->kvp_hdr.operation) {
+ case KVP_OP_GET_IP_INFO:
+ ret = process_ob_ipinfo(msg_to_host,
+ (struct hv_kvp_ip_msg *)kvp_msg,
+ KVP_OP_GET_IP_INFO);
+ if (ret < 0)
+ icmsghdrp->status = HV_E_FAIL;
+
+ goto response_done;
+ case KVP_OP_SET_IP_INFO:
+ goto response_done;
case KVP_OP_GET:
kvp_data = &kvp_msg->body.kvp_get.data;
goto copy_value;
@@ -321,7 +518,7 @@ kvp_respond_to_host(char *key, char *value, int error)
}
kvp_data = &kvp_msg->body.kvp_enum_data.data;
- key_name = key;
+ key_name = msg_to_host->body.kvp_enum_data.data.key;
/*
* The windows host expects the key/value pair to be encoded
@@ -335,6 +532,7 @@ kvp_respond_to_host(char *key, char *value, int error)
kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */
copy_value:
+ value = msg_to_host->body.kvp_enum_data.data.value;
valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
(wchar_t *) kvp_data->value,
(HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2);
@@ -387,7 +585,8 @@ void hv_kvp_onchannelcallback(void *context)
return;
}
- vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid);
+ vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
+ &requestid);
if (recvlen > 0) {
icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index d3ac6a4..a0667de 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -263,7 +263,7 @@ static int util_probe(struct hv_device *dev,
(struct hv_util_service *)dev_id->driver_data;
int ret;
- srv->recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ srv->recv_buffer = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
if (!srv->recv_buffer)
return -ENOMEM;
if (srv->util_init) {
@@ -274,7 +274,7 @@ static int util_probe(struct hv_device *dev,
}
}
- ret = vmbus_open(dev->channel, 2 * PAGE_SIZE, 2 * PAGE_SIZE, NULL, 0,
+ ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
srv->util_cb, dev->channel);
if (ret)
goto error;
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 0614ff3..d8d1fad 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -410,10 +410,49 @@ enum {
#define HV_PRESENT_BIT 0x80000000
-#define HV_LINUX_GUEST_ID_LO 0x00000000
-#define HV_LINUX_GUEST_ID_HI 2976579765
-#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \
- HV_LINUX_GUEST_ID_LO)
+/*
+ * The guest OS needs to register the guest ID with the hypervisor.
+ * The guest ID is a 64 bit entity and the structure of this ID is
+ * specified in the Hyper-V specification:
+ *
+ * http://msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx
+ *
+ * While the current guideline does not specify how Linux guest ID(s)
+ * need to be generated, our plan is to publish the guidelines for
+ * Linux and other guest operating systems that currently are hosted
+ * on Hyper-V. The implementation here conforms to this yet
+ * unpublished guidelines.
+ *
+ *
+ * Bit(s)
+ * 63 - Indicates if the OS is Open Source or not; 1 is Open Source
+ * 62:56 - Os Type; Linux is 0x100
+ * 55:48 - Distro specific identification
+ * 47:16 - Linux kernel version number
+ * 15:0 - Distro specific identification
+ *
+ *
+ */
+
+#define HV_LINUX_VENDOR_ID 0x8100
+
+/*
+ * Generate the guest ID based on the guideline described above.
+ */
+
+static inline __u64 generate_guest_id(__u8 d_info1, __u32 kernel_version,
+ __u16 d_info2)
+{
+ __u64 guest_id = 0;
+
+ guest_id = (((__u64)HV_LINUX_VENDOR_ID) << 48);
+ guest_id |= (((__u64)(d_info1)) << 48);
+ guest_id |= (((__u64)(kernel_version)) << 16);
+ guest_id |= ((__u64)(d_info2));
+
+ return guest_id;
+}
+
#define HV_CPU_POWER_MANAGEMENT (1 << 0)
#define HV_RECOMMENDATIONS_MAX 4
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 4748086..8e1a9ec 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -34,6 +34,7 @@
#include <linux/completion.h>
#include <linux/hyperv.h>
#include <asm/hyperv.h>
+#include <asm/hypervisor.h>
#include "hyperv_vmbus.h"
@@ -146,43 +147,9 @@ static ssize_t vmbus_show_device_attr(struct device *dev,
get_channel_info(hv_dev, device_info);
if (!strcmp(dev_attr->attr.name, "class_id")) {
- ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
- "%02x%02x%02x%02x%02x%02x%02x%02x}\n",
- device_info->chn_type.b[3],
- device_info->chn_type.b[2],
- device_info->chn_type.b[1],
- device_info->chn_type.b[0],
- device_info->chn_type.b[5],
- device_info->chn_type.b[4],
- device_info->chn_type.b[7],
- device_info->chn_type.b[6],
- device_info->chn_type.b[8],
- device_info->chn_type.b[9],
- device_info->chn_type.b[10],
- device_info->chn_type.b[11],
- device_info->chn_type.b[12],
- device_info->chn_type.b[13],
- device_info->chn_type.b[14],
- device_info->chn_type.b[15]);
+ ret = sprintf(buf, "{%pUl}\n", device_info->chn_type.b);
} else if (!strcmp(dev_attr->attr.name, "device_id")) {
- ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
- "%02x%02x%02x%02x%02x%02x%02x%02x}\n",
- device_info->chn_instance.b[3],
- device_info->chn_instance.b[2],
- device_info->chn_instance.b[1],
- device_info->chn_instance.b[0],
- device_info->chn_instance.b[5],
- device_info->chn_instance.b[4],
- device_info->chn_instance.b[7],
- device_info->chn_instance.b[6],
- device_info->chn_instance.b[8],
- device_info->chn_instance.b[9],
- device_info->chn_instance.b[10],
- device_info->chn_instance.b[11],
- device_info->chn_instance.b[12],
- device_info->chn_instance.b[13],
- device_info->chn_instance.b[14],
- device_info->chn_instance.b[15]);
+ ret = sprintf(buf, "{%pUl}\n", device_info->chn_instance.b);
} else if (!strcmp(dev_attr->attr.name, "modalias")) {
print_alias_name(hv_dev, alias_name);
ret = sprintf(buf, "vmbus:%s\n", alias_name);
@@ -757,6 +724,9 @@ static int __init hv_acpi_init(void)
{
int ret, t;
+ if (x86_hyper != &x86_hyper_ms_hyperv)
+ return -ENODEV;
+
init_completion(&probe_event);
/*
diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c
index cfec802..f915eb1 100644
--- a/drivers/hwmon/ad7314.c
+++ b/drivers/hwmon/ad7314.c
@@ -87,10 +87,18 @@ static ssize_t ad7314_show_temperature(struct device *dev,
}
}
+static ssize_t ad7314_show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, ad7314_show_name, NULL);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
ad7314_show_temperature, NULL, 0);
static struct attribute *ad7314_attributes[] = {
+ &dev_attr_name.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c
index e65c6e4..7bf4ce3 100644
--- a/drivers/hwmon/ads7871.c
+++ b/drivers/hwmon/ads7871.c
@@ -139,6 +139,12 @@ static ssize_t show_voltage(struct device *dev,
}
}
+static ssize_t ads7871_show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_voltage, NULL, 0);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 1);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 2);
@@ -148,6 +154,8 @@ static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 5);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 6);
static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 7);
+static DEVICE_ATTR(name, S_IRUGO, ads7871_show_name, NULL);
+
static struct attribute *ads7871_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
@@ -157,6 +165,7 @@ static struct attribute *ads7871_attributes[] = {
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in7_input.dev_attr.attr,
+ &dev_attr_name.attr,
NULL
};
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 2827088..8f3f6f2 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -53,10 +53,10 @@
#define APPLESMC_MAX_DATA_LENGTH 32
-/* wait up to 32 ms for a status change. */
+/* wait up to 128 ms for a status change. */
#define APPLESMC_MIN_WAIT 0x0010
#define APPLESMC_RETRY_WAIT 0x0100
-#define APPLESMC_MAX_WAIT 0x8000
+#define APPLESMC_MAX_WAIT 0x20000
#define APPLESMC_READ_CMD 0x10
#define APPLESMC_WRITE_CMD 0x11
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 0fa356f..984a3f1 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -815,17 +815,20 @@ static int __init coretemp_init(void)
if (err)
goto exit;
+ get_online_cpus();
for_each_online_cpu(i)
get_core_online(i);
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
+ put_online_cpus();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
register_hotcpu_notifier(&coretemp_cpu_notifier);
+ put_online_cpus();
return 0;
#ifndef CONFIG_HOTPLUG_CPU
@@ -840,6 +843,7 @@ static void __exit coretemp_exit(void)
{
struct pdev_entry *p, *n;
+ get_online_cpus();
unregister_hotcpu_notifier(&coretemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
@@ -848,6 +852,7 @@ static void __exit coretemp_exit(void)
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
+ put_online_cpus();
platform_driver_unregister(&coretemp_driver);
}
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
index 2764b78..af69073 100644
--- a/drivers/hwmon/fam15h_power.c
+++ b/drivers/hwmon/fam15h_power.c
@@ -129,12 +129,12 @@ static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
* counter saturations resulting in bogus power readings.
* We correct this value ourselves to cope with older BIOSes.
*/
-static DEFINE_PCI_DEVICE_TABLE(affected_device) = {
+static const struct pci_device_id affected_device[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
{ 0 }
};
-static void __devinit tweak_runavg_range(struct pci_dev *pdev)
+static void tweak_runavg_range(struct pci_dev *pdev)
{
u32 val;
@@ -158,6 +158,16 @@ static void __devinit tweak_runavg_range(struct pci_dev *pdev)
REG_TDP_RUNNING_AVERAGE, val);
}
+#ifdef CONFIG_PM
+static int fam15h_power_resume(struct pci_dev *pdev)
+{
+ tweak_runavg_range(pdev);
+ return 0;
+}
+#else
+#define fam15h_power_resume NULL
+#endif
+
static void __devinit fam15h_power_init_data(struct pci_dev *f4,
struct fam15h_power_data *data)
{
@@ -256,6 +266,7 @@ static struct pci_driver fam15h_power_driver = {
.id_table = fam15h_power_id_table,
.probe = fam15h_power_probe,
.remove = __devexit_p(fam15h_power_remove),
+ .resume = fam15h_power_resume,
};
module_pci_driver(fam15h_power_driver);
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index ee4ebc1..2e56c6c 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -328,6 +328,7 @@ static int __init via_cputemp_init(void)
if (err)
goto exit;
+ get_online_cpus();
for_each_online_cpu(i) {
struct cpuinfo_x86 *c = &cpu_data(i);
@@ -347,12 +348,14 @@ static int __init via_cputemp_init(void)
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
+ put_online_cpus();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
register_hotcpu_notifier(&via_cputemp_cpu_notifier);
+ put_online_cpus();
return 0;
#ifndef CONFIG_HOTPLUG_CPU
@@ -367,6 +370,7 @@ static void __exit via_cputemp_exit(void)
{
struct pdev_entry *p, *n;
+ get_online_cpus();
unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
@@ -375,6 +379,7 @@ static void __exit via_cputemp_exit(void)
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
+ put_online_cpus();
platform_driver_unregister(&via_cputemp_driver);
}
diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c
index 1201a15..db713c0 100644
--- a/drivers/hwspinlock/hwspinlock_core.c
+++ b/drivers/hwspinlock/hwspinlock_core.c
@@ -552,7 +552,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);
*/
int hwspin_lock_free(struct hwspinlock *hwlock)
{
- struct device *dev = hwlock->bank->dev;
+ struct device *dev;
struct hwspinlock *tmp;
int ret;
@@ -561,6 +561,7 @@ int hwspin_lock_free(struct hwspinlock *hwlock)
return -EINVAL;
}
+ dev = hwlock->bank->dev;
mutex_lock(&hwspinlock_tree_lock);
/* make sure the hwspinlock is used */
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 51f4206..6cfd4d8 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -1361,11 +1361,11 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
struct tid_info *t = dev->rdev.lldi.tids;
ep = lookup_tid(t, tid);
- PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
if (!ep) {
printk(KERN_WARNING MOD "Abort rpl to freed endpoint\n");
return 0;
}
+ PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
mutex_lock(&ep->com.mutex);
switch (ep->com.state) {
case ABORTING:
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c
index 5358900..8615d7c 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.c
+++ b/drivers/infiniband/hw/ehca/ehca_irq.c
@@ -42,6 +42,7 @@
*/
#include <linux/slab.h>
+#include <linux/smpboot.h>
#include "ehca_classes.h"
#include "ehca_irq.h"
@@ -652,7 +653,7 @@ void ehca_tasklet_eq(unsigned long data)
ehca_process_eq((struct ehca_shca*)data, 1);
}
-static inline int find_next_online_cpu(struct ehca_comp_pool *pool)
+static int find_next_online_cpu(struct ehca_comp_pool *pool)
{
int cpu;
unsigned long flags;
@@ -662,17 +663,20 @@ static inline int find_next_online_cpu(struct ehca_comp_pool *pool)
ehca_dmp(cpu_online_mask, cpumask_size(), "");
spin_lock_irqsave(&pool->last_cpu_lock, flags);
- cpu = cpumask_next(pool->last_cpu, cpu_online_mask);
- if (cpu >= nr_cpu_ids)
- cpu = cpumask_first(cpu_online_mask);
- pool->last_cpu = cpu;
+ do {
+ cpu = cpumask_next(pool->last_cpu, cpu_online_mask);
+ if (cpu >= nr_cpu_ids)
+ cpu = cpumask_first(cpu_online_mask);
+ pool->last_cpu = cpu;
+ } while (!per_cpu_ptr(pool->cpu_comp_tasks, cpu)->active);
spin_unlock_irqrestore(&pool->last_cpu_lock, flags);
return cpu;
}
static void __queue_comp_task(struct ehca_cq *__cq,
- struct ehca_cpu_comp_task *cct)
+ struct ehca_cpu_comp_task *cct,
+ struct task_struct *thread)
{
unsigned long flags;
@@ -683,7 +687,7 @@ static void __queue_comp_task(struct ehca_cq *__cq,
__cq->nr_callbacks++;
list_add_tail(&__cq->entry, &cct->cq_list);
cct->cq_jobs++;
- wake_up(&cct->wait_queue);
+ wake_up_process(thread);
} else
__cq->nr_callbacks++;
@@ -695,6 +699,7 @@ static void queue_comp_task(struct ehca_cq *__cq)
{
int cpu_id;
struct ehca_cpu_comp_task *cct;
+ struct task_struct *thread;
int cq_jobs;
unsigned long flags;
@@ -702,7 +707,8 @@ static void queue_comp_task(struct ehca_cq *__cq)
BUG_ON(!cpu_online(cpu_id));
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id);
- BUG_ON(!cct);
+ thread = *per_cpu_ptr(pool->cpu_comp_threads, cpu_id);
+ BUG_ON(!cct || !thread);
spin_lock_irqsave(&cct->task_lock, flags);
cq_jobs = cct->cq_jobs;
@@ -710,28 +716,25 @@ static void queue_comp_task(struct ehca_cq *__cq)
if (cq_jobs > 0) {
cpu_id = find_next_online_cpu(pool);
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id);
- BUG_ON(!cct);
+ thread = *per_cpu_ptr(pool->cpu_comp_threads, cpu_id);
+ BUG_ON(!cct || !thread);
}
-
- __queue_comp_task(__cq, cct);
+ __queue_comp_task(__cq, cct, thread);
}
static void run_comp_task(struct ehca_cpu_comp_task *cct)
{
struct ehca_cq *cq;
- unsigned long flags;
-
- spin_lock_irqsave(&cct->task_lock, flags);
while (!list_empty(&cct->cq_list)) {
cq = list_entry(cct->cq_list.next, struct ehca_cq, entry);
- spin_unlock_irqrestore(&cct->task_lock, flags);
+ spin_unlock_irq(&cct->task_lock);
comp_event_callback(cq);
if (atomic_dec_and_test(&cq->nr_events))
wake_up(&cq->wait_completion);
- spin_lock_irqsave(&cct->task_lock, flags);
+ spin_lock_irq(&cct->task_lock);
spin_lock(&cq->task_lock);
cq->nr_callbacks--;
if (!cq->nr_callbacks) {
@@ -740,159 +743,76 @@ static void run_comp_task(struct ehca_cpu_comp_task *cct)
}
spin_unlock(&cq->task_lock);
}
-
- spin_unlock_irqrestore(&cct->task_lock, flags);
}
-static int comp_task(void *__cct)
+static void comp_task_park(unsigned int cpu)
{
- struct ehca_cpu_comp_task *cct = __cct;
- int cql_empty;
- DECLARE_WAITQUEUE(wait, current);
-
- set_current_state(TASK_INTERRUPTIBLE);
- while (!kthread_should_stop()) {
- add_wait_queue(&cct->wait_queue, &wait);
-
- spin_lock_irq(&cct->task_lock);
- cql_empty = list_empty(&cct->cq_list);
- spin_unlock_irq(&cct->task_lock);
- if (cql_empty)
- schedule();
- else
- __set_current_state(TASK_RUNNING);
-
- remove_wait_queue(&cct->wait_queue, &wait);
+ struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
+ struct ehca_cpu_comp_task *target;
+ struct task_struct *thread;
+ struct ehca_cq *cq, *tmp;
+ LIST_HEAD(list);
- spin_lock_irq(&cct->task_lock);
- cql_empty = list_empty(&cct->cq_list);
- spin_unlock_irq(&cct->task_lock);
- if (!cql_empty)
- run_comp_task(__cct);
+ spin_lock_irq(&cct->task_lock);
+ cct->cq_jobs = 0;
+ cct->active = 0;
+ list_splice_init(&cct->cq_list, &list);
+ spin_unlock_irq(&cct->task_lock);
- set_current_state(TASK_INTERRUPTIBLE);
+ cpu = find_next_online_cpu(pool);
+ target = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
+ thread = *per_cpu_ptr(pool->cpu_comp_threads, cpu);
+ spin_lock_irq(&target->task_lock);
+ list_for_each_entry_safe(cq, tmp, &list, entry) {
+ list_del(&cq->entry);
+ __queue_comp_task(cq, target, thread);
}
- __set_current_state(TASK_RUNNING);
-
- return 0;
-}
-
-static struct task_struct *create_comp_task(struct ehca_comp_pool *pool,
- int cpu)
-{
- struct ehca_cpu_comp_task *cct;
-
- cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
- spin_lock_init(&cct->task_lock);
- INIT_LIST_HEAD(&cct->cq_list);
- init_waitqueue_head(&cct->wait_queue);
- cct->task = kthread_create_on_node(comp_task, cct, cpu_to_node(cpu),
- "ehca_comp/%d", cpu);
-
- return cct->task;
+ spin_unlock_irq(&target->task_lock);
}
-static void destroy_comp_task(struct ehca_comp_pool *pool,
- int cpu)
+static void comp_task_stop(unsigned int cpu, bool online)
{
- struct ehca_cpu_comp_task *cct;
- struct task_struct *task;
- unsigned long flags_cct;
-
- cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
-
- spin_lock_irqsave(&cct->task_lock, flags_cct);
+ struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
- task = cct->task;
- cct->task = NULL;
+ spin_lock_irq(&cct->task_lock);
cct->cq_jobs = 0;
-
- spin_unlock_irqrestore(&cct->task_lock, flags_cct);
-
- if (task)
- kthread_stop(task);
+ cct->active = 0;
+ WARN_ON(!list_empty(&cct->cq_list));
+ spin_unlock_irq(&cct->task_lock);
}
-static void __cpuinit take_over_work(struct ehca_comp_pool *pool, int cpu)
+static int comp_task_should_run(unsigned int cpu)
{
struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
- LIST_HEAD(list);
- struct ehca_cq *cq;
- unsigned long flags_cct;
-
- spin_lock_irqsave(&cct->task_lock, flags_cct);
-
- list_splice_init(&cct->cq_list, &list);
-
- while (!list_empty(&list)) {
- cq = list_entry(cct->cq_list.next, struct ehca_cq, entry);
-
- list_del(&cq->entry);
- __queue_comp_task(cq, this_cpu_ptr(pool->cpu_comp_tasks));
- }
-
- spin_unlock_irqrestore(&cct->task_lock, flags_cct);
+ return cct->cq_jobs;
}
-static int __cpuinit comp_pool_callback(struct notifier_block *nfb,
- unsigned long action,
- void *hcpu)
+static void comp_task(unsigned int cpu)
{
- unsigned int cpu = (unsigned long)hcpu;
- struct ehca_cpu_comp_task *cct;
+ struct ehca_cpu_comp_task *cct = this_cpu_ptr(pool->cpu_comp_tasks);
+ int cql_empty;
- switch (action) {
- case CPU_UP_PREPARE:
- case CPU_UP_PREPARE_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_PREPARE)", cpu);
- if (!create_comp_task(pool, cpu)) {
- ehca_gen_err("Can't create comp_task for cpu: %x", cpu);
- return notifier_from_errno(-ENOMEM);
- }
- break;
- case CPU_UP_CANCELED:
- case CPU_UP_CANCELED_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_CANCELED)", cpu);
- cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
- kthread_bind(cct->task, cpumask_any(cpu_online_mask));
- destroy_comp_task(pool, cpu);
- break;
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_ONLINE)", cpu);
- cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
- kthread_bind(cct->task, cpu);
- wake_up_process(cct->task);
- break;
- case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_DOWN_PREPARE)", cpu);
- break;
- case CPU_DOWN_FAILED:
- case CPU_DOWN_FAILED_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_DOWN_FAILED)", cpu);
- break;
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_DEAD)", cpu);
- destroy_comp_task(pool, cpu);
- take_over_work(pool, cpu);
- break;
+ spin_lock_irq(&cct->task_lock);
+ cql_empty = list_empty(&cct->cq_list);
+ if (!cql_empty) {
+ __set_current_state(TASK_RUNNING);
+ run_comp_task(cct);
}
-
- return NOTIFY_OK;
+ spin_unlock_irq(&cct->task_lock);
}
-static struct notifier_block comp_pool_callback_nb __cpuinitdata = {
- .notifier_call = comp_pool_callback,
- .priority = 0,
+static struct smp_hotplug_thread comp_pool_threads = {
+ .thread_should_run = comp_task_should_run,
+ .thread_fn = comp_task,
+ .thread_comm = "ehca_comp/%u",
+ .cleanup = comp_task_stop,
+ .park = comp_task_park,
};
int ehca_create_comp_pool(void)
{
- int cpu;
- struct task_struct *task;
+ int cpu, ret = -ENOMEM;
if (!ehca_scaling_code)
return 0;
@@ -905,38 +825,46 @@ int ehca_create_comp_pool(void)
pool->last_cpu = cpumask_any(cpu_online_mask);
pool->cpu_comp_tasks = alloc_percpu(struct ehca_cpu_comp_task);
- if (pool->cpu_comp_tasks == NULL) {
- kfree(pool);
- return -EINVAL;
- }
+ if (!pool->cpu_comp_tasks)
+ goto out_pool;
- for_each_online_cpu(cpu) {
- task = create_comp_task(pool, cpu);
- if (task) {
- kthread_bind(task, cpu);
- wake_up_process(task);
- }
+ pool->cpu_comp_threads = alloc_percpu(struct task_struct *);
+ if (!pool->cpu_comp_threads)
+ goto out_tasks;
+
+ for_each_present_cpu(cpu) {
+ struct ehca_cpu_comp_task *cct;
+
+ cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
+ spin_lock_init(&cct->task_lock);
+ INIT_LIST_HEAD(&cct->cq_list);
}
- register_hotcpu_notifier(&comp_pool_callback_nb);
+ comp_pool_threads.store = pool->cpu_comp_threads;
+ ret = smpboot_register_percpu_thread(&comp_pool_threads);
+ if (ret)
+ goto out_threads;
- printk(KERN_INFO "eHCA scaling code enabled\n");
+ pr_info("eHCA scaling code enabled\n");
+ return ret;
- return 0;
+out_threads:
+ free_percpu(pool->cpu_comp_threads);
+out_tasks:
+ free_percpu(pool->cpu_comp_tasks);
+out_pool:
+ kfree(pool);
+ return ret;
}
void ehca_destroy_comp_pool(void)
{
- int i;
-
if (!ehca_scaling_code)
return;
- unregister_hotcpu_notifier(&comp_pool_callback_nb);
-
- for_each_online_cpu(i)
- destroy_comp_task(pool, i);
+ smpboot_unregister_percpu_thread(&comp_pool_threads);
+ free_percpu(pool->cpu_comp_threads);
free_percpu(pool->cpu_comp_tasks);
kfree(pool);
}
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.h b/drivers/infiniband/hw/ehca/ehca_irq.h
index 3346cb0..5370199 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.h
+++ b/drivers/infiniband/hw/ehca/ehca_irq.h
@@ -58,15 +58,15 @@ void ehca_tasklet_eq(unsigned long data);
void ehca_process_eq(struct ehca_shca *shca, int is_irq);
struct ehca_cpu_comp_task {
- wait_queue_head_t wait_queue;
struct list_head cq_list;
- struct task_struct *task;
spinlock_t task_lock;
int cq_jobs;
+ int active;
};
struct ehca_comp_pool {
- struct ehca_cpu_comp_task *cpu_comp_tasks;
+ struct ehca_cpu_comp_task __percpu *cpu_comp_tasks;
+ struct task_struct * __percpu *cpu_comp_threads;
int last_cpu;
spinlock_t last_cpu_lock;
};
diff --git a/drivers/infiniband/hw/mthca/mthca_reset.c b/drivers/infiniband/hw/mthca/mthca_reset.c
index 4fa3534..74c6a94 100644
--- a/drivers/infiniband/hw/mthca/mthca_reset.c
+++ b/drivers/infiniband/hw/mthca/mthca_reset.c
@@ -241,16 +241,16 @@ good:
if (hca_pcie_cap) {
devctl = hca_header[(hca_pcie_cap + PCI_EXP_DEVCTL) / 4];
- if (pci_write_config_word(mdev->pdev, hca_pcie_cap + PCI_EXP_DEVCTL,
- devctl)) {
+ if (pcie_capability_write_word(mdev->pdev, PCI_EXP_DEVCTL,
+ devctl)) {
err = -ENODEV;
mthca_err(mdev, "Couldn't restore HCA PCI Express "
"Device Control register, aborting.\n");
goto out;
}
linkctl = hca_header[(hca_pcie_cap + PCI_EXP_LNKCTL) / 4];
- if (pci_write_config_word(mdev->pdev, hca_pcie_cap + PCI_EXP_LNKCTL,
- linkctl)) {
+ if (pcie_capability_write_word(mdev->pdev, PCI_EXP_LNKCTL,
+ linkctl)) {
err = -ENODEV;
mthca_err(mdev, "Couldn't restore HCA PCI Express "
"Link control register, aborting.\n");
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
index cb5b7f7..b29a424 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
@@ -2219,7 +2219,6 @@ static bool ocrdma_poll_success_scqe(struct ocrdma_qp *qp,
u32 wqe_idx;
if (!qp->wqe_wr_id_tbl[tail].signaled) {
- expand = true; /* CQE cannot be consumed yet */
*polled = false; /* WC cannot be consumed yet */
} else {
ibwc->status = IB_WC_SUCCESS;
@@ -2227,10 +2226,11 @@ static bool ocrdma_poll_success_scqe(struct ocrdma_qp *qp,
ibwc->qp = &qp->ibqp;
ocrdma_update_wc(qp, ibwc, tail);
*polled = true;
- wqe_idx = le32_to_cpu(cqe->wq.wqeidx) & OCRDMA_CQE_WQEIDX_MASK;
- if (tail != wqe_idx)
- expand = true; /* Coalesced CQE can't be consumed yet */
}
+ wqe_idx = le32_to_cpu(cqe->wq.wqeidx) & OCRDMA_CQE_WQEIDX_MASK;
+ if (tail != wqe_idx)
+ expand = true; /* Coalesced CQE can't be consumed yet */
+
ocrdma_hwq_inc_tail(&qp->sq);
return expand;
}
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index 7b1b8669..4d11575 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -87,7 +87,7 @@ struct qlogic_ib_stats {
};
extern struct qlogic_ib_stats qib_stats;
-extern struct pci_error_handlers qib_pci_err_handler;
+extern const struct pci_error_handlers qib_pci_err_handler;
extern struct pci_driver qib_driver;
#define QIB_CHIP_SWVERSION QIB_CHIP_VERS_MAJ
diff --git a/drivers/infiniband/hw/qib/qib_mad.c b/drivers/infiniband/hw/qib/qib_mad.c
index 19f1e6c..ccb1191 100644
--- a/drivers/infiniband/hw/qib/qib_mad.c
+++ b/drivers/infiniband/hw/qib/qib_mad.c
@@ -471,9 +471,10 @@ static int subn_get_portinfo(struct ib_smp *smp, struct ib_device *ibdev,
if (port_num != port) {
ibp = to_iport(ibdev, port_num);
ret = check_mkey(ibp, smp, 0);
- if (ret)
+ if (ret) {
ret = IB_MAD_RESULT_FAILURE;
goto bail;
+ }
}
}
diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c
index 062c301eb..c574ec7 100644
--- a/drivers/infiniband/hw/qib/qib_pcie.c
+++ b/drivers/infiniband/hw/qib/qib_pcie.c
@@ -273,10 +273,9 @@ int qib_pcie_params(struct qib_devdata *dd, u32 minw, u32 *nent,
struct qib_msix_entry *entry)
{
u16 linkstat, speed;
- int pos = 0, pose, ret = 1;
+ int pos = 0, ret = 1;
- pose = pci_pcie_cap(dd->pcidev);
- if (!pose) {
+ if (!pci_is_pcie(dd->pcidev)) {
qib_dev_err(dd, "Can't find PCI Express capability!\n");
/* set up something... */
dd->lbus_width = 1;
@@ -298,7 +297,7 @@ int qib_pcie_params(struct qib_devdata *dd, u32 minw, u32 *nent,
if (!pos)
qib_enable_intx(dd->pcidev);
- pci_read_config_word(dd->pcidev, pose + PCI_EXP_LNKSTA, &linkstat);
+ pcie_capability_read_word(dd->pcidev, PCI_EXP_LNKSTA, &linkstat);
/*
* speed is bits 0-3, linkwidth is bits 4-8
* no defines for them in headers
@@ -516,7 +515,6 @@ static int qib_tune_pcie_coalesce(struct qib_devdata *dd)
{
int r;
struct pci_dev *parent;
- int ppos;
u16 devid;
u32 mask, bits, val;
@@ -529,8 +527,7 @@ static int qib_tune_pcie_coalesce(struct qib_devdata *dd)
qib_devinfo(dd->pcidev, "Parent not root\n");
return 1;
}
- ppos = pci_pcie_cap(parent);
- if (!ppos)
+ if (!pci_is_pcie(parent))
return 1;
if (parent->vendor != 0x8086)
return 1;
@@ -587,7 +584,6 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd)
{
int ret = 1; /* Assume the worst */
struct pci_dev *parent;
- int ppos, epos;
u16 pcaps, pctl, ecaps, ectl;
int rc_sup, ep_sup;
int rc_cur, ep_cur;
@@ -598,19 +594,15 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd)
qib_devinfo(dd->pcidev, "Parent not root\n");
goto bail;
}
- ppos = pci_pcie_cap(parent);
- if (ppos) {
- pci_read_config_word(parent, ppos + PCI_EXP_DEVCAP, &pcaps);
- pci_read_config_word(parent, ppos + PCI_EXP_DEVCTL, &pctl);
- } else
+
+ if (!pci_is_pcie(parent) || !pci_is_pcie(dd->pcidev))
goto bail;
+ pcie_capability_read_word(parent, PCI_EXP_DEVCAP, &pcaps);
+ pcie_capability_read_word(parent, PCI_EXP_DEVCTL, &pctl);
/* Find out supported and configured values for endpoint (us) */
- epos = pci_pcie_cap(dd->pcidev);
- if (epos) {
- pci_read_config_word(dd->pcidev, epos + PCI_EXP_DEVCAP, &ecaps);
- pci_read_config_word(dd->pcidev, epos + PCI_EXP_DEVCTL, &ectl);
- } else
- goto bail;
+ pcie_capability_read_word(dd->pcidev, PCI_EXP_DEVCAP, &ecaps);
+ pcie_capability_read_word(dd->pcidev, PCI_EXP_DEVCTL, &ectl);
+
ret = 0;
/* Find max payload supported by root, endpoint */
rc_sup = fld2val(pcaps, PCI_EXP_DEVCAP_PAYLOAD);
@@ -629,14 +621,14 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd)
rc_cur = rc_sup;
pctl = (pctl & ~PCI_EXP_DEVCTL_PAYLOAD) |
val2fld(rc_cur, PCI_EXP_DEVCTL_PAYLOAD);
- pci_write_config_word(parent, ppos + PCI_EXP_DEVCTL, pctl);
+ pcie_capability_write_word(parent, PCI_EXP_DEVCTL, pctl);
}
/* If less than (allowed, supported), bump endpoint payload */
if (rc_sup > ep_cur) {
ep_cur = rc_sup;
ectl = (ectl & ~PCI_EXP_DEVCTL_PAYLOAD) |
val2fld(ep_cur, PCI_EXP_DEVCTL_PAYLOAD);
- pci_write_config_word(dd->pcidev, epos + PCI_EXP_DEVCTL, ectl);
+ pcie_capability_write_word(dd->pcidev, PCI_EXP_DEVCTL, ectl);
}
/*
@@ -654,13 +646,13 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd)
rc_cur = rc_sup;
pctl = (pctl & ~PCI_EXP_DEVCTL_READRQ) |
val2fld(rc_cur, PCI_EXP_DEVCTL_READRQ);
- pci_write_config_word(parent, ppos + PCI_EXP_DEVCTL, pctl);
+ pcie_capability_write_word(parent, PCI_EXP_DEVCTL, pctl);
}
if (rc_sup > ep_cur) {
ep_cur = rc_sup;
ectl = (ectl & ~PCI_EXP_DEVCTL_READRQ) |
val2fld(ep_cur, PCI_EXP_DEVCTL_READRQ);
- pci_write_config_word(dd->pcidev, epos + PCI_EXP_DEVCTL, ectl);
+ pcie_capability_write_word(dd->pcidev, PCI_EXP_DEVCTL, ectl);
}
bail:
return ret;
@@ -753,7 +745,7 @@ qib_pci_resume(struct pci_dev *pdev)
qib_init(dd, 1); /* same as re-init after reset */
}
-struct pci_error_handlers qib_pci_err_handler = {
+const struct pci_error_handlers qib_pci_err_handler = {
.error_detected = qib_pci_error_detected,
.mmio_enabled = qib_pci_mmio_enabled,
.link_reset = qib_pci_link_reset,
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index ca43901..0af216d 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -262,7 +262,10 @@ struct ipoib_ethtool_st {
u16 max_coalesced_frames;
};
+struct ipoib_neigh_table;
+
struct ipoib_neigh_hash {
+ struct ipoib_neigh_table *ntbl;
struct ipoib_neigh __rcu **buckets;
struct rcu_head rcu;
u32 mask;
@@ -271,9 +274,9 @@ struct ipoib_neigh_hash {
struct ipoib_neigh_table {
struct ipoib_neigh_hash __rcu *htbl;
- rwlock_t rwlock;
atomic_t entries;
struct completion flushed;
+ struct completion deleted;
};
/*
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 3e2085a..1e19b5a 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -546,15 +546,15 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
struct ipoib_neigh *neigh;
unsigned long flags;
+ spin_lock_irqsave(&priv->lock, flags);
neigh = ipoib_neigh_alloc(daddr, dev);
if (!neigh) {
+ spin_unlock_irqrestore(&priv->lock, flags);
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
return;
}
- spin_lock_irqsave(&priv->lock, flags);
-
path = __path_find(dev, daddr + 4);
if (!path) {
path = path_rec_create(dev, daddr + 4);
@@ -863,10 +863,10 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
return;
- write_lock_bh(&ntbl->rwlock);
+ spin_lock_irqsave(&priv->lock, flags);
htbl = rcu_dereference_protected(ntbl->htbl,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
if (!htbl)
goto out_unlock;
@@ -883,16 +883,14 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
struct ipoib_neigh __rcu **np = &htbl->buckets[i];
while ((neigh = rcu_dereference_protected(*np,
- lockdep_is_held(&ntbl->rwlock))) != NULL) {
+ lockdep_is_held(&priv->lock))) != NULL) {
/* was the neigh idle for two GC periods */
if (time_after(neigh_obsolete, neigh->alive)) {
rcu_assign_pointer(*np,
rcu_dereference_protected(neigh->hnext,
- lockdep_is_held(&ntbl->rwlock)));
+ lockdep_is_held(&priv->lock)));
/* remove from path/mc list */
- spin_lock_irqsave(&priv->lock, flags);
list_del(&neigh->list);
- spin_unlock_irqrestore(&priv->lock, flags);
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
} else {
np = &neigh->hnext;
@@ -902,7 +900,7 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
}
out_unlock:
- write_unlock_bh(&ntbl->rwlock);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipoib_reap_neigh(struct work_struct *work)
@@ -947,10 +945,8 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
struct ipoib_neigh *neigh;
u32 hash_val;
- write_lock_bh(&ntbl->rwlock);
-
htbl = rcu_dereference_protected(ntbl->htbl,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
if (!htbl) {
neigh = NULL;
goto out_unlock;
@@ -961,10 +957,10 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
*/
hash_val = ipoib_addr_hash(htbl, daddr);
for (neigh = rcu_dereference_protected(htbl->buckets[hash_val],
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
neigh != NULL;
neigh = rcu_dereference_protected(neigh->hnext,
- lockdep_is_held(&ntbl->rwlock))) {
+ lockdep_is_held(&priv->lock))) {
if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) {
/* found, take one ref on behalf of the caller */
if (!atomic_inc_not_zero(&neigh->refcnt)) {
@@ -987,12 +983,11 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
/* put in hash */
rcu_assign_pointer(neigh->hnext,
rcu_dereference_protected(htbl->buckets[hash_val],
- lockdep_is_held(&ntbl->rwlock)));
+ lockdep_is_held(&priv->lock)));
rcu_assign_pointer(htbl->buckets[hash_val], neigh);
atomic_inc(&ntbl->entries);
out_unlock:
- write_unlock_bh(&ntbl->rwlock);
return neigh;
}
@@ -1040,35 +1035,29 @@ void ipoib_neigh_free(struct ipoib_neigh *neigh)
struct ipoib_neigh *n;
u32 hash_val;
- write_lock_bh(&ntbl->rwlock);
-
htbl = rcu_dereference_protected(ntbl->htbl,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
if (!htbl)
- goto out_unlock;
+ return;
hash_val = ipoib_addr_hash(htbl, neigh->daddr);
np = &htbl->buckets[hash_val];
for (n = rcu_dereference_protected(*np,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
n != NULL;
n = rcu_dereference_protected(*np,
- lockdep_is_held(&ntbl->rwlock))) {
+ lockdep_is_held(&priv->lock))) {
if (n == neigh) {
/* found */
rcu_assign_pointer(*np,
rcu_dereference_protected(neigh->hnext,
- lockdep_is_held(&ntbl->rwlock)));
+ lockdep_is_held(&priv->lock)));
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
- goto out_unlock;
+ return;
} else {
np = &n->hnext;
}
}
-
-out_unlock:
- write_unlock_bh(&ntbl->rwlock);
-
}
static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
@@ -1080,7 +1069,6 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
ntbl->htbl = NULL;
- rwlock_init(&ntbl->rwlock);
htbl = kzalloc(sizeof(*htbl), GFP_KERNEL);
if (!htbl)
return -ENOMEM;
@@ -1095,6 +1083,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
htbl->mask = (size - 1);
htbl->buckets = buckets;
ntbl->htbl = htbl;
+ htbl->ntbl = ntbl;
atomic_set(&ntbl->entries, 0);
/* start garbage collection */
@@ -1111,9 +1100,11 @@ static void neigh_hash_free_rcu(struct rcu_head *head)
struct ipoib_neigh_hash,
rcu);
struct ipoib_neigh __rcu **buckets = htbl->buckets;
+ struct ipoib_neigh_table *ntbl = htbl->ntbl;
kfree(buckets);
kfree(htbl);
+ complete(&ntbl->deleted);
}
void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
@@ -1125,10 +1116,10 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
int i;
/* remove all neigh connected to a given path or mcast */
- write_lock_bh(&ntbl->rwlock);
+ spin_lock_irqsave(&priv->lock, flags);
htbl = rcu_dereference_protected(ntbl->htbl,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
if (!htbl)
goto out_unlock;
@@ -1138,16 +1129,14 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
struct ipoib_neigh __rcu **np = &htbl->buckets[i];
while ((neigh = rcu_dereference_protected(*np,
- lockdep_is_held(&ntbl->rwlock))) != NULL) {
+ lockdep_is_held(&priv->lock))) != NULL) {
/* delete neighs belong to this parent */
if (!memcmp(gid, neigh->daddr + 4, sizeof (union ib_gid))) {
rcu_assign_pointer(*np,
rcu_dereference_protected(neigh->hnext,
- lockdep_is_held(&ntbl->rwlock)));
+ lockdep_is_held(&priv->lock)));
/* remove from parent list */
- spin_lock_irqsave(&priv->lock, flags);
list_del(&neigh->list);
- spin_unlock_irqrestore(&priv->lock, flags);
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
} else {
np = &neigh->hnext;
@@ -1156,7 +1145,7 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
}
}
out_unlock:
- write_unlock_bh(&ntbl->rwlock);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
@@ -1164,37 +1153,44 @@ static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
struct ipoib_neigh_table *ntbl = &priv->ntbl;
struct ipoib_neigh_hash *htbl;
unsigned long flags;
- int i;
+ int i, wait_flushed = 0;
- write_lock_bh(&ntbl->rwlock);
+ init_completion(&priv->ntbl.flushed);
+
+ spin_lock_irqsave(&priv->lock, flags);
htbl = rcu_dereference_protected(ntbl->htbl,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
if (!htbl)
goto out_unlock;
+ wait_flushed = atomic_read(&priv->ntbl.entries);
+ if (!wait_flushed)
+ goto free_htbl;
+
for (i = 0; i < htbl->size; i++) {
struct ipoib_neigh *neigh;
struct ipoib_neigh __rcu **np = &htbl->buckets[i];
while ((neigh = rcu_dereference_protected(*np,
- lockdep_is_held(&ntbl->rwlock))) != NULL) {
+ lockdep_is_held(&priv->lock))) != NULL) {
rcu_assign_pointer(*np,
rcu_dereference_protected(neigh->hnext,
- lockdep_is_held(&ntbl->rwlock)));
+ lockdep_is_held(&priv->lock)));
/* remove from path/mc list */
- spin_lock_irqsave(&priv->lock, flags);
list_del(&neigh->list);
- spin_unlock_irqrestore(&priv->lock, flags);
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
}
}
+free_htbl:
rcu_assign_pointer(ntbl->htbl, NULL);
call_rcu(&htbl->rcu, neigh_hash_free_rcu);
out_unlock:
- write_unlock_bh(&ntbl->rwlock);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ if (wait_flushed)
+ wait_for_completion(&priv->ntbl.flushed);
}
static void ipoib_neigh_hash_uninit(struct net_device *dev)
@@ -1203,7 +1199,7 @@ static void ipoib_neigh_hash_uninit(struct net_device *dev)
int stopped;
ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n");
- init_completion(&priv->ntbl.flushed);
+ init_completion(&priv->ntbl.deleted);
set_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
/* Stop GC if called at init fail need to cancel work */
@@ -1211,10 +1207,9 @@ static void ipoib_neigh_hash_uninit(struct net_device *dev)
if (!stopped)
cancel_delayed_work(&priv->neigh_reap_task);
- if (atomic_read(&priv->ntbl.entries)) {
- ipoib_flush_neighs(priv);
- wait_for_completion(&priv->ntbl.flushed);
- }
+ ipoib_flush_neighs(priv);
+
+ wait_for_completion(&priv->ntbl.deleted);
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 13f4aa7..7536724 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -707,9 +707,7 @@ out:
neigh = ipoib_neigh_get(dev, daddr);
spin_lock_irqsave(&priv->lock, flags);
if (!neigh) {
- spin_unlock_irqrestore(&priv->lock, flags);
neigh = ipoib_neigh_alloc(daddr, dev);
- spin_lock_irqsave(&priv->lock, flags);
if (neigh) {
kref_get(&mcast->ah->ref);
neigh->ah = mcast->ah;
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 6c58bff..118d030 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -54,16 +54,9 @@ struct evdev_client {
static struct evdev *evdev_table[EVDEV_MINORS];
static DEFINE_MUTEX(evdev_table_mutex);
-static void evdev_pass_event(struct evdev_client *client,
- struct input_event *event,
- ktime_t mono, ktime_t real)
+static void __pass_event(struct evdev_client *client,
+ const struct input_event *event)
{
- event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
- mono : real);
-
- /* Interrupts are disabled, just acquire the lock. */
- spin_lock(&client->buffer_lock);
-
client->buffer[client->head++] = *event;
client->head &= client->bufsize - 1;
@@ -86,42 +79,74 @@ static void evdev_pass_event(struct evdev_client *client,
client->packet_head = client->head;
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
+}
+
+static void evdev_pass_values(struct evdev_client *client,
+ const struct input_value *vals, unsigned int count,
+ ktime_t mono, ktime_t real)
+{
+ struct evdev *evdev = client->evdev;
+ const struct input_value *v;
+ struct input_event event;
+ bool wakeup = false;
+
+ event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
+ mono : real);
+
+ /* Interrupts are disabled, just acquire the lock. */
+ spin_lock(&client->buffer_lock);
+
+ for (v = vals; v != vals + count; v++) {
+ event.type = v->type;
+ event.code = v->code;
+ event.value = v->value;
+ __pass_event(client, &event);
+ if (v->type == EV_SYN && v->code == SYN_REPORT)
+ wakeup = true;
+ }
spin_unlock(&client->buffer_lock);
+
+ if (wakeup)
+ wake_up_interruptible(&evdev->wait);
}
/*
- * Pass incoming event to all connected clients.
+ * Pass incoming events to all connected clients.
*/
-static void evdev_event(struct input_handle *handle,
- unsigned int type, unsigned int code, int value)
+static void evdev_events(struct input_handle *handle,
+ const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
- struct input_event event;
ktime_t time_mono, time_real;
time_mono = ktime_get();
time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
- event.type = type;
- event.code = code;
- event.value = value;
-
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
- evdev_pass_event(client, &event, time_mono, time_real);
+ evdev_pass_values(client, vals, count, time_mono, time_real);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
- evdev_pass_event(client, &event, time_mono, time_real);
+ evdev_pass_values(client, vals, count,
+ time_mono, time_real);
rcu_read_unlock();
+}
- if (type == EV_SYN && code == SYN_REPORT)
- wake_up_interruptible(&evdev->wait);
+/*
+ * Pass incoming event to all connected clients.
+ */
+static void evdev_event(struct input_handle *handle,
+ unsigned int type, unsigned int code, int value)
+{
+ struct input_value vals[] = { { type, code, value } };
+
+ evdev_events(handle, vals, 1);
}
static int evdev_fasync(int fd, struct file *file, int on)
@@ -653,20 +678,22 @@ static int evdev_handle_mt_request(struct input_dev *dev,
unsigned int size,
int __user *ip)
{
- const struct input_mt_slot *mt = dev->mt;
+ const struct input_mt *mt = dev->mt;
unsigned int code;
int max_slots;
int i;
if (get_user(code, &ip[0]))
return -EFAULT;
- if (!input_is_mt_value(code))
+ if (!mt || !input_is_mt_value(code))
return -EINVAL;
max_slots = (size - sizeof(__u32)) / sizeof(__s32);
- for (i = 0; i < dev->mtsize && i < max_slots; i++)
- if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i]))
+ for (i = 0; i < mt->num_slots && i < max_slots; i++) {
+ int value = input_mt_get_value(&mt->slots[i], code);
+ if (put_user(value, &ip[1 + i]))
return -EFAULT;
+ }
return 0;
}
@@ -1048,6 +1075,7 @@ MODULE_DEVICE_TABLE(input, evdev_ids);
static struct input_handler evdev_handler = {
.event = evdev_event,
+ .events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index 70a16c7..c0ec7d4 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -14,6 +14,14 @@
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
+static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
+{
+ if (dev->absinfo && test_bit(src, dev->absbit)) {
+ dev->absinfo[dst] = dev->absinfo[src];
+ dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
+ }
+}
+
/**
* input_mt_init_slots() - initialize MT input slots
* @dev: input device supporting MT events and finger tracking
@@ -25,29 +33,63 @@
* May be called repeatedly. Returns -EINVAL if attempting to
* reinitialize with a different number of slots.
*/
-int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots)
+int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
+ unsigned int flags)
{
+ struct input_mt *mt = dev->mt;
int i;
if (!num_slots)
return 0;
- if (dev->mt)
- return dev->mtsize != num_slots ? -EINVAL : 0;
+ if (mt)
+ return mt->num_slots != num_slots ? -EINVAL : 0;
- dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL);
- if (!dev->mt)
- return -ENOMEM;
+ mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL);
+ if (!mt)
+ goto err_mem;
- dev->mtsize = num_slots;
+ mt->num_slots = num_slots;
+ mt->flags = flags;
input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0);
- input_set_events_per_packet(dev, 6 * num_slots);
+
+ if (flags & (INPUT_MT_POINTER | INPUT_MT_DIRECT)) {
+ __set_bit(EV_KEY, dev->evbit);
+ __set_bit(BTN_TOUCH, dev->keybit);
+
+ copy_abs(dev, ABS_X, ABS_MT_POSITION_X);
+ copy_abs(dev, ABS_Y, ABS_MT_POSITION_Y);
+ copy_abs(dev, ABS_PRESSURE, ABS_MT_PRESSURE);
+ }
+ if (flags & INPUT_MT_POINTER) {
+ __set_bit(BTN_TOOL_FINGER, dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+ if (num_slots >= 3)
+ __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+ if (num_slots >= 4)
+ __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+ if (num_slots >= 5)
+ __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
+ __set_bit(INPUT_PROP_POINTER, dev->propbit);
+ }
+ if (flags & INPUT_MT_DIRECT)
+ __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+ if (flags & INPUT_MT_TRACK) {
+ unsigned int n2 = num_slots * num_slots;
+ mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL);
+ if (!mt->red)
+ goto err_mem;
+ }
/* Mark slots as 'unused' */
for (i = 0; i < num_slots; i++)
- input_mt_set_value(&dev->mt[i], ABS_MT_TRACKING_ID, -1);
+ input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);
+ dev->mt = mt;
return 0;
+err_mem:
+ kfree(mt);
+ return -ENOMEM;
}
EXPORT_SYMBOL(input_mt_init_slots);
@@ -60,11 +102,11 @@ EXPORT_SYMBOL(input_mt_init_slots);
*/
void input_mt_destroy_slots(struct input_dev *dev)
{
- kfree(dev->mt);
+ if (dev->mt) {
+ kfree(dev->mt->red);
+ kfree(dev->mt);
+ }
dev->mt = NULL;
- dev->mtsize = 0;
- dev->slot = 0;
- dev->trkid = 0;
}
EXPORT_SYMBOL(input_mt_destroy_slots);
@@ -83,18 +125,24 @@ EXPORT_SYMBOL(input_mt_destroy_slots);
void input_mt_report_slot_state(struct input_dev *dev,
unsigned int tool_type, bool active)
{
- struct input_mt_slot *mt;
+ struct input_mt *mt = dev->mt;
+ struct input_mt_slot *slot;
int id;
- if (!dev->mt || !active) {
+ if (!mt)
+ return;
+
+ slot = &mt->slots[mt->slot];
+ slot->frame = mt->frame;
+
+ if (!active) {
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
return;
}
- mt = &dev->mt[dev->slot];
- id = input_mt_get_value(mt, ABS_MT_TRACKING_ID);
- if (id < 0 || input_mt_get_value(mt, ABS_MT_TOOL_TYPE) != tool_type)
- id = input_mt_new_trkid(dev);
+ id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
+ if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type)
+ id = input_mt_new_trkid(mt);
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id);
input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type);
@@ -135,13 +183,19 @@ EXPORT_SYMBOL(input_mt_report_finger_count);
*/
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
{
- struct input_mt_slot *oldest = NULL;
- int oldid = dev->trkid;
- int count = 0;
- int i;
+ struct input_mt *mt = dev->mt;
+ struct input_mt_slot *oldest;
+ int oldid, count, i;
+
+ if (!mt)
+ return;
+
+ oldest = 0;
+ oldid = mt->trkid;
+ count = 0;
- for (i = 0; i < dev->mtsize; ++i) {
- struct input_mt_slot *ps = &dev->mt[i];
+ for (i = 0; i < mt->num_slots; ++i) {
+ struct input_mt_slot *ps = &mt->slots[i];
int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
if (id < 0)
@@ -160,13 +214,208 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
if (oldest) {
int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y);
- int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
input_event(dev, EV_ABS, ABS_X, x);
input_event(dev, EV_ABS, ABS_Y, y);
- input_event(dev, EV_ABS, ABS_PRESSURE, p);
+
+ if (test_bit(ABS_MT_PRESSURE, dev->absbit)) {
+ int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
+ input_event(dev, EV_ABS, ABS_PRESSURE, p);
+ }
} else {
- input_event(dev, EV_ABS, ABS_PRESSURE, 0);
+ if (test_bit(ABS_MT_PRESSURE, dev->absbit))
+ input_event(dev, EV_ABS, ABS_PRESSURE, 0);
}
}
EXPORT_SYMBOL(input_mt_report_pointer_emulation);
+
+/**
+ * input_mt_sync_frame() - synchronize mt frame
+ * @dev: input device with allocated MT slots
+ *
+ * Close the frame and prepare the internal state for a new one.
+ * Depending on the flags, marks unused slots as inactive and performs
+ * pointer emulation.
+ */
+void input_mt_sync_frame(struct input_dev *dev)
+{
+ struct input_mt *mt = dev->mt;
+ struct input_mt_slot *s;
+
+ if (!mt)
+ return;
+
+ if (mt->flags & INPUT_MT_DROP_UNUSED) {
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+ if (s->frame == mt->frame)
+ continue;
+ input_mt_slot(dev, s - mt->slots);
+ input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ }
+ }
+
+ input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER));
+
+ mt->frame++;
+}
+EXPORT_SYMBOL(input_mt_sync_frame);
+
+static int adjust_dual(int *begin, int step, int *end, int eq)
+{
+ int f, *p, s, c;
+
+ if (begin == end)
+ return 0;
+
+ f = *begin;
+ p = begin + step;
+ s = p == end ? f + 1 : *p;
+
+ for (; p != end; p += step)
+ if (*p < f)
+ s = f, f = *p;
+ else if (*p < s)
+ s = *p;
+
+ c = (f + s + 1) / 2;
+ if (c == 0 || (c > 0 && !eq))
+ return 0;
+ if (s < 0)
+ c *= 2;
+
+ for (p = begin; p != end; p += step)
+ *p -= c;
+
+ return (c < s && s <= 0) || (f >= 0 && f < c);
+}
+
+static void find_reduced_matrix(int *w, int nr, int nc, int nrc)
+{
+ int i, k, sum;
+
+ for (k = 0; k < nrc; k++) {
+ for (i = 0; i < nr; i++)
+ adjust_dual(w + i, nr, w + i + nrc, nr <= nc);
+ sum = 0;
+ for (i = 0; i < nrc; i += nr)
+ sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr);
+ if (!sum)
+ break;
+ }
+}
+
+static int input_mt_set_matrix(struct input_mt *mt,
+ const struct input_mt_pos *pos, int num_pos)
+{
+ const struct input_mt_pos *p;
+ struct input_mt_slot *s;
+ int *w = mt->red;
+ int x, y;
+
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+ if (!input_mt_is_active(s))
+ continue;
+ x = input_mt_get_value(s, ABS_MT_POSITION_X);
+ y = input_mt_get_value(s, ABS_MT_POSITION_Y);
+ for (p = pos; p != pos + num_pos; p++) {
+ int dx = x - p->x, dy = y - p->y;
+ *w++ = dx * dx + dy * dy;
+ }
+ }
+
+ return w - mt->red;
+}
+
+static void input_mt_set_slots(struct input_mt *mt,
+ int *slots, int num_pos)
+{
+ struct input_mt_slot *s;
+ int *w = mt->red, *p;
+
+ for (p = slots; p != slots + num_pos; p++)
+ *p = -1;
+
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+ if (!input_mt_is_active(s))
+ continue;
+ for (p = slots; p != slots + num_pos; p++)
+ if (*w++ < 0)
+ *p = s - mt->slots;
+ }
+
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+ if (input_mt_is_active(s))
+ continue;
+ for (p = slots; p != slots + num_pos; p++)
+ if (*p < 0) {
+ *p = s - mt->slots;
+ break;
+ }
+ }
+}
+
+/**
+ * input_mt_assign_slots() - perform a best-match assignment
+ * @dev: input device with allocated MT slots
+ * @slots: the slot assignment to be filled
+ * @pos: the position array to match
+ * @num_pos: number of positions
+ *
+ * Performs a best match against the current contacts and returns
+ * the slot assignment list. New contacts are assigned to unused
+ * slots.
+ *
+ * Returns zero on success, or negative error in case of failure.
+ */
+int input_mt_assign_slots(struct input_dev *dev, int *slots,
+ const struct input_mt_pos *pos, int num_pos)
+{
+ struct input_mt *mt = dev->mt;
+ int nrc;
+
+ if (!mt || !mt->red)
+ return -ENXIO;
+ if (num_pos > mt->num_slots)
+ return -EINVAL;
+ if (num_pos < 1)
+ return 0;
+
+ nrc = input_mt_set_matrix(mt, pos, num_pos);
+ find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc);
+ input_mt_set_slots(mt, slots, num_pos);
+
+ return 0;
+}
+EXPORT_SYMBOL(input_mt_assign_slots);
+
+/**
+ * input_mt_get_slot_by_key() - return slot matching key
+ * @dev: input device with allocated MT slots
+ * @key: the key of the sought slot
+ *
+ * Returns the slot of the given key, if it exists, otherwise
+ * set the key on the first unused slot and return.
+ *
+ * If no available slot can be found, -1 is returned.
+ */
+int input_mt_get_slot_by_key(struct input_dev *dev, int key)
+{
+ struct input_mt *mt = dev->mt;
+ struct input_mt_slot *s;
+
+ if (!mt)
+ return -1;
+
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
+ if (input_mt_is_active(s) && s->key == key)
+ return s - mt->slots;
+
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
+ if (!input_mt_is_active(s)) {
+ s->key = key;
+ return s - mt->slots;
+ }
+
+ return -1;
+}
+EXPORT_SYMBOL(input_mt_get_slot_by_key);
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 8921c61..5244f3d 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -47,6 +47,8 @@ static DEFINE_MUTEX(input_mutex);
static struct input_handler *input_table[8];
+static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 };
+
static inline int is_event_supported(unsigned int code,
unsigned long *bm, unsigned int max)
{
@@ -69,42 +71,102 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz)
return value;
}
+static void input_start_autorepeat(struct input_dev *dev, int code)
+{
+ if (test_bit(EV_REP, dev->evbit) &&
+ dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
+ dev->timer.data) {
+ dev->repeat_key = code;
+ mod_timer(&dev->timer,
+ jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
+ }
+}
+
+static void input_stop_autorepeat(struct input_dev *dev)
+{
+ del_timer(&dev->timer);
+}
+
/*
* Pass event first through all filters and then, if event has not been
* filtered out, through all open handles. This function is called with
* dev->event_lock held and interrupts disabled.
*/
-static void input_pass_event(struct input_dev *dev,
- unsigned int type, unsigned int code, int value)
+static unsigned int input_to_handler(struct input_handle *handle,
+ struct input_value *vals, unsigned int count)
+{
+ struct input_handler *handler = handle->handler;
+ struct input_value *end = vals;
+ struct input_value *v;
+
+ for (v = vals; v != vals + count; v++) {
+ if (handler->filter &&
+ handler->filter(handle, v->type, v->code, v->value))
+ continue;
+ if (end != v)
+ *end = *v;
+ end++;
+ }
+
+ count = end - vals;
+ if (!count)
+ return 0;
+
+ if (handler->events)
+ handler->events(handle, vals, count);
+ else if (handler->event)
+ for (v = vals; v != end; v++)
+ handler->event(handle, v->type, v->code, v->value);
+
+ return count;
+}
+
+/*
+ * Pass values first through all filters and then, if event has not been
+ * filtered out, through all open handles. This function is called with
+ * dev->event_lock held and interrupts disabled.
+ */
+static void input_pass_values(struct input_dev *dev,
+ struct input_value *vals, unsigned int count)
{
- struct input_handler *handler;
struct input_handle *handle;
+ struct input_value *v;
+
+ if (!count)
+ return;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
- if (handle)
- handle->handler->event(handle, type, code, value);
- else {
- bool filtered = false;
-
- list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
- if (!handle->open)
- continue;
+ if (handle) {
+ count = input_to_handler(handle, vals, count);
+ } else {
+ list_for_each_entry_rcu(handle, &dev->h_list, d_node)
+ if (handle->open)
+ count = input_to_handler(handle, vals, count);
+ }
- handler = handle->handler;
- if (!handler->filter) {
- if (filtered)
- break;
+ rcu_read_unlock();
- handler->event(handle, type, code, value);
+ add_input_randomness(vals->type, vals->code, vals->value);
- } else if (handler->filter(handle, type, code, value))
- filtered = true;
+ /* trigger auto repeat for key events */
+ for (v = vals; v != vals + count; v++) {
+ if (v->type == EV_KEY && v->value != 2) {
+ if (v->value)
+ input_start_autorepeat(dev, v->code);
+ else
+ input_stop_autorepeat(dev);
}
}
+}
- rcu_read_unlock();
+static void input_pass_event(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
+{
+ struct input_value vals[] = { { type, code, value } };
+
+ input_pass_values(dev, vals, ARRAY_SIZE(vals));
}
/*
@@ -121,18 +183,12 @@ static void input_repeat_key(unsigned long data)
if (test_bit(dev->repeat_key, dev->key) &&
is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
+ struct input_value vals[] = {
+ { EV_KEY, dev->repeat_key, 2 },
+ input_value_sync
+ };
- input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
-
- if (dev->sync) {
- /*
- * Only send SYN_REPORT if we are not in a middle
- * of driver parsing a new hardware packet.
- * Otherwise assume that the driver will send
- * SYN_REPORT once it's done.
- */
- input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
- }
+ input_pass_values(dev, vals, ARRAY_SIZE(vals));
if (dev->rep[REP_PERIOD])
mod_timer(&dev->timer, jiffies +
@@ -142,30 +198,17 @@ static void input_repeat_key(unsigned long data)
spin_unlock_irqrestore(&dev->event_lock, flags);
}
-static void input_start_autorepeat(struct input_dev *dev, int code)
-{
- if (test_bit(EV_REP, dev->evbit) &&
- dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
- dev->timer.data) {
- dev->repeat_key = code;
- mod_timer(&dev->timer,
- jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
- }
-}
-
-static void input_stop_autorepeat(struct input_dev *dev)
-{
- del_timer(&dev->timer);
-}
-
#define INPUT_IGNORE_EVENT 0
#define INPUT_PASS_TO_HANDLERS 1
#define INPUT_PASS_TO_DEVICE 2
+#define INPUT_SLOT 4
+#define INPUT_FLUSH 8
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
static int input_handle_abs_event(struct input_dev *dev,
unsigned int code, int *pval)
{
+ struct input_mt *mt = dev->mt;
bool is_mt_event;
int *pold;
@@ -174,8 +217,8 @@ static int input_handle_abs_event(struct input_dev *dev,
* "Stage" the event; we'll flush it later, when we
* get actual touch data.
*/
- if (*pval >= 0 && *pval < dev->mtsize)
- dev->slot = *pval;
+ if (mt && *pval >= 0 && *pval < mt->num_slots)
+ mt->slot = *pval;
return INPUT_IGNORE_EVENT;
}
@@ -184,9 +227,8 @@ static int input_handle_abs_event(struct input_dev *dev,
if (!is_mt_event) {
pold = &dev->absinfo[code].value;
- } else if (dev->mt) {
- struct input_mt_slot *mtslot = &dev->mt[dev->slot];
- pold = &mtslot->abs[code - ABS_MT_FIRST];
+ } else if (mt) {
+ pold = &mt->slots[mt->slot].abs[code - ABS_MT_FIRST];
} else {
/*
* Bypass filtering for multi-touch events when
@@ -205,16 +247,16 @@ static int input_handle_abs_event(struct input_dev *dev,
}
/* Flush pending "slot" event */
- if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
- input_abs_set_val(dev, ABS_MT_SLOT, dev->slot);
- input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);
+ if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
+ input_abs_set_val(dev, ABS_MT_SLOT, mt->slot);
+ return INPUT_PASS_TO_HANDLERS | INPUT_SLOT;
}
return INPUT_PASS_TO_HANDLERS;
}
-static void input_handle_event(struct input_dev *dev,
- unsigned int type, unsigned int code, int value)
+static int input_get_disposition(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
@@ -227,37 +269,34 @@ static void input_handle_event(struct input_dev *dev,
break;
case SYN_REPORT:
- if (!dev->sync) {
- dev->sync = true;
- disposition = INPUT_PASS_TO_HANDLERS;
- }
+ disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
break;
case SYN_MT_REPORT:
- dev->sync = false;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
- if (is_event_supported(code, dev->keybit, KEY_MAX) &&
- !!test_bit(code, dev->key) != value) {
+ if (is_event_supported(code, dev->keybit, KEY_MAX)) {
- if (value != 2) {
- __change_bit(code, dev->key);
- if (value)
- input_start_autorepeat(dev, code);
- else
- input_stop_autorepeat(dev);
+ /* auto-repeat bypasses state updates */
+ if (value == 2) {
+ disposition = INPUT_PASS_TO_HANDLERS;
+ break;
}
- disposition = INPUT_PASS_TO_HANDLERS;
+ if (!!test_bit(code, dev->key) != !!value) {
+
+ __change_bit(code, dev->key);
+ disposition = INPUT_PASS_TO_HANDLERS;
+ }
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
- !!test_bit(code, dev->sw) != value) {
+ !!test_bit(code, dev->sw) != !!value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
@@ -284,7 +323,7 @@ static void input_handle_event(struct input_dev *dev,
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
- !!test_bit(code, dev->led) != value) {
+ !!test_bit(code, dev->led) != !!value) {
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
@@ -317,14 +356,48 @@ static void input_handle_event(struct input_dev *dev,
break;
}
- if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- dev->sync = false;
+ return disposition;
+}
+
+static void input_handle_event(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
+{
+ int disposition;
+
+ disposition = input_get_disposition(dev, type, code, value);
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
- if (disposition & INPUT_PASS_TO_HANDLERS)
- input_pass_event(dev, type, code, value);
+ if (!dev->vals)
+ return;
+
+ if (disposition & INPUT_PASS_TO_HANDLERS) {
+ struct input_value *v;
+
+ if (disposition & INPUT_SLOT) {
+ v = &dev->vals[dev->num_vals++];
+ v->type = EV_ABS;
+ v->code = ABS_MT_SLOT;
+ v->value = dev->mt->slot;
+ }
+
+ v = &dev->vals[dev->num_vals++];
+ v->type = type;
+ v->code = code;
+ v->value = value;
+ }
+
+ if (disposition & INPUT_FLUSH) {
+ if (dev->num_vals >= 2)
+ input_pass_values(dev, dev->vals, dev->num_vals);
+ dev->num_vals = 0;
+ } else if (dev->num_vals >= dev->max_vals - 2) {
+ dev->vals[dev->num_vals++] = input_value_sync;
+ input_pass_values(dev, dev->vals, dev->num_vals);
+ dev->num_vals = 0;
+ }
+
}
/**
@@ -352,7 +425,6 @@ void input_event(struct input_dev *dev,
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
- add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
@@ -831,10 +903,12 @@ int input_set_keycode(struct input_dev *dev,
if (test_bit(EV_KEY, dev->evbit) &&
!is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
__test_and_clear_bit(old_keycode, dev->key)) {
+ struct input_value vals[] = {
+ { EV_KEY, old_keycode, 0 },
+ input_value_sync
+ };
- input_pass_event(dev, EV_KEY, old_keycode, 0);
- if (dev->sync)
- input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
+ input_pass_values(dev, vals, ARRAY_SIZE(vals));
}
out:
@@ -1416,6 +1490,7 @@ static void input_dev_release(struct device *device)
input_ff_destroy(dev);
input_mt_destroy_slots(dev);
kfree(dev->absinfo);
+ kfree(dev->vals);
kfree(dev);
module_put(THIS_MODULE);
@@ -1751,8 +1826,8 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
int i;
unsigned int events;
- if (dev->mtsize) {
- mt_slots = dev->mtsize;
+ if (dev->mt) {
+ mt_slots = dev->mt->num_slots;
} else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) {
mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum -
dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1,
@@ -1778,6 +1853,9 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
if (test_bit(i, dev->relbit))
events++;
+ /* Make room for KEY and MSC events */
+ events += 7;
+
return events;
}
@@ -1816,6 +1894,7 @@ int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
+ unsigned int packet_size;
const char *path;
int error;
@@ -1828,9 +1907,14 @@ int input_register_device(struct input_dev *dev)
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
- if (!dev->hint_events_per_packet)
- dev->hint_events_per_packet =
- input_estimate_events_per_packet(dev);
+ packet_size = input_estimate_events_per_packet(dev);
+ if (dev->hint_events_per_packet < packet_size)
+ dev->hint_events_per_packet = packet_size;
+
+ dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2;
+ dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
+ if (!dev->vals)
+ return -ENOMEM;
/*
* If delay and period are pre-set by the driver, then autorepeating
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c
index ce68e36..cdc2526 100644
--- a/drivers/input/keyboard/imx_keypad.c
+++ b/drivers/input/keyboard/imx_keypad.c
@@ -516,9 +516,9 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev)
input_set_drvdata(input_dev, keypad);
/* Ensure that the keypad will stay dormant until opened */
- clk_enable(keypad->clk);
+ clk_prepare_enable(keypad->clk);
imx_keypad_inhibit(keypad);
- clk_disable(keypad->clk);
+ clk_disable_unprepare(keypad->clk);
error = request_irq(irq, imx_keypad_irq_handler, 0,
pdev->name, keypad);
diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c
index f06231b..84ec691 100644
--- a/drivers/input/misc/ab8500-ponkey.c
+++ b/drivers/input/misc/ab8500-ponkey.c
@@ -74,8 +74,8 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
ponkey->idev = input;
ponkey->ab8500 = ab8500;
- ponkey->irq_dbf = ab8500_irq_get_virq(ab8500, irq_dbf);
- ponkey->irq_dbr = ab8500_irq_get_virq(ab8500, irq_dbr);
+ ponkey->irq_dbf = irq_dbf;
+ ponkey->irq_dbr = irq_dbr;
input->name = "AB8500 POn(PowerOn) Key";
input->dev.parent = &pdev->dev;
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 7360568..6b17975 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -405,7 +405,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
goto exit;
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
- input_mt_init_slots(dev, nslot);
+ input_mt_init_slots(dev, nslot, 0);
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
input_set_events_per_packet(dev, 60);
}
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 4a1347e..cf5af1f 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -1620,7 +1620,7 @@ int alps_init(struct psmouse *psmouse)
case ALPS_PROTO_V3:
case ALPS_PROTO_V4:
set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
- input_mt_init_slots(dev1, 2);
+ input_mt_init_slots(dev1, 2, 0);
input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0);
input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0);
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index d528c23..3a78f23 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -40,6 +40,7 @@
#include <linux/usb/input.h>
#include <linux/hid.h>
#include <linux/mutex.h>
+#include <linux/input/mt.h>
#define USB_VENDOR_ID_APPLE 0x05ac
@@ -183,26 +184,26 @@ struct tp_finger {
__le16 abs_y; /* absolute y coodinate */
__le16 rel_x; /* relative x coodinate */
__le16 rel_y; /* relative y coodinate */
- __le16 size_major; /* finger size, major axis? */
- __le16 size_minor; /* finger size, minor axis? */
+ __le16 tool_major; /* tool area, major axis */
+ __le16 tool_minor; /* tool area, minor axis */
__le16 orientation; /* 16384 when point, else 15 bit angle */
- __le16 force_major; /* trackpad force, major axis? */
- __le16 force_minor; /* trackpad force, minor axis? */
+ __le16 touch_major; /* touch area, major axis */
+ __le16 touch_minor; /* touch area, minor axis */
__le16 unused[3]; /* zeros */
__le16 multi; /* one finger: varies, more fingers: constant */
} __attribute__((packed,aligned(2)));
/* trackpad finger data size, empirically at least ten fingers */
+#define MAX_FINGERS 16
#define SIZEOF_FINGER sizeof(struct tp_finger)
-#define SIZEOF_ALL_FINGERS (16 * SIZEOF_FINGER)
+#define SIZEOF_ALL_FINGERS (MAX_FINGERS * SIZEOF_FINGER)
#define MAX_FINGER_ORIENTATION 16384
/* device-specific parameters */
struct bcm5974_param {
- int dim; /* logical dimension */
- int fuzz; /* logical noise value */
- int devmin; /* device minimum reading */
- int devmax; /* device maximum reading */
+ int snratio; /* signal-to-noise ratio */
+ int min; /* device minimum reading */
+ int max; /* device maximum reading */
};
/* device-specific configuration */
@@ -219,6 +220,7 @@ struct bcm5974_config {
struct bcm5974_param w; /* finger width limits */
struct bcm5974_param x; /* horizontal limits */
struct bcm5974_param y; /* vertical limits */
+ struct bcm5974_param o; /* orientation limits */
};
/* logical device structure */
@@ -234,23 +236,16 @@ struct bcm5974 {
struct bt_data *bt_data; /* button transferred data */
struct urb *tp_urb; /* trackpad usb request block */
u8 *tp_data; /* trackpad transferred data */
- int fingers; /* number of fingers on trackpad */
+ const struct tp_finger *index[MAX_FINGERS]; /* finger index data */
+ struct input_mt_pos pos[MAX_FINGERS]; /* position array */
+ int slots[MAX_FINGERS]; /* slot assignments */
};
-/* logical dimensions */
-#define DIM_PRESSURE 256 /* maximum finger pressure */
-#define DIM_WIDTH 16 /* maximum finger width */
-#define DIM_X 1280 /* maximum trackpad x value */
-#define DIM_Y 800 /* maximum trackpad y value */
-
/* logical signal quality */
#define SN_PRESSURE 45 /* pressure signal-to-noise ratio */
-#define SN_WIDTH 100 /* width signal-to-noise ratio */
+#define SN_WIDTH 25 /* width signal-to-noise ratio */
#define SN_COORD 250 /* coordinate signal-to-noise ratio */
-
-/* pressure thresholds */
-#define PRESSURE_LOW (2 * DIM_PRESSURE / SN_PRESSURE)
-#define PRESSURE_HIGH (3 * PRESSURE_LOW)
+#define SN_ORIENT 10 /* orientation signal-to-noise ratio */
/* device constants */
static const struct bcm5974_config bcm5974_config_table[] = {
@@ -261,10 +256,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
0,
0x84, sizeof(struct bt_data),
0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4824, 5342 },
- { DIM_Y, DIM_Y / SN_COORD, -172, 5820 }
+ { SN_PRESSURE, 0, 256 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4824, 5342 },
+ { SN_COORD, -172, 5820 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
@@ -273,10 +269,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
0,
0x84, sizeof(struct bt_data),
0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4824, 4824 },
- { DIM_Y, DIM_Y / SN_COORD, -172, 4290 }
+ { SN_PRESSURE, 0, 256 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4824, 4824 },
+ { SN_COORD, -172, 4290 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI,
@@ -285,10 +282,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4460, 5166 },
- { DIM_Y, DIM_Y / SN_COORD, -75, 6700 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4460, 5166 },
+ { SN_COORD, -75, 6700 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI,
@@ -297,10 +295,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
- { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4620, 5140 },
+ { SN_COORD, -150, 6600 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI,
@@ -309,10 +308,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4616, 5112 },
- { DIM_Y, DIM_Y / SN_COORD, -142, 5234 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4616, 5112 },
+ { SN_COORD, -142, 5234 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI,
@@ -321,10 +321,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4415, 5050 },
- { DIM_Y, DIM_Y / SN_COORD, -55, 6680 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4415, 5050 },
+ { SN_COORD, -55, 6680 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI,
@@ -333,10 +334,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
- { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4620, 5140 },
+ { SN_COORD, -150, 6600 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI,
@@ -345,10 +347,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4750, 5280 },
- { DIM_Y, DIM_Y / SN_COORD, -150, 6730 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4750, 5280 },
+ { SN_COORD, -150, 6730 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI,
@@ -357,10 +360,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
- { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4620, 5140 },
+ { SN_COORD, -150, 6600 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI,
@@ -369,10 +373,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4750, 5280 },
- { DIM_Y, DIM_Y / SN_COORD, -150, 6730 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4750, 5280 },
+ { SN_COORD, -150, 6730 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{}
};
@@ -396,18 +401,11 @@ static inline int raw2int(__le16 x)
return (signed short)le16_to_cpu(x);
}
-/* scale device data to logical dimensions (asserts devmin < devmax) */
-static inline int int2scale(const struct bcm5974_param *p, int x)
-{
- return x * p->dim / (p->devmax - p->devmin);
-}
-
-/* all logical value ranges are [0,dim). */
-static inline int int2bound(const struct bcm5974_param *p, int x)
+static void set_abs(struct input_dev *input, unsigned int code,
+ const struct bcm5974_param *p)
{
- int s = int2scale(p, x);
-
- return clamp_val(s, 0, p->dim - 1);
+ int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0;
+ input_set_abs_params(input, code, p->min, p->max, fuzz, 0);
}
/* setup which logical events to report */
@@ -416,48 +414,30 @@ static void setup_events_to_report(struct input_dev *input_dev,
{
__set_bit(EV_ABS, input_dev->evbit);
- input_set_abs_params(input_dev, ABS_PRESSURE,
- 0, cfg->p.dim, cfg->p.fuzz, 0);
- input_set_abs_params(input_dev, ABS_TOOL_WIDTH,
- 0, cfg->w.dim, cfg->w.fuzz, 0);
- input_set_abs_params(input_dev, ABS_X,
- 0, cfg->x.dim, cfg->x.fuzz, 0);
- input_set_abs_params(input_dev, ABS_Y,
- 0, cfg->y.dim, cfg->y.fuzz, 0);
+ /* for synaptics only */
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0);
+ input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0);
/* finger touch area */
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- cfg->w.devmin, cfg->w.devmax, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
- cfg->w.devmin, cfg->w.devmax, 0, 0);
+ set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w);
+ set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w);
/* finger approach area */
- input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR,
- cfg->w.devmin, cfg->w.devmax, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR,
- cfg->w.devmin, cfg->w.devmax, 0, 0);
+ set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w);
+ set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w);
/* finger orientation */
- input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
- -MAX_FINGER_ORIENTATION,
- MAX_FINGER_ORIENTATION, 0, 0);
+ set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o);
/* finger position */
- input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- cfg->x.devmin, cfg->x.devmax, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- cfg->y.devmin, cfg->y.devmax, 0, 0);
+ set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x);
+ set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y);
__set_bit(EV_KEY, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
- __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
- __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
- __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
- __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
__set_bit(BTN_LEFT, input_dev->keybit);
- __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
if (cfg->caps & HAS_INTEGRATED_BUTTON)
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
- input_set_events_per_packet(input_dev, 60);
+ input_mt_init_slots(input_dev, MAX_FINGERS,
+ INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
}
/* report button data as logical button state */
@@ -477,24 +457,44 @@ static int report_bt_state(struct bcm5974 *dev, int size)
return 0;
}
-static void report_finger_data(struct input_dev *input,
- const struct bcm5974_config *cfg,
+static void report_finger_data(struct input_dev *input, int slot,
+ const struct input_mt_pos *pos,
const struct tp_finger *f)
{
+ input_mt_slot(input, slot);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+
input_report_abs(input, ABS_MT_TOUCH_MAJOR,
- raw2int(f->force_major) << 1);
+ raw2int(f->touch_major) << 1);
input_report_abs(input, ABS_MT_TOUCH_MINOR,
- raw2int(f->force_minor) << 1);
+ raw2int(f->touch_minor) << 1);
input_report_abs(input, ABS_MT_WIDTH_MAJOR,
- raw2int(f->size_major) << 1);
+ raw2int(f->tool_major) << 1);
input_report_abs(input, ABS_MT_WIDTH_MINOR,
- raw2int(f->size_minor) << 1);
+ raw2int(f->tool_minor) << 1);
input_report_abs(input, ABS_MT_ORIENTATION,
MAX_FINGER_ORIENTATION - raw2int(f->orientation));
- input_report_abs(input, ABS_MT_POSITION_X, raw2int(f->abs_x));
- input_report_abs(input, ABS_MT_POSITION_Y,
- cfg->y.devmin + cfg->y.devmax - raw2int(f->abs_y));
- input_mt_sync(input);
+ input_report_abs(input, ABS_MT_POSITION_X, pos->x);
+ input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
+}
+
+static void report_synaptics_data(struct input_dev *input,
+ const struct bcm5974_config *cfg,
+ const struct tp_finger *f, int raw_n)
+{
+ int abs_p = 0, abs_w = 0;
+
+ if (raw_n) {
+ int p = raw2int(f->touch_major);
+ int w = raw2int(f->tool_major);
+ if (p > 0 && raw2int(f->origin)) {
+ abs_p = clamp_val(256 * p / cfg->p.max, 0, 255);
+ abs_w = clamp_val(16 * w / cfg->w.max, 0, 15);
+ }
+ }
+
+ input_report_abs(input, ABS_PRESSURE, abs_p);
+ input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
}
/* report trackpad data as logical trackpad state */
@@ -503,9 +503,7 @@ static int report_tp_state(struct bcm5974 *dev, int size)
const struct bcm5974_config *c = &dev->cfg;
const struct tp_finger *f;
struct input_dev *input = dev->input;
- int raw_p, raw_w, raw_x, raw_y, raw_n, i;
- int ptest, origin, ibt = 0, nmin = 0, nmax = 0;
- int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0;
+ int raw_n, i, n = 0;
if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0)
return -EIO;
@@ -514,76 +512,29 @@ static int report_tp_state(struct bcm5974 *dev, int size)
f = (const struct tp_finger *)(dev->tp_data + c->tp_offset);
raw_n = (size - c->tp_offset) / SIZEOF_FINGER;
- /* always track the first finger; when detached, start over */
- if (raw_n) {
-
- /* report raw trackpad data */
- for (i = 0; i < raw_n; i++)
- report_finger_data(input, c, &f[i]);
-
- raw_p = raw2int(f->force_major);
- raw_w = raw2int(f->size_major);
- raw_x = raw2int(f->abs_x);
- raw_y = raw2int(f->abs_y);
-
- dprintk(9,
- "bcm5974: "
- "raw: p: %+05d w: %+05d x: %+05d y: %+05d n: %d\n",
- raw_p, raw_w, raw_x, raw_y, raw_n);
-
- ptest = int2bound(&c->p, raw_p);
- origin = raw2int(f->origin);
-
- /* while tracking finger still valid, count all fingers */
- if (ptest > PRESSURE_LOW && origin) {
- abs_p = ptest;
- abs_w = int2bound(&c->w, raw_w);
- abs_x = int2bound(&c->x, raw_x - c->x.devmin);
- abs_y = int2bound(&c->y, c->y.devmax - raw_y);
- while (raw_n--) {
- ptest = int2bound(&c->p,
- raw2int(f->force_major));
- if (ptest > PRESSURE_LOW)
- nmax++;
- if (ptest > PRESSURE_HIGH)
- nmin++;
- f++;
- }
- }
+ for (i = 0; i < raw_n; i++) {
+ if (raw2int(f[i].touch_major) == 0)
+ continue;
+ dev->pos[n].x = raw2int(f[i].abs_x);
+ dev->pos[n].y = c->y.min + c->y.max - raw2int(f[i].abs_y);
+ dev->index[n++] = &f[i];
}
- /* set the integrated button if applicable */
- if (c->tp_type == TYPE2)
- ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
-
- if (dev->fingers < nmin)
- dev->fingers = nmin;
- if (dev->fingers > nmax)
- dev->fingers = nmax;
-
- input_report_key(input, BTN_TOUCH, dev->fingers > 0);
- input_report_key(input, BTN_TOOL_FINGER, dev->fingers == 1);
- input_report_key(input, BTN_TOOL_DOUBLETAP, dev->fingers == 2);
- input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers == 3);
- input_report_key(input, BTN_TOOL_QUADTAP, dev->fingers > 3);
-
- input_report_abs(input, ABS_PRESSURE, abs_p);
- input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
+ input_mt_assign_slots(input, dev->slots, dev->pos, n);
- if (abs_p) {
- input_report_abs(input, ABS_X, abs_x);
- input_report_abs(input, ABS_Y, abs_y);
+ for (i = 0; i < n; i++)
+ report_finger_data(input, dev->slots[i],
+ &dev->pos[i], dev->index[i]);
- dprintk(8,
- "bcm5974: abs: p: %+05d w: %+05d x: %+05d y: %+05d "
- "nmin: %d nmax: %d n: %d ibt: %d\n", abs_p, abs_w,
- abs_x, abs_y, nmin, nmax, dev->fingers, ibt);
+ input_mt_sync_frame(input);
- }
+ report_synaptics_data(input, c, f, raw_n);
/* type 2 reports button events via ibt only */
- if (c->tp_type == TYPE2)
+ if (c->tp_type == TYPE2) {
+ int ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
input_report_key(input, BTN_LEFT, ibt);
+ }
input_sync(input);
@@ -742,9 +693,11 @@ static int bcm5974_start_traffic(struct bcm5974 *dev)
goto err_out;
}
- error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
- if (error)
- goto err_reset_mode;
+ if (dev->bt_urb) {
+ error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
+ if (error)
+ goto err_reset_mode;
+ }
error = usb_submit_urb(dev->tp_urb, GFP_KERNEL);
if (error)
@@ -868,19 +821,23 @@ static int bcm5974_probe(struct usb_interface *iface,
mutex_init(&dev->pm_mutex);
/* setup urbs */
- dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->bt_urb)
- goto err_free_devs;
+ if (cfg->tp_type == TYPE1) {
+ dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->bt_urb)
+ goto err_free_devs;
+ }
dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->tp_urb)
goto err_free_bt_urb;
- dev->bt_data = usb_alloc_coherent(dev->udev,
+ if (dev->bt_urb) {
+ dev->bt_data = usb_alloc_coherent(dev->udev,
dev->cfg.bt_datalen, GFP_KERNEL,
&dev->bt_urb->transfer_dma);
- if (!dev->bt_data)
- goto err_free_urb;
+ if (!dev->bt_data)
+ goto err_free_urb;
+ }
dev->tp_data = usb_alloc_coherent(dev->udev,
dev->cfg.tp_datalen, GFP_KERNEL,
@@ -888,10 +845,11 @@ static int bcm5974_probe(struct usb_interface *iface,
if (!dev->tp_data)
goto err_free_bt_buffer;
- usb_fill_int_urb(dev->bt_urb, udev,
- usb_rcvintpipe(udev, cfg->bt_ep),
- dev->bt_data, dev->cfg.bt_datalen,
- bcm5974_irq_button, dev, 1);
+ if (dev->bt_urb)
+ usb_fill_int_urb(dev->bt_urb, udev,
+ usb_rcvintpipe(udev, cfg->bt_ep),
+ dev->bt_data, dev->cfg.bt_datalen,
+ bcm5974_irq_button, dev, 1);
usb_fill_int_urb(dev->tp_urb, udev,
usb_rcvintpipe(udev, cfg->tp_ep),
@@ -929,8 +887,9 @@ err_free_buffer:
usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
dev->tp_data, dev->tp_urb->transfer_dma);
err_free_bt_buffer:
- usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
- dev->bt_data, dev->bt_urb->transfer_dma);
+ if (dev->bt_urb)
+ usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
+ dev->bt_data, dev->bt_urb->transfer_dma);
err_free_urb:
usb_free_urb(dev->tp_urb);
err_free_bt_urb:
@@ -951,8 +910,9 @@ static void bcm5974_disconnect(struct usb_interface *iface)
input_unregister_device(dev->input);
usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
dev->tp_data, dev->tp_urb->transfer_dma);
- usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
- dev->bt_data, dev->bt_urb->transfer_dma);
+ if (dev->bt_urb)
+ usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
+ dev->bt_data, dev->bt_urb->transfer_dma);
usb_free_urb(dev->tp_urb);
usb_free_urb(dev->bt_urb);
kfree(dev);
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 4790110..1e8e42f 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1004,7 +1004,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
ETP_WMAX_V2, 0, 0);
}
- input_mt_init_slots(dev, 2);
+ input_mt_init_slots(dev, 2, 0);
input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
break;
@@ -1035,7 +1035,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
ETP_WMAX_V2, 0, 0);
/* Multitouch capable pad, up to 5 fingers. */
- input_mt_init_slots(dev, ETP_MAX_FINGERS);
+ input_mt_init_slots(dev, ETP_MAX_FINGERS, 0);
input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index 3f5649f..e582922 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -721,6 +721,17 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
case FSP_PKT_TYPE_ABS:
+
+ if ((packet[0] == 0x48 || packet[0] == 0x49) &&
+ packet[1] == 0 && packet[2] == 0) {
+ /*
+ * Ignore coordinate noise when finger leaving the
+ * surface, otherwise cursor may jump to upper-left
+ * corner.
+ */
+ packet[3] &= 0xf0;
+ }
+
abs_x = GET_ABS_X(packet);
abs_y = GET_ABS_Y(packet);
@@ -960,7 +971,7 @@ static int fsp_set_input_params(struct psmouse *psmouse)
input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0);
- input_mt_init_slots(dev, 2);
+ input_mt_init_slots(dev, 2, 0);
input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0);
}
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 14eaece..37033ad 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -1232,7 +1232,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
- input_mt_init_slots(dev, 2);
+ input_mt_init_slots(dev, 2, 0);
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
ABS_MT_POSITION_Y);
/* Image sensors can report per-contact pressure */
@@ -1244,7 +1244,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
} else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
/* Non-image sensors with AGM use semi-mt */
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
- input_mt_init_slots(dev, 2);
+ input_mt_init_slots(dev, 2, 0);
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
ABS_MT_POSITION_Y);
}
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
index 2ffd110..2e77246 100644
--- a/drivers/input/serio/ambakmi.c
+++ b/drivers/input/serio/ambakmi.c
@@ -72,7 +72,7 @@ static int amba_kmi_open(struct serio *io)
unsigned int divisor;
int ret;
- ret = clk_enable(kmi->clk);
+ ret = clk_prepare_enable(kmi->clk);
if (ret)
goto out;
@@ -92,7 +92,7 @@ static int amba_kmi_open(struct serio *io)
return 0;
clk_disable:
- clk_disable(kmi->clk);
+ clk_disable_unprepare(kmi->clk);
out:
return ret;
}
@@ -104,7 +104,7 @@ static void amba_kmi_close(struct serio *io)
writeb(0, KMICR);
free_irq(kmi->irq, kmi);
- clk_disable(kmi->clk);
+ clk_disable_unprepare(kmi->clk);
}
static int __devinit amba_kmi_probe(struct amba_device *dev,
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 6918773..d6cc77a 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -335,6 +335,12 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
},
{
.matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"),
+ },
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"),
},
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 532d067..2a81ce3 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -1530,7 +1530,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
- input_mt_init_slots(input_dev, features->touch_max);
+ input_mt_init_slots(input_dev, features->touch_max, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, 255, 0, 0);
@@ -1575,7 +1575,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case TABLETPC2FG:
if (features->device_type == BTN_TOOL_FINGER) {
- input_mt_init_slots(input_dev, features->touch_max);
+ input_mt_init_slots(input_dev, features->touch_max, 0);
input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
0, MT_TOOL_MAX, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
@@ -1631,7 +1631,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
- input_mt_init_slots(input_dev, features->touch_max);
+ input_mt_init_slots(input_dev, features->touch_max, 0);
if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
__set_bit(BTN_TOOL_TRIPLETAP,
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 4623cc6..e92615d 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1152,7 +1152,7 @@ static int __devinit mxt_probe(struct i2c_client *client,
/* For multi touch */
num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
- error = input_mt_init_slots(input_dev, num_mt_slots);
+ error = input_mt_init_slots(input_dev, num_mt_slots, 0);
if (error)
goto err_free_object;
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
index f030d9e..8e60437 100644
--- a/drivers/input/touchscreen/cyttsp_core.c
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -571,7 +571,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, CY_MAXZ, 0, 0);
- input_mt_init_slots(input_dev, CY_MAX_ID);
+ input_mt_init_slots(input_dev, CY_MAX_ID, 0);
error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index b06a5e3..099d144a 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -566,9 +566,12 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
}
read = min_t(size_t, count, tsdata->raw_bufsize - *off);
- error = copy_to_user(buf, tsdata->raw_buffer + *off, read);
- if (!error)
- *off += read;
+ if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) {
+ error = -EFAULT;
+ goto out;
+ }
+
+ *off += read;
out:
mutex_unlock(&tsdata->mutex);
return error ?: read;
@@ -779,7 +782,7 @@ static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client,
0, tsdata->num_x * 64 - 1, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
0, tsdata->num_y * 64 - 1, 0, 0);
- error = input_mt_init_slots(input, MAX_SUPPORT_POINTS);
+ error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
if (error) {
dev_err(&client->dev, "Unable to init MT slots.\n");
goto err_free_mem;
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
index 70524dd..c1e3460 100644
--- a/drivers/input/touchscreen/egalax_ts.c
+++ b/drivers/input/touchscreen/egalax_ts.c
@@ -204,7 +204,7 @@ static int __devinit egalax_ts_probe(struct i2c_client *client,
ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0);
- input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS);
+ input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0);
input_set_drvdata(input_dev, ts);
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
index c004417..4ac6976 100644
--- a/drivers/input/touchscreen/ili210x.c
+++ b/drivers/input/touchscreen/ili210x.c
@@ -252,7 +252,7 @@ static int __devinit ili210x_i2c_probe(struct i2c_client *client,
input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
/* Multi touch */
- input_mt_init_slots(input, MAX_TOUCHES);
+ input_mt_init_slots(input, MAX_TOUCHES, 0);
input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c
index 49c44bb..560cf09 100644
--- a/drivers/input/touchscreen/mms114.c
+++ b/drivers/input/touchscreen/mms114.c
@@ -404,7 +404,7 @@ static int __devinit mms114_probe(struct i2c_client *client,
input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0);
/* For multi touch */
- input_mt_init_slots(input_dev, MMS114_MAX_TOUCH);
+ input_mt_init_slots(input_dev, MMS114_MAX_TOUCH, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, MMS114_MAX_AREA, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
index 4ccde45..b49f0b8 100644
--- a/drivers/input/touchscreen/penmount.c
+++ b/drivers/input/touchscreen/penmount.c
@@ -264,7 +264,7 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)
input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0);
if (pm->maxcontacts > 1) {
- input_mt_init_slots(pm->dev, pm->maxcontacts);
+ input_mt_init_slots(pm->dev, pm->maxcontacts, 0);
input_set_abs_params(pm->dev,
ABS_MT_POSITION_X, 0, max_x, 0, 0);
input_set_abs_params(pm->dev,
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index e32709e..721fdb3 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -304,6 +304,45 @@ static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
#define EGALAX_PKT_TYPE_REPT 0x80
#define EGALAX_PKT_TYPE_DIAG 0x0A
+static int egalax_init(struct usbtouch_usb *usbtouch)
+{
+ int ret, i;
+ unsigned char *buf;
+ struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+ /*
+ * An eGalax diagnostic packet kicks the device into using the right
+ * protocol. We send a "check active" packet. The response will be
+ * read later and ignored.
+ */
+
+ buf = kmalloc(3, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = EGALAX_PKT_TYPE_DIAG;
+ buf[1] = 1; /* length */
+ buf[2] = 'A'; /* command - check active */
+
+ for (i = 0; i < 3; i++) {
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, buf, 3,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret >= 0) {
+ ret = 0;
+ break;
+ }
+ if (ret != -EPIPE)
+ break;
+ }
+
+ kfree(buf);
+
+ return ret;
+}
+
static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
@@ -1056,6 +1095,7 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.process_pkt = usbtouch_process_multi,
.get_pkt_len = egalax_get_pkt_len,
.read_data = egalax_read_data,
+ .init = egalax_init,
},
#endif
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index 8f9ad2f..9a83be6 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -471,7 +471,7 @@ static int w8001_setup(struct w8001 *w8001)
case 5:
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
- input_mt_init_slots(dev, 2);
+ input_mt_init_slots(dev, 2, 0);
input_set_abs_params(dev, ABS_MT_POSITION_X,
0, touch.x, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y,
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index b64502d..e89daf1 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -266,7 +266,7 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
static int iommu_init_device(struct device *dev)
{
- struct pci_dev *dma_pdev, *pdev = to_pci_dev(dev);
+ struct pci_dev *dma_pdev = NULL, *pdev = to_pci_dev(dev);
struct iommu_dev_data *dev_data;
struct iommu_group *group;
u16 alias;
@@ -293,7 +293,9 @@ static int iommu_init_device(struct device *dev)
dev_data->alias_data = alias_data;
dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
- } else
+ }
+
+ if (dma_pdev == NULL)
dma_pdev = pci_dev_get(pdev);
/* Account for quirked devices */
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 2297ec1..db820d7 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2351,7 +2351,7 @@ static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
return 0;
if (pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI)
return 0;
- } else if (pdev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
+ } else if (pci_pcie_type(pdev) == PCI_EXP_TYPE_PCI_BRIDGE)
return 0;
/*
@@ -3546,10 +3546,10 @@ found:
struct pci_dev *bridge = bus->self;
if (!bridge || !pci_is_pcie(bridge) ||
- bridge->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
+ pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE)
return 0;
- if (bridge->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
+ if (pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT) {
for (i = 0; i < atsru->devices_cnt; i++)
if (atsru->devices[i] == bridge)
return 1;
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c
index 39809035..4af12e1 100644
--- a/drivers/lguest/x86/core.c
+++ b/drivers/lguest/x86/core.c
@@ -203,8 +203,8 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
* we set it now, so we can trap and pass that trap to the Guest if it
* uses the FPU.
*/
- if (cpu->ts)
- unlazy_fpu(current);
+ if (cpu->ts && user_has_fpu())
+ stts();
/*
* SYSENTER is an optimized way of doing system calls. We can't allow
@@ -234,6 +234,10 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
if (boot_cpu_has(X86_FEATURE_SEP))
wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0);
+ /* Clear the host TS bit if it was set above. */
+ if (cpu->ts && user_has_fpu())
+ clts();
+
/*
* If the Guest page faulted, then the cr2 register will tell us the
* bad virtual address. We have to grab this now, because once we
@@ -249,7 +253,7 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
* a different CPU. So all the critical stuff should be done
* before this.
*/
- else if (cpu->regs->trapnum == 7)
+ else if (cpu->regs->trapnum == 7 && !user_has_fpu())
math_state_restore();
}
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index d8abb90..034233e 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1555,6 +1555,7 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
unsigned long arg)
{
struct multipath *m = ti->private;
+ struct pgpath *pgpath;
struct block_device *bdev;
fmode_t mode;
unsigned long flags;
@@ -1570,12 +1571,14 @@ again:
if (!m->current_pgpath)
__choose_pgpath(m, 0);
- if (m->current_pgpath) {
- bdev = m->current_pgpath->path.dev->bdev;
- mode = m->current_pgpath->path.dev->mode;
+ pgpath = m->current_pgpath;
+
+ if (pgpath) {
+ bdev = pgpath->path.dev->bdev;
+ mode = pgpath->path.dev->mode;
}
- if (m->queue_io)
+ if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
r = -EAGAIN;
else if (!bdev)
r = -EIO;
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index f900690..100368e 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -1212,6 +1212,41 @@ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
return &t->targets[(KEYS_PER_NODE * n) + k];
}
+static int count_device(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ unsigned *num_devices = data;
+
+ (*num_devices)++;
+
+ return 0;
+}
+
+/*
+ * Check whether a table has no data devices attached using each
+ * target's iterate_devices method.
+ * Returns false if the result is unknown because a target doesn't
+ * support iterate_devices.
+ */
+bool dm_table_has_no_data_devices(struct dm_table *table)
+{
+ struct dm_target *uninitialized_var(ti);
+ unsigned i = 0, num_devices = 0;
+
+ while (i < dm_table_get_num_targets(table)) {
+ ti = dm_table_get_target(table, i++);
+
+ if (!ti->type->iterate_devices)
+ return false;
+
+ ti->type->iterate_devices(ti, count_device, &num_devices);
+ if (num_devices)
+ return false;
+ }
+
+ return true;
+}
+
/*
* Establish the new table's queue_limits and validate them.
*/
@@ -1354,17 +1389,25 @@ static int device_is_nonrot(struct dm_target *ti, struct dm_dev *dev,
return q && blk_queue_nonrot(q);
}
-static bool dm_table_is_nonrot(struct dm_table *t)
+static int device_is_not_random(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+
+ return q && !blk_queue_add_random(q);
+}
+
+static bool dm_table_all_devices_attribute(struct dm_table *t,
+ iterate_devices_callout_fn func)
{
struct dm_target *ti;
unsigned i = 0;
- /* Ensure that all underlying device are non-rotational. */
while (i < dm_table_get_num_targets(t)) {
ti = dm_table_get_target(t, i++);
if (!ti->type->iterate_devices ||
- !ti->type->iterate_devices(ti, device_is_nonrot, NULL))
+ !ti->type->iterate_devices(ti, func, NULL))
return 0;
}
@@ -1396,7 +1439,8 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
if (!dm_table_discard_zeroes_data(t))
q->limits.discard_zeroes_data = 0;
- if (dm_table_is_nonrot(t))
+ /* Ensure that all underlying devices are non-rotational. */
+ if (dm_table_all_devices_attribute(t, device_is_nonrot))
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
else
queue_flag_clear_unlocked(QUEUE_FLAG_NONROT, q);
@@ -1404,6 +1448,15 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
dm_table_set_integrity(t);
/*
+ * Determine whether or not this queue's I/O timings contribute
+ * to the entropy pool, Only request-based targets use this.
+ * Clear QUEUE_FLAG_ADD_RANDOM if any underlying device does not
+ * have it set.
+ */
+ if (blk_queue_add_random(q) && dm_table_all_devices_attribute(t, device_is_not_random))
+ queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q);
+
+ /*
* QUEUE_FLAG_STACKABLE must be set after all queue settings are
* visible to other CPUs because, once the flag is set, incoming bios
* are processed by request-based dm, which refers to the queue
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index af1fc3b..c29410a 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -509,9 +509,9 @@ enum pool_mode {
struct pool_features {
enum pool_mode mode;
- unsigned zero_new_blocks:1;
- unsigned discard_enabled:1;
- unsigned discard_passdown:1;
+ bool zero_new_blocks:1;
+ bool discard_enabled:1;
+ bool discard_passdown:1;
};
struct thin_c;
@@ -580,7 +580,8 @@ struct pool_c {
struct dm_target_callbacks callbacks;
dm_block_t low_water_blocks;
- struct pool_features pf;
+ struct pool_features requested_pf; /* Features requested during table load */
+ struct pool_features adjusted_pf; /* Features used after adjusting for constituent devices */
};
/*
@@ -1839,6 +1840,47 @@ static void __requeue_bios(struct pool *pool)
/*----------------------------------------------------------------
* Binding of control targets to a pool object
*--------------------------------------------------------------*/
+static bool data_dev_supports_discard(struct pool_c *pt)
+{
+ struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
+
+ return q && blk_queue_discard(q);
+}
+
+/*
+ * If discard_passdown was enabled verify that the data device
+ * supports discards. Disable discard_passdown if not.
+ */
+static void disable_passdown_if_not_supported(struct pool_c *pt)
+{
+ struct pool *pool = pt->pool;
+ struct block_device *data_bdev = pt->data_dev->bdev;
+ struct queue_limits *data_limits = &bdev_get_queue(data_bdev)->limits;
+ sector_t block_size = pool->sectors_per_block << SECTOR_SHIFT;
+ const char *reason = NULL;
+ char buf[BDEVNAME_SIZE];
+
+ if (!pt->adjusted_pf.discard_passdown)
+ return;
+
+ if (!data_dev_supports_discard(pt))
+ reason = "discard unsupported";
+
+ else if (data_limits->max_discard_sectors < pool->sectors_per_block)
+ reason = "max discard sectors smaller than a block";
+
+ else if (data_limits->discard_granularity > block_size)
+ reason = "discard granularity larger than a block";
+
+ else if (block_size & (data_limits->discard_granularity - 1))
+ reason = "discard granularity not a factor of block size";
+
+ if (reason) {
+ DMWARN("Data device (%s) %s: Disabling discard passdown.", bdevname(data_bdev, buf), reason);
+ pt->adjusted_pf.discard_passdown = false;
+ }
+}
+
static int bind_control_target(struct pool *pool, struct dm_target *ti)
{
struct pool_c *pt = ti->private;
@@ -1847,31 +1889,16 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti)
* We want to make sure that degraded pools are never upgraded.
*/
enum pool_mode old_mode = pool->pf.mode;
- enum pool_mode new_mode = pt->pf.mode;
+ enum pool_mode new_mode = pt->adjusted_pf.mode;
if (old_mode > new_mode)
new_mode = old_mode;
pool->ti = ti;
pool->low_water_blocks = pt->low_water_blocks;
- pool->pf = pt->pf;
- set_pool_mode(pool, new_mode);
+ pool->pf = pt->adjusted_pf;
- /*
- * If discard_passdown was enabled verify that the data device
- * supports discards. Disable discard_passdown if not; otherwise
- * -EOPNOTSUPP will be returned.
- */
- /* FIXME: pull this out into a sep fn. */
- if (pt->pf.discard_passdown) {
- struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
- if (!q || !blk_queue_discard(q)) {
- char buf[BDEVNAME_SIZE];
- DMWARN("Discard unsupported by data device (%s): Disabling discard passdown.",
- bdevname(pt->data_dev->bdev, buf));
- pool->pf.discard_passdown = 0;
- }
- }
+ set_pool_mode(pool, new_mode);
return 0;
}
@@ -1889,9 +1916,9 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti)
static void pool_features_init(struct pool_features *pf)
{
pf->mode = PM_WRITE;
- pf->zero_new_blocks = 1;
- pf->discard_enabled = 1;
- pf->discard_passdown = 1;
+ pf->zero_new_blocks = true;
+ pf->discard_enabled = true;
+ pf->discard_passdown = true;
}
static void __pool_destroy(struct pool *pool)
@@ -2119,13 +2146,13 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
argc--;
if (!strcasecmp(arg_name, "skip_block_zeroing"))
- pf->zero_new_blocks = 0;
+ pf->zero_new_blocks = false;
else if (!strcasecmp(arg_name, "ignore_discard"))
- pf->discard_enabled = 0;
+ pf->discard_enabled = false;
else if (!strcasecmp(arg_name, "no_discard_passdown"))
- pf->discard_passdown = 0;
+ pf->discard_passdown = false;
else if (!strcasecmp(arg_name, "read_only"))
pf->mode = PM_READ_ONLY;
@@ -2259,8 +2286,9 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
pt->metadata_dev = metadata_dev;
pt->data_dev = data_dev;
pt->low_water_blocks = low_water_blocks;
- pt->pf = pf;
+ pt->adjusted_pf = pt->requested_pf = pf;
ti->num_flush_requests = 1;
+
/*
* Only need to enable discards if the pool should pass
* them down to the data device. The thin device's discard
@@ -2268,12 +2296,14 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
*/
if (pf.discard_enabled && pf.discard_passdown) {
ti->num_discard_requests = 1;
+
/*
* Setting 'discards_supported' circumvents the normal
* stacking of discard limits (this keeps the pool and
* thin devices' discard limits consistent).
*/
ti->discards_supported = true;
+ ti->discard_zeroes_data_unsupported = true;
}
ti->private = pt;
@@ -2703,7 +2733,7 @@ static int pool_status(struct dm_target *ti, status_type_t type,
format_dev_t(buf2, pt->data_dev->bdev->bd_dev),
(unsigned long)pool->sectors_per_block,
(unsigned long long)pt->low_water_blocks);
- emit_flags(&pt->pf, result, sz, maxlen);
+ emit_flags(&pt->requested_pf, result, sz, maxlen);
break;
}
@@ -2732,20 +2762,21 @@ static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
}
-static void set_discard_limits(struct pool *pool, struct queue_limits *limits)
+static void set_discard_limits(struct pool_c *pt, struct queue_limits *limits)
{
- /*
- * FIXME: these limits may be incompatible with the pool's data device
- */
+ struct pool *pool = pt->pool;
+ struct queue_limits *data_limits;
+
limits->max_discard_sectors = pool->sectors_per_block;
/*
- * This is just a hint, and not enforced. We have to cope with
- * bios that cover a block partially. A discard that spans a block
- * boundary is not sent to this target.
+ * discard_granularity is just a hint, and not enforced.
*/
- limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
- limits->discard_zeroes_data = pool->pf.zero_new_blocks;
+ if (pt->adjusted_pf.discard_passdown) {
+ data_limits = &bdev_get_queue(pt->data_dev->bdev)->limits;
+ limits->discard_granularity = data_limits->discard_granularity;
+ } else
+ limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
}
static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
@@ -2755,15 +2786,25 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
blk_limits_io_min(limits, 0);
blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
- if (pool->pf.discard_enabled)
- set_discard_limits(pool, limits);
+
+ /*
+ * pt->adjusted_pf is a staging area for the actual features to use.
+ * They get transferred to the live pool in bind_control_target()
+ * called from pool_preresume().
+ */
+ if (!pt->adjusted_pf.discard_enabled)
+ return;
+
+ disable_passdown_if_not_supported(pt);
+
+ set_discard_limits(pt, limits);
}
static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
@@ -3042,19 +3083,19 @@ static int thin_iterate_devices(struct dm_target *ti,
return 0;
}
+/*
+ * A thin device always inherits its queue limits from its pool.
+ */
static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct thin_c *tc = ti->private;
- struct pool *pool = tc->pool;
- blk_limits_io_min(limits, 0);
- blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
- set_discard_limits(pool, limits);
+ *limits = bdev_get_queue(tc->pool_dev->bdev)->limits;
}
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
index 254d192..892ae27 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity.c
@@ -718,8 +718,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
v->hash_dev_block_bits = ffs(num) - 1;
if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
- num_ll << (v->data_dev_block_bits - SECTOR_SHIFT) !=
- (sector_t)num_ll << (v->data_dev_block_bits - SECTOR_SHIFT)) {
+ (sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
+ >> (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll) {
ti->error = "Invalid data blocks";
r = -EINVAL;
goto bad;
@@ -733,8 +733,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
if (sscanf(argv[6], "%llu%c", &num_ll, &dummy) != 1 ||
- num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT) !=
- (sector_t)num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT)) {
+ (sector_t)(num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT))
+ >> (v->hash_dev_block_bits - SECTOR_SHIFT) != num_ll) {
ti->error = "Invalid hash start";
r = -EINVAL;
goto bad;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 4e09b6f..67ffa39 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -865,10 +865,14 @@ static void dm_done(struct request *clone, int error, bool mapped)
{
int r = error;
struct dm_rq_target_io *tio = clone->end_io_data;
- dm_request_endio_fn rq_end_io = tio->ti->type->rq_end_io;
+ dm_request_endio_fn rq_end_io = NULL;
- if (mapped && rq_end_io)
- r = rq_end_io(tio->ti, clone, error, &tio->info);
+ if (tio->ti) {
+ rq_end_io = tio->ti->type->rq_end_io;
+
+ if (mapped && rq_end_io)
+ r = rq_end_io(tio->ti, clone, error, &tio->info);
+ }
if (r <= 0)
/* The target wants to complete the I/O */
@@ -1588,15 +1592,6 @@ static int map_request(struct dm_target *ti, struct request *clone,
int r, requeued = 0;
struct dm_rq_target_io *tio = clone->end_io_data;
- /*
- * Hold the md reference here for the in-flight I/O.
- * We can't rely on the reference count by device opener,
- * because the device may be closed during the request completion
- * when all bios are completed.
- * See the comment in rq_completed() too.
- */
- dm_get(md);
-
tio->ti = ti;
r = ti->type->map_rq(ti, clone, &tio->info);
switch (r) {
@@ -1628,6 +1623,26 @@ static int map_request(struct dm_target *ti, struct request *clone,
return requeued;
}
+static struct request *dm_start_request(struct mapped_device *md, struct request *orig)
+{
+ struct request *clone;
+
+ blk_start_request(orig);
+ clone = orig->special;
+ atomic_inc(&md->pending[rq_data_dir(clone)]);
+
+ /*
+ * Hold the md reference here for the in-flight I/O.
+ * We can't rely on the reference count by device opener,
+ * because the device may be closed during the request completion
+ * when all bios are completed.
+ * See the comment in rq_completed() too.
+ */
+ dm_get(md);
+
+ return clone;
+}
+
/*
* q->request_fn for request-based dm.
* Called with the queue lock held.
@@ -1657,14 +1672,21 @@ static void dm_request_fn(struct request_queue *q)
pos = blk_rq_pos(rq);
ti = dm_table_find_target(map, pos);
- BUG_ON(!dm_target_is_valid(ti));
+ if (!dm_target_is_valid(ti)) {
+ /*
+ * Must perform setup, that dm_done() requires,
+ * before calling dm_kill_unmapped_request
+ */
+ DMERR_LIMIT("request attempted access beyond the end of device");
+ clone = dm_start_request(md, rq);
+ dm_kill_unmapped_request(clone, -EIO);
+ continue;
+ }
if (ti->type->busy && ti->type->busy(ti))
goto delay_and_out;
- blk_start_request(rq);
- clone = rq->special;
- atomic_inc(&md->pending[rq_data_dir(clone)]);
+ clone = dm_start_request(md, rq);
spin_unlock(q->queue_lock);
if (map_request(ti, clone, md))
@@ -1684,8 +1706,6 @@ delay_and_out:
blk_delay_queue(q, HZ / 10);
out:
dm_table_put(map);
-
- return;
}
int dm_underlying_device_busy(struct request_queue *q)
@@ -2409,7 +2429,7 @@ static void dm_queue_flush(struct mapped_device *md)
*/
struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
{
- struct dm_table *map = ERR_PTR(-EINVAL);
+ struct dm_table *live_map, *map = ERR_PTR(-EINVAL);
struct queue_limits limits;
int r;
@@ -2419,6 +2439,19 @@ struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
if (!dm_suspended_md(md))
goto out;
+ /*
+ * If the new table has no data devices, retain the existing limits.
+ * This helps multipath with queue_if_no_path if all paths disappear,
+ * then new I/O is queued based on these limits, and then some paths
+ * reappear.
+ */
+ if (dm_table_has_no_data_devices(table)) {
+ live_map = dm_get_live_table(md);
+ if (live_map)
+ limits = md->queue->limits;
+ dm_table_put(live_map);
+ }
+
r = dm_calculate_queue_limits(table, &limits);
if (r) {
map = ERR_PTR(r);
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 52eef49..6a99fef 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -54,6 +54,7 @@ void dm_table_event_callback(struct dm_table *t,
void (*fn)(void *), void *context);
struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
+bool dm_table_has_no_data_devices(struct dm_table *table);
int dm_calculate_queue_limits(struct dm_table *table,
struct queue_limits *limits);
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 3f6203a..308e87b 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -7619,6 +7619,8 @@ static int remove_and_add_spares(struct mddev *mddev)
}
}
}
+ if (removed)
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
return spares;
}
@@ -7632,9 +7634,11 @@ static void reap_sync_thread(struct mddev *mddev)
!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
/* success...*/
/* activate any spares */
- if (mddev->pers->spare_active(mddev))
+ if (mddev->pers->spare_active(mddev)) {
sysfs_notify(&mddev->kobj, NULL,
"degraded");
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
+ }
}
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
mddev->pers->finish_reshape)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 1c2eb38..0138a72 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1512,14 +1512,16 @@ static int _enough(struct r10conf *conf, struct geom *geo, int ignore)
do {
int n = conf->copies;
int cnt = 0;
+ int this = first;
while (n--) {
- if (conf->mirrors[first].rdev &&
- first != ignore)
+ if (conf->mirrors[this].rdev &&
+ this != ignore)
cnt++;
- first = (first+1) % geo->raid_disks;
+ this = (this+1) % geo->raid_disks;
}
if (cnt == 0)
return 0;
+ first = (first + geo->near_copies) % geo->raid_disks;
} while (first != 0);
return 1;
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index adda94d..0689173 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -393,6 +393,8 @@ static int calc_degraded(struct r5conf *conf)
degraded = 0;
for (i = 0; i < conf->previous_raid_disks; i++) {
struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
+ if (rdev && test_bit(Faulty, &rdev->flags))
+ rdev = rcu_dereference(conf->disks[i].replacement);
if (!rdev || test_bit(Faulty, &rdev->flags))
degraded++;
else if (test_bit(In_sync, &rdev->flags))
@@ -417,6 +419,8 @@ static int calc_degraded(struct r5conf *conf)
degraded2 = 0;
for (i = 0; i < conf->raid_disks; i++) {
struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
+ if (rdev && test_bit(Faulty, &rdev->flags))
+ rdev = rcu_dereference(conf->disks[i].replacement);
if (!rdev || test_bit(Faulty, &rdev->flags))
degraded2++;
else if (test_bit(In_sync, &rdev->flags))
@@ -1587,6 +1591,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
#ifdef CONFIG_MULTICORE_RAID456
init_waitqueue_head(&nsh->ops.wait_for_ops);
#endif
+ spin_lock_init(&nsh->stripe_lock);
list_add(&nsh->lru, &newstripes);
}
@@ -4192,7 +4197,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
finish_wait(&conf->wait_for_overlap, &w);
set_bit(STRIPE_HANDLE, &sh->state);
clear_bit(STRIPE_DELAYED, &sh->state);
- if ((bi->bi_rw & REQ_NOIDLE) &&
+ if ((bi->bi_rw & REQ_SYNC) &&
!test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
atomic_inc(&conf->preread_active_stripes);
release_stripe_plug(mddev, sh);
diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c
index 72ee8de..0a497be 100644
--- a/drivers/media/dvb/ngene/ngene-cards.c
+++ b/drivers/media/dvb/ngene/ngene-cards.c
@@ -524,7 +524,7 @@ static void ngene_resume(struct pci_dev *dev)
printk(KERN_INFO DEVICE_NAME ": resume\n");
}
-static struct pci_error_handlers ngene_errors = {
+static const struct pci_error_handlers ngene_errors = {
.error_detected = ngene_error_detected,
.link_reset = ngene_link_reset,
.slot_reset = ngene_slot_reset,
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 42b3ce9..9cce5d7 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -2,6 +2,9 @@
# Makefile for memory devices
#
+ifeq ($(CONFIG_DDR),y)
+obj-$(CONFIG_OF) += of_memory.o
+endif
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
index 33a4396..06d31c9 100644
--- a/drivers/memory/emif.c
+++ b/drivers/memory/emif.c
@@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/module.h>
@@ -25,6 +26,7 @@
#include <linux/spinlock.h>
#include <memory/jedec_ddr.h>
#include "emif.h"
+#include "of_memory.h"
/**
* struct emif_data - Per device static data for driver's use
@@ -49,6 +51,7 @@
* frequency in effect at the moment)
* @plat_data: Pointer to saved platform data.
* @debugfs_root: dentry to the root folder for EMIF in debugfs
+ * @np_ddr: Pointer to ddr device tree node
*/
struct emif_data {
u8 duplicate;
@@ -63,6 +66,7 @@ struct emif_data {
struct emif_regs *curr_regs;
struct emif_platform_data *plat_data;
struct dentry *debugfs_root;
+ struct device_node *np_ddr;
};
static struct emif_data *emif1;
@@ -71,6 +75,7 @@ static unsigned long irq_state;
static u32 t_ck; /* DDR clock period in ps */
static LIST_HEAD(device_list);
+#ifdef CONFIG_DEBUG_FS
static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif,
struct emif_regs *regs)
{
@@ -162,23 +167,23 @@ static int __init_or_module emif_debugfs_init(struct emif_data *emif)
int ret;
dentry = debugfs_create_dir(dev_name(emif->dev), NULL);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
+ if (!dentry) {
+ ret = -ENOMEM;
goto err0;
}
emif->debugfs_root = dentry;
dentry = debugfs_create_file("regcache_dump", S_IRUGO,
emif->debugfs_root, emif, &emif_regdump_fops);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
+ if (!dentry) {
+ ret = -ENOMEM;
goto err1;
}
dentry = debugfs_create_file("mr4", S_IRUGO,
emif->debugfs_root, emif, &emif_mr4_fops);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
+ if (!dentry) {
+ ret = -ENOMEM;
goto err1;
}
@@ -194,6 +199,16 @@ static void __exit emif_debugfs_exit(struct emif_data *emif)
debugfs_remove_recursive(emif->debugfs_root);
emif->debugfs_root = NULL;
}
+#else
+static inline int __init_or_module emif_debugfs_init(struct emif_data *emif)
+{
+ return 0;
+}
+
+static inline void __exit emif_debugfs_exit(struct emif_data *emif)
+{
+}
+#endif
/*
* Calculate the period of DDR clock from frequency value
@@ -1148,6 +1163,168 @@ static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs,
return valid;
}
+#if defined(CONFIG_OF)
+static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
+ struct emif_data *emif)
+{
+ struct emif_custom_configs *cust_cfgs = NULL;
+ int len;
+ const int *lpmode, *poll_intvl;
+
+ lpmode = of_get_property(np_emif, "low-power-mode", &len);
+ poll_intvl = of_get_property(np_emif, "temp-alert-poll-interval", &len);
+
+ if (lpmode || poll_intvl)
+ cust_cfgs = devm_kzalloc(emif->dev, sizeof(*cust_cfgs),
+ GFP_KERNEL);
+
+ if (!cust_cfgs)
+ return;
+
+ if (lpmode) {
+ cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_LPMODE;
+ cust_cfgs->lpmode = *lpmode;
+ of_property_read_u32(np_emif,
+ "low-power-mode-timeout-performance",
+ &cust_cfgs->lpmode_timeout_performance);
+ of_property_read_u32(np_emif,
+ "low-power-mode-timeout-power",
+ &cust_cfgs->lpmode_timeout_power);
+ of_property_read_u32(np_emif,
+ "low-power-mode-freq-threshold",
+ &cust_cfgs->lpmode_freq_threshold);
+ }
+
+ if (poll_intvl) {
+ cust_cfgs->mask |=
+ EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL;
+ cust_cfgs->temp_alert_poll_interval_ms = *poll_intvl;
+ }
+
+ if (!is_custom_config_valid(cust_cfgs, emif->dev)) {
+ devm_kfree(emif->dev, cust_cfgs);
+ return;
+ }
+
+ emif->plat_data->custom_configs = cust_cfgs;
+}
+
+static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
+ struct device_node *np_ddr,
+ struct ddr_device_info *dev_info)
+{
+ u32 density = 0, io_width = 0;
+ int len;
+
+ if (of_find_property(np_emif, "cs1-used", &len))
+ dev_info->cs1_used = true;
+
+ if (of_find_property(np_emif, "cal-resistor-per-cs", &len))
+ dev_info->cal_resistors_per_cs = true;
+
+ if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s4"))
+ dev_info->type = DDR_TYPE_LPDDR2_S4;
+ else if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s2"))
+ dev_info->type = DDR_TYPE_LPDDR2_S2;
+
+ of_property_read_u32(np_ddr, "density", &density);
+ of_property_read_u32(np_ddr, "io-width", &io_width);
+
+ /* Convert from density in Mb to the density encoding in jedc_ddr.h */
+ if (density & (density - 1))
+ dev_info->density = 0;
+ else
+ dev_info->density = __fls(density) - 5;
+
+ /* Convert from io_width in bits to io_width encoding in jedc_ddr.h */
+ if (io_width & (io_width - 1))
+ dev_info->io_width = 0;
+ else
+ dev_info->io_width = __fls(io_width) - 1;
+}
+
+static struct emif_data * __init_or_module of_get_memory_device_details(
+ struct device_node *np_emif, struct device *dev)
+{
+ struct emif_data *emif = NULL;
+ struct ddr_device_info *dev_info = NULL;
+ struct emif_platform_data *pd = NULL;
+ struct device_node *np_ddr;
+ int len;
+
+ np_ddr = of_parse_phandle(np_emif, "device-handle", 0);
+ if (!np_ddr)
+ goto error;
+ emif = devm_kzalloc(dev, sizeof(struct emif_data), GFP_KERNEL);
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL);
+
+ if (!emif || !pd || !dev_info) {
+ dev_err(dev, "%s: Out of memory!!\n",
+ __func__);
+ goto error;
+ }
+
+ emif->plat_data = pd;
+ pd->device_info = dev_info;
+ emif->dev = dev;
+ emif->np_ddr = np_ddr;
+ emif->temperature_level = SDRAM_TEMP_NOMINAL;
+
+ if (of_device_is_compatible(np_emif, "ti,emif-4d"))
+ emif->plat_data->ip_rev = EMIF_4D;
+ else if (of_device_is_compatible(np_emif, "ti,emif-4d5"))
+ emif->plat_data->ip_rev = EMIF_4D5;
+
+ of_property_read_u32(np_emif, "phy-type", &pd->phy_type);
+
+ if (of_find_property(np_emif, "hw-caps-ll-interface", &len))
+ pd->hw_caps |= EMIF_HW_CAPS_LL_INTERFACE;
+
+ of_get_ddr_info(np_emif, np_ddr, dev_info);
+ if (!is_dev_data_valid(pd->device_info->type, pd->device_info->density,
+ pd->device_info->io_width, pd->phy_type, pd->ip_rev,
+ emif->dev)) {
+ dev_err(dev, "%s: invalid device data!!\n", __func__);
+ goto error;
+ }
+ /*
+ * For EMIF instances other than EMIF1 see if the devices connected
+ * are exactly same as on EMIF1(which is typically the case). If so,
+ * mark it as a duplicate of EMIF1. This will save some memory and
+ * computation.
+ */
+ if (emif1 && emif1->np_ddr == np_ddr) {
+ emif->duplicate = true;
+ goto out;
+ } else if (emif1) {
+ dev_warn(emif->dev, "%s: Non-symmetric DDR geometry\n",
+ __func__);
+ }
+
+ of_get_custom_configs(np_emif, emif);
+ emif->plat_data->timings = of_get_ddr_timings(np_ddr, emif->dev,
+ emif->plat_data->device_info->type,
+ &emif->plat_data->timings_arr_size);
+
+ emif->plat_data->min_tck = of_get_min_tck(np_ddr, emif->dev);
+ goto out;
+
+error:
+ return NULL;
+out:
+ return emif;
+}
+
+#else
+
+static struct emif_data * __init_or_module of_get_memory_device_details(
+ struct device_node *np_emif, struct device *dev)
+{
+ return NULL;
+}
+#endif
+
static struct emif_data *__init_or_module get_device_details(
struct platform_device *pdev)
{
@@ -1267,7 +1444,11 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
struct resource *res;
int irq;
- emif = get_device_details(pdev);
+ if (pdev->dev.of_node)
+ emif = of_get_memory_device_details(pdev->dev.of_node, &pdev->dev);
+ else
+ emif = get_device_details(pdev);
+
if (!emif) {
pr_err("%s: error getting device data\n", __func__);
goto error;
@@ -1644,11 +1825,21 @@ static void __attribute__((unused)) freq_post_notify_handling(void)
spin_unlock_irqrestore(&emif_lock, irq_state);
}
+#if defined(CONFIG_OF)
+static const struct of_device_id emif_of_match[] = {
+ { .compatible = "ti,emif-4d" },
+ { .compatible = "ti,emif-4d5" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, emif_of_match);
+#endif
+
static struct platform_driver emif_driver = {
.remove = __exit_p(emif_remove),
.shutdown = emif_shutdown,
.driver = {
.name = "emif",
+ .of_match_table = of_match_ptr(emif_of_match),
},
};
diff --git a/drivers/memory/of_memory.c b/drivers/memory/of_memory.c
new file mode 100644
index 0000000..6007435
--- /dev/null
+++ b/drivers/memory/of_memory.c
@@ -0,0 +1,153 @@
+/*
+ * OpenFirmware helpers for memory drivers
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/gfp.h>
+#include <memory/jedec_ddr.h>
+#include <linux/export.h>
+
+/**
+ * of_get_min_tck() - extract min timing values for ddr
+ * @np: pointer to ddr device tree node
+ * @device: device requesting for min timing values
+ *
+ * Populates the lpddr2_min_tck structure by extracting data
+ * from device tree node. Returns a pointer to the populated
+ * structure. If any error in populating the structure, returns
+ * default min timings provided by JEDEC.
+ */
+const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
+ struct device *dev)
+{
+ int ret = 0;
+ struct lpddr2_min_tck *min;
+
+ min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL);
+ if (!min)
+ goto default_min_tck;
+
+ ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab);
+ ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD);
+ ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR);
+ ret |= of_property_read_u32(np, "tRASmin-min-tck", &min->tRASmin);
+ ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD);
+ ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR);
+ ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP);
+ ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP);
+ ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE);
+ ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR);
+ ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW);
+
+ if (ret) {
+ devm_kfree(dev, min);
+ goto default_min_tck;
+ }
+
+ return min;
+
+default_min_tck:
+ dev_warn(dev, "%s: using default min-tck values\n", __func__);
+ return &lpddr2_jedec_min_tck;
+}
+EXPORT_SYMBOL(of_get_min_tck);
+
+static int of_do_get_timings(struct device_node *np,
+ struct lpddr2_timings *tim)
+{
+ int ret;
+
+ ret = of_property_read_u32(np, "max-freq", &tim->max_freq);
+ ret |= of_property_read_u32(np, "min-freq", &tim->min_freq);
+ ret |= of_property_read_u32(np, "tRPab", &tim->tRPab);
+ ret |= of_property_read_u32(np, "tRCD", &tim->tRCD);
+ ret |= of_property_read_u32(np, "tWR", &tim->tWR);
+ ret |= of_property_read_u32(np, "tRAS-min", &tim->tRAS_min);
+ ret |= of_property_read_u32(np, "tRRD", &tim->tRRD);
+ ret |= of_property_read_u32(np, "tWTR", &tim->tWTR);
+ ret |= of_property_read_u32(np, "tXP", &tim->tXP);
+ ret |= of_property_read_u32(np, "tRTP", &tim->tRTP);
+ ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR);
+ ret |= of_property_read_u32(np, "tDQSCK-max", &tim->tDQSCK_max);
+ ret |= of_property_read_u32(np, "tFAW", &tim->tFAW);
+ ret |= of_property_read_u32(np, "tZQCS", &tim->tZQCS);
+ ret |= of_property_read_u32(np, "tZQCL", &tim->tZQCL);
+ ret |= of_property_read_u32(np, "tZQinit", &tim->tZQinit);
+ ret |= of_property_read_u32(np, "tRAS-max-ns", &tim->tRAS_max_ns);
+ ret |= of_property_read_u32(np, "tDQSCK-max-derated",
+ &tim->tDQSCK_max_derated);
+
+ return ret;
+}
+
+/**
+ * of_get_ddr_timings() - extracts the ddr timings and updates no of
+ * frequencies available.
+ * @np_ddr: Pointer to ddr device tree node
+ * @dev: Device requesting for ddr timings
+ * @device_type: Type of ddr(LPDDR2 S2/S4)
+ * @nr_frequencies: No of frequencies available for ddr
+ * (updated by this function)
+ *
+ * Populates lpddr2_timings structure by extracting data from device
+ * tree node. Returns pointer to populated structure. If any error
+ * while populating, returns default timings provided by JEDEC.
+ */
+const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
+ struct device *dev, u32 device_type, u32 *nr_frequencies)
+{
+ struct lpddr2_timings *timings = NULL;
+ u32 arr_sz = 0, i = 0;
+ struct device_node *np_tim;
+ char *tim_compat;
+
+ switch (device_type) {
+ case DDR_TYPE_LPDDR2_S2:
+ case DDR_TYPE_LPDDR2_S4:
+ tim_compat = "jedec,lpddr2-timings";
+ break;
+ default:
+ dev_warn(dev, "%s: un-supported memory type\n", __func__);
+ }
+
+ for_each_child_of_node(np_ddr, np_tim)
+ if (of_device_is_compatible(np_tim, tim_compat))
+ arr_sz++;
+
+ if (arr_sz)
+ timings = devm_kzalloc(dev, sizeof(*timings) * arr_sz,
+ GFP_KERNEL);
+
+ if (!timings)
+ goto default_timings;
+
+ for_each_child_of_node(np_ddr, np_tim) {
+ if (of_device_is_compatible(np_tim, tim_compat)) {
+ if (of_do_get_timings(np_tim, &timings[i])) {
+ devm_kfree(dev, timings);
+ goto default_timings;
+ }
+ i++;
+ }
+ }
+
+ *nr_frequencies = arr_sz;
+
+ return timings;
+
+default_timings:
+ dev_warn(dev, "%s: using default timings\n", __func__);
+ *nr_frequencies = ARRAY_SIZE(lpddr2_jedec_timings);
+ return lpddr2_jedec_timings;
+}
+EXPORT_SYMBOL(of_get_ddr_timings);
diff --git a/drivers/memory/of_memory.h b/drivers/memory/of_memory.h
new file mode 100644
index 0000000..ef2514f
--- /dev/null
+++ b/drivers/memory/of_memory.h
@@ -0,0 +1,36 @@
+/*
+ * OpenFirmware helpers for memory drivers
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __LINUX_MEMORY_OF_REG_H
+#define __LINUX_MEMORY_OF_REG_H
+
+#if defined(CONFIG_OF) && defined(CONFIG_DDR)
+extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
+ struct device *dev);
+extern const struct lpddr2_timings
+ *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
+ u32 device_type, u32 *nr_frequencies);
+#else
+static inline const struct lpddr2_min_tck
+ *of_get_min_tck(struct device_node *np, struct device *dev)
+{
+ return NULL;
+}
+
+static inline const struct lpddr2_timings
+ *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
+ u32 device_type, u32 *nr_frequencies)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF && CONFIG_DDR */
+
+#endif /* __LINUX_MEMORY_OF_REG_ */
diff --git a/drivers/memory/tegra20-mc.c b/drivers/memory/tegra20-mc.c
index 3ed49c1..e6764bb 100644
--- a/drivers/memory/tegra20-mc.c
+++ b/drivers/memory/tegra20-mc.c
@@ -57,7 +57,7 @@ static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
if (offs < 0x24)
val = readl(mc->regs[0] + offs);
- if (offs < 0x400)
+ else if (offs < 0x400)
val = readl(mc->regs[1] + offs - 0x3c);
return val;
@@ -65,14 +65,10 @@ static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
static inline void mc_writel(struct tegra20_mc *mc, u32 val, u32 offs)
{
- if (offs < 0x24) {
+ if (offs < 0x24)
writel(val, mc->regs[0] + offs);
- return;
- }
- if (offs < 0x400) {
+ else if (offs < 0x400)
writel(val, mc->regs[1] + offs - 0x3c);
- return;
- }
}
static const char * const tegra20_mc_client[] = {
diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c
index e56ff04..802b9ea 100644
--- a/drivers/memory/tegra30-mc.c
+++ b/drivers/memory/tegra30-mc.c
@@ -95,11 +95,11 @@ static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
if (offs < 0x10)
val = readl(mc->regs[0] + offs);
- if (offs < 0x1f0)
+ else if (offs < 0x1f0)
val = readl(mc->regs[1] + offs - 0x3c);
- if (offs < 0x228)
+ else if (offs < 0x228)
val = readl(mc->regs[2] + offs - 0x200);
- if (offs < 0x400)
+ else if (offs < 0x400)
val = readl(mc->regs[3] + offs - 0x284);
return val;
@@ -107,22 +107,14 @@ static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs)
{
- if (offs < 0x10) {
+ if (offs < 0x10)
writel(val, mc->regs[0] + offs);
- return;
- }
- if (offs < 0x1f0) {
+ else if (offs < 0x1f0)
writel(val, mc->regs[1] + offs - 0x3c);
- return;
- }
- if (offs < 0x228) {
+ else if (offs < 0x228)
writel(val, mc->regs[2] + offs - 0x200);
- return;
- }
- if (offs < 0x400) {
+ else if (offs < 0x400)
writel(val, mc->regs[3] + offs - 0x284);
- return;
- }
}
static const char * const tegra30_mc_client[] = {
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index 866f959..29d72a2 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -342,7 +342,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
/*
* Delay might be needed for ABB8500 cut 3.0, if not, remove
- * when hardware will be availible
+ * when hardware will be available
*/
msleep(1);
break;
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 0e63cdd..6b67edb 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -418,6 +418,9 @@ static struct {
static atomic_t ac_wake_req_state = ATOMIC_INIT(0);
+/* Functions definition */
+static void compute_armss_rate(void);
+
/* Spinlocks */
static DEFINE_SPINLOCK(prcmu_lock);
static DEFINE_SPINLOCK(clkout_lock);
@@ -517,6 +520,7 @@ static struct dsiescclk dsiescclk[3] = {
}
};
+
/*
* Used by MCDE to setup all necessary PRCMU registers
*/
@@ -1013,6 +1017,7 @@ int db8500_prcmu_set_arm_opp(u8 opp)
(mb1_transfer.ack.arm_opp != opp))
r = -EIO;
+ compute_armss_rate();
mutex_unlock(&mb1_transfer.lock);
return r;
@@ -1612,6 +1617,7 @@ static unsigned long pll_rate(void __iomem *reg, unsigned long src_rate,
if ((branch == PLL_FIX) || ((branch == PLL_DIV) &&
(val & PRCM_PLL_FREQ_DIV2EN) &&
((reg == PRCM_PLLSOC0_FREQ) ||
+ (reg == PRCM_PLLARM_FREQ) ||
(reg == PRCM_PLLDDR_FREQ))))
div *= 2;
@@ -1661,6 +1667,39 @@ static unsigned long clock_rate(u8 clock)
else
return 0;
}
+static unsigned long latest_armss_rate;
+static unsigned long armss_rate(void)
+{
+ return latest_armss_rate;
+}
+
+static void compute_armss_rate(void)
+{
+ u32 r;
+ unsigned long rate;
+
+ r = readl(PRCM_ARM_CHGCLKREQ);
+
+ if (r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ) {
+ /* External ARMCLKFIX clock */
+
+ rate = pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_FIX);
+
+ /* Check PRCM_ARM_CHGCLKREQ divider */
+ if (!(r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL))
+ rate /= 2;
+
+ /* Check PRCM_ARMCLKFIX_MGT divider */
+ r = readl(PRCM_ARMCLKFIX_MGT);
+ r &= PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ rate /= r;
+
+ } else {/* ARM PLL */
+ rate = pll_rate(PRCM_PLLARM_FREQ, ROOT_CLOCK_RATE, PLL_DIV);
+ }
+
+ latest_armss_rate = rate;
+}
static unsigned long dsiclk_rate(u8 n)
{
@@ -1707,6 +1746,8 @@ unsigned long prcmu_clock_rate(u8 clock)
return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
else if (clock == PRCMU_PLLSOC1)
return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
+ else if (clock == PRCMU_ARMSS)
+ return armss_rate();
else if (clock == PRCMU_PLLDDR)
return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
else if (clock == PRCMU_PLLDSI)
@@ -2693,6 +2734,7 @@ void __init db8500_prcmu_early_init(void)
handle_simple_irq);
set_irq_flags(irq, IRQF_VALID);
}
+ compute_armss_rate();
}
static void __init init_prcm_registers(void)
diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h
index 23108a6..79c76eb 100644
--- a/drivers/mfd/dbx500-prcmu-regs.h
+++ b/drivers/mfd/dbx500-prcmu-regs.h
@@ -61,7 +61,8 @@
#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2
#define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114)
-#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1
+#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ BIT(0)
+#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL BIT(16)
#define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98)
#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1
@@ -140,6 +141,7 @@
/* PRCMU clock/PLL/reset registers */
#define PRCM_PLLSOC0_FREQ (_PRCMU_BASE + 0x080)
#define PRCM_PLLSOC1_FREQ (_PRCMU_BASE + 0x084)
+#define PRCM_PLLARM_FREQ (_PRCMU_BASE + 0x088)
#define PRCM_PLLDDR_FREQ (_PRCMU_BASE + 0x08C)
#define PRCM_PLL_FREQ_D_SHIFT 0
#define PRCM_PLL_FREQ_D_MASK BITS(0, 7)
diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c
index 3a8fa88..ff61efc 100644
--- a/drivers/mfd/rc5t583.c
+++ b/drivers/mfd/rc5t583.c
@@ -281,7 +281,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
if (i2c->irq) {
ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base);
- /* Still continue with waring if irq init fails */
+ /* Still continue with warning, if irq init fails */
if (ret)
dev_warn(&i2c->dev, "IRQ init failed: %d\n", ret);
else
diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c
index 0f70dce..fbabc3cb 100644
--- a/drivers/mfd/rdc321x-southbridge.c
+++ b/drivers/mfd/rdc321x-southbridge.c
@@ -1,5 +1,5 @@
/*
- * RDC321x MFD southbrige driver
+ * RDC321x MFD southbridge driver
*
* Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
* Copyright (C) 2010 Bernhard Loos <bernhardloos@googlemail.com>
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index 5f58370..345960ca 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -25,6 +25,7 @@
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps6586x.h>
@@ -346,6 +347,7 @@ failed:
#ifdef CONFIG_OF
static struct of_regulator_match tps6586x_matches[] = {
+ { .name = "sys", .driver_data = (void *)TPS6586X_ID_SYS },
{ .name = "sm0", .driver_data = (void *)TPS6586X_ID_SM_0 },
{ .name = "sm1", .driver_data = (void *)TPS6586X_ID_SM_1 },
{ .name = "sm2", .driver_data = (void *)TPS6586X_ID_SM_2 },
@@ -369,6 +371,7 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien
struct tps6586x_platform_data *pdata;
struct tps6586x_subdev_info *devs;
struct device_node *regs;
+ const char *sys_rail_name = NULL;
unsigned int count;
unsigned int i, j;
int err;
@@ -391,12 +394,22 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien
return NULL;
for (i = 0, j = 0; i < num && j < count; i++) {
+ struct regulator_init_data *reg_idata;
+
if (!tps6586x_matches[i].init_data)
continue;
+ reg_idata = tps6586x_matches[i].init_data;
devs[j].name = "tps6586x-regulator";
devs[j].platform_data = tps6586x_matches[i].init_data;
devs[j].id = (int)tps6586x_matches[i].driver_data;
+ if (devs[j].id == TPS6586X_ID_SYS)
+ sys_rail_name = reg_idata->constraints.name;
+
+ if ((devs[j].id == TPS6586X_ID_LDO_5) ||
+ (devs[j].id == TPS6586X_ID_LDO_RTC))
+ reg_idata->supply_regulator = sys_rail_name;
+
devs[j].of_node = tps6586x_matches[i].of_node;
j++;
}
diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c
index 5a62e6bf..0b6e361 100644
--- a/drivers/mfd/tps65911-comparator.c
+++ b/drivers/mfd/tps65911-comparator.c
@@ -136,7 +136,7 @@ static __devinit int tps65911_comparator_probe(struct platform_device *pdev)
ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold);
if (ret < 0) {
- dev_err(&pdev->dev, "cannot set COMP2 theshold\n");
+ dev_err(&pdev->dev, "cannot set COMP2 threshold\n");
return ret;
}
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index 0aac4af..a050e56 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -135,6 +135,7 @@ static struct regmap_irq_chip wm8994_irq_chip = {
.status_base = WM8994_INTERRUPT_STATUS_1,
.mask_base = WM8994_INTERRUPT_STATUS_1_MASK,
.ack_base = WM8994_INTERRUPT_STATUS_1,
+ .runtime_pm = true,
};
int wm8994_irq_init(struct wm8994 *wm8994)
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 98a442d..99c7335 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -105,7 +105,7 @@ config ATMEL_TCB_CLKSRC_BLOCK
config IBM_ASM
tristate "Device driver for IBM RSA service processor"
- depends on X86 && PCI && INPUT && EXPERIMENTAL
+ depends on X86 && PCI && INPUT
---help---
This option enables device driver support for in-band access to the
IBM RSA (Condor) service processor in eServer xSeries systems.
@@ -162,8 +162,8 @@ config SGI_IOC4
Otherwise say N.
config TIFM_CORE
- tristate "TI Flash Media interface support (EXPERIMENTAL)"
- depends on EXPERIMENTAL && PCI
+ tristate "TI Flash Media interface support"
+ depends on PCI
help
If you want support for Texas Instruments(R) Flash Media adapters
you should select this option and then also choose an appropriate
@@ -178,8 +178,8 @@ config TIFM_CORE
be called tifm_core.
config TIFM_7XX1
- tristate "TI Flash Media PCI74xx/PCI76xx host adapter support (EXPERIMENTAL)"
- depends on PCI && TIFM_CORE && EXPERIMENTAL
+ tristate "TI Flash Media PCI74xx/PCI76xx host adapter support"
+ depends on PCI && TIFM_CORE
default TIFM_CORE
help
This option enables support for Texas Instruments(R) PCI74xx and
@@ -192,7 +192,7 @@ config TIFM_7XX1
config ICS932S401
tristate "Integrated Circuits ICS932S401"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Integrated Circuits
ICS932S401 clock control chips.
@@ -398,7 +398,7 @@ config EP93XX_PWM
config DS1682
tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for Dallas Semiconductor
DS1682 Total Elapsed Time Recorder.
diff --git a/drivers/misc/bmp085-i2c.c b/drivers/misc/bmp085-i2c.c
index 9943971..a4f33c9 100644
--- a/drivers/misc/bmp085-i2c.c
+++ b/drivers/misc/bmp085-i2c.c
@@ -57,12 +57,6 @@ static int bmp085_i2c_remove(struct i2c_client *client)
return bmp085_remove(&client->dev);
}
-static const struct of_device_id bmp085_of_match[] = {
- { .compatible = "bosch,bmp085", },
- { },
-};
-MODULE_DEVICE_TABLE(of, bmp085_of_match);
-
static const struct i2c_device_id bmp085_id[] = {
{ BMP085_NAME, 0 },
{ "bmp180", 0 },
@@ -74,7 +68,6 @@ static struct i2c_driver bmp085_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = BMP085_NAME,
- .of_match_table = bmp085_of_match
},
.id_table = bmp085_id,
.probe = bmp085_i2c_probe,
diff --git a/drivers/misc/bmp085-spi.c b/drivers/misc/bmp085-spi.c
index 78aaff9..5e982af 100644
--- a/drivers/misc/bmp085-spi.c
+++ b/drivers/misc/bmp085-spi.c
@@ -73,19 +73,8 @@ static struct spi_driver bmp085_spi_driver = {
.remove = __devexit_p(bmp085_spi_remove)
};
-static int __init bmp085_spi_init(void)
-{
- return spi_register_driver(&bmp085_spi_driver);
-}
-
-static void __exit bmp085_spi_exit(void)
-{
- spi_unregister_driver(&bmp085_spi_driver);
-}
+module_spi_driver(bmp085_spi_driver);
MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
MODULE_DESCRIPTION("BMP085 SPI bus driver");
MODULE_LICENSE("GPL");
-
-module_init(bmp085_spi_init);
-module_exit(bmp085_spi_exit);
diff --git a/drivers/misc/c2port/Kconfig b/drivers/misc/c2port/Kconfig
index 33ee834..0dd690e 100644
--- a/drivers/misc/c2port/Kconfig
+++ b/drivers/misc/c2port/Kconfig
@@ -3,8 +3,7 @@
#
menuconfig C2PORT
- tristate "Silicon Labs C2 port support (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ tristate "Silicon Labs C2 port support"
default n
help
This option enables support for Silicon Labs C2 port used to
@@ -22,7 +21,7 @@ menuconfig C2PORT
if C2PORT
config C2PORT_DURAMAR_2150
- tristate "C2 port support for Eurotech's Duramar 2150 (EXPERIMENTAL)"
+ tristate "C2 port support for Eurotech's Duramar 2150"
depends on X86
default n
help
diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c
index a2d25e4..eaddfe9 100644
--- a/drivers/misc/carma/carma-fpga-program.c
+++ b/drivers/misc/carma/carma-fpga-program.c
@@ -978,7 +978,6 @@ static int fpga_of_probe(struct platform_device *op)
dev_set_drvdata(priv->dev, priv);
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
- dma_cap_set(DMA_INTERRUPT, mask);
dma_cap_set(DMA_SLAVE, mask);
dma_cap_set(DMA_SG, mask);
diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c
index 8c279da..0c43297 100644
--- a/drivers/misc/carma/carma-fpga.c
+++ b/drivers/misc/carma/carma-fpga.c
@@ -666,7 +666,7 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf)
src = SYS_FPGA_BLOCK;
tx = chan->device->device_prep_dma_memcpy(chan, dst, src,
REG_BLOCK_SIZE,
- DMA_PREP_INTERRUPT);
+ 0);
if (!tx) {
dev_err(priv->dev, "unable to prep SYS-FPGA DMA\n");
return -ENOMEM;
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 701edf6..c9e695e 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -50,7 +50,7 @@ config EEPROM_LEGACY
config EEPROM_MAX6875
tristate "Maxim MAX6874/5 power supply supervisor"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get read-only support for the user EEPROM of
the Maxim MAX6874/5 EEPROM-programmable, quad power-supply
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index 25003d6..4ed93dd 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -302,6 +302,61 @@ static ssize_t at25_mem_write(struct memory_accessor *mem, const char *buf,
/*-------------------------------------------------------------------------*/
+static int at25_np_to_chip(struct device *dev,
+ struct device_node *np,
+ struct spi_eeprom *chip)
+{
+ u32 val;
+
+ memset(chip, 0, sizeof(*chip));
+ strncpy(chip->name, np->name, sizeof(chip->name));
+
+ if (of_property_read_u32(np, "size", &val) == 0 ||
+ of_property_read_u32(np, "at25,byte-len", &val) == 0) {
+ chip->byte_len = val;
+ } else {
+ dev_err(dev, "Error: missing \"size\" property\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(np, "pagesize", &val) == 0 ||
+ of_property_read_u32(np, "at25,page-size", &val) == 0) {
+ chip->page_size = (u16)val;
+ } else {
+ dev_err(dev, "Error: missing \"pagesize\" property\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(np, "at25,addr-mode", &val) == 0) {
+ chip->flags = (u16)val;
+ } else {
+ if (of_property_read_u32(np, "address-width", &val)) {
+ dev_err(dev,
+ "Error: missing \"address-width\" property\n");
+ return -ENODEV;
+ }
+ switch (val) {
+ case 8:
+ chip->flags |= EE_ADDR1;
+ break;
+ case 16:
+ chip->flags |= EE_ADDR2;
+ break;
+ case 24:
+ chip->flags |= EE_ADDR3;
+ break;
+ default:
+ dev_err(dev,
+ "Error: bad \"address-width\" property: %u\n",
+ val);
+ return -ENODEV;
+ }
+ if (of_find_property(np, "read-only", NULL))
+ chip->flags |= EE_READONLY;
+ }
+ return 0;
+}
+
static int at25_probe(struct spi_device *spi)
{
struct at25_data *at25 = NULL;
@@ -314,33 +369,11 @@ static int at25_probe(struct spi_device *spi)
/* Chip description */
if (!spi->dev.platform_data) {
if (np) {
- u32 val;
-
- memset(&chip, 0, sizeof(chip));
- strncpy(chip.name, np->name, 10);
-
- err = of_property_read_u32(np, "at25,byte-len", &val);
- if (err) {
- dev_dbg(&spi->dev, "invalid chip dt description\n");
- goto fail;
- }
- chip.byte_len = val;
-
- err = of_property_read_u32(np, "at25,addr-mode", &val);
- if (err) {
- dev_dbg(&spi->dev, "invalid chip dt description\n");
- goto fail;
- }
- chip.flags = (u16)val;
-
- err = of_property_read_u32(np, "at25,page-size", &val);
- if (err) {
- dev_dbg(&spi->dev, "invalid chip dt description\n");
+ err = at25_np_to_chip(&spi->dev, np, &chip);
+ if (err)
goto fail;
- }
- chip.page_size = (u16)val;
} else {
- dev_dbg(&spi->dev, "no chip description\n");
+ dev_err(&spi->dev, "Error: no chip description\n");
err = -ENODEV;
goto fail;
}
diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c
index 6df0da4..12ccdf9 100644
--- a/drivers/misc/hpilo.c
+++ b/drivers/misc/hpilo.c
@@ -736,7 +736,14 @@ static void ilo_remove(struct pci_dev *pdev)
free_irq(pdev->irq, ilo_hw);
ilo_unmap_device(pdev, ilo_hw);
pci_release_regions(pdev);
- pci_disable_device(pdev);
+ /*
+ * pci_disable_device(pdev) used to be here. But this PCI device has
+ * two functions with interrupt lines connected to a single pin. The
+ * other one is a USB host controller. So when we disable the PIN here
+ * e.g. by rmmod hpilo, the controller stops working. It is because
+ * the interrupt link is disabled in ACPI since it is not refcounted
+ * yet. See acpi_pci_link_free_irq called from acpi_pci_irq_disable.
+ */
kfree(ilo_hw);
ilo_hwdev[(minor / max_ccb)] = 0;
}
@@ -826,7 +833,7 @@ unmap:
free_regions:
pci_release_regions(pdev);
disable:
- pci_disable_device(pdev);
+/* pci_disable_device(pdev); see comment in ilo_remove */
free:
kfree(ilo_hw);
out:
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c
index a981e2a..4a87e5c 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d.c
@@ -39,6 +39,7 @@
#include <linux/miscdevice.h>
#include <linux/pm_runtime.h>
#include <linux/atomic.h>
+#include <linux/of_device.h>
#include "lis3lv02d.h"
#define DRIVER_NAME "lis3lv02d"
@@ -80,6 +81,15 @@
#define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024)
#define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY)
+/*
+ * LIS331DLH spec says 1LSBs corresponds 4G/4096 -> 1LSB is 1000/1024 mG.
+ * Below macros defines sensitivity values for +/-2G. Dataout bits for
+ * +/-2G range is 12 bits so 4 bits adjustment must be done to get 12bit
+ * data from 16bit value. Currently this driver supports only 2G range.
+ */
+#define LIS3DLH_SENSITIVITY_2G ((LIS3_ACCURACY * 1000) / 1024)
+#define SHIFT_ADJ_2G 4
+
#define LIS3_DEFAULT_FUZZ_12B 3
#define LIS3_DEFAULT_FLAT_12B 3
#define LIS3_DEFAULT_FUZZ_8B 1
@@ -135,6 +145,19 @@ static s16 lis3lv02d_read_12(struct lis3lv02d *lis3, int reg)
return (s16)((hi << 8) | lo);
}
+/* 12bits for 2G range, 13 bits for 4G range and 14 bits for 8G range */
+static s16 lis331dlh_read_data(struct lis3lv02d *lis3, int reg)
+{
+ u8 lo, hi;
+ int v;
+
+ lis3->read(lis3, reg - 1, &lo);
+ lis3->read(lis3, reg, &hi);
+ v = (int) ((hi << 8) | lo);
+
+ return (s16) v >> lis3->shift_adj;
+}
+
/**
* lis3lv02d_get_axis - For the given axis, give the value converted
* @axis: 1,2,3 - can also be negative
@@ -195,6 +218,7 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
static int lis3_12_rates[4] = {40, 160, 640, 2560};
static int lis3_8_rates[2] = {100, 400};
static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000};
+static int lis3_3dlh_rates[4] = {50, 100, 400, 1000};
/* ODR is Output Data Rate */
static int lis3lv02d_get_odr(struct lis3lv02d *lis3)
@@ -267,7 +291,7 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
(LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY));
}
- if (lis3->whoami == WAI_3DC) {
+ if ((lis3->whoami == WAI_3DC) || (lis3->whoami == WAI_3DLH)) {
ctlreg = CTRL_REG4;
selftest = CTRL4_ST0;
} else {
@@ -398,9 +422,17 @@ int lis3lv02d_poweron(struct lis3lv02d *lis3)
lis3->read(lis3, CTRL_REG2, &reg);
if (lis3->whoami == WAI_12B)
reg |= CTRL2_BDU | CTRL2_BOOT;
+ else if (lis3->whoami == WAI_3DLH)
+ reg |= CTRL2_BOOT_3DLH;
else
reg |= CTRL2_BOOT_8B;
lis3->write(lis3, CTRL_REG2, reg);
+
+ if (lis3->whoami == WAI_3DLH) {
+ lis3->read(lis3, CTRL_REG4, &reg);
+ reg |= CTRL4_BDU;
+ lis3->write(lis3, CTRL_REG4, reg);
+ }
}
err = lis3lv02d_get_pwron_wait(lis3);
@@ -912,6 +944,154 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *lis3,
}
}
+#ifdef CONFIG_OF
+int lis3lv02d_init_dt(struct lis3lv02d *lis3)
+{
+ struct lis3lv02d_platform_data *pdata;
+ struct device_node *np = lis3->of_node;
+ u32 val;
+
+ if (!lis3->of_node)
+ return 0;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (of_get_property(np, "st,click-single-x", NULL))
+ pdata->click_flags |= LIS3_CLICK_SINGLE_X;
+ if (of_get_property(np, "st,click-double-x", NULL))
+ pdata->click_flags |= LIS3_CLICK_DOUBLE_X;
+
+ if (of_get_property(np, "st,click-single-y", NULL))
+ pdata->click_flags |= LIS3_CLICK_SINGLE_Y;
+ if (of_get_property(np, "st,click-double-y", NULL))
+ pdata->click_flags |= LIS3_CLICK_DOUBLE_Y;
+
+ if (of_get_property(np, "st,click-single-z", NULL))
+ pdata->click_flags |= LIS3_CLICK_SINGLE_Z;
+ if (of_get_property(np, "st,click-double-z", NULL))
+ pdata->click_flags |= LIS3_CLICK_DOUBLE_Z;
+
+ if (!of_property_read_u32(np, "st,click-threshold-x", &val))
+ pdata->click_thresh_x = val;
+ if (!of_property_read_u32(np, "st,click-threshold-y", &val))
+ pdata->click_thresh_y = val;
+ if (!of_property_read_u32(np, "st,click-threshold-z", &val))
+ pdata->click_thresh_z = val;
+
+ if (!of_property_read_u32(np, "st,click-time-limit", &val))
+ pdata->click_time_limit = val;
+ if (!of_property_read_u32(np, "st,click-latency", &val))
+ pdata->click_latency = val;
+ if (!of_property_read_u32(np, "st,click-window", &val))
+ pdata->click_window = val;
+
+ if (of_get_property(np, "st,irq1-disable", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_DISABLE;
+ if (of_get_property(np, "st,irq1-ff-wu-1", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_FF_WU_1;
+ if (of_get_property(np, "st,irq1-ff-wu-2", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_FF_WU_2;
+ if (of_get_property(np, "st,irq1-data-ready", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_DATA_READY;
+ if (of_get_property(np, "st,irq1-click", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_CLICK;
+
+ if (of_get_property(np, "st,irq2-disable", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_DISABLE;
+ if (of_get_property(np, "st,irq2-ff-wu-1", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_FF_WU_1;
+ if (of_get_property(np, "st,irq2-ff-wu-2", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_FF_WU_2;
+ if (of_get_property(np, "st,irq2-data-ready", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_DATA_READY;
+ if (of_get_property(np, "st,irq2-click", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_CLICK;
+
+ if (of_get_property(np, "st,irq-open-drain", NULL))
+ pdata->irq_cfg |= LIS3_IRQ_OPEN_DRAIN;
+ if (of_get_property(np, "st,irq-active-low", NULL))
+ pdata->irq_cfg |= LIS3_IRQ_ACTIVE_LOW;
+
+ if (!of_property_read_u32(np, "st,wu-duration-1", &val))
+ pdata->duration1 = val;
+ if (!of_property_read_u32(np, "st,wu-duration-2", &val))
+ pdata->duration2 = val;
+
+ if (of_get_property(np, "st,wakeup-x-lo", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_X_LO;
+ if (of_get_property(np, "st,wakeup-x-hi", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_X_HI;
+ if (of_get_property(np, "st,wakeup-y-lo", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Y_LO;
+ if (of_get_property(np, "st,wakeup-y-hi", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Y_HI;
+ if (of_get_property(np, "st,wakeup-z-lo", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Z_LO;
+ if (of_get_property(np, "st,wakeup-z-hi", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Z_HI;
+
+ if (!of_property_read_u32(np, "st,highpass-cutoff-hz", &val)) {
+ switch (val) {
+ case 1:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_1HZ;
+ break;
+ case 2:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_2HZ;
+ break;
+ case 4:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_4HZ;
+ break;
+ case 8:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_8HZ;
+ break;
+ }
+ }
+
+ if (of_get_property(np, "st,hipass1-disable", NULL))
+ pdata->hipass_ctrl |= LIS3_HIPASS1_DISABLE;
+ if (of_get_property(np, "st,hipass2-disable", NULL))
+ pdata->hipass_ctrl |= LIS3_HIPASS2_DISABLE;
+
+ if (of_get_property(np, "st,axis-x", &val))
+ pdata->axis_x = val;
+ if (of_get_property(np, "st,axis-y", &val))
+ pdata->axis_y = val;
+ if (of_get_property(np, "st,axis-z", &val))
+ pdata->axis_z = val;
+
+ if (of_get_property(np, "st,default-rate", NULL))
+ pdata->default_rate = val;
+
+ if (of_get_property(np, "st,min-limit-x", &val))
+ pdata->st_min_limits[0] = val;
+ if (of_get_property(np, "st,min-limit-y", &val))
+ pdata->st_min_limits[1] = val;
+ if (of_get_property(np, "st,min-limit-z", &val))
+ pdata->st_min_limits[2] = val;
+
+ if (of_get_property(np, "st,max-limit-x", &val))
+ pdata->st_max_limits[0] = val;
+ if (of_get_property(np, "st,max-limit-y", &val))
+ pdata->st_max_limits[1] = val;
+ if (of_get_property(np, "st,max-limit-z", &val))
+ pdata->st_max_limits[2] = val;
+
+
+ lis3->pdata = pdata;
+
+ return 0;
+}
+
+#else
+int lis3lv02d_init_dt(struct lis3lv02d *lis3)
+{
+ return 0;
+}
+#endif
+EXPORT_SYMBOL_GPL(lis3lv02d_init_dt);
+
/*
* Initialise the accelerometer and the various subsystems.
* Should be rather independent of the bus system.
@@ -956,6 +1136,16 @@ int lis3lv02d_init_device(struct lis3lv02d *lis3)
lis3->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;
lis3->scale = LIS3_SENSITIVITY_8B;
break;
+ case WAI_3DLH:
+ pr_info("16 bits lis331dlh sensor found\n");
+ lis3->read_data = lis331dlh_read_data;
+ lis3->mdps_max_val = 2048; /* 12 bits for 2G */
+ lis3->shift_adj = SHIFT_ADJ_2G;
+ lis3->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
+ lis3->odrs = lis3_3dlh_rates;
+ lis3->odr_mask = CTRL1_DR0 | CTRL1_DR1;
+ lis3->scale = LIS3DLH_SENSITIVITY_2G;
+ break;
default:
pr_err("unknown sensor type 0x%X\n", lis3->whoami);
return -EINVAL;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.h b/drivers/misc/lis3lv02d/lis3lv02d.h
index 2b1482a..c439c82 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.h
+++ b/drivers/misc/lis3lv02d/lis3lv02d.h
@@ -26,12 +26,12 @@
/*
* This driver tries to support the "digital" accelerometer chips from
* STMicroelectronics such as LIS3LV02DL, LIS302DL, LIS3L02DQ, LIS331DL,
- * LIS35DE, or LIS202DL. They are very similar in terms of programming, with
- * almost the same registers. In addition to differing on physical properties,
- * they differ on the number of axes (2/3), precision (8/12 bits), and special
- * features (freefall detection, click...). Unfortunately, not all the
- * differences can be probed via a register.
- * They can be connected either via I²C or SPI.
+ * LIS331DLH, LIS35DE, or LIS202DL. They are very similar in terms of
+ * programming, with almost the same registers. In addition to differing
+ * on physical properties, they differ on the number of axes (2/3),
+ * precision (8/12 bits), and special features (freefall detection,
+ * click...). Unfortunately, not all the differences can be probed via
+ * a register. They can be connected either via I²C or SPI.
*/
#include <linux/lis3lv02d.h>
@@ -96,12 +96,22 @@ enum lis3lv02d_reg {
};
enum lis3_who_am_i {
+ WAI_3DLH = 0x32, /* 16 bits: LIS331DLH */
WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */
WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */
WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */
WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */
};
+enum lis3_type {
+ LIS3LV02D,
+ LIS3DC,
+ HP3DC,
+ LIS2302D,
+ LIS331DLF,
+ LIS331DLH,
+};
+
enum lis3lv02d_ctrl1_12b {
CTRL1_Xen = 0x01,
CTRL1_Yen = 0x02,
@@ -129,6 +139,27 @@ enum lis3lv02d_ctrl1_3dc {
CTRL1_ODR3 = 0x80,
};
+enum lis331dlh_ctrl1 {
+ CTRL1_DR0 = 0x08,
+ CTRL1_DR1 = 0x10,
+ CTRL1_PM0 = 0x20,
+ CTRL1_PM1 = 0x40,
+ CTRL1_PM2 = 0x80,
+};
+
+enum lis331dlh_ctrl2 {
+ CTRL2_HPEN1 = 0x04,
+ CTRL2_HPEN2 = 0x08,
+ CTRL2_FDS_3DLH = 0x10,
+ CTRL2_BOOT_3DLH = 0x80,
+};
+
+enum lis331dlh_ctrl4 {
+ CTRL4_STSIGN = 0x08,
+ CTRL4_BLE = 0x40,
+ CTRL4_BDU = 0x80,
+};
+
enum lis3lv02d_ctrl2 {
CTRL2_DAS = 0x01,
CTRL2_SIM = 0x02,
@@ -279,9 +310,14 @@ struct lis3lv02d {
int data_ready_count[2];
atomic_t wake_thread;
unsigned char irq_cfg;
+ unsigned int shift_adj;
struct lis3lv02d_platform_data *pdata; /* for passing board config */
struct mutex mutex; /* Serialize poll and selftest */
+
+#ifdef CONFIG_OF
+ struct device_node *of_node;
+#endif
};
int lis3lv02d_init_device(struct lis3lv02d *lis3);
@@ -290,5 +326,6 @@ void lis3lv02d_joystick_disable(struct lis3lv02d *lis3);
void lis3lv02d_poweroff(struct lis3lv02d *lis3);
int lis3lv02d_poweron(struct lis3lv02d *lis3);
int lis3lv02d_remove_fs(struct lis3lv02d *lis3);
+int lis3lv02d_init_dt(struct lis3lv02d *lis3);
extern struct lis3lv02d lis3_dev;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
index e8c0019..60ec868 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
@@ -31,6 +31,10 @@
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+
#include "lis3lv02d.h"
#define DRV_NAME "lis3lv02d_i2c"
@@ -90,7 +94,11 @@ static int lis3_i2c_init(struct lis3lv02d *lis3)
if (ret < 0)
return ret;
- reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
+ if (lis3->whoami == WAI_3DLH)
+ reg |= CTRL1_PM0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
+ else
+ reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
+
return lis3->write(lis3, CTRL_REG1, reg);
}
@@ -98,12 +106,30 @@ static int lis3_i2c_init(struct lis3lv02d *lis3)
static union axis_conversion lis3lv02d_axis_map =
{ .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } };
+#ifdef CONFIG_OF
+static struct of_device_id lis3lv02d_i2c_dt_ids[] = {
+ { .compatible = "st,lis3lv02d" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids);
+#endif
+
static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = 0;
struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
+#ifdef CONFIG_OF
+ if (of_match_device(lis3lv02d_i2c_dt_ids, &client->dev)) {
+ lis3_dev.of_node = client->dev.of_node;
+ ret = lis3lv02d_init_dt(&lis3_dev);
+ if (ret)
+ return ret;
+ pdata = lis3_dev.pdata;
+ }
+#endif
+
if (pdata) {
if ((pdata->driver_features & LIS3_USE_BLOCK_READ) &&
(i2c_check_functionality(client->adapter,
@@ -231,7 +257,8 @@ static int lis3_i2c_runtime_resume(struct device *dev)
#endif /* CONFIG_PM_RUNTIME */
static const struct i2c_device_id lis3lv02d_id[] = {
- {"lis3lv02d", 0 },
+ {"lis3lv02d", LIS3LV02D},
+ {"lis331dlh", LIS331DLH},
{}
};
@@ -250,6 +277,7 @@ static struct i2c_driver lis3lv02d_i2c_driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lis3_pm_ops,
+ .of_match_table = of_match_ptr(lis3lv02d_i2c_dt_ids),
},
.probe = lis3lv02d_i2c_probe,
.remove = __devexit_p(lis3lv02d_i2c_remove),
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
index 80880e9..ccb6475f 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
@@ -17,6 +17,9 @@
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
#include "lis3lv02d.h"
@@ -58,6 +61,14 @@ static int lis3_spi_init(struct lis3lv02d *lis3)
static union axis_conversion lis3lv02d_axis_normal =
{ .as_array = { 1, 2, 3 } };
+#ifdef CONFIG_OF
+static struct of_device_id lis302dl_spi_dt_ids[] = {
+ { .compatible = "st,lis302dl-spi" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids);
+#endif
+
static int __devinit lis302dl_spi_probe(struct spi_device *spi)
{
int ret;
@@ -75,6 +86,15 @@ static int __devinit lis302dl_spi_probe(struct spi_device *spi)
lis3_dev.irq = spi->irq;
lis3_dev.ac = lis3lv02d_axis_normal;
lis3_dev.pdata = spi->dev.platform_data;
+
+#ifdef CONFIG_OF
+ if (of_match_device(lis302dl_spi_dt_ids, &spi->dev)) {
+ lis3_dev.of_node = spi->dev.of_node;
+ ret = lis3lv02d_init_dt(&lis3_dev);
+ if (ret)
+ return ret;
+ }
+#endif
spi_set_drvdata(spi, &lis3_dev);
return lis3lv02d_init_device(&lis3_dev);
@@ -121,6 +141,7 @@ static struct spi_driver lis302dl_spi_driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lis3lv02d_spi_pm,
+ .of_match_table = of_match_ptr(lis302dl_spi_dt_ids),
},
.probe = lis302dl_spi_probe,
.remove = __devexit_p(lis302dl_spi_remove),
diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig
index 47d78a7..5a79ccd 100644
--- a/drivers/misc/mei/Kconfig
+++ b/drivers/misc/mei/Kconfig
@@ -1,6 +1,6 @@
config INTEL_MEI
tristate "Intel Management Engine Interface (Intel MEI)"
- depends on X86 && PCI && EXPERIMENTAL && WATCHDOG_CORE
+ depends on X86 && PCI && WATCHDOG_CORE
help
The Intel Management Engine (Intel ME) provides Manageability,
Security and Media services for system containing Intel chipsets.
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index 24c4c96..9700532 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -40,47 +40,48 @@
/*
* MEI device IDs
*/
-#define MEI_DEV_ID_82946GZ 0x2974 /* 82946GZ/GL */
-#define MEI_DEV_ID_82G35 0x2984 /* 82G35 Express */
-#define MEI_DEV_ID_82Q965 0x2994 /* 82Q963/Q965 */
-#define MEI_DEV_ID_82G965 0x29A4 /* 82P965/G965 */
-
-#define MEI_DEV_ID_82GM965 0x2A04 /* Mobile PM965/GM965 */
-#define MEI_DEV_ID_82GME965 0x2A14 /* Mobile GME965/GLE960 */
-
-#define MEI_DEV_ID_ICH9_82Q35 0x29B4 /* 82Q35 Express */
-#define MEI_DEV_ID_ICH9_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */
-#define MEI_DEV_ID_ICH9_82Q33 0x29D4 /* 82Q33 Express */
-#define MEI_DEV_ID_ICH9_82X38 0x29E4 /* 82X38/X48 Express */
-#define MEI_DEV_ID_ICH9_3200 0x29F4 /* 3200/3210 Server */
-
-#define MEI_DEV_ID_ICH9_6 0x28B4 /* Bearlake */
-#define MEI_DEV_ID_ICH9_7 0x28C4 /* Bearlake */
-#define MEI_DEV_ID_ICH9_8 0x28D4 /* Bearlake */
-#define MEI_DEV_ID_ICH9_9 0x28E4 /* Bearlake */
-#define MEI_DEV_ID_ICH9_10 0x28F4 /* Bearlake */
-
-#define MEI_DEV_ID_ICH9M_1 0x2A44 /* Cantiga */
-#define MEI_DEV_ID_ICH9M_2 0x2A54 /* Cantiga */
-#define MEI_DEV_ID_ICH9M_3 0x2A64 /* Cantiga */
-#define MEI_DEV_ID_ICH9M_4 0x2A74 /* Cantiga */
-
-#define MEI_DEV_ID_ICH10_1 0x2E04 /* Eaglelake */
-#define MEI_DEV_ID_ICH10_2 0x2E14 /* Eaglelake */
-#define MEI_DEV_ID_ICH10_3 0x2E24 /* Eaglelake */
-#define MEI_DEV_ID_ICH10_4 0x2E34 /* Eaglelake */
-
-#define MEI_DEV_ID_IBXPK_1 0x3B64 /* Calpella */
-#define MEI_DEV_ID_IBXPK_2 0x3B65 /* Calpella */
-
-#define MEI_DEV_ID_CPT_1 0x1C3A /* Cougerpoint */
-#define MEI_DEV_ID_PBG_1 0x1D3A /* PBG */
-
-#define MEI_DEV_ID_PPT_1 0x1E3A /* Pantherpoint PPT */
-#define MEI_DEV_ID_PPT_2 0x1CBA /* Pantherpoint PPT */
-#define MEI_DEV_ID_PPT_3 0x1DBA /* Pantherpoint PPT */
-
-
+#define MEI_DEV_ID_82946GZ 0x2974 /* 82946GZ/GL */
+#define MEI_DEV_ID_82G35 0x2984 /* 82G35 Express */
+#define MEI_DEV_ID_82Q965 0x2994 /* 82Q963/Q965 */
+#define MEI_DEV_ID_82G965 0x29A4 /* 82P965/G965 */
+
+#define MEI_DEV_ID_82GM965 0x2A04 /* Mobile PM965/GM965 */
+#define MEI_DEV_ID_82GME965 0x2A14 /* Mobile GME965/GLE960 */
+
+#define MEI_DEV_ID_ICH9_82Q35 0x29B4 /* 82Q35 Express */
+#define MEI_DEV_ID_ICH9_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */
+#define MEI_DEV_ID_ICH9_82Q33 0x29D4 /* 82Q33 Express */
+#define MEI_DEV_ID_ICH9_82X38 0x29E4 /* 82X38/X48 Express */
+#define MEI_DEV_ID_ICH9_3200 0x29F4 /* 3200/3210 Server */
+
+#define MEI_DEV_ID_ICH9_6 0x28B4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_7 0x28C4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_8 0x28D4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_9 0x28E4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_10 0x28F4 /* Bearlake */
+
+#define MEI_DEV_ID_ICH9M_1 0x2A44 /* Cantiga */
+#define MEI_DEV_ID_ICH9M_2 0x2A54 /* Cantiga */
+#define MEI_DEV_ID_ICH9M_3 0x2A64 /* Cantiga */
+#define MEI_DEV_ID_ICH9M_4 0x2A74 /* Cantiga */
+
+#define MEI_DEV_ID_ICH10_1 0x2E04 /* Eaglelake */
+#define MEI_DEV_ID_ICH10_2 0x2E14 /* Eaglelake */
+#define MEI_DEV_ID_ICH10_3 0x2E24 /* Eaglelake */
+#define MEI_DEV_ID_ICH10_4 0x2E34 /* Eaglelake */
+
+#define MEI_DEV_ID_IBXPK_1 0x3B64 /* Calpella */
+#define MEI_DEV_ID_IBXPK_2 0x3B65 /* Calpella */
+
+#define MEI_DEV_ID_CPT_1 0x1C3A /* Couger Point */
+#define MEI_DEV_ID_PBG_1 0x1D3A /* C600/X79 Patsburg */
+
+#define MEI_DEV_ID_PPT_1 0x1E3A /* Panther Point */
+#define MEI_DEV_ID_PPT_2 0x1CBA /* Panther Point */
+#define MEI_DEV_ID_PPT_3 0x1DBA /* Panther Point */
+
+#define MEI_DEV_ID_LPT 0x8C3A /* Lynx Point */
+#define MEI_DEV_ID_LPT_LP 0x9C3A /* Lynx Point LP */
/*
* MEI HW Section
*/
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index e77f86e..98f1430 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -24,6 +24,25 @@
#include "interface.h"
#include <linux/mei.h>
+const char *mei_dev_state_str(int state)
+{
+#define MEI_DEV_STATE(state) case MEI_DEV_##state: return #state
+ switch (state) {
+ MEI_DEV_STATE(INITIALIZING);
+ MEI_DEV_STATE(INIT_CLIENTS);
+ MEI_DEV_STATE(ENABLED);
+ MEI_DEV_STATE(RESETING);
+ MEI_DEV_STATE(DISABLED);
+ MEI_DEV_STATE(RECOVERING_FROM_RESET);
+ MEI_DEV_STATE(POWER_DOWN);
+ MEI_DEV_STATE(POWER_UP);
+ default:
+ return "unkown";
+ }
+#undef MEI_DEV_STATE
+}
+
+
const uuid_le mei_amthi_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac,
0xa8, 0x46, 0xe0, 0xff, 0x65,
0x81, 0x4c);
@@ -123,7 +142,7 @@ struct mei_device *mei_device_init(struct pci_dev *pdev)
mutex_init(&dev->device_lock);
init_waitqueue_head(&dev->wait_recvd_msg);
init_waitqueue_head(&dev->wait_stop_wd);
- dev->mei_state = MEI_INITIALIZING;
+ dev->dev_state = MEI_DEV_INITIALIZING;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
dev->wd_interface_reg = false;
@@ -182,7 +201,7 @@ int mei_hw_init(struct mei_device *dev)
}
if (err <= 0 && !dev->recvd_msg) {
- dev->mei_state = MEI_DISABLED;
+ dev->dev_state = MEI_DEV_DISABLED;
dev_dbg(&dev->pdev->dev,
"wait_event_interruptible_timeout failed"
"on wait for ME to turn on ME_RDY.\n");
@@ -192,7 +211,7 @@ int mei_hw_init(struct mei_device *dev)
if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
- dev->mei_state = MEI_DISABLED;
+ dev->dev_state = MEI_DEV_DISABLED;
dev_dbg(&dev->pdev->dev,
"host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
dev->host_hw_state, dev->me_hw_state);
@@ -258,15 +277,15 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
struct mei_cl_cb *cb_next = NULL;
bool unexpected;
- if (dev->mei_state == MEI_RECOVERING_FROM_RESET) {
+ if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) {
dev->need_reset = true;
return;
}
- unexpected = (dev->mei_state != MEI_INITIALIZING &&
- dev->mei_state != MEI_DISABLED &&
- dev->mei_state != MEI_POWER_DOWN &&
- dev->mei_state != MEI_POWER_UP);
+ unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&
+ dev->dev_state != MEI_DEV_DISABLED &&
+ dev->dev_state != MEI_DEV_POWER_DOWN &&
+ dev->dev_state != MEI_DEV_POWER_UP);
dev->host_hw_state = mei_hcsr_read(dev);
@@ -285,10 +304,10 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
dev->need_reset = false;
- if (dev->mei_state != MEI_INITIALIZING) {
- if (dev->mei_state != MEI_DISABLED &&
- dev->mei_state != MEI_POWER_DOWN)
- dev->mei_state = MEI_RESETING;
+ if (dev->dev_state != MEI_DEV_INITIALIZING) {
+ if (dev->dev_state != MEI_DEV_DISABLED &&
+ dev->dev_state != MEI_DEV_POWER_DOWN)
+ dev->dev_state = MEI_DEV_RESETING;
list_for_each_entry_safe(cl_pos,
cl_next, &dev->file_list, link) {
@@ -311,7 +330,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
dev->me_clients_num = 0;
dev->rd_msg_hdr = 0;
- dev->stop = false;
dev->wd_pending = false;
/* update the state of the registers after reset */
@@ -322,7 +340,8 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
dev->host_hw_state, dev->me_hw_state);
if (unexpected)
- dev_warn(&dev->pdev->dev, "unexpected reset.\n");
+ dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
+ mei_dev_state_str(dev->dev_state));
/* Wake up all readings so they can be interrupted */
list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
@@ -371,7 +390,7 @@ void mei_host_start_message(struct mei_device *dev)
if (mei_write_message(dev, mei_hdr, (unsigned char *)host_start_req,
mei_hdr->length)) {
dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
- dev->mei_state = MEI_RESETING;
+ dev->dev_state = MEI_DEV_RESETING;
mei_reset(dev, 1);
}
dev->init_clients_state = MEI_START_MESSAGE;
@@ -403,7 +422,7 @@ void mei_host_enum_clients_message(struct mei_device *dev)
host_enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
if (mei_write_message(dev, mei_hdr, (unsigned char *)host_enum_req,
mei_hdr->length)) {
- dev->mei_state = MEI_RESETING;
+ dev->dev_state = MEI_DEV_RESETING;
dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
mei_reset(dev, 1);
}
@@ -444,7 +463,7 @@ void mei_allocate_me_clients_storage(struct mei_device *dev)
sizeof(struct mei_me_client), GFP_KERNEL);
if (!clients) {
dev_dbg(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
- dev->mei_state = MEI_RESETING;
+ dev->dev_state = MEI_DEV_RESETING;
mei_reset(dev, 1);
return ;
}
@@ -490,7 +509,7 @@ int mei_host_client_properties(struct mei_device *dev)
if (mei_write_message(dev, mei_header,
(unsigned char *)host_cli_req,
mei_header->length)) {
- dev->mei_state = MEI_RESETING;
+ dev->dev_state = MEI_DEV_RESETING;
dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
mei_reset(dev, 1);
return -EIO;
@@ -522,12 +541,12 @@ void mei_cl_init(struct mei_cl *priv, struct mei_device *dev)
priv->dev = dev;
}
-int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid)
+int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *cuuid)
{
- int i, res = -1;
+ int i, res = -ENOENT;
for (i = 0; i < dev->me_clients_num; ++i)
- if (uuid_le_cmp(cuuid,
+ if (uuid_le_cmp(*cuuid,
dev->me_clients[i].props.protocol_name) == 0) {
res = i;
break;
@@ -538,35 +557,35 @@ int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid)
/**
- * mei_find_me_client_update_filext - searches for ME client guid
+ * mei_me_cl_update_filext - searches for ME client guid
* sets client_id in mei_file_private if found
* @dev: the device structure
- * @priv: private file structure to set client_id in
- * @cguid: searched guid of ME client
+ * @cl: private file structure to set client_id in
+ * @cuuid: searched uuid of ME client
* @client_id: id of host client to be set in file private structure
*
* returns ME client index
*/
-u8 mei_find_me_client_update_filext(struct mei_device *dev, struct mei_cl *priv,
- const uuid_le *cguid, u8 client_id)
+int mei_me_cl_update_filext(struct mei_device *dev, struct mei_cl *cl,
+ const uuid_le *cuuid, u8 host_cl_id)
{
int i;
- if (!dev || !priv || !cguid)
- return 0;
+ if (!dev || !cl || !cuuid)
+ return -EINVAL;
/* check for valid client id */
- i = mei_find_me_client_index(dev, *cguid);
+ i = mei_me_cl_by_uuid(dev, cuuid);
if (i >= 0) {
- priv->me_client_id = dev->me_clients[i].client_id;
- priv->state = MEI_FILE_CONNECTING;
- priv->host_client_id = client_id;
+ cl->me_client_id = dev->me_clients[i].client_id;
+ cl->state = MEI_FILE_CONNECTING;
+ cl->host_client_id = host_cl_id;
- list_add_tail(&priv->link, &dev->file_list);
+ list_add_tail(&cl->link, &dev->file_list);
return (u8)i;
}
- return 0;
+ return -ENOENT;
}
/**
@@ -577,16 +596,16 @@ u8 mei_find_me_client_update_filext(struct mei_device *dev, struct mei_cl *priv,
*/
void mei_host_init_iamthif(struct mei_device *dev)
{
- u8 i;
+ int i;
unsigned char *msg_buf;
mei_cl_init(&dev->iamthif_cl, dev);
dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
/* find ME amthi client */
- i = mei_find_me_client_update_filext(dev, &dev->iamthif_cl,
+ i = mei_me_cl_update_filext(dev, &dev->iamthif_cl,
&mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID);
- if (dev->iamthif_cl.state != MEI_FILE_CONNECTING) {
+ if (i < 0) {
dev_dbg(&dev->pdev->dev, "failed to find iamthif client.\n");
return;
}
diff --git a/drivers/misc/mei/interface.h b/drivers/misc/mei/interface.h
index fb5c7db..ec6c785 100644
--- a/drivers/misc/mei/interface.h
+++ b/drivers/misc/mei/interface.h
@@ -23,14 +23,6 @@
#include "mei_dev.h"
-#define AMT_WD_DEFAULT_TIMEOUT 120 /* seconds */
-#define AMT_WD_MIN_TIMEOUT 120 /* seconds */
-#define AMT_WD_MAX_TIMEOUT 65535 /* seconds */
-
-#define MEI_WATCHDOG_DATA_SIZE 16
-#define MEI_START_WD_DATA_SIZE 20
-#define MEI_WD_PARAMS_SIZE 4
-
void mei_read_slots(struct mei_device *dev,
unsigned char *buffer,
@@ -64,7 +56,7 @@ int mei_flow_ctrl_creds(struct mei_device *dev, struct mei_cl *cl);
int mei_wd_send(struct mei_device *dev);
-int mei_wd_stop(struct mei_device *dev, bool preserve);
+int mei_wd_stop(struct mei_device *dev);
int mei_wd_host_init(struct mei_device *dev);
/*
* mei_watchdog_register - Registering watchdog interface
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index d78c05e..3533edd 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -221,17 +221,10 @@ static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list,
cl->status = 0;
list_del(&cb_pos->cb_list);
dev_dbg(&dev->pdev->dev,
- "completed read host client = %d,"
- "ME client = %d, "
- "data length = %lu\n",
+ "completed read H cl = %d, ME cl = %d, length = %lu\n",
cl->host_client_id,
cl->me_client_id,
cb_pos->information);
-
- *(cb_pos->response_buffer.data +
- cb_pos->information) = '\0';
- dev_dbg(&dev->pdev->dev, "cb_pos->res_buffer - %s\n",
- cb_pos->response_buffer.data);
list_add_tail(&cb_pos->cb_list,
&complete_list->mei_cb.cb_list);
}
@@ -633,7 +626,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
if (version_res->host_version_supported) {
dev->version.major_version = HBM_MAJOR_VERSION;
dev->version.minor_version = HBM_MINOR_VERSION;
- if (dev->mei_state == MEI_INIT_CLIENTS &&
+ if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
dev->init_clients_state == MEI_START_MESSAGE) {
dev->init_clients_timer = 0;
mei_host_enum_clients_message(dev);
@@ -707,7 +700,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
dev->me_clients[dev->me_client_presentation_num].props
= props_res->client_properties;
- if (dev->mei_state == MEI_INIT_CLIENTS &&
+ if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
dev->init_clients_state ==
MEI_CLIENT_PROPERTIES_MESSAGE) {
dev->me_client_index++;
@@ -734,7 +727,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
* Client ID 2 - Reserved for AMTHI
*/
bitmap_set(dev->host_clients_map, 0, 3);
- dev->mei_state = MEI_ENABLED;
+ dev->dev_state = MEI_DEV_ENABLED;
/* if wd initialization fails, initialization the AMTHI client,
* otherwise the AMTHI client will be initialized after the WD client connect response
@@ -759,7 +752,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
case HOST_ENUM_RES_CMD:
enum_res = (struct hbm_host_enum_response *) mei_msg;
memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
- if (dev->mei_state == MEI_INIT_CLIENTS &&
+ if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
dev->init_clients_timer = 0;
dev->me_client_presentation_num = 0;
@@ -776,7 +769,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
break;
case HOST_STOP_RES_CMD:
- dev->mei_state = MEI_DISABLED;
+ dev->dev_state = MEI_DEV_DISABLED;
dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
mei_reset(dev, 1);
break;
@@ -1224,10 +1217,9 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
}
}
- if (dev->stop && !dev->wd_pending) {
- dev->wd_stopped = true;
+ if (dev->wd_state == MEI_WD_STOPPING) {
+ dev->wd_state = MEI_WD_IDLE;
wake_up_interruptible(&dev->wait_stop_wd);
- return 0;
}
if (dev->extra_write_index) {
@@ -1240,7 +1232,7 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
*slots -= dev->extra_write_index;
dev->extra_write_index = 0;
}
- if (dev->mei_state == MEI_ENABLED) {
+ if (dev->dev_state == MEI_DEV_ENABLED) {
if (dev->wd_pending &&
mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
if (mei_wd_send(dev))
@@ -1250,14 +1242,12 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
dev->wd_pending = false;
- if (dev->wd_timeout)
- *slots -= mei_data2slots(MEI_START_WD_DATA_SIZE);
+ if (dev->wd_state == MEI_WD_RUNNING)
+ *slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
else
- *slots -= mei_data2slots(MEI_WD_PARAMS_SIZE);
+ *slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
}
}
- if (dev->stop)
- return -ENODEV;
/* complete control write list CB */
dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
@@ -1361,8 +1351,8 @@ void mei_timer(struct work_struct *work)
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED) {
- if (dev->mei_state == MEI_INIT_CLIENTS) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
+ if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
if (dev->init_clients_timer) {
if (--dev->init_clients_timer == 0) {
dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
@@ -1484,8 +1474,8 @@ irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
/* check if ME wants a reset */
if ((dev->me_hw_state & ME_RDY_HRA) == 0 &&
- dev->mei_state != MEI_RESETING &&
- dev->mei_state != MEI_INITIALIZING) {
+ dev->dev_state != MEI_DEV_RESETING &&
+ dev->dev_state != MEI_DEV_INITIALIZING) {
dev_dbg(&dev->pdev->dev, "FW not ready.\n");
mei_reset(dev, 1);
mutex_unlock(&dev->device_lock);
@@ -1498,7 +1488,7 @@ irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
dev->host_hw_state |= (H_IE | H_IG | H_RDY);
mei_hcsr_set(dev);
- dev->mei_state = MEI_INIT_CLIENTS;
+ dev->dev_state = MEI_DEV_INIT_CLIENTS;
dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
/* link is established
* start sending messages.
diff --git a/drivers/misc/mei/iorw.c b/drivers/misc/mei/iorw.c
index 50f52e2..fcba98e 100644
--- a/drivers/misc/mei/iorw.c
+++ b/drivers/misc/mei/iorw.c
@@ -38,7 +38,31 @@
#include <linux/mei.h>
#include "interface.h"
+/**
+ * mei_me_cl_by_id return index to me_clients for client_id
+ *
+ * @dev: the device structure
+ * @client_id: me client id
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * returns index on success, -ENOENT on failure.
+ */
+int mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
+{
+ int i;
+ for (i = 0; i < dev->me_clients_num; i++)
+ if (dev->me_clients[i].client_id == client_id)
+ break;
+ if (WARN_ON(dev->me_clients[i].client_id != client_id))
+ return -ENOENT;
+
+ if (i == dev->me_clients_num)
+ return -ENOENT;
+
+ return i;
+}
/**
* mei_ioctl_connect_client - the connect to fw client IOCTL function
@@ -84,7 +108,7 @@ int mei_ioctl_connect_client(struct file *file,
cb->major_file_operations = MEI_IOCTL;
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
rets = -ENODEV;
goto end;
}
@@ -95,7 +119,7 @@ int mei_ioctl_connect_client(struct file *file,
}
/* find ME client we're trying to connect to */
- i = mei_find_me_client_index(dev, data->in_client_uuid);
+ i = mei_me_cl_by_uuid(dev, &data->in_client_uuid);
if (i >= 0 && !dev->me_clients[i].props.fixed_address) {
cl->me_client_id = dev->me_clients[i].client_id;
cl->state = MEI_FILE_CONNECTING;
@@ -273,19 +297,12 @@ int amthi_read(struct mei_device *dev, struct file *file,
return -ETIMEDOUT;
}
- for (i = 0; i < dev->me_clients_num; i++) {
- if (dev->me_clients[i].client_id ==
- dev->iamthif_cl.me_client_id)
- break;
- }
+ i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id);
- if (i == dev->me_clients_num) {
+ if (i < 0) {
dev_dbg(&dev->pdev->dev, "amthi client not found.\n");
return -ENODEV;
}
- if (WARN_ON(dev->me_clients[i].client_id != cl->me_client_id))
- return -ENODEV;
-
dev_dbg(&dev->pdev->dev, "checking amthi data\n");
cb = find_amthi_read_list_entry(dev, file);
@@ -316,8 +333,7 @@ int amthi_read(struct mei_device *dev, struct file *file,
dev->iamthif_timer = 0;
if (cb) {
- timeout = cb->read_time +
- msecs_to_jiffies(IAMTHIF_READ_TIMER);
+ timeout = cb->read_time + msecs_to_jiffies(IAMTHIF_READ_TIMER);
dev_dbg(&dev->pdev->dev, "amthi timeout = %lud\n",
timeout);
@@ -386,7 +402,7 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl)
if (cl->state != MEI_FILE_CONNECTED)
return -ENODEV;
- if (dev->mei_state != MEI_ENABLED)
+ if (dev->dev_state != MEI_DEV_ENABLED)
return -ENODEV;
dev_dbg(&dev->pdev->dev, "check if read is pending.\n");
@@ -401,19 +417,8 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl)
dev_dbg(&dev->pdev->dev, "allocation call back successful. host client = %d, ME client = %d\n",
cl->host_client_id, cl->me_client_id);
-
- for (i = 0; i < dev->me_clients_num; i++) {
- if (dev->me_clients[i].client_id == cl->me_client_id)
- break;
-
- }
-
- if (WARN_ON(dev->me_clients[i].client_id != cl->me_client_id)) {
- rets = -ENODEV;
- goto unlock;
- }
-
- if (i == dev->me_clients_num) {
+ i = mei_me_cl_by_id(dev, cl->me_client_id);
+ if (i < 0) {
rets = -ENODEV;
goto unlock;
}
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 7422c76..e8b0858 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -41,11 +41,8 @@
#include <linux/mei.h>
#include "interface.h"
-static const char mei_driver_name[] = "mei";
-
-/* The device pointer */
-/* Currently this driver works as long as there is only a single AMT device. */
-struct pci_dev *mei_device;
+/* AMT device is a singleton on the platform */
+static struct pci_dev *mei_pdev;
/* mei_pci_tbl - PCI Device ID Table */
static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = {
@@ -80,6 +77,8 @@ static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)},
/* required last entry */
{0, }
@@ -220,10 +219,10 @@ static int mei_open(struct inode *inode, struct file *file)
int err;
err = -ENODEV;
- if (!mei_device)
+ if (!mei_pdev)
goto out;
- dev = pci_get_drvdata(mei_device);
+ dev = pci_get_drvdata(mei_pdev);
if (!dev)
goto out;
@@ -234,18 +233,24 @@ static int mei_open(struct inode *inode, struct file *file)
goto out_unlock;
err = -ENODEV;
- if (dev->mei_state != MEI_ENABLED) {
- dev_dbg(&dev->pdev->dev, "mei_state != MEI_ENABLED mei_state= %d\n",
- dev->mei_state);
+ if (dev->dev_state != MEI_DEV_ENABLED) {
+ dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n",
+ mei_dev_state_str(dev->dev_state));
goto out_unlock;
}
err = -EMFILE;
- if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT)
+ if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
+ dev_err(&dev->pdev->dev, "open_handle_count exceded %d",
+ MEI_MAX_OPEN_HANDLE_COUNT);
goto out_unlock;
+ }
cl_id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX);
- if (cl_id >= MEI_CLIENTS_MAX)
+ if (cl_id >= MEI_CLIENTS_MAX) {
+ dev_err(&dev->pdev->dev, "client_id exceded %d",
+ MEI_CLIENTS_MAX) ;
goto out_unlock;
+ }
cl->host_client_id = cl_id;
@@ -386,17 +391,16 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
dev = cl->dev;
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
rets = -ENODEV;
goto out;
}
if ((cl->sm_state & MEI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) {
/* Do not allow to read watchdog client */
- i = mei_find_me_client_index(dev, mei_wd_guid);
+ i = mei_me_cl_by_uuid(dev, &mei_wd_guid);
if (i >= 0) {
struct mei_me_client *me_client = &dev->me_clients[i];
-
if (cl->me_client_id == me_client->client_id) {
rets = -EBADF;
goto out;
@@ -541,7 +545,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
mutex_unlock(&dev->device_lock);
return -ENODEV;
}
@@ -616,26 +620,16 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
rets = -ENOMEM;
goto unlock_dev;
}
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
rets = -ENODEV;
goto unlock_dev;
}
- for (i = 0; i < dev->me_clients_num; i++) {
- if (dev->me_clients[i].client_id ==
- dev->iamthif_cl.me_client_id)
- break;
- }
-
- if (WARN_ON(dev->me_clients[i].client_id != cl->me_client_id)) {
+ i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id);
+ if (i < 0) {
rets = -ENODEV;
goto unlock_dev;
}
- if (i == dev->me_clients_num ||
- (dev->me_clients[i].client_id !=
- dev->iamthif_cl.me_client_id)) {
- rets = -ENODEV;
- goto unlock_dev;
- } else if (length > dev->me_clients[i].props.max_msg_length ||
+ if (length > dev->me_clients[i].props.max_msg_length ||
length <= 0) {
rets = -EMSGSIZE;
goto unlock_dev;
@@ -688,16 +682,8 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
cl->me_client_id);
goto unlock_dev;
}
- for (i = 0; i < dev->me_clients_num; i++) {
- if (dev->me_clients[i].client_id ==
- cl->me_client_id)
- break;
- }
- if (WARN_ON(dev->me_clients[i].client_id != cl->me_client_id)) {
- rets = -ENODEV;
- goto unlock_dev;
- }
- if (i == dev->me_clients_num) {
+ i = mei_me_cl_by_id(dev, cl->me_client_id);
+ if (i < 0) {
rets = -ENODEV;
goto unlock_dev;
}
@@ -790,7 +776,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
dev_dbg(&dev->pdev->dev, "IOCTL cmd = 0x%x", cmd);
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
rets = -ENODEV;
goto out;
}
@@ -869,7 +855,7 @@ static unsigned int mei_poll(struct file *file, poll_table *wait)
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED)
+ if (dev->dev_state != MEI_DEV_ENABLED)
goto out;
@@ -966,7 +952,7 @@ static int __devinit mei_probe(struct pci_dev *pdev,
goto end;
}
- if (mei_device) {
+ if (mei_pdev) {
err = -EEXIST;
goto end;
}
@@ -979,7 +965,7 @@ static int __devinit mei_probe(struct pci_dev *pdev,
/* set PCI host mastering */
pci_set_master(pdev);
/* pci request regions for mei driver */
- err = pci_request_regions(pdev, mei_driver_name);
+ err = pci_request_regions(pdev, KBUILD_MODNAME);
if (err) {
dev_err(&pdev->dev, "failed to get pci regions.\n");
goto disable_device;
@@ -1004,12 +990,12 @@ static int __devinit mei_probe(struct pci_dev *pdev,
err = request_threaded_irq(pdev->irq,
NULL,
mei_interrupt_thread_handler,
- IRQF_ONESHOT, mei_driver_name, dev);
+ IRQF_ONESHOT, KBUILD_MODNAME, dev);
else
err = request_threaded_irq(pdev->irq,
mei_interrupt_quick_handler,
mei_interrupt_thread_handler,
- IRQF_SHARED, mei_driver_name, dev);
+ IRQF_SHARED, KBUILD_MODNAME, dev);
if (err) {
dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n",
@@ -1027,7 +1013,7 @@ static int __devinit mei_probe(struct pci_dev *pdev,
if (err)
goto release_irq;
- mei_device = pdev;
+ mei_pdev = pdev;
pci_set_drvdata(pdev, dev);
@@ -1072,7 +1058,7 @@ static void __devexit mei_remove(struct pci_dev *pdev)
{
struct mei_device *dev;
- if (mei_device != pdev)
+ if (mei_pdev != pdev)
return;
dev = pci_get_drvdata(pdev);
@@ -1081,9 +1067,11 @@ static void __devexit mei_remove(struct pci_dev *pdev)
mutex_lock(&dev->device_lock);
- mei_wd_stop(dev, false);
+ cancel_delayed_work(&dev->timer_work);
- mei_device = NULL;
+ mei_wd_stop(dev);
+
+ mei_pdev = NULL;
if (dev->iamthif_cl.state == MEI_FILE_CONNECTED) {
dev->iamthif_cl.state = MEI_FILE_DISCONNECTING;
@@ -1136,12 +1124,15 @@ static int mei_pci_suspend(struct device *device)
if (!dev)
return -ENODEV;
mutex_lock(&dev->device_lock);
+
+ cancel_delayed_work(&dev->timer_work);
+
/* Stop watchdog if exists */
- err = mei_wd_stop(dev, true);
+ err = mei_wd_stop(dev);
/* Set new mei state */
- if (dev->mei_state == MEI_ENABLED ||
- dev->mei_state == MEI_RECOVERING_FROM_RESET) {
- dev->mei_state = MEI_POWER_DOWN;
+ if (dev->dev_state == MEI_DEV_ENABLED ||
+ dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) {
+ dev->dev_state = MEI_DEV_POWER_DOWN;
mei_reset(dev, 0);
}
mutex_unlock(&dev->device_lock);
@@ -1169,12 +1160,12 @@ static int mei_pci_resume(struct device *device)
err = request_threaded_irq(pdev->irq,
NULL,
mei_interrupt_thread_handler,
- IRQF_ONESHOT, mei_driver_name, dev);
+ IRQF_ONESHOT, KBUILD_MODNAME, dev);
else
err = request_threaded_irq(pdev->irq,
mei_interrupt_quick_handler,
mei_interrupt_thread_handler,
- IRQF_SHARED, mei_driver_name, dev);
+ IRQF_SHARED, KBUILD_MODNAME, dev);
if (err) {
dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n",
@@ -1183,7 +1174,7 @@ static int mei_pci_resume(struct device *device)
}
mutex_lock(&dev->device_lock);
- dev->mei_state = MEI_POWER_UP;
+ dev->dev_state = MEI_DEV_POWER_UP;
mei_reset(dev, 1);
mutex_unlock(&dev->device_lock);
@@ -1201,7 +1192,7 @@ static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume);
* PCI driver structure
*/
static struct pci_driver mei_driver = {
- .name = mei_driver_name,
+ .name = KBUILD_MODNAME,
.id_table = mei_pci_tbl,
.probe = mei_probe,
.remove = __devexit_p(mei_remove),
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index d61c4dd..adb35fb 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -25,18 +25,20 @@
/*
* watch dog definition
*/
-#define MEI_WATCHDOG_DATA_SIZE 16
-#define MEI_START_WD_DATA_SIZE 20
-#define MEI_WD_PARAMS_SIZE 4
+#define MEI_WD_HDR_SIZE 4
+#define MEI_WD_STOP_MSG_SIZE MEI_WD_HDR_SIZE
+#define MEI_WD_START_MSG_SIZE (MEI_WD_HDR_SIZE + 16)
+
+#define MEI_WD_DEFAULT_TIMEOUT 120 /* seconds */
+#define MEI_WD_MIN_TIMEOUT 120 /* seconds */
+#define MEI_WD_MAX_TIMEOUT 65535 /* seconds */
+
+#define MEI_WD_STOP_TIMEOUT 10 /* msecs */
+
#define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0)
#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32))
-/*
- * MEI PCI Device object
- */
-extern struct pci_dev *mei_device;
-
/*
* AMTHI Client UUID
@@ -54,19 +56,21 @@ extern const uuid_le mei_wd_guid;
extern const u8 mei_wd_state_independence_msg[3][4];
/*
+ * Number of Maximum MEI Clients
+ */
+#define MEI_CLIENTS_MAX 256
+
+/*
* Number of File descriptors/handles
* that can be opened to the driver.
*
- * Limit to 253: 255 Total Clients
+ * Limit to 253: 256 Total Clients
+ * minus internal client for MEI Bus Messags
* minus internal client for AMTHI
* minus internal client for Watchdog
*/
-#define MEI_MAX_OPEN_HANDLE_COUNT 253
+#define MEI_MAX_OPEN_HANDLE_COUNT (MEI_CLIENTS_MAX - 3)
-/*
- * Number of Maximum MEI Clients
- */
-#define MEI_CLIENTS_MAX 255
/* File state */
enum file_state {
@@ -78,17 +82,19 @@ enum file_state {
};
/* MEI device states */
-enum mei_states {
- MEI_INITIALIZING = 0,
- MEI_INIT_CLIENTS,
- MEI_ENABLED,
- MEI_RESETING,
- MEI_DISABLED,
- MEI_RECOVERING_FROM_RESET,
- MEI_POWER_DOWN,
- MEI_POWER_UP
+enum mei_dev_state {
+ MEI_DEV_INITIALIZING = 0,
+ MEI_DEV_INIT_CLIENTS,
+ MEI_DEV_ENABLED,
+ MEI_DEV_RESETING,
+ MEI_DEV_DISABLED,
+ MEI_DEV_RECOVERING_FROM_RESET,
+ MEI_DEV_POWER_DOWN,
+ MEI_DEV_POWER_UP
};
+const char *mei_dev_state_str(int state);
+
/* init clients states*/
enum mei_init_clients_states {
MEI_START_MESSAGE = 0,
@@ -113,6 +119,12 @@ enum mei_file_transaction_states {
MEI_READ_COMPLETE
};
+enum mei_wd_states {
+ MEI_WD_IDLE,
+ MEI_WD_RUNNING,
+ MEI_WD_STOPPING,
+};
+
/* MEI CB */
enum mei_cb_major_types {
MEI_READ = 0,
@@ -128,7 +140,7 @@ enum mei_cb_major_types {
struct mei_message_data {
u32 size;
unsigned char *data;
-} __packed;
+};
struct mei_cl_cb {
@@ -218,10 +230,9 @@ struct mei_device {
/*
* mei device states
*/
- enum mei_states mei_state;
+ enum mei_dev_state dev_state;
enum mei_init_clients_states init_clients_state;
u16 init_clients_timer;
- bool stop;
bool need_reset;
u32 extra_write_index;
@@ -241,12 +252,11 @@ struct mei_device {
bool mei_host_buffer_is_empty;
struct mei_cl wd_cl;
+ enum mei_wd_states wd_state;
bool wd_interface_reg;
bool wd_pending;
- bool wd_stopped;
- bool wd_bypass; /* if false, don't refresh watchdog ME client */
- u16 wd_timeout; /* seconds ((wd_data[1] << 8) + wd_data[0]) */
- unsigned char wd_data[MEI_START_WD_DATA_SIZE];
+ u16 wd_timeout;
+ unsigned char wd_data[MEI_WD_START_MSG_SIZE];
struct file *iamthif_file_object;
@@ -279,9 +289,10 @@ void mei_host_init_iamthif(struct mei_device *dev);
void mei_allocate_me_clients_storage(struct mei_device *dev);
-u8 mei_find_me_client_update_filext(struct mei_device *dev,
- struct mei_cl *priv,
- const uuid_le *cguid, u8 client_id);
+int mei_me_cl_update_filext(struct mei_device *dev, struct mei_cl *cl,
+ const uuid_le *cguid, u8 host_client_id);
+int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *cuuid);
+int mei_me_cl_by_id(struct mei_device *dev, u8 client_id);
/*
* MEI IO List Functions
@@ -348,7 +359,6 @@ void mei_run_next_iamthif_cmd(struct mei_device *dev);
void mei_free_cb_private(struct mei_cl_cb *priv_cb);
-int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid);
/*
* Register Access Function
diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c
index 5133fd7..d96c537 100644
--- a/drivers/misc/mei/wd.c
+++ b/drivers/misc/mei/wd.c
@@ -48,8 +48,8 @@ const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89,
static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
{
dev_dbg(&dev->pdev->dev, "wd: set timeout=%d.\n", timeout);
- memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_PARAMS_SIZE);
- memcpy(dev->wd_data + MEI_WD_PARAMS_SIZE, &timeout, sizeof(u16));
+ memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE);
+ memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16));
}
/**
@@ -66,10 +66,11 @@ int mei_wd_host_init(struct mei_device *dev)
/* look for WD client and connect to it */
dev->wd_cl.state = MEI_FILE_DISCONNECTED;
- dev->wd_timeout = AMT_WD_DEFAULT_TIMEOUT;
+ dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT;
+ dev->wd_state = MEI_WD_IDLE;
/* find ME WD client */
- mei_find_me_client_update_filext(dev, &dev->wd_cl,
+ mei_me_cl_update_filext(dev, &dev->wd_cl,
&mei_wd_guid, MEI_WD_HOST_CLIENT_ID);
dev_dbg(&dev->pdev->dev, "wd: check client\n");
@@ -108,10 +109,10 @@ int mei_wd_send(struct mei_device *dev)
mei_hdr->msg_complete = 1;
mei_hdr->reserved = 0;
- if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_PARAMS_SIZE))
- mei_hdr->length = MEI_START_WD_DATA_SIZE;
- else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_PARAMS_SIZE))
- mei_hdr->length = MEI_WD_PARAMS_SIZE;
+ if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE))
+ mei_hdr->length = MEI_WD_START_MSG_SIZE;
+ else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE))
+ mei_hdr->length = MEI_WD_STOP_MSG_SIZE;
else
return -EINVAL;
@@ -128,18 +129,17 @@ int mei_wd_send(struct mei_device *dev)
* -EIO when message send fails
* -EINVAL when invalid message is to be sent
*/
-int mei_wd_stop(struct mei_device *dev, bool preserve)
+int mei_wd_stop(struct mei_device *dev)
{
int ret;
- u16 wd_timeout = dev->wd_timeout;
- cancel_delayed_work(&dev->timer_work);
- if (dev->wd_cl.state != MEI_FILE_CONNECTED || !dev->wd_timeout)
+ if (dev->wd_cl.state != MEI_FILE_CONNECTED ||
+ dev->wd_state != MEI_WD_RUNNING)
return 0;
- dev->wd_timeout = 0;
- memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_PARAMS_SIZE);
- dev->stop = true;
+ memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE);
+
+ dev->wd_state = MEI_WD_STOPPING;
ret = mei_flow_ctrl_creds(dev, &dev->wd_cl);
if (ret < 0)
@@ -161,13 +161,14 @@ int mei_wd_stop(struct mei_device *dev, bool preserve)
} else {
dev->wd_pending = true;
}
- dev->wd_stopped = false;
+
mutex_unlock(&dev->device_lock);
ret = wait_event_interruptible_timeout(dev->wait_stop_wd,
- dev->wd_stopped, 10 * HZ);
+ dev->wd_state == MEI_WD_IDLE,
+ msecs_to_jiffies(MEI_WD_STOP_TIMEOUT));
mutex_lock(&dev->device_lock);
- if (dev->wd_stopped) {
+ if (dev->wd_state == MEI_WD_IDLE) {
dev_dbg(&dev->pdev->dev, "wd: stop completed ret=%d.\n", ret);
ret = 0;
} else {
@@ -177,9 +178,6 @@ int mei_wd_stop(struct mei_device *dev, bool preserve)
"wd: stop failed to complete ret=%d.\n", ret);
}
- if (preserve)
- dev->wd_timeout = wd_timeout;
-
out:
return ret;
}
@@ -196,16 +194,16 @@ static int mei_wd_ops_start(struct watchdog_device *wd_dev)
int err = -ENODEV;
struct mei_device *dev;
- dev = pci_get_drvdata(mei_device);
+ dev = watchdog_get_drvdata(wd_dev);
if (!dev)
return -ENODEV;
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
dev_dbg(&dev->pdev->dev,
- "wd: mei_state != MEI_ENABLED mei_state = %d\n",
- dev->mei_state);
+ "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n",
+ mei_dev_state_str(dev->dev_state));
goto end_unlock;
}
@@ -233,13 +231,13 @@ end_unlock:
static int mei_wd_ops_stop(struct watchdog_device *wd_dev)
{
struct mei_device *dev;
- dev = pci_get_drvdata(mei_device);
+ dev = watchdog_get_drvdata(wd_dev);
if (!dev)
return -ENODEV;
mutex_lock(&dev->device_lock);
- mei_wd_stop(dev, false);
+ mei_wd_stop(dev);
mutex_unlock(&dev->device_lock);
return 0;
@@ -256,8 +254,8 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev)
{
int ret = 0;
struct mei_device *dev;
- dev = pci_get_drvdata(mei_device);
+ dev = watchdog_get_drvdata(wd_dev);
if (!dev)
return -ENODEV;
@@ -269,6 +267,8 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev)
goto end;
}
+ dev->wd_state = MEI_WD_RUNNING;
+
/* Check if we can send the ping to HW*/
if (dev->mei_host_buffer_is_empty &&
mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
@@ -309,13 +309,13 @@ end:
static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, unsigned int timeout)
{
struct mei_device *dev;
- dev = pci_get_drvdata(mei_device);
+ dev = watchdog_get_drvdata(wd_dev);
if (!dev)
return -ENODEV;
/* Check Timeout value */
- if (timeout < AMT_WD_MIN_TIMEOUT || timeout > AMT_WD_MAX_TIMEOUT)
+ if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT)
return -EINVAL;
mutex_lock(&dev->device_lock);
@@ -341,37 +341,42 @@ static const struct watchdog_ops wd_ops = {
};
static const struct watchdog_info wd_info = {
.identity = INTEL_AMT_WATCHDOG_ID,
- .options = WDIOF_KEEPALIVEPING | WDIOF_ALARMONLY,
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_ALARMONLY,
};
static struct watchdog_device amt_wd_dev = {
.info = &wd_info,
.ops = &wd_ops,
- .timeout = AMT_WD_DEFAULT_TIMEOUT,
- .min_timeout = AMT_WD_MIN_TIMEOUT,
- .max_timeout = AMT_WD_MAX_TIMEOUT,
+ .timeout = MEI_WD_DEFAULT_TIMEOUT,
+ .min_timeout = MEI_WD_MIN_TIMEOUT,
+ .max_timeout = MEI_WD_MAX_TIMEOUT,
};
-void mei_watchdog_register(struct mei_device *dev)
+void mei_watchdog_register(struct mei_device *dev)
{
- dev_dbg(&dev->pdev->dev, "dev->wd_timeout =%d.\n", dev->wd_timeout);
-
if (watchdog_register_device(&amt_wd_dev)) {
dev_err(&dev->pdev->dev,
"wd: unable to register watchdog device.\n");
dev->wd_interface_reg = false;
- } else {
- dev_dbg(&dev->pdev->dev,
- "wd: successfully register watchdog interface.\n");
- dev->wd_interface_reg = true;
+ return;
}
+
+ dev_dbg(&dev->pdev->dev,
+ "wd: successfully register watchdog interface.\n");
+ dev->wd_interface_reg = true;
+ watchdog_set_drvdata(&amt_wd_dev, dev);
}
void mei_watchdog_unregister(struct mei_device *dev)
{
- if (dev->wd_interface_reg)
- watchdog_unregister_device(&amt_wd_dev);
+ if (!dev->wd_interface_reg)
+ return;
+
+ watchdog_set_drvdata(&amt_wd_dev, NULL);
+ watchdog_unregister_device(&amt_wd_dev);
dev->wd_interface_reg = false;
}
diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c
index 9fbcacd..c9f20da 100644
--- a/drivers/misc/pch_phub.c
+++ b/drivers/misc/pch_phub.c
@@ -699,7 +699,7 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
chip->pch_phub_base_address = pci_iomap(pdev, 1, 0);
- if (chip->pch_phub_base_address == 0) {
+ if (chip->pch_phub_base_address == NULL) {
dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__);
ret = -ENOMEM;
goto err_pci_iomap;
@@ -893,18 +893,7 @@ static struct pci_driver pch_phub_driver = {
.resume = pch_phub_resume
};
-static int __init pch_phub_pci_init(void)
-{
- return pci_register_driver(&pch_phub_driver);
-}
-
-static void __exit pch_phub_pci_exit(void)
-{
- pci_unregister_driver(&pch_phub_driver);
-}
-
-module_init(pch_phub_pci_init);
-module_exit(pch_phub_pci_exit);
+module_pci_driver(pch_phub_driver);
MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7223) PHUB");
MODULE_LICENSE("GPL");
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
index acfaeeb..46937b1 100644
--- a/drivers/misc/ti-st/st_core.c
+++ b/drivers/misc/ti-st/st_core.c
@@ -30,11 +30,13 @@
#include <linux/ti_wilink_st.h>
+extern void st_kim_recv(void *, const unsigned char *, long);
+void st_int_recv(void *, const unsigned char *, long);
/* function pointer pointing to either,
* st_kim_recv during registration to receive fw download responses
* st_int_recv after registration to receive proto stack responses
*/
-void (*st_recv) (void*, const unsigned char*, long);
+static void (*st_recv) (void *, const unsigned char *, long);
/********************************************************************/
static void add_channel_to_table(struct st_data_s *st_gdata,
@@ -100,7 +102,7 @@ int st_int_write(struct st_data_s *st_gdata,
* push the skb received to relevant
* protocol stacks
*/
-void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)
+static void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)
{
pr_debug(" %s(prot:%d) ", __func__, chnl_id);
@@ -140,7 +142,7 @@ void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)
* This function is being called with spin lock held, protocol drivers are
* only expected to complete their waits and do nothing more than that.
*/
-void st_reg_complete(struct st_data_s *st_gdata, char err)
+static void st_reg_complete(struct st_data_s *st_gdata, char err)
{
unsigned char i = 0;
pr_info(" %s ", __func__);
@@ -379,7 +381,7 @@ done:
* completely, return that skb which has the pending data.
* In normal cases, return top of txq.
*/
-struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata)
+static struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata)
{
struct sk_buff *returning_skb;
@@ -401,7 +403,7 @@ struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata)
* txq and waitq needs protection since the other contexts
* may be sending data, waking up chip.
*/
-void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb)
+static void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb)
{
unsigned long flags = 0;
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index 7c14f8f..04a8199 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -63,10 +63,27 @@ static struct platform_device *st_get_plat_device(int id)
* in case of error don't complete so that waiting for proper
* response times out
*/
-void validate_firmware_response(struct kim_data_s *kim_gdata)
+static void validate_firmware_response(struct kim_data_s *kim_gdata)
{
struct sk_buff *skb = kim_gdata->rx_skb;
- if (unlikely(skb->data[5] != 0)) {
+ if (!skb)
+ return;
+
+ /* these magic numbers are the position in the response buffer which
+ * allows us to distinguish whether the response is for the read
+ * version info. command
+ */
+ if (skb->data[2] == 0x01 && skb->data[3] == 0x01 &&
+ skb->data[4] == 0x10 && skb->data[5] == 0x00) {
+ /* fw version response */
+ memcpy(kim_gdata->resp_buffer,
+ kim_gdata->rx_skb->data,
+ kim_gdata->rx_skb->len);
+ complete_all(&kim_gdata->kim_rcvd);
+ kim_gdata->rx_state = ST_W4_PACKET_TYPE;
+ kim_gdata->rx_skb = NULL;
+ kim_gdata->rx_count = 0;
+ } else if (unlikely(skb->data[5] != 0)) {
pr_err("no proper response during fw download");
pr_err("data6 %x", skb->data[5]);
kfree_skb(skb);
@@ -119,7 +136,7 @@ static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len)
* have been observed to come in bursts of different
* tty_receive and hence the logic
*/
-void kim_int_recv(struct kim_data_s *kim_gdata,
+static void kim_int_recv(struct kim_data_s *kim_gdata,
const unsigned char *data, long count)
{
const unsigned char *ptr;
@@ -207,16 +224,19 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
return -EIO;
}
- if (!wait_for_completion_timeout
- (&kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME))) {
+ if (!wait_for_completion_interruptible_timeout(
+ &kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME))) {
pr_err(" waiting for ver info- timed out ");
return -ETIMEDOUT;
}
INIT_COMPLETION(kim_gdata->kim_rcvd);
+ /* the positions 12 & 13 in the response buffer provide with the
+ * chip, major & minor numbers
+ */
version =
- MAKEWORD(kim_gdata->resp_buffer[13],
- kim_gdata->resp_buffer[14]);
+ MAKEWORD(kim_gdata->resp_buffer[12],
+ kim_gdata->resp_buffer[13]);
chip = (version & 0x7C00) >> 10;
min_ver = (version & 0x007F);
maj_ver = (version & 0x0380) >> 7;
@@ -236,7 +256,7 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
return 0;
}
-void skip_change_remote_baud(unsigned char **ptr, long *len)
+static void skip_change_remote_baud(unsigned char **ptr, long *len)
{
unsigned char *nxt_action, *cur_action;
cur_action = *ptr;
@@ -370,9 +390,9 @@ static long download_firmware(struct kim_data_s *kim_gdata)
break;
case ACTION_WAIT_EVENT: /* wait */
pr_debug("W");
- if (!wait_for_completion_timeout
- (&kim_gdata->kim_rcvd,
- msecs_to_jiffies(CMD_RESP_TIME))) {
+ if (!wait_for_completion_interruptible_timeout(
+ &kim_gdata->kim_rcvd,
+ msecs_to_jiffies(CMD_RESP_TIME))) {
pr_err("response timeout during fw download ");
/* timed out */
release_firmware(kim_gdata->fw_entry);
@@ -410,16 +430,10 @@ void st_kim_recv(void *disc_data, const unsigned char *data, long count)
struct st_data_s *st_gdata = (struct st_data_s *)disc_data;
struct kim_data_s *kim_gdata = st_gdata->kim_data;
- /* copy to local buffer */
- if (unlikely(data[4] == 0x01 && data[5] == 0x10 && data[0] == 0x04)) {
- /* must be the read_ver_cmd */
- memcpy(kim_gdata->resp_buffer, data, count);
- complete_all(&kim_gdata->kim_rcvd);
- return;
- } else {
- kim_int_recv(kim_gdata, data, count);
- /* either completes or times out */
- }
+ /* proceed to gather all data and distinguish read fw version response
+ * from other fw responses when data gathering is complete
+ */
+ kim_int_recv(kim_gdata, data, count);
return;
}
@@ -454,11 +468,6 @@ long st_kim_start(void *kim_data)
if (pdata->chip_enable)
pdata->chip_enable(kim_gdata);
- /* Configure BT nShutdown to HIGH state */
- gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
- mdelay(5); /* FIXME: a proper toggle */
- gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH);
- mdelay(100);
/* re-initialize the completion */
INIT_COMPLETION(kim_gdata->ldisc_installed);
/* send notification to UIM */
@@ -467,8 +476,8 @@ long st_kim_start(void *kim_data)
sysfs_notify(&kim_gdata->kim_pdev->dev.kobj,
NULL, "install");
/* wait for ldisc to be installed */
- err = wait_for_completion_timeout(&kim_gdata->ldisc_installed,
- msecs_to_jiffies(LDISC_TIME));
+ err = wait_for_completion_interruptible_timeout(
+ &kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME));
if (!err) {
/* ldisc installation timeout,
* flush uart, power cycle BT_EN */
@@ -500,8 +509,7 @@ long st_kim_start(void *kim_data)
* (b) upon failure to either install ldisc or download firmware.
* The function is responsible to (a) notify UIM about un-installation,
* (b) flush UART if the ldisc was installed.
- * (c) reset BT_EN - pull down nshutdown at the end.
- * (d) invoke platform's chip disabling routine.
+ * (c) invoke platform's chip disabling routine.
*/
long st_kim_stop(void *kim_data)
{
@@ -526,20 +534,13 @@ long st_kim_stop(void *kim_data)
sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, NULL, "install");
/* wait for ldisc to be un-installed */
- err = wait_for_completion_timeout(&kim_gdata->ldisc_installed,
- msecs_to_jiffies(LDISC_TIME));
+ err = wait_for_completion_interruptible_timeout(
+ &kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME));
if (!err) { /* timeout */
pr_err(" timed out waiting for ldisc to be un-installed");
- return -ETIMEDOUT;
+ err = -ETIMEDOUT;
}
- /* By default configure BT nShutdown to LOW state */
- gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
- mdelay(1);
- gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH);
- mdelay(1);
- gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
-
/* platform specific disable */
if (pdata->chip_disable)
pdata->chip_disable(kim_gdata);
@@ -701,7 +702,7 @@ static const struct file_operations list_debugfs_fops = {
* board-*.c file
*/
-struct dentry *kim_debugfs_dir;
+static struct dentry *kim_debugfs_dir;
static int kim_probe(struct platform_device *pdev)
{
long status;
@@ -731,20 +732,6 @@ static int kim_probe(struct platform_device *pdev)
/* refer to itself */
kim_gdata->core_data->kim_data = kim_gdata;
- /* Claim the chip enable nShutdown gpio from the system */
- kim_gdata->nshutdown = pdata->nshutdown_gpio;
- status = gpio_request(kim_gdata->nshutdown, "kim");
- if (unlikely(status)) {
- pr_err(" gpio %ld request failed ", kim_gdata->nshutdown);
- return status;
- }
-
- /* Configure nShutdown GPIO as output=0 */
- status = gpio_direction_output(kim_gdata->nshutdown, 0);
- if (unlikely(status)) {
- pr_err(" unable to configure gpio %ld", kim_gdata->nshutdown);
- return status;
- }
/* get reference of pdev for request_firmware
*/
kim_gdata->kim_pdev = pdev;
@@ -780,18 +767,10 @@ static int kim_probe(struct platform_device *pdev)
static int kim_remove(struct platform_device *pdev)
{
- /* free the GPIOs requested */
- struct ti_st_plat_data *pdata = pdev->dev.platform_data;
struct kim_data_s *kim_gdata;
kim_gdata = dev_get_drvdata(&pdev->dev);
- /* Free the Bluetooth/FM/GPIO
- * nShutdown gpio from the system
- */
- gpio_free(pdata->nshutdown_gpio);
- pr_info("nshutdown GPIO Freed");
-
debugfs_remove_recursive(kim_debugfs_dir);
sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp);
pr_info("sysfs entries removed");
@@ -804,7 +783,7 @@ static int kim_remove(struct platform_device *pdev)
return 0;
}
-int kim_suspend(struct platform_device *pdev, pm_message_t state)
+static int kim_suspend(struct platform_device *pdev, pm_message_t state)
{
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
@@ -814,7 +793,7 @@ int kim_suspend(struct platform_device *pdev, pm_message_t state)
return -EOPNOTSUPP;
}
-int kim_resume(struct platform_device *pdev)
+static int kim_resume(struct platform_device *pdev)
{
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c
index ba24790..f8d66543 100644
--- a/drivers/misc/tifm_7xx1.c
+++ b/drivers/misc/tifm_7xx1.c
@@ -434,21 +434,9 @@ static struct pci_driver tifm_7xx1_driver = {
.resume = tifm_7xx1_resume,
};
-static int __init tifm_7xx1_init(void)
-{
- return pci_register_driver(&tifm_7xx1_driver);
-}
-
-static void __exit tifm_7xx1_exit(void)
-{
- pci_unregister_driver(&tifm_7xx1_driver);
-}
-
+module_pci_driver(tifm_7xx1_driver);
MODULE_AUTHOR("Alex Dubov");
MODULE_DESCRIPTION("TI FlashMedia host driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, tifm_7xx1_pci_tbl);
MODULE_VERSION(DRIVER_VERSION);
-
-module_init(tifm_7xx1_init);
-module_exit(tifm_7xx1_exit);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index d4619e2..2273ce6 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -641,7 +641,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
/*
* If the host and card support UHS-I mode request the card
* to switch to 1.8V signaling level. No 1.8v signalling if
- * UHS mode is not enabled to maintain compatibilty and some
+ * UHS mode is not enabled to maintain compatibility and some
* systems that claim 1.8v signalling in fact do not support
* it.
*/
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index efdb81d..74bed0f 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -356,7 +356,7 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host)
}
/*
- * Update bytes tranfered count during a write operation
+ * Update bytes transfered count during a write operation
*/
static void at91_mci_update_bytes_xfered(struct at91mci_host *host)
{
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index a53c7c4..852d5fb 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -1022,7 +1022,7 @@ static void atmci_stop_transfer(struct atmel_mci *host)
}
/*
- * Stop data transfer because error(s) occured.
+ * Stop data transfer because error(s) occurred.
*/
static void atmci_stop_transfer_pdc(struct atmel_mci *host)
{
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 3a09f93..686e256 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -447,7 +447,7 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
if ((OMAP_HSMMC_READ(host->base, SYSCTL) & CEN) != 0x0)
- dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
+ dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stopped\n");
}
static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index e23f813..32f4a07 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -315,7 +315,7 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
new_val = val & (SDHCI_CTRL_LED | \
SDHCI_CTRL_4BITBUS | \
SDHCI_CTRL_D3CD);
- /* ensure the endianess */
+ /* ensure the endianness */
new_val |= ESDHC_HOST_CONTROL_LE;
/* bits 8&9 are reserved on mx25 */
if (!is_imx25_esdhc(imx_data)) {
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index 3135a1a..58eab9a 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -806,7 +806,7 @@ static void command_res_completed(struct urb *urb)
* we suspect a buggy USB host controller
*/
} else if (!vub300->data) {
- /* this means that the command (typically CMD52) suceeded */
+ /* this means that the command (typically CMD52) succeeded */
} else if (vub300->resp.common.header_type != 0x02) {
/*
* this is an error response from the VUB300 chip
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index f2f482b..a6e7451 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -1123,6 +1123,33 @@ static unsigned long mtdchar_get_unmapped_area(struct file *file,
}
#endif
+static inline unsigned long get_vm_size(struct vm_area_struct *vma)
+{
+ return vma->vm_end - vma->vm_start;
+}
+
+static inline resource_size_t get_vm_offset(struct vm_area_struct *vma)
+{
+ return (resource_size_t) vma->vm_pgoff << PAGE_SHIFT;
+}
+
+/*
+ * Set a new vm offset.
+ *
+ * Verify that the incoming offset really works as a page offset,
+ * and that the offset and size fit in a resource_size_t.
+ */
+static inline int set_vm_offset(struct vm_area_struct *vma, resource_size_t off)
+{
+ pgoff_t pgoff = off >> PAGE_SHIFT;
+ if (off != (resource_size_t) pgoff << PAGE_SHIFT)
+ return -EINVAL;
+ if (off + get_vm_size(vma) - 1 < off)
+ return -EINVAL;
+ vma->vm_pgoff = pgoff;
+ return 0;
+}
+
/*
* set up a mapping for shared memory segments
*/
@@ -1132,20 +1159,29 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma)
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
struct map_info *map = mtd->priv;
- unsigned long start;
- unsigned long off;
- u32 len;
+ resource_size_t start, off;
+ unsigned long len, vma_len;
if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) {
- off = vma->vm_pgoff << PAGE_SHIFT;
+ off = get_vm_offset(vma);
start = map->phys;
len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size);
start &= PAGE_MASK;
- if ((vma->vm_end - vma->vm_start + off) > len)
+ vma_len = get_vm_size(vma);
+
+ /* Overflow in off+len? */
+ if (vma_len + off < off)
+ return -EINVAL;
+ /* Does it fit in the mapping? */
+ if (vma_len + off > len)
return -EINVAL;
off += start;
- vma->vm_pgoff = off >> PAGE_SHIFT;
+ /* Did that overflow? */
+ if (off < start)
+ return -EINVAL;
+ if (set_vm_offset(vma, off) < 0)
+ return -EINVAL;
vma->vm_flags |= VM_IO | VM_RESERVED;
#ifdef pgprot_noncached
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
index 98ee438..7edadee 100644
--- a/drivers/net/can/janz-ican3.c
+++ b/drivers/net/can/janz-ican3.c
@@ -1391,7 +1391,6 @@ static irqreturn_t ican3_irq(int irq, void *dev_id)
*/
static int ican3_reset_module(struct ican3_dev *mod)
{
- u8 val = 1 << mod->num;
unsigned long start;
u8 runold, runnew;
@@ -1405,8 +1404,7 @@ static int ican3_reset_module(struct ican3_dev *mod)
runold = ioread8(mod->dpm + TARGET_RUNNING);
/* reset the module */
- iowrite8(val, &mod->ctrl->reset_assert);
- iowrite8(val, &mod->ctrl->reset_deassert);
+ iowrite8(0x00, &mod->dpmctrl->hwreset);
/* wait until the module has finished resetting and is running */
start = jiffies;
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 527dbcf..9ded21e 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -984,12 +984,12 @@ static int __devexit ti_hecc_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct ti_hecc_priv *priv = netdev_priv(ndev);
+ unregister_candev(ndev);
clk_disable(priv->clk);
clk_put(priv->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(priv->base);
release_mem_region(res->start, resource_size(res));
- unregister_candev(ndev);
free_candev(ndev);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c
index b153666..bb9670f 100644
--- a/drivers/net/ethernet/3com/typhoon.c
+++ b/drivers/net/ethernet/3com/typhoon.c
@@ -364,7 +364,7 @@ typhoon_inc_rxfree_index(u32 *index, const int count)
static inline void
typhoon_inc_tx_index(u32 *index, const int count)
{
- /* if we start using the Hi Tx ring, this needs updateing */
+ /* if we start using the Hi Tx ring, this needs updating */
typhoon_inc_index(index, count, TXLO_ENTRIES);
}
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
index 1bf5bbf..55a2e37 100644
--- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
@@ -149,7 +149,7 @@ static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag)
data &= ~(PCI_ERR_UNC_DLP | PCI_ERR_UNC_FCP);
pci_write_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, data);
/* clear error status */
- pci_write_config_word(pdev, pci_pcie_cap(pdev) + PCI_EXP_DEVSTA,
+ pcie_capability_write_word(pdev, PCI_EXP_DEVSTA,
PCI_EXP_DEVSTA_NFED |
PCI_EXP_DEVSTA_FED |
PCI_EXP_DEVSTA_CED |
@@ -2685,7 +2685,7 @@ static void atl1c_io_resume(struct pci_dev *pdev)
netif_device_attach(netdev);
}
-static struct pci_error_handlers atl1c_err_handler = {
+static const struct pci_error_handlers atl1c_err_handler = {
.error_detected = atl1c_io_error_detected,
.slot_reset = atl1c_io_slot_reset,
.resume = atl1c_io_resume,
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
index a98acc8..e213da2 100644
--- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
@@ -2489,7 +2489,7 @@ static void atl1e_io_resume(struct pci_dev *pdev)
netif_device_attach(netdev);
}
-static struct pci_error_handlers atl1e_err_handler = {
+static const struct pci_error_handlers atl1e_err_handler = {
.error_detected = atl1e_io_error_detected,
.slot_reset = atl1e_io_slot_reset,
.resume = atl1e_io_resume,
diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c
index 79cebd8..d431070 100644
--- a/drivers/net/ethernet/broadcom/bnx2.c
+++ b/drivers/net/ethernet/broadcom/bnx2.c
@@ -8564,7 +8564,7 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
error:
- iounmap(bp->regview);
+ pci_iounmap(pdev, bp->regview);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
@@ -8742,7 +8742,7 @@ static void bnx2_io_resume(struct pci_dev *pdev)
rtnl_unlock();
}
-static struct pci_error_handlers bnx2_err_handler = {
+static const struct pci_error_handlers bnx2_err_handler = {
.error_detected = bnx2_io_error_detected,
.slot_reset = bnx2_io_slot_reset,
.resume = bnx2_io_resume,
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index 6d1a24a..eac2523 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -1458,7 +1458,7 @@ struct bnx2x {
int fw_stats_req_sz;
/*
- * FW statistics data shortcut (points at the begining of
+ * FW statistics data shortcut (points at the beginning of
* fw_stats buffer + fw_stats_req_sz).
*/
struct bnx2x_fw_stats_data *fw_stats_data;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index af20c6e..e8e97a7 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -662,14 +662,16 @@ void bnx2x_csum_validate(struct sk_buff *skb, union eth_rx_cqe *cqe,
struct bnx2x_fastpath *fp,
struct bnx2x_eth_q_stats *qstats)
{
- /* Do nothing if no IP/L4 csum validation was done */
-
+ /* Do nothing if no L4 csum validation was done.
+ * We do not check whether IP csum was validated. For IPv4 we assume
+ * that if the card got as far as validating the L4 csum, it also
+ * validated the IP csum. IPv6 has no IP csum.
+ */
if (cqe->fast_path_cqe.status_flags &
- (ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG |
- ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG))
+ ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG)
return;
- /* If both IP/L4 validation were done, check if an error was found. */
+ /* If L4 validation was done, check if an error was found. */
if (cqe->fast_path_cqe.type_error_flags &
(ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG |
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 211753e..e11485c 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -1162,14 +1162,9 @@ static int bnx2x_send_final_clnup(struct bnx2x *bp, u8 clnup_func,
static u8 bnx2x_is_pcie_pending(struct pci_dev *dev)
{
- int pos;
u16 status;
- pos = pci_pcie_cap(dev);
- if (!pos)
- return false;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status);
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
return status & PCI_EXP_DEVSTA_TRPND;
}
@@ -6135,8 +6130,7 @@ static void bnx2x_init_pxp(struct bnx2x *bp)
u16 devctl;
int r_order, w_order;
- pci_read_config_word(bp->pdev,
- pci_pcie_cap(bp->pdev) + PCI_EXP_DEVCTL, &devctl);
+ pcie_capability_read_word(bp->pdev, PCI_EXP_DEVCTL, &devctl);
DP(NETIF_MSG_HW, "read 0x%x from devctl\n", devctl);
w_order = ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
if (bp->mrrs == -1)
@@ -9380,7 +9374,7 @@ static int __devinit bnx2x_prev_mark_path(struct bnx2x *bp)
static int __devinit bnx2x_do_flr(struct bnx2x *bp)
{
- int i, pos;
+ int i;
u16 status;
struct pci_dev *dev = bp->pdev;
@@ -9397,16 +9391,12 @@ static int __devinit bnx2x_do_flr(struct bnx2x *bp)
return -EINVAL;
}
- pos = pci_pcie_cap(dev);
- if (!pos)
- return -ENOTTY;
-
/* Wait for Transaction Pending bit clean */
for (i = 0; i < 4; i++) {
if (i)
msleep((1 << (i - 1)) * 100);
- pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status);
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
if (!(status & PCI_EXP_DEVSTA_TRPND))
goto clear;
}
@@ -9831,12 +9821,13 @@ static void __devinit bnx2x_get_igu_cam_info(struct bnx2x *bp)
}
#ifdef CONFIG_PCI_MSI
- /*
- * It's expected that number of CAM entries for this functions is equal
- * to the number evaluated based on the MSI-X table size. We want a
- * harsh warning if these values are different!
+ /* Due to new PF resource allocation by MFW T7.4 and above, it's
+ * optional that number of CAM entries will not be equal to the value
+ * advertised in PCI.
+ * Driver should use the minimal value of both as the actual status
+ * block count
*/
- WARN_ON(bp->igu_sb_cnt != igu_sb_cnt);
+ bp->igu_sb_cnt = min_t(int, bp->igu_sb_cnt, igu_sb_cnt);
#endif
if (igu_sb_cnt == 0)
@@ -12166,7 +12157,7 @@ static void bnx2x_io_resume(struct pci_dev *pdev)
rtnl_unlock();
}
-static struct pci_error_handlers bnx2x_err_handler = {
+static const struct pci_error_handlers bnx2x_err_handler = {
.error_detected = bnx2x_io_error_detected,
.slot_reset = bnx2x_io_slot_reset,
.resume = bnx2x_io_resume,
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
index f83e033..acf2fe4 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
@@ -1321,7 +1321,7 @@ void bnx2x_init_mcast_obj(struct bnx2x *bp,
* the current command will be enqueued to the tail of the
* pending commands list.
*
- * Return: 0 is operation was sucessfull and there are no pending completions,
+ * Return: 0 is operation was successfull and there are no pending completions,
* negative if there were errors, positive if there are pending
* completions.
*/
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index bf906c5..388d322 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -3653,17 +3653,9 @@ static int tg3_power_down_prepare(struct tg3 *tp)
tg3_enable_register_access(tp);
/* Restore the CLKREQ setting. */
- if (tg3_flag(tp, CLKREQ_BUG)) {
- u16 lnkctl;
-
- pci_read_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
- &lnkctl);
- lnkctl |= PCI_EXP_LNKCTL_CLKREQ_EN;
- pci_write_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
- lnkctl);
- }
+ if (tg3_flag(tp, CLKREQ_BUG))
+ pcie_capability_set_word(tp->pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL);
tw32(TG3PCI_MISC_HOST_CTRL,
@@ -4434,20 +4426,13 @@ relink:
/* Prevent send BD corruption. */
if (tg3_flag(tp, CLKREQ_BUG)) {
- u16 oldlnkctl, newlnkctl;
-
- pci_read_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
- &oldlnkctl);
if (tp->link_config.active_speed == SPEED_100 ||
tp->link_config.active_speed == SPEED_10)
- newlnkctl = oldlnkctl & ~PCI_EXP_LNKCTL_CLKREQ_EN;
+ pcie_capability_clear_word(tp->pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
else
- newlnkctl = oldlnkctl | PCI_EXP_LNKCTL_CLKREQ_EN;
- if (newlnkctl != oldlnkctl)
- pci_write_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) +
- PCI_EXP_LNKCTL, newlnkctl);
+ pcie_capability_set_word(tp->pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
}
if (current_link_up != netif_carrier_ok(tp->dev)) {
@@ -8054,7 +8039,7 @@ static int tg3_chip_reset(struct tg3 *tp)
udelay(120);
- if (tg3_flag(tp, PCI_EXPRESS) && pci_pcie_cap(tp->pdev)) {
+ if (tg3_flag(tp, PCI_EXPRESS) && pci_is_pcie(tp->pdev)) {
u16 val16;
if (tp->pci_chip_rev_id == CHIPREV_ID_5750_A0) {
@@ -8071,24 +8056,17 @@ static int tg3_chip_reset(struct tg3 *tp)
}
/* Clear the "no snoop" and "relaxed ordering" bits. */
- pci_read_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_DEVCTL,
- &val16);
- val16 &= ~(PCI_EXP_DEVCTL_RELAX_EN |
- PCI_EXP_DEVCTL_NOSNOOP_EN);
+ val16 = PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN;
/*
* Older PCIe devices only support the 128 byte
* MPS setting. Enforce the restriction.
*/
if (!tg3_flag(tp, CPMU_PRESENT))
- val16 &= ~PCI_EXP_DEVCTL_PAYLOAD;
- pci_write_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_DEVCTL,
- val16);
+ val16 |= PCI_EXP_DEVCTL_PAYLOAD;
+ pcie_capability_clear_word(tp->pdev, PCI_EXP_DEVCTL, val16);
/* Clear error status */
- pci_write_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_DEVSTA,
+ pcie_capability_write_word(tp->pdev, PCI_EXP_DEVSTA,
PCI_EXP_DEVSTA_CED |
PCI_EXP_DEVSTA_NFED |
PCI_EXP_DEVSTA_FED |
@@ -14565,9 +14543,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tg3_flag_set(tp, PCI_EXPRESS);
- pci_read_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
- &lnkctl);
+ pcie_capability_read_word(tp->pdev, PCI_EXP_LNKCTL, &lnkctl);
if (lnkctl & PCI_EXP_LNKCTL_CLKREQ_EN) {
if (GET_ASIC_REV(tp->pci_chip_rev_id) ==
ASIC_REV_5906) {
@@ -16397,7 +16373,7 @@ done:
rtnl_unlock();
}
-static struct pci_error_handlers tg3_err_handler = {
+static const struct pci_error_handlers tg3_err_handler = {
.error_detected = tg3_io_error_detected,
.slot_reset = tg3_io_slot_reset,
.resume = tg3_io_resume
diff --git a/drivers/net/ethernet/cadence/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c
index 7788419..4e980a78 100644
--- a/drivers/net/ethernet/cadence/at91_ether.c
+++ b/drivers/net/ethernet/cadence/at91_ether.c
@@ -1086,7 +1086,7 @@ static int __init at91ether_probe(struct platform_device *pdev)
/* Clock */
lp->ether_clk = clk_get(&pdev->dev, "ether_clk");
if (IS_ERR(lp->ether_clk)) {
- res = -ENODEV;
+ res = PTR_ERR(lp->ether_clk);
goto err_ioumap;
}
clk_enable(lp->ether_clk);
diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
index 6505070..875bbb9 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
@@ -3036,7 +3036,7 @@ static void t3_io_resume(struct pci_dev *pdev)
t3_resume_ports(adapter);
}
-static struct pci_error_handlers t3_err_handler = {
+static const struct pci_error_handlers t3_err_handler = {
.error_detected = t3_io_error_detected,
.slot_reset = t3_io_slot_reset,
.resume = t3_io_resume,
diff --git a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
index bff8a3c..aef45d3 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
@@ -3289,22 +3289,18 @@ static void config_pcie(struct adapter *adap)
unsigned int log2_width, pldsize;
unsigned int fst_trn_rx, fst_trn_tx, acklat, rpllmt;
- pci_read_config_word(adap->pdev,
- adap->pdev->pcie_cap + PCI_EXP_DEVCTL,
- &val);
+ pcie_capability_read_word(adap->pdev, PCI_EXP_DEVCTL, &val);
pldsize = (val & PCI_EXP_DEVCTL_PAYLOAD) >> 5;
pci_read_config_word(adap->pdev, 0x2, &devid);
if (devid == 0x37) {
- pci_write_config_word(adap->pdev,
- adap->pdev->pcie_cap + PCI_EXP_DEVCTL,
- val & ~PCI_EXP_DEVCTL_READRQ &
- ~PCI_EXP_DEVCTL_PAYLOAD);
+ pcie_capability_write_word(adap->pdev, PCI_EXP_DEVCTL,
+ val & ~PCI_EXP_DEVCTL_READRQ &
+ ~PCI_EXP_DEVCTL_PAYLOAD);
pldsize = 0;
}
- pci_read_config_word(adap->pdev, adap->pdev->pcie_cap + PCI_EXP_LNKCTL,
- &val);
+ pcie_capability_read_word(adap->pdev, PCI_EXP_LNKCTL, &val);
fst_trn_tx = G_NUMFSTTRNSEQ(t3_read_reg(adap, A_PCIE_PEX_CTRL0));
fst_trn_rx = adap->params.rev == 0 ? fst_trn_tx :
@@ -3425,15 +3421,13 @@ out_err:
static void get_pci_mode(struct adapter *adapter, struct pci_params *p)
{
static unsigned short speed_map[] = { 33, 66, 100, 133 };
- u32 pci_mode, pcie_cap;
+ u32 pci_mode;
- pcie_cap = pci_pcie_cap(adapter->pdev);
- if (pcie_cap) {
+ if (pci_is_pcie(adapter->pdev)) {
u16 val;
p->variant = PCI_VARIANT_PCIE;
- pci_read_config_word(adapter->pdev, pcie_cap + PCI_EXP_LNKSTA,
- &val);
+ pcie_capability_read_word(adapter->pdev, PCI_EXP_LNKSTA, &val);
p->width = (val >> 4) & 0x3f;
return;
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 5ed49af..9339854 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -3453,7 +3453,7 @@ static void eeh_resume(struct pci_dev *pdev)
rtnl_unlock();
}
-static struct pci_error_handlers cxgb4_eeh = {
+static const struct pci_error_handlers cxgb4_eeh = {
.error_detected = eeh_err_detected,
.slot_reset = eeh_slot_reset,
.resume = eeh_resume,
@@ -3694,15 +3694,7 @@ static void __devinit print_port_info(const struct net_device *dev)
static void __devinit enable_pcie_relaxed_ordering(struct pci_dev *dev)
{
- u16 v;
- int pos;
-
- pos = pci_pcie_cap(dev);
- if (pos > 0) {
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &v);
- v |= PCI_EXP_DEVCTL_RELAX_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, v);
- }
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN);
}
/*
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index fa947df..af16013 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -2741,11 +2741,9 @@ static void __devinit get_pci_mode(struct adapter *adapter,
struct pci_params *p)
{
u16 val;
- u32 pcie_cap = pci_pcie_cap(adapter->pdev);
- if (pcie_cap) {
- pci_read_config_word(adapter->pdev, pcie_cap + PCI_EXP_LNKSTA,
- &val);
+ if (pci_is_pcie(adapter->pdev)) {
+ pcie_capability_read_word(adapter->pdev, PCI_EXP_LNKSTA, &val);
p->speed = val & PCI_EXP_LNKSTA_CLS;
p->width = (val & PCI_EXP_LNKSTA_NLW) >> 4;
}
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 78b8aa8..95d1047 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -4106,7 +4106,7 @@ err:
dev_err(&adapter->pdev->dev, "EEH resume failed\n");
}
-static struct pci_error_handlers be_eeh_handlers = {
+static const struct pci_error_handlers be_eeh_handlers = {
.error_detected = be_eeh_err_detected,
.slot_reset = be_eeh_reset,
.resume = be_eeh_resume,
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
index 8971921..ab6762c 100644
--- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
+++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
@@ -1773,6 +1773,7 @@ static int gfar_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
}
int gfar_phc_index = -1;
+EXPORT_SYMBOL(gfar_phc_index);
static int gfar_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c
index c08e5d4..0daa66b 100644
--- a/drivers/net/ethernet/freescale/gianfar_ptp.c
+++ b/drivers/net/ethernet/freescale/gianfar_ptp.c
@@ -515,7 +515,7 @@ static int gianfar_ptp_probe(struct platform_device *dev)
err = PTR_ERR(etsects->clock);
goto no_clock;
}
- gfar_phc_clock = ptp_clock_index(etsects->clock);
+ gfar_phc_index = ptp_clock_index(etsects->clock);
dev_set_drvdata(&dev->dev, etsects);
@@ -539,7 +539,7 @@ static int gianfar_ptp_remove(struct platform_device *dev)
gfar_write(&etsects->regs->tmr_temask, 0);
gfar_write(&etsects->regs->tmr_ctrl, 0);
- gfar_phc_clock = -1;
+ gfar_phc_index = -1;
ptp_clock_unregister(etsects->clock);
iounmap(etsects->regs);
release_resource(etsects->rsrc);
diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c
index 535f94f..29ce9bd 100644
--- a/drivers/net/ethernet/intel/e100.c
+++ b/drivers/net/ethernet/intel/e100.c
@@ -3157,7 +3157,7 @@ static void e100_io_resume(struct pci_dev *pdev)
}
}
-static struct pci_error_handlers e100_err_handler = {
+static const struct pci_error_handlers e100_err_handler = {
.error_detected = e100_io_error_detected,
.slot_reset = e100_io_slot_reset,
.resume = e100_io_resume,
diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c
index 3bfbb8d..f3f9aeb 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_main.c
@@ -192,7 +192,7 @@ static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev,
static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev);
static void e1000_io_resume(struct pci_dev *pdev);
-static struct pci_error_handlers e1000_err_handler = {
+static const struct pci_error_handlers e1000_err_handler = {
.error_detected = e1000_io_error_detected,
.slot_reset = e1000_io_slot_reset,
.resume = e1000_io_resume,
@@ -3149,6 +3149,17 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
return NETDEV_TX_OK;
}
+ /* On PCI/PCI-X HW, if packet size is less than ETH_ZLEN,
+ * packets may get corrupted during padding by HW.
+ * To WA this issue, pad all small packets manually.
+ */
+ if (skb->len < ETH_ZLEN) {
+ if (skb_pad(skb, ETH_ZLEN - skb->len))
+ return NETDEV_TX_OK;
+ skb->len = ETH_ZLEN;
+ skb_set_tail_pointer(skb, ETH_ZLEN);
+ }
+
mss = skb_shinfo(skb)->gso_size;
/* The controller does a simple calculation to
* make sure there is enough room in the FIFO before
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index d01a099..3f0223a 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -5584,16 +5584,15 @@ static void e1000_complete_shutdown(struct pci_dev *pdev, bool sleep,
*/
if (adapter->flags & FLAG_IS_QUAD_PORT) {
struct pci_dev *us_dev = pdev->bus->self;
- int pos = pci_pcie_cap(us_dev);
u16 devctl;
- pci_read_config_word(us_dev, pos + PCI_EXP_DEVCTL, &devctl);
- pci_write_config_word(us_dev, pos + PCI_EXP_DEVCTL,
- (devctl & ~PCI_EXP_DEVCTL_CERE));
+ pcie_capability_read_word(us_dev, PCI_EXP_DEVCTL, &devctl);
+ pcie_capability_write_word(us_dev, PCI_EXP_DEVCTL,
+ (devctl & ~PCI_EXP_DEVCTL_CERE));
e1000_power_off(pdev, sleep, wake);
- pci_write_config_word(us_dev, pos + PCI_EXP_DEVCTL, devctl);
+ pcie_capability_write_word(us_dev, PCI_EXP_DEVCTL, devctl);
} else {
e1000_power_off(pdev, sleep, wake);
}
@@ -5607,25 +5606,15 @@ static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
#else
static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
{
- int pos;
- u16 reg16;
-
/*
* Both device and parent should have the same ASPM setting.
* Disable ASPM in downstream component first and then upstream.
*/
- pos = pci_pcie_cap(pdev);
- pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 &= ~state;
- pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
-
- if (!pdev->bus->self)
- return;
+ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, state);
- pos = pci_pcie_cap(pdev->bus->self);
- pci_read_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 &= ~state;
- pci_write_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, reg16);
+ if (pdev->bus->self)
+ pcie_capability_clear_word(pdev->bus->self, PCI_EXP_LNKCTL,
+ state);
}
#endif
static void e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
@@ -6486,7 +6475,7 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
}
/* PCI Error Recovery (ERS) */
-static struct pci_error_handlers e1000_err_handler = {
+static const struct pci_error_handlers e1000_err_handler = {
.error_detected = e1000_io_error_detected,
.slot_reset = e1000_io_slot_reset,
.resume = e1000_io_resume,
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 48cc4fb..f88c822 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -217,7 +217,7 @@ static pci_ers_result_t igb_io_error_detected(struct pci_dev *,
static pci_ers_result_t igb_io_slot_reset(struct pci_dev *);
static void igb_io_resume(struct pci_dev *);
-static struct pci_error_handlers igb_err_handler = {
+static const struct pci_error_handlers igb_err_handler = {
.error_detected = igb_io_error_detected,
.slot_reset = igb_io_slot_reset,
.resume = igb_io_resume,
@@ -6538,28 +6538,20 @@ static int igb_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
{
struct igb_adapter *adapter = hw->back;
- u16 cap_offset;
- cap_offset = adapter->pdev->pcie_cap;
- if (!cap_offset)
+ if (pcie_capability_read_word(adapter->pdev, reg, value))
return -E1000_ERR_CONFIG;
- pci_read_config_word(adapter->pdev, cap_offset + reg, value);
-
return 0;
}
s32 igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
{
struct igb_adapter *adapter = hw->back;
- u16 cap_offset;
- cap_offset = adapter->pdev->pcie_cap;
- if (!cap_offset)
+ if (pcie_capability_write_word(adapter->pdev, reg, *value))
return -E1000_ERR_CONFIG;
- pci_write_config_word(adapter->pdev, cap_offset + reg, *value);
-
return 0;
}
diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c
index 0696abf..0ac11f5 100644
--- a/drivers/net/ethernet/intel/igbvf/netdev.c
+++ b/drivers/net/ethernet/intel/igbvf/netdev.c
@@ -2833,7 +2833,7 @@ static void __devexit igbvf_remove(struct pci_dev *pdev)
}
/* PCI Error Recovery (ERS) */
-static struct pci_error_handlers igbvf_err_handler = {
+static const struct pci_error_handlers igbvf_err_handler = {
.error_detected = igbvf_io_error_detected,
.slot_reset = igbvf_io_slot_reset,
.resume = igbvf_io_resume,
diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c
index d05fc95..d99a2d5 100644
--- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c
@@ -115,7 +115,7 @@ static pci_ers_result_t ixgb_io_error_detected (struct pci_dev *pdev,
static pci_ers_result_t ixgb_io_slot_reset (struct pci_dev *pdev);
static void ixgb_io_resume (struct pci_dev *pdev);
-static struct pci_error_handlers ixgb_err_handler = {
+static const struct pci_error_handlers ixgb_err_handler = {
.error_detected = ixgb_io_error_detected,
.slot_reset = ixgb_io_slot_reset,
.resume = ixgb_io_resume,
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 4326f74..ee61819 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7527,7 +7527,7 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
goto skip_bad_vf_detection;
bdev = pdev->bus->self;
- while (bdev && (bdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT))
+ while (bdev && (pci_pcie_type(bdev) != PCI_EXP_TYPE_ROOT_PORT))
bdev = bdev->bus->self;
if (!bdev)
@@ -7677,7 +7677,7 @@ static void ixgbe_io_resume(struct pci_dev *pdev)
netif_device_attach(netdev);
}
-static struct pci_error_handlers ixgbe_err_handler = {
+static const struct pci_error_handlers ixgbe_err_handler = {
.error_detected = ixgbe_io_error_detected,
.slot_reset = ixgbe_io_slot_reset,
.resume = ixgbe_io_resume,
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 60ef645..6647383 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -3256,7 +3256,7 @@ static void ixgbevf_io_resume(struct pci_dev *pdev)
}
/* PCI Error Recovery (ERS) */
-static struct pci_error_handlers ixgbevf_err_handler = {
+static const struct pci_error_handlers ixgbevf_err_handler = {
.error_detected = ixgbevf_io_error_detected,
.slot_reset = ixgbevf_io_slot_reset,
.resume = ixgbevf_io_resume,
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c
index daf4179..31d0264 100644
--- a/drivers/net/ethernet/mellanox/mlx4/icm.c
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.c
@@ -227,9 +227,10 @@ int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev)
MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
}
-int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
+int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj)
{
- int i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
+ u32 i = (obj & (table->num_obj - 1)) /
+ (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
int ret = 0;
mutex_lock(&table->mutex);
@@ -262,16 +263,18 @@ out:
return ret;
}
-void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
+void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj)
{
- int i;
+ u32 i;
+ u64 offset;
i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
mutex_lock(&table->mutex);
if (--table->icm[i]->refcount == 0) {
- mlx4_UNMAP_ICM(dev, table->virt + i * MLX4_TABLE_CHUNK_SIZE,
+ offset = (u64) i * MLX4_TABLE_CHUNK_SIZE;
+ mlx4_UNMAP_ICM(dev, table->virt + offset,
MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE);
mlx4_free_icm(dev, table->icm[i], table->coherent);
table->icm[i] = NULL;
@@ -280,9 +283,11 @@ void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
mutex_unlock(&table->mutex);
}
-void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_handle)
+void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj,
+ dma_addr_t *dma_handle)
{
- int idx, offset, dma_offset, i;
+ int offset, dma_offset, i;
+ u64 idx;
struct mlx4_icm_chunk *chunk;
struct mlx4_icm *icm;
struct page *page = NULL;
@@ -292,7 +297,7 @@ void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_han
mutex_lock(&table->mutex);
- idx = (obj & (table->num_obj - 1)) * table->obj_size;
+ idx = (u64) (obj & (table->num_obj - 1)) * table->obj_size;
icm = table->icm[idx / MLX4_TABLE_CHUNK_SIZE];
dma_offset = offset = idx % MLX4_TABLE_CHUNK_SIZE;
@@ -326,10 +331,11 @@ out:
}
int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end)
+ u32 start, u32 end)
{
int inc = MLX4_TABLE_CHUNK_SIZE / table->obj_size;
- int i, err;
+ int err;
+ u32 i;
for (i = start; i <= end; i += inc) {
err = mlx4_table_get(dev, table, i);
@@ -349,9 +355,9 @@ fail:
}
void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end)
+ u32 start, u32 end)
{
- int i;
+ u32 i;
for (i = start; i <= end; i += MLX4_TABLE_CHUNK_SIZE / table->obj_size)
mlx4_table_put(dev, table, i);
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.h b/drivers/net/ethernet/mellanox/mlx4/icm.h
index a67744f..dee67fa 100644
--- a/drivers/net/ethernet/mellanox/mlx4/icm.h
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.h
@@ -71,17 +71,17 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages,
gfp_t gfp_mask, int coherent);
void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent);
-int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj);
-void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj);
+int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj);
+void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj);
int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end);
+ u32 start, u32 end);
void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end);
+ u32 start, u32 end);
int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table,
u64 virt, int obj_size, u32 nobj, int reserved,
int use_lowmem, int use_coherent);
void mlx4_cleanup_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table);
-void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_handle);
+void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj, dma_addr_t *dma_handle);
static inline void mlx4_icm_first(struct mlx4_icm *icm,
struct mlx4_icm_iter *iter)
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 2f816c6..dd6ea94 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -2300,7 +2300,7 @@ static pci_ers_result_t mlx4_pci_slot_reset(struct pci_dev *pdev)
return ret ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
}
-static struct pci_error_handlers mlx4_err_handler = {
+static const struct pci_error_handlers mlx4_err_handler = {
.error_detected = mlx4_pci_err_detected,
.slot_reset = mlx4_pci_slot_reset,
};
diff --git a/drivers/net/ethernet/mellanox/mlx4/reset.c b/drivers/net/ethernet/mellanox/mlx4/reset.c
index 11e7c1c..dd1b509 100644
--- a/drivers/net/ethernet/mellanox/mlx4/reset.c
+++ b/drivers/net/ethernet/mellanox/mlx4/reset.c
@@ -141,16 +141,16 @@ int mlx4_reset(struct mlx4_dev *dev)
/* Now restore the PCI headers */
if (pcie_cap) {
devctl = hca_header[(pcie_cap + PCI_EXP_DEVCTL) / 4];
- if (pci_write_config_word(dev->pdev, pcie_cap + PCI_EXP_DEVCTL,
- devctl)) {
+ if (pcie_capability_write_word(dev->pdev, PCI_EXP_DEVCTL,
+ devctl)) {
err = -ENODEV;
mlx4_err(dev, "Couldn't restore HCA PCI Express "
"Device Control register, aborting.\n");
goto out;
}
linkctl = hca_header[(pcie_cap + PCI_EXP_LNKCTL) / 4];
- if (pci_write_config_word(dev->pdev, pcie_cap + PCI_EXP_LNKCTL,
- linkctl)) {
+ if (pcie_capability_write_word(dev->pdev, PCI_EXP_LNKCTL,
+ linkctl)) {
err = -ENODEV;
mlx4_err(dev, "Couldn't restore HCA PCI Express "
"Link control register, aborting.\n");
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
index fa85cf1..83516e3 100644
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
@@ -1078,22 +1078,16 @@ static int myri10ge_reset(struct myri10ge_priv *mgp)
#ifdef CONFIG_MYRI10GE_DCA
static int myri10ge_toggle_relaxed(struct pci_dev *pdev, int on)
{
- int ret, cap, err;
+ int ret;
u16 ctl;
- cap = pci_pcie_cap(pdev);
- if (!cap)
- return 0;
-
- err = pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl);
- if (err)
- return 0;
+ pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &ctl);
ret = (ctl & PCI_EXP_DEVCTL_RELAX_EN) >> 4;
if (ret != on) {
ctl &= ~PCI_EXP_DEVCTL_RELAX_EN;
ctl |= (on << 4);
- pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl);
+ pcie_capability_write_word(pdev, PCI_EXP_DEVCTL, ctl);
}
return ret;
}
@@ -3192,18 +3186,13 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
struct device *dev = &mgp->pdev->dev;
int cap;
unsigned err_cap;
- u16 val;
- u8 ext_type;
int ret;
if (!myri10ge_ecrc_enable || !bridge)
return;
/* check that the bridge is a root port */
- cap = pci_pcie_cap(bridge);
- pci_read_config_word(bridge, cap + PCI_CAP_FLAGS, &val);
- ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4;
- if (ext_type != PCI_EXP_TYPE_ROOT_PORT) {
+ if (pci_pcie_type(bridge) != PCI_EXP_TYPE_ROOT_PORT) {
if (myri10ge_ecrc_enable > 1) {
struct pci_dev *prev_bridge, *old_bridge = bridge;
@@ -3218,11 +3207,8 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
" to force ECRC\n");
return;
}
- cap = pci_pcie_cap(bridge);
- pci_read_config_word(bridge,
- cap + PCI_CAP_FLAGS, &val);
- ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4;
- } while (ext_type != PCI_EXP_TYPE_ROOT_PORT);
+ } while (pci_pcie_type(bridge) !=
+ PCI_EXP_TYPE_ROOT_PORT);
dev_info(dev,
"Forcing ECRC on non-root port %s"
@@ -3335,11 +3321,10 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
int overridden = 0;
if (myri10ge_force_firmware == 0) {
- int link_width, exp_cap;
+ int link_width;
u16 lnk;
- exp_cap = pci_pcie_cap(mgp->pdev);
- pci_read_config_word(mgp->pdev, exp_cap + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(mgp->pdev, PCI_EXP_LNKSTA, &lnk);
link_width = (lnk >> 4) & 0x3f;
/* Check to see if Link is less than 8 or if the
diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c
index d958c22..de50547 100644
--- a/drivers/net/ethernet/neterion/s2io.c
+++ b/drivers/net/ethernet/neterion/s2io.c
@@ -484,7 +484,7 @@ static DEFINE_PCI_DEVICE_TABLE(s2io_tbl) = {
MODULE_DEVICE_TABLE(pci, s2io_tbl);
-static struct pci_error_handlers s2io_err_handler = {
+static const struct pci_error_handlers s2io_err_handler = {
.error_detected = s2io_io_error_detected,
.slot_reset = s2io_io_slot_reset,
.resume = s2io_io_resume,
diff --git a/drivers/net/ethernet/neterion/vxge/vxge-config.c b/drivers/net/ethernet/neterion/vxge/vxge-config.c
index 32d0682..c2e420a 100644
--- a/drivers/net/ethernet/neterion/vxge/vxge-config.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-config.c
@@ -757,7 +757,7 @@ __vxge_hw_verify_pci_e_info(struct __vxge_hw_device *hldev)
u16 lnk;
/* Get the negotiated link width and speed from PCI config space */
- pci_read_config_word(dev, dev->pcie_cap + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnk);
if ((lnk & PCI_EXP_LNKSTA_CLS) != 1)
return VXGE_HW_ERR_INVALID_PCI_INFO;
@@ -1982,7 +1982,7 @@ u16 vxge_hw_device_link_width_get(struct __vxge_hw_device *hldev)
struct pci_dev *dev = hldev->pdev;
u16 lnk;
- pci_read_config_word(dev, dev->pcie_cap + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnk);
return (lnk & VXGE_HW_PCI_EXP_LNKCAP_LNK_WIDTH) >> 4;
}
diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c
index de21904..cfa71a3 100644
--- a/drivers/net/ethernet/neterion/vxge/vxge-main.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c
@@ -4799,7 +4799,7 @@ static void __devexit vxge_remove(struct pci_dev *pdev)
__LINE__);
}
-static struct pci_error_handlers vxge_err_handler = {
+static const struct pci_error_handlers vxge_err_handler = {
.error_detected = vxge_io_error_detected,
.slot_reset = vxge_io_slot_reset,
.resume = vxge_io_resume,
diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c
index c42bbb1..a688a2d 100644
--- a/drivers/net/ethernet/octeon/octeon_mgmt.c
+++ b/drivers/net/ethernet/octeon/octeon_mgmt.c
@@ -722,10 +722,8 @@ static int octeon_mgmt_init_phy(struct net_device *netdev)
octeon_mgmt_adjust_link, 0,
PHY_INTERFACE_MODE_MII);
- if (IS_ERR(p->phydev)) {
- p->phydev = NULL;
+ if (!p->phydev)
return -1;
- }
phy_start_aneg(p->phydev);
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
index feb85d5..b2a94d0 100644
--- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
@@ -2795,7 +2795,7 @@ static const struct dev_pm_ops pch_gbe_pm_ops = {
};
#endif
-static struct pci_error_handlers pch_gbe_err_handler = {
+static const struct pci_error_handlers pch_gbe_err_handler = {
.error_detected = pch_gbe_io_error_detected,
.slot_reset = pch_gbe_io_slot_reset,
.resume = pch_gbe_io_resume
diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c
index e559dfa..6fa74d5 100644
--- a/drivers/net/ethernet/pasemi/pasemi_mac.c
+++ b/drivers/net/ethernet/pasemi/pasemi_mac.c
@@ -1101,9 +1101,9 @@ static int pasemi_mac_phy_init(struct net_device *dev)
phydev = of_phy_connect(dev, phy_dn, &pasemi_adjust_link, 0,
PHY_INTERFACE_MODE_SGMII);
- if (IS_ERR(phydev)) {
+ if (!phydev) {
printk(KERN_ERR "%s: Could not attach to phy\n", dev->name);
- return PTR_ERR(phydev);
+ return -ENODEV;
}
mac->phydev = phydev;
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
index 342b3a7..df45061 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
@@ -1378,11 +1378,15 @@ static void netxen_mask_aer_correctable(struct netxen_adapter *adapter)
struct pci_dev *root = pdev->bus->self;
u32 aer_pos;
+ /* root bus? */
+ if (!root)
+ return;
+
if (adapter->ahw.board_type != NETXEN_BRDTYPE_P3_4_GB_MM &&
adapter->ahw.board_type != NETXEN_BRDTYPE_P3_10G_TP)
return;
- if (root->pcie_type != PCI_EXP_TYPE_ROOT_PORT)
+ if (pci_pcie_type(root) != PCI_EXP_TYPE_ROOT_PORT)
return;
aer_pos = pci_find_ext_capability(root, PCI_EXT_CAP_ID_ERR);
@@ -3336,7 +3340,7 @@ netxen_free_vlan_ip_list(struct netxen_adapter *adapter)
{ }
#endif
-static struct pci_error_handlers netxen_err_handler = {
+static const struct pci_error_handlers netxen_err_handler = {
.error_detected = netxen_io_error_detected,
.slot_reset = netxen_io_slot_reset,
.resume = netxen_io_resume,
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
index b8ead69..2a179d0 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
@@ -15,7 +15,7 @@ qlcnic_poll_rsp(struct qlcnic_adapter *adapter)
do {
/* give atleast 1ms for firmware to respond */
- msleep(1);
+ mdelay(1);
if (++timeout > QLCNIC_OS_CRB_RETRY_COUNT)
return QLCNIC_CDRP_RSP_TIMEOUT;
@@ -601,7 +601,7 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)
qlcnic_fw_cmd_destroy_tx_ctx(adapter);
/* Allow dma queues to drain after context reset */
- msleep(20);
+ mdelay(20);
}
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 212c121..473ce13 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -4522,7 +4522,7 @@ static void
qlcnic_restore_indev_addr(struct net_device *dev, unsigned long event)
{ }
#endif
-static struct pci_error_handlers qlcnic_err_handler = {
+static const struct pci_error_handlers qlcnic_err_handler = {
.error_detected = qlcnic_io_error_detected,
.slot_reset = qlcnic_io_slot_reset,
.resume = qlcnic_io_resume,
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
index b53a3b6..b262d61 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
@@ -4847,7 +4847,7 @@ static void qlge_io_resume(struct pci_dev *pdev)
netif_device_attach(ndev);
}
-static struct pci_error_handlers qlge_err_handler = {
+static const struct pci_error_handlers qlge_err_handler = {
.error_detected = qlge_io_error_detected,
.slot_reset = qlge_io_slot_reset,
.resume = qlge_io_resume,
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index b47d5b3..a7cc560 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -833,15 +833,8 @@ static void rtl_unlock_work(struct rtl8169_private *tp)
static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force)
{
- int cap = pci_pcie_cap(pdev);
-
- if (cap) {
- u16 ctl;
-
- pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl);
- ctl = (ctl & ~PCI_EXP_DEVCTL_READRQ) | force;
- pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl);
- }
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_READRQ, force);
}
struct rtl_cond {
@@ -4739,28 +4732,14 @@ static void rtl_ephy_init(struct rtl8169_private *tp, const struct ephy_info *e,
static void rtl_disable_clock_request(struct pci_dev *pdev)
{
- int cap = pci_pcie_cap(pdev);
-
- if (cap) {
- u16 ctl;
-
- pci_read_config_word(pdev, cap + PCI_EXP_LNKCTL, &ctl);
- ctl &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
- pci_write_config_word(pdev, cap + PCI_EXP_LNKCTL, ctl);
- }
+ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
}
static void rtl_enable_clock_request(struct pci_dev *pdev)
{
- int cap = pci_pcie_cap(pdev);
-
- if (cap) {
- u16 ctl;
-
- pci_read_config_word(pdev, cap + PCI_EXP_LNKCTL, &ctl);
- ctl |= PCI_EXP_LNKCTL_CLKREQ_EN;
- pci_write_config_word(pdev, cap + PCI_EXP_LNKCTL, ctl);
- }
+ pcie_capability_set_word(pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
}
#define R8168_CPCMD_QUIRK_MASK (\
@@ -5405,14 +5384,9 @@ static void rtl_hw_start_8101(struct net_device *dev)
tp->event_slow &= ~RxFIFOOver;
if (tp->mac_version == RTL_GIGA_MAC_VER_13 ||
- tp->mac_version == RTL_GIGA_MAC_VER_16) {
- int cap = pci_pcie_cap(pdev);
-
- if (cap) {
- pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL,
- PCI_EXP_DEVCTL_NOSNOOP_EN);
- }
- }
+ tp->mac_version == RTL_GIGA_MAC_VER_16)
+ pcie_capability_set_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_NOSNOOP_EN);
RTL_W8(Cfg9346, Cfg9346_Unlock);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index c136162..3be8833 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1066,7 +1066,7 @@ static int stmmac_open(struct net_device *dev)
} else
priv->tm->enable = 1;
#endif
- clk_enable(priv->stmmac_clk);
+ clk_prepare_enable(priv->stmmac_clk);
stmmac_check_ether_addr(priv);
@@ -1188,7 +1188,7 @@ open_error:
if (priv->phydev)
phy_disconnect(priv->phydev);
- clk_disable(priv->stmmac_clk);
+ clk_disable_unprepare(priv->stmmac_clk);
return ret;
}
@@ -1246,7 +1246,7 @@ static int stmmac_release(struct net_device *dev)
#ifdef CONFIG_STMMAC_DEBUG_FS
stmmac_exit_fs();
#endif
- clk_disable(priv->stmmac_clk);
+ clk_disable_unprepare(priv->stmmac_clk);
return 0;
}
@@ -2178,7 +2178,7 @@ int stmmac_suspend(struct net_device *ndev)
else {
stmmac_set_mac(priv->ioaddr, false);
/* Disable clock in case of PWM is off */
- clk_disable(priv->stmmac_clk);
+ clk_disable_unprepare(priv->stmmac_clk);
}
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
@@ -2203,7 +2203,7 @@ int stmmac_resume(struct net_device *ndev)
priv->hw->mac->pmt(priv->ioaddr, 0);
else
/* enable the clk prevously disabled */
- clk_enable(priv->stmmac_clk);
+ clk_prepare_enable(priv->stmmac_clk);
netif_device_attach(ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
index 2a0e1ab..4ccd4e2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
@@ -97,19 +97,19 @@ static struct clk *timer_clock;
static void stmmac_tmu_start(unsigned int new_freq)
{
clk_set_rate(timer_clock, new_freq);
- clk_enable(timer_clock);
+ clk_prepare_enable(timer_clock);
}
static void stmmac_tmu_stop(void)
{
- clk_disable(timer_clock);
+ clk_disable_unprepare(timer_clock);
}
int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm)
{
timer_clock = clk_get(NULL, TMU_CHANNEL);
- if (timer_clock == NULL)
+ if (IS_ERR(timer_clock))
return -1;
if (tmu2_register_user(stmmac_timer_handler, (void *)dev) < 0) {
@@ -126,7 +126,7 @@ int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm)
int stmmac_close_ext_timer(void)
{
- clk_disable(timer_clock);
+ clk_disable_unprepare(timer_clock);
tmu2_unregister_user();
clk_put(timer_clock);
return 0;
diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c
index c2a0fe3..3208dca 100644
--- a/drivers/net/ethernet/sun/niu.c
+++ b/drivers/net/ethernet/sun/niu.c
@@ -9762,9 +9762,8 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev,
union niu_parent_id parent_id;
struct net_device *dev;
struct niu *np;
- int err, pos;
+ int err;
u64 dma_mask;
- u16 val16;
niu_driver_version();
@@ -9787,8 +9786,7 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev,
goto err_out_disable_pdev;
}
- pos = pci_pcie_cap(pdev);
- if (pos <= 0) {
+ if (!pci_is_pcie(pdev)) {
dev_err(&pdev->dev, "Cannot find PCI Express capability, aborting\n");
goto err_out_free_res;
}
@@ -9813,14 +9811,11 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev,
goto err_out_free_dev;
}
- pci_read_config_word(pdev, pos + PCI_EXP_DEVCTL, &val16);
- val16 &= ~PCI_EXP_DEVCTL_NOSNOOP_EN;
- val16 |= (PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE |
- PCI_EXP_DEVCTL_RELAX_EN);
- pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16);
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_NOSNOOP_EN,
+ PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE |
+ PCI_EXP_DEVCTL_RELAX_EN);
dma_mask = DMA_BIT_MASK(44);
err = pci_set_dma_mask(pdev, dma_mask);
diff --git a/drivers/net/irda/sh_sir.c b/drivers/net/irda/sh_sir.c
index 256eddf..7951094 100644
--- a/drivers/net/irda/sh_sir.c
+++ b/drivers/net/irda/sh_sir.c
@@ -280,7 +280,7 @@ static int sh_sir_set_baudrate(struct sh_sir_self *self, u32 baudrate)
}
clk = clk_get(NULL, "irda_clk");
- if (!clk) {
+ if (IS_ERR(clk)) {
dev_err(dev, "can not get irda_clk\n");
return -EIO;
}
diff --git a/drivers/net/phy/bcm87xx.c b/drivers/net/phy/bcm87xx.c
index 2346b38..7997895 100644
--- a/drivers/net/phy/bcm87xx.c
+++ b/drivers/net/phy/bcm87xx.c
@@ -229,3 +229,5 @@ static void __exit bcm87xx_exit(void)
ARRAY_SIZE(bcm87xx_driver));
}
module_exit(bcm87xx_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index cf287e0..2165d5f 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -21,6 +21,12 @@
#include <linux/phy.h>
#include <linux/micrel_phy.h>
+/* Operation Mode Strap Override */
+#define MII_KSZPHY_OMSO 0x16
+#define KSZPHY_OMSO_B_CAST_OFF (1 << 9)
+#define KSZPHY_OMSO_RMII_OVERRIDE (1 << 1)
+#define KSZPHY_OMSO_MII_OVERRIDE (1 << 0)
+
/* general Interrupt control/status reg in vendor specific block. */
#define MII_KSZPHY_INTCS 0x1B
#define KSZPHY_INTCS_JABBER (1 << 15)
@@ -101,6 +107,13 @@ static int kszphy_config_init(struct phy_device *phydev)
return 0;
}
+static int ksz8021_config_init(struct phy_device *phydev)
+{
+ const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE;
+ phy_write(phydev, MII_KSZPHY_OMSO, val);
+ return 0;
+}
+
static int ks8051_config_init(struct phy_device *phydev)
{
int regval;
@@ -128,9 +141,22 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = ks8737_config_intr,
.driver = { .owner = THIS_MODULE,},
}, {
- .phy_id = PHY_ID_KS8041,
+ .phy_id = PHY_ID_KSZ8021,
+ .phy_id_mask = 0x00ffffff,
+ .name = "Micrel KSZ8021",
+ .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause),
+ .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .config_init = ksz8021_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = kszphy_ack_interrupt,
+ .config_intr = kszphy_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+}, {
+ .phy_id = PHY_ID_KSZ8041,
.phy_id_mask = 0x00fffff0,
- .name = "Micrel KS8041",
+ .name = "Micrel KSZ8041",
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -141,9 +167,9 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = kszphy_config_intr,
.driver = { .owner = THIS_MODULE,},
}, {
- .phy_id = PHY_ID_KS8051,
+ .phy_id = PHY_ID_KSZ8051,
.phy_id_mask = 0x00fffff0,
- .name = "Micrel KS8051",
+ .name = "Micrel KSZ8051",
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -154,8 +180,8 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = kszphy_config_intr,
.driver = { .owner = THIS_MODULE,},
}, {
- .phy_id = PHY_ID_KS8001,
- .name = "Micrel KS8001 or KS8721",
+ .phy_id = PHY_ID_KSZ8001,
+ .name = "Micrel KSZ8001 or KS8721",
.phy_id_mask = 0x00ffffff,
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -201,10 +227,11 @@ MODULE_LICENSE("GPL");
static struct mdio_device_id __maybe_unused micrel_tbl[] = {
{ PHY_ID_KSZ9021, 0x000ffffe },
- { PHY_ID_KS8001, 0x00ffffff },
+ { PHY_ID_KSZ8001, 0x00ffffff },
{ PHY_ID_KS8737, 0x00fffff0 },
- { PHY_ID_KS8041, 0x00fffff0 },
- { PHY_ID_KS8051, 0x00fffff0 },
+ { PHY_ID_KSZ8021, 0x00ffffff },
+ { PHY_ID_KSZ8041, 0x00fffff0 },
+ { PHY_ID_KSZ8051, 0x00fffff0 },
{ }
};
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index 6d61923..88e3991 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -56,6 +56,32 @@ static int smsc_phy_config_init(struct phy_device *phydev)
return smsc_phy_ack_interrupt (phydev);
}
+static int lan87xx_config_init(struct phy_device *phydev)
+{
+ /*
+ * Make sure the EDPWRDOWN bit is NOT set. Setting this bit on
+ * LAN8710/LAN8720 PHY causes the PHY to misbehave, likely due
+ * to a bug on the chip.
+ *
+ * When the system is powered on with the network cable being
+ * disconnected all the way until after ifconfig ethX up is
+ * issued for the LAN port with this PHY, connecting the cable
+ * afterwards does not cause LINK change detection, while the
+ * expected behavior is the Link UP being detected.
+ */
+ int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
+ if (rc < 0)
+ return rc;
+
+ rc &= ~MII_LAN83C185_EDPWRDOWN;
+
+ rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc);
+ if (rc < 0)
+ return rc;
+
+ return smsc_phy_ack_interrupt(phydev);
+}
+
static int lan911x_config_init(struct phy_device *phydev)
{
return smsc_phy_ack_interrupt(phydev);
@@ -162,7 +188,7 @@ static struct phy_driver smsc_phy_driver[] = {
/* basic functions */
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
- .config_init = smsc_phy_config_init,
+ .config_init = lan87xx_config_init,
/* IRQ related */
.ack_interrupt = smsc_phy_ack_interrupt,
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index cbf7047..20f31d0 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -570,7 +570,7 @@ static int pppoe_release(struct socket *sock)
po = pppox_sk(sk);
- if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
+ if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) {
dev_put(po->pppoe_dev);
po->pppoe_dev = NULL;
}
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 341b65d..f8cd61f 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -848,7 +848,7 @@ static struct netpoll_info *team_netpoll_info(struct team *team)
}
#endif
-static void __team_port_change_check(struct team_port *port, bool linkup);
+static void __team_port_change_port_added(struct team_port *port, bool linkup);
static int team_port_add(struct team *team, struct net_device *port_dev)
{
@@ -948,7 +948,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
team_port_enable(team, port);
list_add_tail_rcu(&port->list, &team->port_list);
__team_compute_features(team);
- __team_port_change_check(port, !!netif_carrier_ok(port_dev));
+ __team_port_change_port_added(port, !!netif_carrier_ok(port_dev));
__team_options_change_check(team);
netdev_info(dev, "Port device %s added\n", portname);
@@ -983,6 +983,8 @@ err_set_mtu:
return err;
}
+static void __team_port_change_port_removed(struct team_port *port);
+
static int team_port_del(struct team *team, struct net_device *port_dev)
{
struct net_device *dev = team->dev;
@@ -999,8 +1001,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
__team_option_inst_mark_removed_port(team, port);
__team_options_change_check(team);
__team_option_inst_del_port(team, port);
- port->removed = true;
- __team_port_change_check(port, false);
+ __team_port_change_port_removed(port);
team_port_disable(team, port);
list_del_rcu(&port->list);
netdev_rx_handler_unregister(port_dev);
@@ -1652,8 +1653,8 @@ static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
&team_nl_family, 0, TEAM_CMD_NOOP);
- if (IS_ERR(hdr)) {
- err = PTR_ERR(hdr);
+ if (!hdr) {
+ err = -EMSGSIZE;
goto err_msg_put;
}
@@ -1847,8 +1848,8 @@ start_again:
hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags | NLM_F_MULTI,
TEAM_CMD_OPTIONS_GET);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -EMSGSIZE;
if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
goto nla_put_failure;
@@ -2067,8 +2068,8 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
TEAM_CMD_PORT_LIST_GET);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -EMSGSIZE;
if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
goto nla_put_failure;
@@ -2251,13 +2252,11 @@ static void __team_options_change_check(struct team *team)
}
/* rtnl lock is held */
-static void __team_port_change_check(struct team_port *port, bool linkup)
+
+static void __team_port_change_send(struct team_port *port, bool linkup)
{
int err;
- if (!port->removed && port->state.linkup == linkup)
- return;
-
port->changed = true;
port->state.linkup = linkup;
team_refresh_port_linkup(port);
@@ -2282,6 +2281,23 @@ send_event:
}
+static void __team_port_change_check(struct team_port *port, bool linkup)
+{
+ if (port->state.linkup != linkup)
+ __team_port_change_send(port, linkup);
+}
+
+static void __team_port_change_port_added(struct team_port *port, bool linkup)
+{
+ __team_port_change_send(port, linkup);
+}
+
+static void __team_port_change_port_removed(struct team_port *port)
+{
+ port->removed = true;
+ __team_port_change_send(port, false);
+}
+
static void team_port_change_check(struct team_port *port, bool linkup)
{
struct team *team = port->team;
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index 4fd48df..32e31c5 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -962,6 +962,10 @@ static const struct usb_device_id products [] = {
USB_DEVICE (0x2001, 0x3c05),
.driver_info = (unsigned long) &ax88772_info,
}, {
+ // DLink DUB-E100 H/W Ver C1
+ USB_DEVICE (0x2001, 0x1a02),
+ .driver_info = (unsigned long) &ax88772_info,
+}, {
// Linksys USB1000
USB_DEVICE (0x1737, 0x0039),
.driver_info = (unsigned long) &ax88178_info,
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index b1ba68f1..3543c9e 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -366,16 +366,20 @@ static const struct usb_device_id products[] = {
},
/* 2. Combined interface devices matching on class+protocol */
+ { /* Huawei E367 and possibly others in "Windows mode" */
+ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 7),
+ .driver_info = (unsigned long)&qmi_wwan_info,
+ },
{ /* Huawei E392, E398 and possibly others in "Windows mode" */
USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 17),
.driver_info = (unsigned long)&qmi_wwan_shared,
},
- { /* Pantech UML290 */
- USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff),
+ { /* Pantech UML290, P4200 and more */
+ USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff),
.driver_info = (unsigned long)&qmi_wwan_shared,
},
{ /* Pantech UML290 - newer firmware */
- USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff),
+ USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff),
.driver_info = (unsigned long)&qmi_wwan_shared,
},
@@ -383,6 +387,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x19d2, 0x0055, 1)}, /* ZTE (Vodafone) K3520-Z */
{QMI_FIXED_INTF(0x19d2, 0x0063, 4)}, /* ZTE (Vodafone) K3565-Z */
{QMI_FIXED_INTF(0x19d2, 0x0104, 4)}, /* ZTE (Vodafone) K4505-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x0157, 5)}, /* ZTE MF683 */
{QMI_FIXED_INTF(0x19d2, 0x0167, 4)}, /* ZTE MF820D */
{QMI_FIXED_INTF(0x19d2, 0x0326, 4)}, /* ZTE MF821D */
{QMI_FIXED_INTF(0x19d2, 0x1008, 4)}, /* ZTE (Vodafone) K3570-Z */
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index f5ab6e6..376143e 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -1253,6 +1253,7 @@ static struct usb_driver smsc75xx_driver = {
.probe = usbnet_probe,
.suspend = usbnet_suspend,
.resume = usbnet_resume,
+ .reset_resume = usbnet_resume,
.disconnect = usbnet_disconnect,
.disable_hub_initiated_lpm = 1,
};
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 2588848..d066f25 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -2982,6 +2982,10 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah,
case EEP_RX_MASK:
return pBase->txrxMask & 0xf;
case EEP_PAPRD:
+ if (AR_SREV_9462(ah))
+ return false;
+ if (!ah->config.enable_paprd);
+ return false;
return !!(pBase->featureEnable & BIT(5));
case EEP_CHAIN_MASK_REDUCE:
return (pBase->miscConfiguration >> 0x3) & 0x1;
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 68b643c..c8ef301 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -1577,6 +1577,8 @@ int ath9k_init_debug(struct ath_hw *ah)
sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_disable_ani);
+ debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
+ &sc->sc_ah->config.enable_paprd);
debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
sc, &fops_regidx);
debugfs_create_file("regval", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 48af401..4faf0a3 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2497,10 +2497,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
pCap->rx_status_len = sizeof(struct ar9003_rxs);
pCap->tx_desc_len = sizeof(struct ar9003_txc);
pCap->txs_len = sizeof(struct ar9003_txs);
- if (!ah->config.paprd_disable &&
- ah->eep_ops->get_eeprom(ah, EEP_PAPRD) &&
- !AR_SREV_9462(ah))
- pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
} else {
pCap->tx_desc_len = sizeof(struct ath_desc);
if (AR_SREV_9280_20(ah))
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 6599a75..de6968f 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -236,7 +236,6 @@ enum ath9k_hw_caps {
ATH9K_HW_CAP_LDPC = BIT(6),
ATH9K_HW_CAP_FASTCLOCK = BIT(7),
ATH9K_HW_CAP_SGI_20 = BIT(8),
- ATH9K_HW_CAP_PAPRD = BIT(9),
ATH9K_HW_CAP_ANT_DIV_COMB = BIT(10),
ATH9K_HW_CAP_2GHZ = BIT(11),
ATH9K_HW_CAP_5GHZ = BIT(12),
@@ -287,12 +286,12 @@ struct ath9k_ops_config {
u8 pcie_clock_req;
u32 pcie_waen;
u8 analog_shiftreg;
- u8 paprd_disable;
u32 ofdm_trig_low;
u32 ofdm_trig_high;
u32 cck_trig_high;
u32 cck_trig_low;
u32 enable_ani;
+ u32 enable_paprd;
int serialize_regmode;
bool rx_intr_mitigation;
bool tx_intr_mitigation;
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 825a29c..7b88b9c 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -423,7 +423,7 @@ set_timer:
cal_interval = min(cal_interval, (u32)short_cal_interval);
mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
- if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) {
+ if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD) && ah->caldata) {
if (!ah->caldata->paprd_done)
ieee80211_queue_work(sc->hw, &sc->paprd_work);
else if (!ah->paprd_table_write_done)
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index a978984..ef11dc6 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -113,41 +113,32 @@ static void ath_pci_aspm_init(struct ath_common *common)
struct ath_hw *ah = sc->sc_ah;
struct pci_dev *pdev = to_pci_dev(sc->dev);
struct pci_dev *parent;
- int pos;
- u8 aspm;
+ u16 aspm;
if (!ah->is_pciexpress)
return;
- pos = pci_pcie_cap(pdev);
- if (!pos)
- return;
-
parent = pdev->bus->self;
if (!parent)
return;
if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) {
/* Bluetooth coexistance requires disabling ASPM. */
- pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &aspm);
- aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
- pci_write_config_byte(pdev, pos + PCI_EXP_LNKCTL, aspm);
+ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL,
+ PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
/*
* Both upstream and downstream PCIe components should
* have the same ASPM settings.
*/
- pos = pci_pcie_cap(parent);
- pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm);
- aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
- pci_write_config_byte(parent, pos + PCI_EXP_LNKCTL, aspm);
+ pcie_capability_clear_word(parent, PCI_EXP_LNKCTL,
+ PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
ath_info(common, "Disabling ASPM since BTCOEX is enabled\n");
return;
}
- pos = pci_pcie_cap(parent);
- pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm);
+ pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm);
if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) {
ah->aspm_enabled = true;
/* Initialize PCIe PM and SERDES registers. */
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig
index 3876c7e..7a28d21 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/b43/Kconfig
@@ -34,8 +34,8 @@ config B43_BCMA
config B43_BCMA_EXTRA
bool "Hardware support that overlaps with the brcmsmac driver"
depends on B43_BCMA
- default n if BRCMSMAC || BRCMSMAC_MODULE
- default y
+ default n if BRCMSMAC
+ default y
config B43_SSB
bool
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 49765d3..7c4ee72 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -638,6 +638,8 @@ static int brcmf_sdio_pd_probe(struct platform_device *pdev)
oobirq_entry = kzalloc(sizeof(struct brcmf_sdio_oobirq),
GFP_KERNEL);
+ if (!oobirq_entry)
+ return -ENOMEM;
oobirq_entry->irq = res->start;
oobirq_entry->flags = res->flags & IRQF_TRIGGER_MASK;
list_add_tail(&oobirq_entry->list, &oobirq_lh);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
index 2621dd3..6f70953 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
@@ -764,8 +764,11 @@ static void brcmf_c_arp_offload_set(struct brcmf_pub *drvr, int arp_mode)
{
char iovbuf[32];
int retcode;
+ __le32 arp_mode_le;
- brcmf_c_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
+ arp_mode_le = cpu_to_le32(arp_mode);
+ brcmf_c_mkiovar("arp_ol", (char *)&arp_mode_le, 4, iovbuf,
+ sizeof(iovbuf));
retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR,
iovbuf, sizeof(iovbuf));
retcode = retcode >= 0 ? 0 : retcode;
@@ -781,8 +784,11 @@ static void brcmf_c_arp_offload_enable(struct brcmf_pub *drvr, int arp_enable)
{
char iovbuf[32];
int retcode;
+ __le32 arp_enable_le;
- brcmf_c_mkiovar("arpoe", (char *)&arp_enable, 4,
+ arp_enable_le = cpu_to_le32(arp_enable);
+
+ brcmf_c_mkiovar("arpoe", (char *)&arp_enable_le, 4,
iovbuf, sizeof(iovbuf));
retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR,
iovbuf, sizeof(iovbuf));
@@ -800,10 +806,10 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
char iovbuf[BRCMF_EVENTING_MASK_LEN + 12]; /* Room for
"event_msgs" + '\0' + bitvec */
char buf[128], *ptr;
- u32 roaming = 1;
- uint bcn_timeout = 3;
- int scan_assoc_time = 40;
- int scan_unassoc_time = 40;
+ __le32 roaming_le = cpu_to_le32(1);
+ __le32 bcn_timeout_le = cpu_to_le32(3);
+ __le32 scan_assoc_time_le = cpu_to_le32(40);
+ __le32 scan_unassoc_time_le = cpu_to_le32(40);
int i;
struct brcmf_bus_dcmd *cmdlst;
struct list_head *cur, *q;
@@ -829,14 +835,14 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
/* Setup timeout if Beacons are lost and roam is off to report
link down */
- brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf,
+ brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout_le, 4, iovbuf,
sizeof(iovbuf));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
sizeof(iovbuf));
/* Enable/Disable build-in roaming to allowed ext supplicant to take
of romaing */
- brcmf_c_mkiovar("roam_off", (char *)&roaming, 4,
+ brcmf_c_mkiovar("roam_off", (char *)&roaming_le, 4,
iovbuf, sizeof(iovbuf));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
sizeof(iovbuf));
@@ -848,9 +854,9 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
sizeof(iovbuf));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_CHANNEL_TIME,
- (char *)&scan_assoc_time, sizeof(scan_assoc_time));
+ (char *)&scan_assoc_time_le, sizeof(scan_assoc_time_le));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_UNASSOC_TIME,
- (char *)&scan_unassoc_time, sizeof(scan_unassoc_time));
+ (char *)&scan_unassoc_time_le, sizeof(scan_unassoc_time_le));
/* Set and enable ARP offload feature */
brcmf_c_arp_offload_set(drvr, BRCMF_ARPOL_MODE);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index c36e923..50b5553 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -500,8 +500,10 @@ static void wl_iscan_prep(struct brcmf_scan_params_le *params_le,
params_le->active_time = cpu_to_le32(-1);
params_le->passive_time = cpu_to_le32(-1);
params_le->home_time = cpu_to_le32(-1);
- if (ssid && ssid->SSID_len)
- memcpy(&params_le->ssid_le, ssid, sizeof(struct brcmf_ssid));
+ if (ssid && ssid->SSID_len) {
+ params_le->ssid_le.SSID_len = cpu_to_le32(ssid->SSID_len);
+ memcpy(&params_le->ssid_le.SSID, ssid->SSID, ssid->SSID_len);
+ }
}
static s32
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
index 7ed7d75..64a48f0 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
@@ -77,7 +77,7 @@
NL80211_RRF_NO_IBSS)
static const struct ieee80211_regdomain brcms_regdom_x2 = {
- .n_reg_rules = 7,
+ .n_reg_rules = 6,
.alpha2 = "X2",
.reg_rules = {
BRCM_2GHZ_2412_2462,
diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h
index 5f50177..7246826 100644
--- a/drivers/net/wireless/iwlegacy/common.h
+++ b/drivers/net/wireless/iwlegacy/common.h
@@ -1832,10 +1832,8 @@ int il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd);
static inline u16
il_pcie_link_ctl(struct il_priv *il)
{
- int pos;
u16 pci_lnk_ctl;
- pos = pci_pcie_cap(il->pci_dev);
- pci_read_config_word(il->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
+ pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &pci_lnk_ctl);
return pci_lnk_ctl;
}
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 1e86ea2..063ecaf 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -675,13 +675,10 @@ static void iwl_set_pwr_vmain(struct iwl_trans *trans)
static u16 iwl_pciexp_link_ctrl(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int pos;
u16 pci_lnk_ctl;
- struct pci_dev *pci_dev = trans_pcie->pci_dev;
-
- pos = pci_pcie_cap(pci_dev);
- pci_read_config_word(pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
+ pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL,
+ &pci_lnk_ctl);
return pci_lnk_ctl;
}
@@ -1442,6 +1439,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
return err;
err_free_irq:
+ trans_pcie->irq_requested = false;
free_irq(trans_pcie->irq, trans);
error:
iwl_free_isr_ict(trans);
diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c
index 80f75d3..5983631 100644
--- a/drivers/net/wireless/rtlwifi/pci.c
+++ b/drivers/net/wireless/rtlwifi/pci.c
@@ -372,13 +372,11 @@ static void rtl_pci_parse_configuration(struct pci_dev *pdev,
struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
u8 tmp;
- int pos;
- u8 linkctrl_reg;
+ u16 linkctrl_reg;
/*Link Control Register */
- pos = pci_pcie_cap(pdev);
- pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &linkctrl_reg);
- pcipriv->ndis_adapter.linkctrl_reg = linkctrl_reg;
+ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &linkctrl_reg);
+ pcipriv->ndis_adapter.linkctrl_reg = (u8)linkctrl_reg;
RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Link Control Register =%x\n",
pcipriv->ndis_adapter.linkctrl_reg);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
index 44febfd..8a7b864 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
@@ -315,7 +315,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
u16 box_reg = 0, box_extreg = 0;
u8 u1b_tmp;
bool isfw_read = false;
- bool bwrite_sucess = false;
+ bool bwrite_success = false;
u8 wait_h2c_limmit = 100;
u8 wait_writeh2c_limmit = 100;
u8 boxcontent[4], boxextcontent[2];
@@ -354,7 +354,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
}
}
- while (!bwrite_sucess) {
+ while (!bwrite_success) {
wait_writeh2c_limmit--;
if (wait_writeh2c_limmit == 0) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -491,7 +491,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
break;
}
- bwrite_sucess = true;
+ bwrite_success = true;
rtlhal->last_hmeboxnum = boxnum + 1;
if (rtlhal->last_hmeboxnum == 4)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
index 04c3aef..2925094 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
@@ -117,6 +117,7 @@
#define CHIP_VER_B BIT(4)
#define CHIP_92C_BITMASK BIT(0)
+#define CHIP_UNKNOWN BIT(7)
#define CHIP_92C_1T2R 0x03
#define CHIP_92C 0x01
#define CHIP_88C 0x00
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
index bd0da7e..dd4bb09 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
@@ -994,8 +994,16 @@ static enum version_8192c _rtl92ce_read_chip_version(struct ieee80211_hw *hw)
version = (value32 & TYPE_ID) ? VERSION_A_CHIP_92C :
VERSION_A_CHIP_88C;
} else {
- version = (value32 & TYPE_ID) ? VERSION_B_CHIP_92C :
- VERSION_B_CHIP_88C;
+ version = (enum version_8192c) (CHIP_VER_B |
+ ((value32 & TYPE_ID) ? CHIP_92C_BITMASK : 0) |
+ ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : 0));
+ if ((!IS_CHIP_VENDOR_UMC(version)) && (value32 &
+ CHIP_VER_RTL_MASK)) {
+ version = (enum version_8192c)(version |
+ ((((value32 & CHIP_VER_RTL_MASK) == BIT(12))
+ ? CHIP_VENDOR_UMC_B_CUT : CHIP_UNKNOWN) |
+ CHIP_VENDOR_UMC));
+ }
}
switch (version) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
index 3aa927f..7d8f964 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
@@ -162,10 +162,12 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
/* request fw */
if (IS_VENDOR_UMC_A_CUT(rtlhal->version) &&
- !IS_92C_SERIAL(rtlhal->version))
+ !IS_92C_SERIAL(rtlhal->version)) {
rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU.bin";
- else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version))
+ } else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version)) {
rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU_B.bin";
+ pr_info("****** This B_CUT device may not work with kernels 3.6 and earlier\n");
+ }
rtlpriv->max_fw_size = 0x4000;
pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
index 895ae6c..eb22dcc 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
@@ -365,7 +365,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw,
u8 u1b_tmp;
bool isfw_read = false;
u8 buf_index = 0;
- bool bwrite_sucess = false;
+ bool bwrite_success = false;
u8 wait_h2c_limmit = 100;
u8 wait_writeh2c_limmit = 100;
u8 boxcontent[4], boxextcontent[2];
@@ -408,7 +408,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw,
break;
}
}
- while (!bwrite_sucess) {
+ while (!bwrite_success) {
wait_writeh2c_limmit--;
if (wait_writeh2c_limmit == 0) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -515,7 +515,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw,
"switch case not processed\n");
break;
}
- bwrite_sucess = true;
+ bwrite_success = true;
rtlhal->last_hmeboxnum = boxnum + 1;
if (rtlhal->last_hmeboxnum == 4)
rtlhal->last_hmeboxnum = 0;
diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c
index b8ef8dd..8aa73fa 100644
--- a/drivers/oprofile/cpu_buffer.c
+++ b/drivers/oprofile/cpu_buffer.c
@@ -451,14 +451,9 @@ static void wq_sync_buffer(struct work_struct *work)
{
struct oprofile_cpu_buffer *b =
container_of(work, struct oprofile_cpu_buffer, work.work);
- if (b->cpu != smp_processor_id()) {
- printk(KERN_DEBUG "WQ on CPU%d, prefer CPU%d\n",
- smp_processor_id(), b->cpu);
-
- if (!cpu_online(b->cpu)) {
- cancel_delayed_work(&b->work);
- return;
- }
+ if (b->cpu != smp_processor_id() && !cpu_online(b->cpu)) {
+ cancel_delayed_work(&b->work);
+ return;
}
sync_buffer(b->cpu);
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
index ffddc4f..fb6a1fe 100644
--- a/drivers/parisc/dino.c
+++ b/drivers/parisc/dino.c
@@ -477,14 +477,12 @@ dino_card_setup(struct pci_bus *bus, void __iomem *base_addr)
if (ccio_allocate_resource(dino_dev->hba.dev, res, _8MB,
F_EXTEND(0xf0000000UL) | _8MB,
F_EXTEND(0xffffffffUL) &~ _8MB, _8MB) < 0) {
- struct list_head *ln, *tmp_ln;
+ struct pci_dev *dev, *tmp;
printk(KERN_ERR "Dino: cannot attach bus %s\n",
dev_name(bus->bridge));
/* kill the bus, we can't do anything with it */
- list_for_each_safe(ln, tmp_ln, &bus->devices) {
- struct pci_dev *dev = pci_dev_b(ln);
-
+ list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
list_del(&dev->bus_list);
}
@@ -549,7 +547,6 @@ dino_card_fixup(struct pci_dev *dev)
static void __init
dino_fixup_bus(struct pci_bus *bus)
{
- struct list_head *ln;
struct pci_dev *dev;
struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge));
@@ -596,8 +593,7 @@ dino_fixup_bus(struct pci_bus *bus)
}
- list_for_each(ln, &bus->devices) {
- dev = pci_dev_b(ln);
+ list_for_each_entry(dev, &bus->devices, bus_list) {
if (is_card_dino(&dino_dev->hba.dev->id))
dino_card_fixup(dev);
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c
index 4f9cf24..fdd63a6 100644
--- a/drivers/parisc/lba_pci.c
+++ b/drivers/parisc/lba_pci.c
@@ -629,7 +629,7 @@ truncate_pat_collision(struct resource *root, struct resource *new)
static void
lba_fixup_bus(struct pci_bus *bus)
{
- struct list_head *ln;
+ struct pci_dev *dev;
#ifdef FBB_SUPPORT
u16 status;
#endif
@@ -710,9 +710,8 @@ lba_fixup_bus(struct pci_bus *bus)
}
- list_for_each(ln, &bus->devices) {
+ list_for_each_entry(dev, &bus->devices, bus_list) {
int i;
- struct pci_dev *dev = pci_dev_b(ln);
DBG("lba_fixup_bus() %s\n", pci_name(dev));
@@ -770,7 +769,7 @@ lba_fixup_bus(struct pci_bus *bus)
}
/* Lastly enable FBB/PERR/SERR on all devices too */
- list_for_each(ln, &bus->devices) {
+ list_for_each_entry(dev, &bus->devices, bus_list) {
(void) pci_read_config_word(dev, PCI_COMMAND, &status);
status |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | fbb_enable;
(void) pci_write_config_word(dev, PCI_COMMAND, status);
diff --git a/drivers/pci/.gitignore b/drivers/pci/.gitignore
deleted file mode 100644
index f297ca8..0000000
--- a/drivers/pci/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-classlist.h
-devlist.h
-gen-devlist
-
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 848bfb8..6d51aa6 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -3,7 +3,6 @@
#
config ARCH_SUPPORTS_MSI
bool
- default n
config PCI_MSI
bool "Message Signaled Interrupts (MSI and MSI-X)"
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index ba91a7e..3af0478 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -469,3 +469,205 @@ void pci_cfg_access_unlock(struct pci_dev *dev)
raw_spin_unlock_irqrestore(&pci_lock, flags);
}
EXPORT_SYMBOL_GPL(pci_cfg_access_unlock);
+
+static inline int pcie_cap_version(const struct pci_dev *dev)
+{
+ return dev->pcie_flags_reg & PCI_EXP_FLAGS_VERS;
+}
+
+static inline bool pcie_cap_has_devctl(const struct pci_dev *dev)
+{
+ return true;
+}
+
+static inline bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return pcie_cap_version(dev) > 1 ||
+ type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_ENDPOINT ||
+ type == PCI_EXP_TYPE_LEG_END;
+}
+
+static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return pcie_cap_version(dev) > 1 ||
+ type == PCI_EXP_TYPE_ROOT_PORT ||
+ (type == PCI_EXP_TYPE_DOWNSTREAM &&
+ dev->pcie_flags_reg & PCI_EXP_FLAGS_SLOT);
+}
+
+static inline bool pcie_cap_has_rtctl(const struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return pcie_cap_version(dev) > 1 ||
+ type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_RC_EC;
+}
+
+static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos)
+{
+ if (!pci_is_pcie(dev))
+ return false;
+
+ switch (pos) {
+ case PCI_EXP_FLAGS_TYPE:
+ return true;
+ case PCI_EXP_DEVCAP:
+ case PCI_EXP_DEVCTL:
+ case PCI_EXP_DEVSTA:
+ return pcie_cap_has_devctl(dev);
+ case PCI_EXP_LNKCAP:
+ case PCI_EXP_LNKCTL:
+ case PCI_EXP_LNKSTA:
+ return pcie_cap_has_lnkctl(dev);
+ case PCI_EXP_SLTCAP:
+ case PCI_EXP_SLTCTL:
+ case PCI_EXP_SLTSTA:
+ return pcie_cap_has_sltctl(dev);
+ case PCI_EXP_RTCTL:
+ case PCI_EXP_RTCAP:
+ case PCI_EXP_RTSTA:
+ return pcie_cap_has_rtctl(dev);
+ case PCI_EXP_DEVCAP2:
+ case PCI_EXP_DEVCTL2:
+ case PCI_EXP_LNKCAP2:
+ case PCI_EXP_LNKCTL2:
+ case PCI_EXP_LNKSTA2:
+ return pcie_cap_version(dev) > 1;
+ default:
+ return false;
+ }
+}
+
+/*
+ * Note that these accessor functions are only for the "PCI Express
+ * Capability" (see PCIe spec r3.0, sec 7.8). They do not apply to the
+ * other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.)
+ */
+int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val)
+{
+ int ret;
+
+ *val = 0;
+ if (pos & 1)
+ return -EINVAL;
+
+ if (pcie_capability_reg_implemented(dev, pos)) {
+ ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val);
+ /*
+ * Reset *val to 0 if pci_read_config_word() fails, it may
+ * have been written as 0xFFFF if hardware error happens
+ * during pci_read_config_word().
+ */
+ if (ret)
+ *val = 0;
+ return ret;
+ }
+
+ /*
+ * For Functions that do not implement the Slot Capabilities,
+ * Slot Status, and Slot Control registers, these spaces must
+ * be hardwired to 0b, with the exception of the Presence Detect
+ * State bit in the Slot Status register of Downstream Ports,
+ * which must be hardwired to 1b. (PCIe Base Spec 3.0, sec 7.8)
+ */
+ if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA &&
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ *val = PCI_EXP_SLTSTA_PDS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pcie_capability_read_word);
+
+int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val)
+{
+ int ret;
+
+ *val = 0;
+ if (pos & 3)
+ return -EINVAL;
+
+ if (pcie_capability_reg_implemented(dev, pos)) {
+ ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val);
+ /*
+ * Reset *val to 0 if pci_read_config_dword() fails, it may
+ * have been written as 0xFFFFFFFF if hardware error happens
+ * during pci_read_config_dword().
+ */
+ if (ret)
+ *val = 0;
+ return ret;
+ }
+
+ if (pci_is_pcie(dev) && pos == PCI_EXP_SLTCTL &&
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ *val = PCI_EXP_SLTSTA_PDS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pcie_capability_read_dword);
+
+int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val)
+{
+ if (pos & 1)
+ return -EINVAL;
+
+ if (!pcie_capability_reg_implemented(dev, pos))
+ return 0;
+
+ return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val);
+}
+EXPORT_SYMBOL(pcie_capability_write_word);
+
+int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val)
+{
+ if (pos & 3)
+ return -EINVAL;
+
+ if (!pcie_capability_reg_implemented(dev, pos))
+ return 0;
+
+ return pci_write_config_dword(dev, pci_pcie_cap(dev) + pos, val);
+}
+EXPORT_SYMBOL(pcie_capability_write_dword);
+
+int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
+ u16 clear, u16 set)
+{
+ int ret;
+ u16 val;
+
+ ret = pcie_capability_read_word(dev, pos, &val);
+ if (!ret) {
+ val &= ~clear;
+ val |= set;
+ ret = pcie_capability_write_word(dev, pos, val);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(pcie_capability_clear_and_set_word);
+
+int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
+ u32 clear, u32 set)
+{
+ int ret;
+ u32 val;
+
+ ret = pcie_capability_read_dword(dev, pos, &val);
+ if (!ret) {
+ val &= ~clear;
+ val |= set;
+ ret = pcie_capability_write_dword(dev, pos, val);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(pcie_capability_clear_and_set_dword);
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 4b0970b..6241fd0 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -87,11 +87,15 @@ EXPORT_SYMBOL_GPL(pci_bus_resource_n);
void pci_bus_remove_resources(struct pci_bus *bus)
{
int i;
+ struct pci_bus_resource *bus_res, *tmp;
for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
bus->resource[i] = NULL;
- pci_free_resource_list(&bus->resources);
+ list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
+ list_del(&bus_res->list);
+ kfree(bus_res);
+ }
}
/**
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index 66f29bc..b0e46de 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -17,28 +17,6 @@ menuconfig HOTPLUG_PCI
if HOTPLUG_PCI
-config HOTPLUG_PCI_FAKE
- tristate "Fake PCI Hotplug driver"
- help
- Say Y here if you want to use the fake PCI hotplug driver. It can
- be used to simulate PCI hotplug events if even if your system is
- not PCI hotplug capable.
-
- This driver will "emulate" removing PCI devices from the system.
- If the "power" file is written to with "0" then the specified PCI
- device will be completely removed from the kernel.
-
- WARNING, this does NOT turn off the power to the PCI device.
- This is a "logical" removal, not a physical or electrical
- removal.
-
- Use this module at your own risk. You have been warned!
-
- To compile this driver as a module, choose M here: the
- module will be called fakephp.
-
- When in doubt, say N.
-
config HOTPLUG_PCI_COMPAQ
tristate "Compaq PCI Hotplug driver"
depends on X86 && PCI_BIOS
@@ -143,7 +121,7 @@ config HOTPLUG_PCI_SHPC
config HOTPLUG_PCI_RPA
tristate "RPA PCI Hotplug driver"
- depends on PPC_PSERIES && EEH && !HOTPLUG_PCI_FAKE
+ depends on PPC_PSERIES && EEH
help
Say Y here if you have a RPA system that supports PCI Hotplug.
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index 6cd9f3c..c459cd4 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -23,9 +23,6 @@ obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o
-# Link this last so it doesn't claim devices that have a real hotplug driver
-obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o
-
pci_hotplug-objs := pci_hotplug_core.o pcihp_slot.o
ifdef CONFIG_HOTPLUG_PCI_CPCI
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index ad6fd66..3d6d4fd 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -115,6 +115,35 @@ static const struct acpi_dock_ops acpiphp_dock_ops = {
.handler = handle_hotplug_event_func,
};
+/* Check whether the PCI device is managed by native PCIe hotplug driver */
+static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
+{
+ u32 reg32;
+ acpi_handle tmp;
+ struct acpi_pci_root *root;
+
+ /* Check whether the PCIe port supports native PCIe hotplug */
+ if (pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32))
+ return false;
+ if (!(reg32 & PCI_EXP_SLTCAP_HPC))
+ return false;
+
+ /*
+ * Check whether native PCIe hotplug has been enabled for
+ * this PCIe hierarchy.
+ */
+ tmp = acpi_find_root_bridge_handle(pdev);
+ if (!tmp)
+ return false;
+ root = acpi_pci_find_root(tmp);
+ if (!root)
+ return false;
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
+ return false;
+
+ return true;
+}
+
/* callback routine to register each ACPI PCI slot object */
static acpi_status
register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
@@ -142,16 +171,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
function = adr & 0xffff;
pdev = pbus->self;
- if (pdev && pci_is_pcie(pdev)) {
- tmp = acpi_find_root_bridge_handle(pdev);
- if (tmp) {
- struct acpi_pci_root *root = acpi_pci_find_root(tmp);
-
- if (root && (root->osc_control_set &
- OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
- return AE_OK;
- }
- }
+ if (pdev && device_is_managed_by_native_pciehp(pdev))
+ return AE_OK;
newfunc = kzalloc(sizeof(struct acpiphp_func), GFP_KERNEL);
if (!newfunc)
@@ -382,10 +403,10 @@ static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge)
/* allocate and initialize host bridge data structure */
-static void add_host_bridge(acpi_handle *handle)
+static void add_host_bridge(struct acpi_pci_root *root)
{
struct acpiphp_bridge *bridge;
- struct acpi_pci_root *root = acpi_pci_find_root(handle);
+ acpi_handle handle = root->device->handle;
bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
if (bridge == NULL)
@@ -468,11 +489,12 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
/* find hot-pluggable slots, and then find P2P bridge */
-static int add_bridge(acpi_handle handle)
+static int add_bridge(struct acpi_pci_root *root)
{
acpi_status status;
unsigned long long tmp;
acpi_handle dummy_handle;
+ acpi_handle handle = root->device->handle;
/* if the bridge doesn't have _STA, we assume it is always there */
status = acpi_get_handle(handle, "_STA", &dummy_handle);
@@ -490,7 +512,7 @@ static int add_bridge(acpi_handle handle)
/* check if this bridge has ejectable slots */
if (detect_ejectable_slots(handle) > 0) {
dbg("found PCI host-bus bridge with hot-pluggable slots\n");
- add_host_bridge(handle);
+ add_host_bridge(root);
}
/* search P2P bridges under this host bridge */
@@ -588,9 +610,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
return AE_OK;
}
-static void remove_bridge(acpi_handle handle)
+static void remove_bridge(struct acpi_pci_root *root)
{
struct acpiphp_bridge *bridge;
+ acpi_handle handle = root->device->handle;
/* cleanup p2p bridges under this host bridge
in a depth-first manner */
@@ -869,17 +892,6 @@ static int __ref enable_device(struct acpiphp_slot *slot)
return retval;
}
-static void disable_bridges(struct pci_bus *bus)
-{
- struct pci_dev *dev;
- list_for_each_entry(dev, &bus->devices, bus_list) {
- if (dev->subordinate) {
- disable_bridges(dev->subordinate);
- pci_disable_device(dev);
- }
- }
-}
-
/* return first device in slot, acquiring a reference on it */
static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
{
@@ -931,12 +943,7 @@ static int disable_device(struct acpiphp_slot *slot)
* here.
*/
while ((pdev = dev_in_slot(slot))) {
- pci_stop_bus_device(pdev);
- if (pdev->subordinate) {
- disable_bridges(pdev->subordinate);
- pci_disable_device(pdev);
- }
- __pci_remove_bus_device(pdev);
+ pci_stop_and_remove_bus_device(pdev);
pci_dev_put(pdev);
}
@@ -1477,34 +1484,6 @@ int __init acpiphp_get_num_slots(void)
}
-#if 0
-/**
- * acpiphp_for_each_slot - call function for each slot
- * @fn: callback function
- * @data: context to be passed to callback function
- */
-static int acpiphp_for_each_slot(acpiphp_callback fn, void *data)
-{
- struct list_head *node;
- struct acpiphp_bridge *bridge;
- struct acpiphp_slot *slot;
- int retval = 0;
-
- list_for_each (node, &bridge_list) {
- bridge = (struct acpiphp_bridge *)node;
- for (slot = bridge->slots; slot; slot = slot->next) {
- retval = fn(slot, data);
- if (!retval)
- goto err_exit;
- }
- }
-
- err_exit:
- return retval;
-}
-#endif
-
-
/**
* acpiphp_enable_slot - power on slot
* @slot: ACPI PHP slot
diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c
index 81af764..a6a71c4 100644
--- a/drivers/pci/hotplug/cpcihp_generic.c
+++ b/drivers/pci/hotplug/cpcihp_generic.c
@@ -154,12 +154,8 @@ static int __init cpcihp_generic_init(void)
if(!r)
return -EBUSY;
- bus = pci_find_bus(0, bridge_busnr);
- if (!bus) {
- err("Invalid bus number %d", bridge_busnr);
- return -EINVAL;
- }
- dev = pci_get_slot(bus, PCI_DEVFN(bridge_slot, 0));
+ dev = pci_get_domain_bus_and_slot(0, bridge_busnr,
+ PCI_DEVFN(bridge_slot, 0));
if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
err("Invalid bridge device %s", bridge);
pci_dev_put(dev);
diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c
index e43908d..36112fe 100644
--- a/drivers/pci/hotplug/cpqphp_ctrl.c
+++ b/drivers/pci/hotplug/cpqphp_ctrl.c
@@ -2890,27 +2890,8 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
func->mem_head = mem_node;
} else
return -ENOMEM;
- } else if ((temp_register & 0x0BL) == 0x04) {
- /* Map memory */
- base = temp_register & 0xFFFFFFF0;
- base = ~base + 1;
-
- dbg("CND: length = 0x%x\n", base);
- mem_node = get_resource(&(resources->mem_head), base);
-
- /* allocate the resource to the board */
- if (mem_node) {
- base = mem_node->base;
-
- mem_node->next = func->mem_head;
- func->mem_head = mem_node;
- } else
- return -ENOMEM;
- } else if ((temp_register & 0x0BL) == 0x06) {
- /* Those bits are reserved, we can't handle this */
- return 1;
} else {
- /* Requesting space below 1M */
+ /* Reserved bits or requesting space below 1M */
return NOT_ENOUGH_RESOURCES;
}
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
deleted file mode 100644
index a019c9a..0000000
--- a/drivers/pci/hotplug/fakephp.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/* Works like the fakephp driver used to, except a little better.
- *
- * - It's possible to remove devices with subordinate busses.
- * - New PCI devices that appear via any method, not just a fakephp triggered
- * rescan, will be noticed.
- * - Devices that are removed via any method, not just a fakephp triggered
- * removal, will also be noticed.
- *
- * Uses nothing from the pci-hotplug subsystem.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/list.h>
-#include <linux/kobject.h>
-#include <linux/sysfs.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include "../pci.h"
-
-struct legacy_slot {
- struct kobject kobj;
- struct pci_dev *dev;
- struct list_head list;
-};
-
-static LIST_HEAD(legacy_list);
-
-static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
- strcpy(buf, "1\n");
- return 2;
-}
-
-static void remove_callback(void *data)
-{
- pci_stop_and_remove_bus_device((struct pci_dev *)data);
-}
-
-static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr,
- const char *buf, size_t len)
-{
- struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
- unsigned long val;
-
- if (strict_strtoul(buf, 0, &val) < 0)
- return -EINVAL;
-
- if (val)
- pci_rescan_bus(slot->dev->bus);
- else
- sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback,
- slot->dev, THIS_MODULE);
- return len;
-}
-
-static struct attribute *legacy_attrs[] = {
- &(struct attribute){ .name = "power", .mode = 0644 },
- NULL,
-};
-
-static void legacy_release(struct kobject *kobj)
-{
- struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
-
- pci_dev_put(slot->dev);
- kfree(slot);
-}
-
-static struct kobj_type legacy_ktype = {
- .sysfs_ops = &(const struct sysfs_ops){
- .store = legacy_store, .show = legacy_show
- },
- .release = &legacy_release,
- .default_attrs = legacy_attrs,
-};
-
-static int legacy_add_slot(struct pci_dev *pdev)
-{
- struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL);
-
- if (!slot)
- return -ENOMEM;
-
- if (kobject_init_and_add(&slot->kobj, &legacy_ktype,
- &pci_slots_kset->kobj, "%s",
- dev_name(&pdev->dev))) {
- dev_warn(&pdev->dev, "Failed to created legacy fake slot\n");
- return -EINVAL;
- }
- slot->dev = pci_dev_get(pdev);
-
- list_add(&slot->list, &legacy_list);
-
- return 0;
-}
-
-static int legacy_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct pci_dev *pdev = to_pci_dev(data);
-
- if (action == BUS_NOTIFY_ADD_DEVICE) {
- legacy_add_slot(pdev);
- } else if (action == BUS_NOTIFY_DEL_DEVICE) {
- struct legacy_slot *slot;
-
- list_for_each_entry(slot, &legacy_list, list)
- if (slot->dev == pdev)
- goto found;
-
- dev_warn(&pdev->dev, "Missing legacy fake slot?");
- return -ENODEV;
-found:
- kobject_del(&slot->kobj);
- list_del(&slot->list);
- kobject_put(&slot->kobj);
- }
-
- return 0;
-}
-
-static struct notifier_block legacy_notifier = {
- .notifier_call = legacy_notify
-};
-
-static int __init init_legacy(void)
-{
- struct pci_dev *pdev = NULL;
-
- /* Add existing devices */
- for_each_pci_dev(pdev)
- legacy_add_slot(pdev);
-
- /* Be alerted of any new ones */
- bus_register_notifier(&pci_bus_type, &legacy_notifier);
- return 0;
-}
-module_init(init_legacy);
-
-static void __exit remove_legacy(void)
-{
- struct legacy_slot *slot, *tmp;
-
- bus_unregister_notifier(&pci_bus_type, &legacy_notifier);
-
- list_for_each_entry_safe(slot, tmp, &legacy_list, list) {
- list_del(&slot->list);
- kobject_del(&slot->kobj);
- kobject_put(&slot->kobj);
- }
-}
-module_exit(remove_legacy);
-
-
-MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");
-MODULE_DESCRIPTION("Legacy version of the fakephp interface");
-MODULE_LICENSE("GPL");
diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c
index 376d70d..24d709b 100644
--- a/drivers/pci/hotplug/pciehp_acpi.c
+++ b/drivers/pci/hotplug/pciehp_acpi.c
@@ -81,16 +81,12 @@ static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots);
/* Dummy driver for dumplicate name detection */
static int __init dummy_probe(struct pcie_device *dev)
{
- int pos;
u32 slot_cap;
acpi_handle handle;
struct dummy_slot *slot, *tmp;
struct pci_dev *pdev = dev->port;
- pos = pci_pcie_cap(pdev);
- if (!pos)
- return -ENODEV;
- pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &slot_cap);
+ pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
if (!slot)
return -ENOMEM;
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 365c6b9..916bf4f 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -300,24 +300,24 @@ static int pciehp_suspend (struct pcie_device *dev)
static int pciehp_resume (struct pcie_device *dev)
{
+ struct controller *ctrl;
+ struct slot *slot;
+ u8 status;
+
dev_info(&dev->device, "%s ENTRY\n", __func__);
- if (pciehp_force) {
- struct controller *ctrl = get_service_data(dev);
- struct slot *slot;
- u8 status;
+ ctrl = get_service_data(dev);
- /* reinitialize the chipset's event detection logic */
- pcie_enable_notification(ctrl);
+ /* reinitialize the chipset's event detection logic */
+ pcie_enable_notification(ctrl);
- slot = ctrl->slot;
+ slot = ctrl->slot;
- /* Check if slot is occupied */
- pciehp_get_adapter_status(slot, &status);
- if (status)
- pciehp_enable_slot(slot);
- else
- pciehp_disable_slot(slot);
- }
+ /* Check if slot is occupied */
+ pciehp_get_adapter_status(slot, &status);
+ if (status)
+ pciehp_enable_slot(slot);
+ else
+ pciehp_disable_slot(slot);
return 0;
}
#endif /* PM */
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 302451e..13b2eaf 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -44,25 +44,25 @@
static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value)
{
struct pci_dev *dev = ctrl->pcie->port;
- return pci_read_config_word(dev, pci_pcie_cap(dev) + reg, value);
+ return pcie_capability_read_word(dev, reg, value);
}
static inline int pciehp_readl(struct controller *ctrl, int reg, u32 *value)
{
struct pci_dev *dev = ctrl->pcie->port;
- return pci_read_config_dword(dev, pci_pcie_cap(dev) + reg, value);
+ return pcie_capability_read_dword(dev, reg, value);
}
static inline int pciehp_writew(struct controller *ctrl, int reg, u16 value)
{
struct pci_dev *dev = ctrl->pcie->port;
- return pci_write_config_word(dev, pci_pcie_cap(dev) + reg, value);
+ return pcie_capability_write_word(dev, reg, value);
}
static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value)
{
struct pci_dev *dev = ctrl->pcie->port;
- return pci_write_config_dword(dev, pci_pcie_cap(dev) + reg, value);
+ return pcie_capability_write_dword(dev, reg, value);
}
/* Power Control Command */
@@ -855,10 +855,6 @@ struct controller *pcie_init(struct pcie_device *dev)
goto abort;
}
ctrl->pcie = dev;
- if (!pci_pcie_cap(pdev)) {
- ctrl_err(ctrl, "Cannot find PCI Express capability\n");
- goto abort_ctrl;
- }
if (pciehp_readl(ctrl, PCI_EXP_SLTCAP, &slot_cap)) {
ctrl_err(ctrl, "Cannot read SLOTCAP register\n");
goto abort_ctrl;
diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c
index 8c05a18..fec2d5b 100644
--- a/drivers/pci/hotplug/pcihp_slot.c
+++ b/drivers/pci/hotplug/pcihp_slot.c
@@ -96,17 +96,11 @@ static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
{
int pos;
- u16 reg16;
u32 reg32;
if (!hpp)
return;
- /* Find PCI Express capability */
- pos = pci_pcie_cap(dev);
- if (!pos)
- return;
-
if (hpp->revision > 1) {
dev_warn(&dev->dev, "PCIe settings rev %d not supported\n",
hpp->revision);
@@ -114,17 +108,13 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
}
/* Initialize Device Control Register */
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
- reg16 = (reg16 & hpp->pci_exp_devctl_and) | hpp->pci_exp_devctl_or;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
+ pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
/* Initialize Link Control Register */
- if (dev->subordinate) {
- pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 = (reg16 & hpp->pci_exp_lnkctl_and)
- | hpp->pci_exp_lnkctl_or;
- pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, reg16);
- }
+ if (dev->subordinate)
+ pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
+ ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
/* Find Advanced Error Reporting Enhanced Capability */
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 74bbaf8..aeccc91 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -433,8 +433,8 @@ static int sriov_init(struct pci_dev *dev, int pos)
struct resource *res;
struct pci_dev *pdev;
- if (dev->pcie_type != PCI_EXP_TYPE_RC_END &&
- dev->pcie_type != PCI_EXP_TYPE_ENDPOINT)
+ if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END &&
+ pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT)
return -ENODEV;
pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
@@ -503,7 +503,7 @@ found:
iov->self = dev;
pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
- if (dev->pcie_type == PCI_EXP_TYPE_RC_END)
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
if (pdev)
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index d6fd6b6..9e1d295 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -139,7 +139,6 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
return retval;
return count;
}
-static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
/**
* store_remove_id - remove a PCI device ID from this driver
@@ -185,38 +184,16 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
return retval;
return count;
}
-static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
-static int
-pci_create_newid_files(struct pci_driver *drv)
-{
- int error = 0;
+static struct driver_attribute pci_drv_attrs[] = {
+ __ATTR(new_id, S_IWUSR, NULL, store_new_id),
+ __ATTR(remove_id, S_IWUSR, NULL, store_remove_id),
+ __ATTR_NULL,
+};
- if (drv->probe != NULL) {
- error = driver_create_file(&drv->driver, &driver_attr_new_id);
- if (error == 0) {
- error = driver_create_file(&drv->driver,
- &driver_attr_remove_id);
- if (error)
- driver_remove_file(&drv->driver,
- &driver_attr_new_id);
- }
- }
- return error;
-}
-
-static void pci_remove_newid_files(struct pci_driver *drv)
-{
- driver_remove_file(&drv->driver, &driver_attr_remove_id);
- driver_remove_file(&drv->driver, &driver_attr_new_id);
-}
-#else /* !CONFIG_HOTPLUG */
-static inline int pci_create_newid_files(struct pci_driver *drv)
-{
- return 0;
-}
-static inline void pci_remove_newid_files(struct pci_driver *drv) {}
-#endif
+#else
+#define pci_drv_attrs NULL
+#endif /* CONFIG_HOTPLUG */
/**
* pci_match_id - See if a pci device matches a given pci_id table
@@ -1162,8 +1139,6 @@ const struct dev_pm_ops pci_dev_pm_ops = {
int __pci_register_driver(struct pci_driver *drv, struct module *owner,
const char *mod_name)
{
- int error;
-
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
@@ -1174,19 +1149,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */
- error = driver_register(&drv->driver);
- if (error)
- goto out;
-
- error = pci_create_newid_files(drv);
- if (error)
- goto out_newid;
-out:
- return error;
-
-out_newid:
- driver_unregister(&drv->driver);
- goto out;
+ return driver_register(&drv->driver);
}
/**
@@ -1202,7 +1165,6 @@ out_newid:
void
pci_unregister_driver(struct pci_driver *drv)
{
- pci_remove_newid_files(drv);
driver_unregister(&drv->driver);
pci_free_dynids(drv);
}
@@ -1302,6 +1264,7 @@ struct bus_type pci_bus_type = {
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
.bus_attrs = pci_bus_attrs,
+ .drv_attrs = pci_drv_attrs,
.pm = PCI_PM_OPS_PTR,
};
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ab4bf5a..5485883 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -254,52 +254,17 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap)
}
/**
- * pci_pcie_cap2 - query for devices' PCI_CAP_ID_EXP v2 capability structure
- * @dev: PCI device to check
- *
- * Like pci_pcie_cap() but also checks that the PCIe capability version is
- * >= 2. Note that v1 capability structures could be sparse in that not
- * all register fields were required. v2 requires the entire structure to
- * be present size wise, while still allowing for non-implemented registers
- * to exist but they must be hardwired to 0.
- *
- * Due to the differences in the versions of capability structures, one
- * must be careful not to try and access non-existant registers that may
- * exist in early versions - v1 - of Express devices.
- *
- * Returns the offset of the PCIe capability structure as long as the
- * capability version is >= 2; otherwise 0 is returned.
- */
-static int pci_pcie_cap2(struct pci_dev *dev)
-{
- u16 flags;
- int pos;
-
- pos = pci_pcie_cap(dev);
- if (pos) {
- pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags);
- if ((flags & PCI_EXP_FLAGS_VERS) < 2)
- pos = 0;
- }
-
- return pos;
-}
-
-/**
- * pci_find_ext_capability - Find an extended capability
+ * pci_find_next_ext_capability - Find an extended capability
* @dev: PCI device to query
+ * @start: address at which to start looking (0 to start at beginning of list)
* @cap: capability code
*
- * Returns the address of the requested extended capability structure
+ * Returns the address of the next matching extended capability structure
* within the device's PCI configuration space or 0 if the device does
- * not support it. Possible values for @cap:
- *
- * %PCI_EXT_CAP_ID_ERR Advanced Error Reporting
- * %PCI_EXT_CAP_ID_VC Virtual Channel
- * %PCI_EXT_CAP_ID_DSN Device Serial Number
- * %PCI_EXT_CAP_ID_PWR Power Budgeting
+ * not support it. Some capabilities can occur several times, e.g., the
+ * vendor-specific capability, and this provides a way to find them all.
*/
-int pci_find_ext_capability(struct pci_dev *dev, int cap)
+int pci_find_next_ext_capability(struct pci_dev *dev, int start, int cap)
{
u32 header;
int ttl;
@@ -311,6 +276,9 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
if (dev->cfg_size <= PCI_CFG_SPACE_SIZE)
return 0;
+ if (start)
+ pos = start;
+
if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
return 0;
@@ -322,7 +290,7 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
return 0;
while (ttl-- > 0) {
- if (PCI_EXT_CAP_ID(header) == cap)
+ if (PCI_EXT_CAP_ID(header) == cap && pos != start)
return pos;
pos = PCI_EXT_CAP_NEXT(header);
@@ -335,6 +303,26 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
return 0;
}
+EXPORT_SYMBOL_GPL(pci_find_next_ext_capability);
+
+/**
+ * pci_find_ext_capability - Find an extended capability
+ * @dev: PCI device to query
+ * @cap: capability code
+ *
+ * Returns the address of the requested extended capability structure
+ * within the device's PCI configuration space or 0 if the device does
+ * not support it. Possible values for @cap:
+ *
+ * %PCI_EXT_CAP_ID_ERR Advanced Error Reporting
+ * %PCI_EXT_CAP_ID_VC Virtual Channel
+ * %PCI_EXT_CAP_ID_DSN Device Serial Number
+ * %PCI_EXT_CAP_ID_PWR Power Budgeting
+ */
+int pci_find_ext_capability(struct pci_dev *dev, int cap)
+{
+ return pci_find_next_ext_capability(dev, 0, cap);
+}
EXPORT_SYMBOL_GPL(pci_find_ext_capability);
static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap)
@@ -854,21 +842,6 @@ EXPORT_SYMBOL(pci_choose_state);
#define PCI_EXP_SAVE_REGS 7
-#define pcie_cap_has_devctl(type, flags) 1
-#define pcie_cap_has_lnkctl(type, flags) \
- ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
- (type == PCI_EXP_TYPE_ROOT_PORT || \
- type == PCI_EXP_TYPE_ENDPOINT || \
- type == PCI_EXP_TYPE_LEG_END))
-#define pcie_cap_has_sltctl(type, flags) \
- ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
- ((type == PCI_EXP_TYPE_ROOT_PORT) || \
- (type == PCI_EXP_TYPE_DOWNSTREAM && \
- (flags & PCI_EXP_FLAGS_SLOT))))
-#define pcie_cap_has_rtctl(type, flags) \
- ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
- (type == PCI_EXP_TYPE_ROOT_PORT || \
- type == PCI_EXP_TYPE_RC_EC))
static struct pci_cap_saved_state *pci_find_saved_cap(
struct pci_dev *pci_dev, char cap)
@@ -885,13 +858,11 @@ static struct pci_cap_saved_state *pci_find_saved_cap(
static int pci_save_pcie_state(struct pci_dev *dev)
{
- int pos, i = 0;
+ int i = 0;
struct pci_cap_saved_state *save_state;
u16 *cap;
- u16 flags;
- pos = pci_pcie_cap(dev);
- if (!pos)
+ if (!pci_is_pcie(dev))
return 0;
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
@@ -899,60 +870,37 @@ static int pci_save_pcie_state(struct pci_dev *dev)
dev_err(&dev->dev, "buffer not found in %s\n", __func__);
return -ENOMEM;
}
- cap = (u16 *)&save_state->cap.data[0];
-
- pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags);
- if (pcie_cap_has_devctl(dev->pcie_type, flags))
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &cap[i++]);
- if (pcie_cap_has_lnkctl(dev->pcie_type, flags))
- pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]);
- if (pcie_cap_has_sltctl(dev->pcie_type, flags))
- pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]);
- if (pcie_cap_has_rtctl(dev->pcie_type, flags))
- pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]);
-
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return 0;
+ cap = (u16 *)&save_state->cap.data[0];
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_SLTCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_RTCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_SLTCTL2, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_LNKCTL2, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_SLTCTL2, &cap[i++]);
return 0;
}
static void pci_restore_pcie_state(struct pci_dev *dev)
{
- int i = 0, pos;
+ int i = 0;
struct pci_cap_saved_state *save_state;
u16 *cap;
- u16 flags;
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!save_state || pos <= 0)
- return;
- cap = (u16 *)&save_state->cap.data[0];
-
- pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags);
-
- if (pcie_cap_has_devctl(dev->pcie_type, flags))
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, cap[i++]);
- if (pcie_cap_has_lnkctl(dev->pcie_type, flags))
- pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]);
- if (pcie_cap_has_sltctl(dev->pcie_type, flags))
- pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]);
- if (pcie_cap_has_rtctl(dev->pcie_type, flags))
- pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]);
-
- pos = pci_pcie_cap2(dev);
- if (!pos)
+ if (!save_state)
return;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_LNKCTL2, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_SLTCTL2, cap[i++]);
+ cap = (u16 *)&save_state->cap.data[0];
+ pcie_capability_write_word(dev, PCI_EXP_DEVCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_LNKCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_SLTCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_RTCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_DEVCTL2, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]);
}
@@ -1543,7 +1491,7 @@ void pci_pme_wakeup_bus(struct pci_bus *bus)
/**
* pci_wakeup - Wake up a PCI device
- * @dev: Device to handle.
+ * @pci_dev: Device to handle.
* @ign: ignored parameter
*/
static int pci_wakeup(struct pci_dev *pci_dev, void *ign)
@@ -2067,35 +2015,24 @@ void pci_free_cap_save_buffers(struct pci_dev *dev)
*/
void pci_enable_ari(struct pci_dev *dev)
{
- int pos;
u32 cap;
- u16 ctrl;
struct pci_dev *bridge;
if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn)
return;
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
- if (!pos)
+ if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI))
return;
bridge = dev->bus->self;
if (!bridge)
return;
- /* ARI is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(bridge);
- if (!pos)
- return;
-
- pci_read_config_dword(bridge, pos + PCI_EXP_DEVCAP2, &cap);
+ pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &cap);
if (!(cap & PCI_EXP_DEVCAP2_ARI))
return;
- pci_read_config_word(bridge, pos + PCI_EXP_DEVCTL2, &ctrl);
- ctrl |= PCI_EXP_DEVCTL2_ARI;
- pci_write_config_word(bridge, pos + PCI_EXP_DEVCTL2, ctrl);
-
+ pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_ARI);
bridge->ari_enabled = 1;
}
@@ -2110,20 +2047,14 @@ void pci_enable_ari(struct pci_dev *dev)
*/
void pci_enable_ido(struct pci_dev *dev, unsigned long type)
{
- int pos;
- u16 ctrl;
+ u16 ctrl = 0;
- /* ID-based Ordering is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
if (type & PCI_EXP_IDO_REQUEST)
ctrl |= PCI_EXP_IDO_REQ_EN;
if (type & PCI_EXP_IDO_COMPLETION)
ctrl |= PCI_EXP_IDO_CMP_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+ if (ctrl)
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, ctrl);
}
EXPORT_SYMBOL(pci_enable_ido);
@@ -2134,20 +2065,14 @@ EXPORT_SYMBOL(pci_enable_ido);
*/
void pci_disable_ido(struct pci_dev *dev, unsigned long type)
{
- int pos;
- u16 ctrl;
+ u16 ctrl = 0;
- /* ID-based Ordering is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
if (type & PCI_EXP_IDO_REQUEST)
- ctrl &= ~PCI_EXP_IDO_REQ_EN;
+ ctrl |= PCI_EXP_IDO_REQ_EN;
if (type & PCI_EXP_IDO_COMPLETION)
- ctrl &= ~PCI_EXP_IDO_CMP_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+ ctrl |= PCI_EXP_IDO_CMP_EN;
+ if (ctrl)
+ pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, ctrl);
}
EXPORT_SYMBOL(pci_disable_ido);
@@ -2172,17 +2097,11 @@ EXPORT_SYMBOL(pci_disable_ido);
*/
int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type)
{
- int pos;
u32 cap;
u16 ctrl;
int ret;
- /* OBFF is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return -ENOTSUPP;
-
- pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP2, &cap);
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap);
if (!(cap & PCI_EXP_OBFF_MASK))
return -ENOTSUPP; /* no OBFF support at all */
@@ -2193,7 +2112,7 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type)
return ret;
}
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &ctrl);
if (cap & PCI_EXP_OBFF_WAKE)
ctrl |= PCI_EXP_OBFF_WAKE_EN;
else {
@@ -2211,7 +2130,7 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type)
return -ENOTSUPP;
}
}
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+ pcie_capability_write_word(dev, PCI_EXP_DEVCTL2, ctrl);
return 0;
}
@@ -2225,17 +2144,7 @@ EXPORT_SYMBOL(pci_enable_obff);
*/
void pci_disable_obff(struct pci_dev *dev)
{
- int pos;
- u16 ctrl;
-
- /* OBFF is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
- ctrl &= ~PCI_EXP_OBFF_WAKE_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+ pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_OBFF_WAKE_EN);
}
EXPORT_SYMBOL(pci_disable_obff);
@@ -2248,15 +2157,9 @@ EXPORT_SYMBOL(pci_disable_obff);
*/
static bool pci_ltr_supported(struct pci_dev *dev)
{
- int pos;
u32 cap;
- /* LTR is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return false;
-
- pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP2, &cap);
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap);
return cap & PCI_EXP_DEVCAP2_LTR;
}
@@ -2273,22 +2176,15 @@ static bool pci_ltr_supported(struct pci_dev *dev)
*/
int pci_enable_ltr(struct pci_dev *dev)
{
- int pos;
- u16 ctrl;
int ret;
- if (!pci_ltr_supported(dev))
- return -ENOTSUPP;
-
- /* LTR is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return -ENOTSUPP;
-
/* Only primary function can enable/disable LTR */
if (PCI_FUNC(dev->devfn) != 0)
return -EINVAL;
+ if (!pci_ltr_supported(dev))
+ return -ENOTSUPP;
+
/* Enable upstream ports first */
if (dev->bus->self) {
ret = pci_enable_ltr(dev->bus->self);
@@ -2296,11 +2192,7 @@ int pci_enable_ltr(struct pci_dev *dev)
return ret;
}
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
- ctrl |= PCI_EXP_LTR_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
-
- return 0;
+ return pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_LTR_EN);
}
EXPORT_SYMBOL(pci_enable_ltr);
@@ -2310,24 +2202,14 @@ EXPORT_SYMBOL(pci_enable_ltr);
*/
void pci_disable_ltr(struct pci_dev *dev)
{
- int pos;
- u16 ctrl;
-
- if (!pci_ltr_supported(dev))
- return;
-
- /* LTR is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return;
-
/* Only primary function can enable/disable LTR */
if (PCI_FUNC(dev->devfn) != 0)
return;
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
- ctrl &= ~PCI_EXP_LTR_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+ if (!pci_ltr_supported(dev))
+ return;
+
+ pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_LTR_EN);
}
EXPORT_SYMBOL(pci_disable_ltr);
@@ -2410,9 +2292,6 @@ void pci_enable_acs(struct pci_dev *dev)
if (!pci_acs_enable)
return;
- if (!pci_is_pcie(dev))
- return;
-
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
if (!pos)
return;
@@ -2460,8 +2339,8 @@ bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags)
acs_flags &= (PCI_ACS_RR | PCI_ACS_CR |
PCI_ACS_EC | PCI_ACS_DT);
- if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM ||
- pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM ||
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
pdev->multifunction) {
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
if (!pos)
@@ -3177,15 +3056,10 @@ EXPORT_SYMBOL(pci_set_dma_seg_boundary);
static int pcie_flr(struct pci_dev *dev, int probe)
{
int i;
- int pos;
u32 cap;
- u16 status, control;
-
- pos = pci_pcie_cap(dev);
- if (!pos)
- return -ENOTTY;
+ u16 status;
- pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP, &cap);
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
if (!(cap & PCI_EXP_DEVCAP_FLR))
return -ENOTTY;
@@ -3197,7 +3071,7 @@ static int pcie_flr(struct pci_dev *dev, int probe)
if (i)
msleep((1 << (i - 1)) * 100);
- pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status);
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
if (!(status & PCI_EXP_DEVSTA_TRPND))
goto clear;
}
@@ -3206,9 +3080,7 @@ static int pcie_flr(struct pci_dev *dev, int probe)
"proceeding with reset anyway\n");
clear:
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &control);
- control |= PCI_EXP_DEVCTL_BCR_FLR;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, control);
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
msleep(100);
@@ -3576,18 +3448,11 @@ EXPORT_SYMBOL(pcix_set_mmrbc);
*/
int pcie_get_readrq(struct pci_dev *dev)
{
- int ret, cap;
u16 ctl;
- cap = pci_pcie_cap(dev);
- if (!cap)
- return -EINVAL;
-
- ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
- if (!ret)
- ret = 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
- return ret;
+ return 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);
}
EXPORT_SYMBOL(pcie_get_readrq);
@@ -3601,19 +3466,11 @@ EXPORT_SYMBOL(pcie_get_readrq);
*/
int pcie_set_readrq(struct pci_dev *dev, int rq)
{
- int cap, err = -EINVAL;
- u16 ctl, v;
+ u16 v;
if (rq < 128 || rq > 4096 || !is_power_of_2(rq))
- goto out;
-
- cap = pci_pcie_cap(dev);
- if (!cap)
- goto out;
+ return -EINVAL;
- err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
- if (err)
- goto out;
/*
* If using the "performance" PCIe config, we clamp the
* read rq size to the max packet size to prevent the
@@ -3631,14 +3488,8 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
v = (ffs(rq) - 8) << 12;
- if ((ctl & PCI_EXP_DEVCTL_READRQ) != v) {
- ctl &= ~PCI_EXP_DEVCTL_READRQ;
- ctl |= v;
- err = pci_write_config_word(dev, cap + PCI_EXP_DEVCTL, ctl);
- }
-
-out:
- return err;
+ return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_READRQ, v);
}
EXPORT_SYMBOL(pcie_set_readrq);
@@ -3651,18 +3502,11 @@ EXPORT_SYMBOL(pcie_set_readrq);
*/
int pcie_get_mps(struct pci_dev *dev)
{
- int ret, cap;
u16 ctl;
- cap = pci_pcie_cap(dev);
- if (!cap)
- return -EINVAL;
-
- ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
- if (!ret)
- ret = 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
- return ret;
+ return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
}
/**
@@ -3675,32 +3519,18 @@ int pcie_get_mps(struct pci_dev *dev)
*/
int pcie_set_mps(struct pci_dev *dev, int mps)
{
- int cap, err = -EINVAL;
- u16 ctl, v;
+ u16 v;
if (mps < 128 || mps > 4096 || !is_power_of_2(mps))
- goto out;
+ return -EINVAL;
v = ffs(mps) - 8;
if (v > dev->pcie_mpss)
- goto out;
+ return -EINVAL;
v <<= 5;
- cap = pci_pcie_cap(dev);
- if (!cap)
- goto out;
-
- err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
- if (err)
- goto out;
-
- if ((ctl & PCI_EXP_DEVCTL_PAYLOAD) != v) {
- ctl &= ~PCI_EXP_DEVCTL_PAYLOAD;
- ctl |= v;
- err = pci_write_config_word(dev, cap + PCI_EXP_DEVCTL, ctl);
- }
-out:
- return err;
+ return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_PAYLOAD, v);
}
/**
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c
index 5222986..4e24cb8 100644
--- a/drivers/pci/pcie/aer/aer_inject.c
+++ b/drivers/pci/pcie/aer/aer_inject.c
@@ -288,7 +288,7 @@ static struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
while (1) {
if (!pci_is_pcie(dev))
break;
- if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
return dev;
if (!dev->bus->self)
break;
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index 58ad791..030cf12 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -48,7 +48,7 @@ static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
static void aer_error_resume(struct pci_dev *dev);
static pci_ers_result_t aer_root_reset(struct pci_dev *dev);
-static struct pci_error_handlers aer_error_handlers = {
+static const struct pci_error_handlers aer_error_handlers = {
.error_detected = aer_error_detected,
.resume = aer_error_resume,
};
@@ -81,10 +81,11 @@ bool pci_aer_available(void)
static int set_device_error_reporting(struct pci_dev *dev, void *data)
{
bool enable = *((bool *)data);
+ int type = pci_pcie_type(dev);
- if ((dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) ||
- (dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) ||
- (dev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)) {
+ if ((type == PCI_EXP_TYPE_ROOT_PORT) ||
+ (type == PCI_EXP_TYPE_UPSTREAM) ||
+ (type == PCI_EXP_TYPE_DOWNSTREAM)) {
if (enable)
pci_enable_pcie_error_reporting(dev);
else
@@ -121,19 +122,17 @@ static void set_downstream_devices_error_reporting(struct pci_dev *dev,
static void aer_enable_rootport(struct aer_rpc *rpc)
{
struct pci_dev *pdev = rpc->rpd->port;
- int pos, aer_pos;
+ int aer_pos;
u16 reg16;
u32 reg32;
- pos = pci_pcie_cap(pdev);
/* Clear PCIe Capability's Device Status */
- pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
- pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
+ pcie_capability_read_word(pdev, PCI_EXP_DEVSTA, &reg16);
+ pcie_capability_write_word(pdev, PCI_EXP_DEVSTA, reg16);
/* Disable system error generation in response to error messages */
- pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
- reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
- pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
+ pcie_capability_clear_word(pdev, PCI_EXP_RTCTL,
+ SYSTEM_ERROR_INTR_ON_MESG_MASK);
aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
/* Clear error status */
@@ -395,9 +394,8 @@ static void aer_error_resume(struct pci_dev *dev)
u16 reg16;
/* Clean up Root device status */
- pos = pci_pcie_cap(dev);
- pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &reg16);
- pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16);
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &reg16);
+ pcie_capability_write_word(dev, PCI_EXP_DEVSTA, reg16);
/* Clean AER Root Error Status */
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c
index 124f20f..5194a7d 100644
--- a/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ b/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -60,7 +60,7 @@ static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data)
p = (struct acpi_hest_aer_common *)(hest_hdr + 1);
if (p->flags & ACPI_HEST_GLOBAL) {
if ((pci_is_pcie(info->pci_dev) &&
- info->pci_dev->pcie_type == pcie_type) || bridge)
+ pci_pcie_type(info->pci_dev) == pcie_type) || bridge)
ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
} else
if (hest_match_pci(p, info->pci_dev))
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index 0ca0535..06bad96 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -32,53 +32,28 @@ static bool nosourceid;
module_param(forceload, bool, 0);
module_param(nosourceid, bool, 0);
+#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
+
int pci_enable_pcie_error_reporting(struct pci_dev *dev)
{
- u16 reg16 = 0;
- int pos;
-
if (pcie_aer_get_firmware_first(dev))
return -EIO;
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
- if (!pos)
+ if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
return -EIO;
- pos = pci_pcie_cap(dev);
- if (!pos)
- return -EIO;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
- reg16 |= (PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE);
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
-
- return 0;
+ return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS);
}
EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
int pci_disable_pcie_error_reporting(struct pci_dev *dev)
{
- u16 reg16 = 0;
- int pos;
-
if (pcie_aer_get_firmware_first(dev))
return -EIO;
- pos = pci_pcie_cap(dev);
- if (!pos)
- return -EIO;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
- reg16 &= ~(PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE);
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
-
- return 0;
+ return pcie_capability_clear_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_AER_FLAGS);
}
EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
@@ -151,18 +126,12 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
*/
if (atomic_read(&dev->enable_cnt) == 0)
return false;
- pos = pci_pcie_cap(dev);
- if (!pos)
- return false;
/* Check if AER is enabled */
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
- if (!(reg16 & (
- PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE)))
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &reg16);
+ if (!(reg16 & PCI_EXP_AER_FLAGS))
return false;
+
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (!pos)
return false;
@@ -240,7 +209,7 @@ static bool find_source_device(struct pci_dev *parent,
static int report_error_detected(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote;
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
struct aer_broadcast_data *result_data;
result_data = (struct aer_broadcast_data *) data;
@@ -274,7 +243,7 @@ static int report_error_detected(struct pci_dev *dev, void *data)
static int report_mmio_enabled(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote;
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
struct aer_broadcast_data *result_data;
result_data = (struct aer_broadcast_data *) data;
@@ -292,7 +261,7 @@ static int report_mmio_enabled(struct pci_dev *dev, void *data)
static int report_slot_reset(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote;
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
struct aer_broadcast_data *result_data;
result_data = (struct aer_broadcast_data *) data;
@@ -309,7 +278,7 @@ static int report_slot_reset(struct pci_dev *dev, void *data)
static int report_resume(struct pci_dev *dev, void *data)
{
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
dev->error_state = pci_channel_io_normal;
@@ -465,7 +434,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev)
if (driver && driver->reset_link) {
status = driver->reset_link(udev);
- } else if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) {
+ } else if (pci_pcie_type(udev) == PCI_EXP_TYPE_DOWNSTREAM) {
status = default_downstream_reset_link(udev);
} else {
dev_printk(KERN_DEBUG, &dev->dev,
@@ -540,14 +509,12 @@ static void do_recovery(struct pci_dev *dev, int severity)
"resume",
report_resume);
- dev_printk(KERN_DEBUG, &dev->dev,
- "AER driver successfully recovered\n");
+ dev_info(&dev->dev, "AER: Device recovery successful\n");
return;
failed:
/* TODO: Should kernel panic here? */
- dev_printk(KERN_DEBUG, &dev->dev,
- "AER driver didn't recover\n");
+ dev_info(&dev->dev, "AER: Device recovery failed\n");
}
/**
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index b500840..213753b 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -125,21 +125,16 @@ static int policy_to_clkpm_state(struct pcie_link_state *link)
static void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable)
{
- int pos;
- u16 reg16;
struct pci_dev *child;
struct pci_bus *linkbus = link->pdev->subordinate;
list_for_each_entry(child, &linkbus->devices, bus_list) {
- pos = pci_pcie_cap(child);
- if (!pos)
- return;
- pci_read_config_word(child, pos + PCI_EXP_LNKCTL, &reg16);
if (enable)
- reg16 |= PCI_EXP_LNKCTL_CLKREQ_EN;
+ pcie_capability_set_word(child, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
else
- reg16 &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
- pci_write_config_word(child, pos + PCI_EXP_LNKCTL, reg16);
+ pcie_capability_clear_word(child, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
}
link->clkpm_enabled = !!enable;
}
@@ -157,7 +152,7 @@ static void pcie_set_clkpm(struct pcie_link_state *link, int enable)
static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
{
- int pos, capable = 1, enabled = 1;
+ int capable = 1, enabled = 1;
u32 reg32;
u16 reg16;
struct pci_dev *child;
@@ -165,16 +160,13 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
/* All functions should have the same cap and state, take the worst */
list_for_each_entry(child, &linkbus->devices, bus_list) {
- pos = pci_pcie_cap(child);
- if (!pos)
- return;
- pci_read_config_dword(child, pos + PCI_EXP_LNKCAP, &reg32);
+ pcie_capability_read_dword(child, PCI_EXP_LNKCAP, &reg32);
if (!(reg32 & PCI_EXP_LNKCAP_CLKPM)) {
capable = 0;
enabled = 0;
break;
}
- pci_read_config_word(child, pos + PCI_EXP_LNKCTL, &reg16);
+ pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN))
enabled = 0;
}
@@ -190,7 +182,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
*/
static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
{
- int ppos, cpos, same_clock = 1;
+ int same_clock = 1;
u16 reg16, parent_reg, child_reg[8];
unsigned long start_jiffies;
struct pci_dev *child, *parent = link->pdev;
@@ -203,46 +195,43 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
BUG_ON(!pci_is_pcie(child));
/* Check downstream component if bit Slot Clock Configuration is 1 */
- cpos = pci_pcie_cap(child);
- pci_read_config_word(child, cpos + PCI_EXP_LNKSTA, &reg16);
+ pcie_capability_read_word(child, PCI_EXP_LNKSTA, &reg16);
if (!(reg16 & PCI_EXP_LNKSTA_SLC))
same_clock = 0;
/* Check upstream component if bit Slot Clock Configuration is 1 */
- ppos = pci_pcie_cap(parent);
- pci_read_config_word(parent, ppos + PCI_EXP_LNKSTA, &reg16);
+ pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &reg16);
if (!(reg16 & PCI_EXP_LNKSTA_SLC))
same_clock = 0;
/* Configure downstream component, all functions */
list_for_each_entry(child, &linkbus->devices, bus_list) {
- cpos = pci_pcie_cap(child);
- pci_read_config_word(child, cpos + PCI_EXP_LNKCTL, &reg16);
+ pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
child_reg[PCI_FUNC(child->devfn)] = reg16;
if (same_clock)
reg16 |= PCI_EXP_LNKCTL_CCC;
else
reg16 &= ~PCI_EXP_LNKCTL_CCC;
- pci_write_config_word(child, cpos + PCI_EXP_LNKCTL, reg16);
+ pcie_capability_write_word(child, PCI_EXP_LNKCTL, reg16);
}
/* Configure upstream component */
- pci_read_config_word(parent, ppos + PCI_EXP_LNKCTL, &reg16);
+ pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
parent_reg = reg16;
if (same_clock)
reg16 |= PCI_EXP_LNKCTL_CCC;
else
reg16 &= ~PCI_EXP_LNKCTL_CCC;
- pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, reg16);
+ pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
/* Retrain link */
reg16 |= PCI_EXP_LNKCTL_RL;
- pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, reg16);
+ pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
/* Wait for link training end. Break out after waiting for timeout */
start_jiffies = jiffies;
for (;;) {
- pci_read_config_word(parent, ppos + PCI_EXP_LNKSTA, &reg16);
+ pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &reg16);
if (!(reg16 & PCI_EXP_LNKSTA_LT))
break;
if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT))
@@ -255,12 +244,10 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
/* Training failed. Restore common clock configurations */
dev_printk(KERN_ERR, &parent->dev,
"ASPM: Could not configure common clock\n");
- list_for_each_entry(child, &linkbus->devices, bus_list) {
- cpos = pci_pcie_cap(child);
- pci_write_config_word(child, cpos + PCI_EXP_LNKCTL,
- child_reg[PCI_FUNC(child->devfn)]);
- }
- pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, parent_reg);
+ list_for_each_entry(child, &linkbus->devices, bus_list)
+ pcie_capability_write_word(child, PCI_EXP_LNKCTL,
+ child_reg[PCI_FUNC(child->devfn)]);
+ pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_reg);
}
/* Convert L0s latency encoding to ns */
@@ -305,16 +292,14 @@ struct aspm_register_info {
static void pcie_get_aspm_reg(struct pci_dev *pdev,
struct aspm_register_info *info)
{
- int pos;
u16 reg16;
u32 reg32;
- pos = pci_pcie_cap(pdev);
- pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, &reg32);
+ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &reg32);
info->support = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10;
info->latency_encoding_l0s = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12;
info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
- pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
+ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &reg16);
info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC;
}
@@ -412,7 +397,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
* do ASPM for now.
*/
list_for_each_entry(child, &linkbus->devices, bus_list) {
- if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
+ if (pci_pcie_type(child) == PCI_EXP_TYPE_PCI_BRIDGE) {
link->aspm_disable = ASPM_STATE_ALL;
break;
}
@@ -420,17 +405,15 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
/* Get and check endpoint acceptable latencies */
list_for_each_entry(child, &linkbus->devices, bus_list) {
- int pos;
u32 reg32, encoding;
struct aspm_latency *acceptable =
&link->acceptable[PCI_FUNC(child->devfn)];
- if (child->pcie_type != PCI_EXP_TYPE_ENDPOINT &&
- child->pcie_type != PCI_EXP_TYPE_LEG_END)
+ if (pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT &&
+ pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END)
continue;
- pos = pci_pcie_cap(child);
- pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, &reg32);
+ pcie_capability_read_dword(child, PCI_EXP_DEVCAP, &reg32);
/* Calculate endpoint L0s acceptable latency */
encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6;
acceptable->l0s = calc_l0s_acceptable(encoding);
@@ -444,13 +427,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
{
- u16 reg16;
- int pos = pci_pcie_cap(pdev);
-
- pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 &= ~0x3;
- reg16 |= val;
- pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, 0x3, val);
}
static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
@@ -505,7 +482,6 @@ static void free_link_state(struct pcie_link_state *link)
static int pcie_aspm_sanity_check(struct pci_dev *pdev)
{
struct pci_dev *child;
- int pos;
u32 reg32;
/*
@@ -513,8 +489,7 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
* very strange. Disable ASPM for the whole slot
*/
list_for_each_entry(child, &pdev->subordinate->devices, bus_list) {
- pos = pci_pcie_cap(child);
- if (!pos)
+ if (!pci_is_pcie(child))
return -EINVAL;
/*
@@ -530,7 +505,7 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
* Disable ASPM for pre-1.1 PCIe device, we follow MS to use
* RBER bit to determine if a function is 1.1 version device
*/
- pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, &reg32);
+ pcie_capability_read_dword(child, PCI_EXP_DEVCAP, &reg32);
if (!(reg32 & PCI_EXP_DEVCAP_RBER) && !aspm_force) {
dev_printk(KERN_INFO, &child->dev, "disabling ASPM"
" on pre-1.1 PCIe device. You can enable it"
@@ -552,7 +527,7 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
INIT_LIST_HEAD(&link->children);
INIT_LIST_HEAD(&link->link);
link->pdev = pdev;
- if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) {
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM) {
struct pcie_link_state *parent;
parent = pdev->bus->parent->self->link_state;
if (!parent) {
@@ -585,12 +560,12 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
if (!pci_is_pcie(pdev) || pdev->link_state)
return;
- if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
- pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+ pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM)
return;
/* VIA has a strange chipset, root port is under a bridge */
- if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT &&
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT &&
pdev->bus->self)
return;
@@ -647,8 +622,8 @@ static void pcie_update_aspm_capable(struct pcie_link_state *root)
if (link->root != root)
continue;
list_for_each_entry(child, &linkbus->devices, bus_list) {
- if ((child->pcie_type != PCI_EXP_TYPE_ENDPOINT) &&
- (child->pcie_type != PCI_EXP_TYPE_LEG_END))
+ if ((pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT) &&
+ (pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END))
continue;
pcie_aspm_check_latency(child);
}
@@ -663,8 +638,8 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
if (!pci_is_pcie(pdev) || !parent || !parent->link_state)
return;
- if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
- (parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))
+ if ((pci_pcie_type(parent) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(parent) != PCI_EXP_TYPE_DOWNSTREAM))
return;
down_read(&pci_bus_sem);
@@ -704,8 +679,8 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev)
if (aspm_disabled || !pci_is_pcie(pdev) || !link)
return;
- if ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
- (pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))
+ if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
return;
/*
* Devices changed PM state, we should recheck if latency
@@ -729,8 +704,8 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
if (aspm_policy != POLICY_POWERSAVE)
return;
- if ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
- (pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))
+ if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
return;
down_read(&pci_bus_sem);
@@ -757,8 +732,8 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
if (!pci_is_pcie(pdev))
return;
- if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
- pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM)
parent = pdev;
if (!parent || !parent->link_state)
return;
@@ -933,8 +908,8 @@ void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev)
struct pcie_link_state *link_state = pdev->link_state;
if (!pci_is_pcie(pdev) ||
- (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
- pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+ pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
return;
if (link_state->aspm_support)
@@ -950,8 +925,8 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
struct pcie_link_state *link_state = pdev->link_state;
if (!pci_is_pcie(pdev) ||
- (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
- pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+ pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
return;
if (link_state->aspm_support)
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 001f1b7..9ca0dc9 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -57,17 +57,12 @@ struct pcie_pme_service_data {
*/
void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
{
- int rtctl_pos;
- u16 rtctl;
-
- rtctl_pos = pci_pcie_cap(dev) + PCI_EXP_RTCTL;
-
- pci_read_config_word(dev, rtctl_pos, &rtctl);
if (enable)
- rtctl |= PCI_EXP_RTCTL_PMEIE;
+ pcie_capability_set_word(dev, PCI_EXP_RTCTL,
+ PCI_EXP_RTCTL_PMEIE);
else
- rtctl &= ~PCI_EXP_RTCTL_PMEIE;
- pci_write_config_word(dev, rtctl_pos, rtctl);
+ pcie_capability_clear_word(dev, PCI_EXP_RTCTL,
+ PCI_EXP_RTCTL_PMEIE);
}
/**
@@ -120,7 +115,7 @@ static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn)
if (!dev)
return false;
- if (pci_is_pcie(dev) && dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
+ if (pci_is_pcie(dev) && pci_pcie_type(dev) == PCI_EXP_TYPE_PCI_BRIDGE) {
down_read(&pci_bus_sem);
if (pcie_pme_walk_bus(bus))
found = true;
@@ -226,18 +221,15 @@ static void pcie_pme_work_fn(struct work_struct *work)
struct pcie_pme_service_data *data =
container_of(work, struct pcie_pme_service_data, work);
struct pci_dev *port = data->srv->port;
- int rtsta_pos;
u32 rtsta;
- rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
-
spin_lock_irq(&data->lock);
for (;;) {
if (data->noirq)
break;
- pci_read_config_dword(port, rtsta_pos, &rtsta);
+ pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
if (rtsta & PCI_EXP_RTSTA_PME) {
/*
* Clear PME status of the port. If there are other
@@ -276,17 +268,14 @@ static irqreturn_t pcie_pme_irq(int irq, void *context)
{
struct pci_dev *port;
struct pcie_pme_service_data *data;
- int rtsta_pos;
u32 rtsta;
unsigned long flags;
port = ((struct pcie_device *)context)->port;
data = get_service_data((struct pcie_device *)context);
- rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
-
spin_lock_irqsave(&data->lock, flags);
- pci_read_config_dword(port, rtsta_pos, &rtsta);
+ pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
if (!(rtsta & PCI_EXP_RTSTA_PME)) {
spin_unlock_irqrestore(&data->lock, flags);
@@ -335,13 +324,13 @@ static void pcie_pme_mark_devices(struct pci_dev *port)
struct pci_dev *dev;
/* Check if this is a root port event collector. */
- if (port->pcie_type != PCI_EXP_TYPE_RC_EC || !bus)
+ if (pci_pcie_type(port) != PCI_EXP_TYPE_RC_EC || !bus)
return;
down_read(&pci_bus_sem);
list_for_each_entry(dev, &bus->devices, bus_list)
if (pci_is_pcie(dev)
- && dev->pcie_type == PCI_EXP_TYPE_RC_END)
+ && pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
pcie_pme_set_native(dev, NULL);
up_read(&pci_bus_sem);
}
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
index 18bf90f..67be55a 100644
--- a/drivers/pci/pcie/portdrv_bus.c
+++ b/drivers/pci/pcie/portdrv_bus.c
@@ -38,7 +38,7 @@ static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
return 0;
if ((driver->port_type != PCIE_ANY_PORT) &&
- (driver->port_type != pciedev->port->pcie_type))
+ (driver->port_type != pci_pcie_type(pciedev->port)))
return 0;
return 1;
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 75915b3..d03a7a3 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -200,10 +200,13 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{
int i, irq = -1;
- /* We have to use INTx if MSI cannot be used for PCIe PME or pciehp. */
+ /*
+ * If MSI cannot be used for PCIe PME or hotplug, we have to use
+ * INTx or other interrupts, e.g. system shared interrupt.
+ */
if (((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) ||
((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())) {
- if (dev->pin)
+ if (dev->irq)
irq = dev->irq;
goto no_msi;
}
@@ -212,8 +215,12 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
if (!pcie_port_enable_msix(dev, irqs, mask))
return 0;
- /* We're not going to use MSI-X, so try MSI and fall back to INTx */
- if (!pci_enable_msi(dev) || dev->pin)
+ /*
+ * We're not going to use MSI-X, so try MSI and fall back to INTx.
+ * If neither MSI/MSI-X nor INTx available, try other interrupt. On
+ * some platforms, root port doesn't support MSI/MSI-X/INTx in RC mode.
+ */
+ if (!pci_enable_msi(dev) || dev->irq)
irq = dev->irq;
no_msi:
@@ -246,8 +253,7 @@ static void cleanup_service_irqs(struct pci_dev *dev)
*/
static int get_port_device_capability(struct pci_dev *dev)
{
- int services = 0, pos;
- u16 reg16;
+ int services = 0;
u32 reg32;
int cap_mask = 0;
int err;
@@ -265,11 +271,9 @@ static int get_port_device_capability(struct pci_dev *dev)
return 0;
}
- pos = pci_pcie_cap(dev);
- pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
/* Hot-Plug Capable */
- if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
- pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
+ if (cap_mask & PCIE_PORT_SERVICE_HP) {
+ pcie_capability_read_dword(dev, PCI_EXP_SLTCAP, &reg32);
if (reg32 & PCI_EXP_SLTCAP_HPC) {
services |= PCIE_PORT_SERVICE_HP;
/*
@@ -277,10 +281,8 @@ static int get_port_device_capability(struct pci_dev *dev)
* enabled by the BIOS and the hot-plug service driver
* is not loaded.
*/
- pos += PCI_EXP_SLTCTL;
- pci_read_config_word(dev, pos, &reg16);
- reg16 &= ~(PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
- pci_write_config_word(dev, pos, reg16);
+ pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
}
}
/* AER capable */
@@ -298,7 +300,7 @@ static int get_port_device_capability(struct pci_dev *dev)
services |= PCIE_PORT_SERVICE_VC;
/* Root ports are capable of generating PME too */
if ((cap_mask & PCIE_PORT_SERVICE_PME)
- && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
+ && pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
services |= PCIE_PORT_SERVICE_PME;
/*
* Disable PME interrupt on this port in case it's been enabled
@@ -336,7 +338,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
device->release = release_pcie_device; /* callback to free pcie dev */
dev_set_name(device, "%s:pcie%02x",
pci_name(pdev),
- get_descriptor_id(pdev->pcie_type, service));
+ get_descriptor_id(pci_pcie_type(pdev), service));
device->parent = &pdev->dev;
device_enable_async_suspend(device);
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index e76b447..0761d90 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -64,14 +64,7 @@ __setup("pcie_ports=", pcie_port_setup);
*/
void pcie_clear_root_pme_status(struct pci_dev *dev)
{
- int rtsta_pos;
- u32 rtsta;
-
- rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA;
-
- pci_read_config_dword(dev, rtsta_pos, &rtsta);
- rtsta |= PCI_EXP_RTSTA_PME;
- pci_write_config_dword(dev, rtsta_pos, rtsta);
+ pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
}
static int pcie_portdrv_restore_config(struct pci_dev *dev)
@@ -95,7 +88,7 @@ static int pcie_port_resume_noirq(struct device *dev)
* which breaks ACPI-based runtime wakeup on PCI Express, so clear those
* bits now just in case (shouldn't hurt).
*/
- if(pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)
pcie_clear_root_pme_status(pdev);
return 0;
}
@@ -195,9 +188,9 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev,
int status;
if (!pci_is_pcie(dev) ||
- ((dev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
- (dev->pcie_type != PCI_EXP_TYPE_UPSTREAM) &&
- (dev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)))
+ ((pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM) &&
+ (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)))
return -ENODEV;
if (!dev->irq && dev->pin) {
@@ -385,11 +378,11 @@ static const struct pci_device_id port_pci_ids[] = { {
};
MODULE_DEVICE_TABLE(pci, port_pci_ids);
-static struct pci_error_handlers pcie_portdrv_err_handler = {
- .error_detected = pcie_portdrv_error_detected,
- .mmio_enabled = pcie_portdrv_mmio_enabled,
- .slot_reset = pcie_portdrv_slot_reset,
- .resume = pcie_portdrv_err_resume,
+static const struct pci_error_handlers pcie_portdrv_err_handler = {
+ .error_detected = pcie_portdrv_error_detected,
+ .mmio_enabled = pcie_portdrv_mmio_enabled,
+ .slot_reset = pcie_portdrv_slot_reset,
+ .resume = pcie_portdrv_err_resume,
};
static struct pci_driver pcie_portdriver = {
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 9f8a6b7..ec909af 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -606,10 +606,10 @@ static void pci_set_bus_speed(struct pci_bus *bus)
u32 linkcap;
u16 linksta;
- pci_read_config_dword(bridge, pos + PCI_EXP_LNKCAP, &linkcap);
+ pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap);
bus->max_bus_speed = pcie_link_speed[linkcap & 0xf];
- pci_read_config_word(bridge, pos + PCI_EXP_LNKSTA, &linksta);
+ pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta);
pcie_update_link_speed(bus, linksta);
}
}
@@ -729,8 +729,10 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
/* Check if setup is sensible at all */
if (!pass &&
- (primary != bus->number || secondary <= bus->number)) {
- dev_dbg(&dev->dev, "bus configuration invalid, reconfiguring\n");
+ (primary != bus->number || secondary <= bus->number ||
+ secondary > subordinate)) {
+ dev_info(&dev->dev, "bridge configuration invalid ([bus %02x-%02x]), reconfiguring\n",
+ secondary, subordinate);
broken = 1;
}
@@ -932,24 +934,16 @@ void set_pcie_port_type(struct pci_dev *pdev)
pdev->is_pcie = 1;
pdev->pcie_cap = pos;
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
- pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4;
+ pdev->pcie_flags_reg = reg16;
pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
}
void set_pcie_hotplug_bridge(struct pci_dev *pdev)
{
- int pos;
- u16 reg16;
u32 reg32;
- pos = pci_pcie_cap(pdev);
- if (!pos)
- return;
- pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
- if (!(reg16 & PCI_EXP_FLAGS_SLOT))
- return;
- pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &reg32);
+ pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32);
if (reg32 & PCI_EXP_SLTCAP_HPC)
pdev->is_hotplug_bridge = 1;
}
@@ -1163,8 +1157,7 @@ int pci_cfg_space_size(struct pci_dev *dev)
if (class == PCI_CLASS_BRIDGE_HOST)
return pci_cfg_space_size_ext(dev);
- pos = pci_pcie_cap(dev);
- if (!pos) {
+ if (!pci_is_pcie(dev)) {
pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!pos)
goto fail;
@@ -1386,9 +1379,9 @@ static int only_one_child(struct pci_bus *bus)
if (!parent || !pci_is_pcie(parent))
return 0;
- if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+ if (pci_pcie_type(parent) == PCI_EXP_TYPE_ROOT_PORT)
return 1;
- if (parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM &&
+ if (pci_pcie_type(parent) == PCI_EXP_TYPE_DOWNSTREAM &&
!pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS))
return 1;
return 0;
@@ -1465,7 +1458,7 @@ static int pcie_find_smpss(struct pci_dev *dev, void *data)
*/
if (dev->is_hotplug_bridge && (!list_is_singular(&dev->bus->devices) ||
(dev->bus->self &&
- dev->bus->self->pcie_type != PCI_EXP_TYPE_ROOT_PORT)))
+ pci_pcie_type(dev->bus->self) != PCI_EXP_TYPE_ROOT_PORT)))
*smpss = 0;
if (*smpss > dev->pcie_mpss)
@@ -1481,7 +1474,8 @@ static void pcie_write_mps(struct pci_dev *dev, int mps)
if (pcie_bus_config == PCIE_BUS_PERFORMANCE) {
mps = 128 << dev->pcie_mpss;
- if (dev->pcie_type != PCI_EXP_TYPE_ROOT_PORT && dev->bus->self)
+ if (pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT &&
+ dev->bus->self)
/* For "Performance", the assumption is made that
* downstream communication will never be larger than
* the MRRS. So, the MPS only needs to be configured
@@ -1756,11 +1750,6 @@ int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max)
"busn_res: can not insert %pR under %s%pR (conflicts with %s %pR)\n",
res, pci_is_root_bus(b) ? "domain " : "",
parent_res, conflict->name, conflict);
- else
- dev_printk(KERN_DEBUG, &b->dev,
- "busn_res: %pR is inserted under %s%pR\n",
- res, pci_is_root_bus(b) ? "domain " : "",
- parent_res);
return conflict == NULL;
}
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 27911b5..eb907a8f 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -434,25 +434,6 @@ int pci_proc_detach_device(struct pci_dev *dev)
return 0;
}
-#if 0
-int pci_proc_attach_bus(struct pci_bus* bus)
-{
- struct proc_dir_entry *de = bus->procdir;
-
- if (!proc_initialized)
- return -EACCES;
-
- if (!de) {
- char name[16];
- sprintf(name, "%02x", bus->number);
- de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
- if (!de)
- return -ENOMEM;
- }
- return 0;
-}
-#endif /* 0 */
-
int pci_proc_detach_bus(struct pci_bus* bus)
{
struct proc_dir_entry *de = bus->procdir;
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 5155317..7a451ff 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3081,17 +3081,36 @@ static int reset_intel_generic_dev(struct pci_dev *dev, int probe)
static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, int probe)
{
- int pos;
+ int i;
+ u16 status;
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!pos)
- return -ENOTTY;
+ /*
+ * http://www.intel.com/content/dam/doc/datasheet/82599-10-gbe-controller-datasheet.pdf
+ *
+ * The 82599 supports FLR on VFs, but FLR support is reported only
+ * in the PF DEVCAP (sec 9.3.10.4), not in the VF DEVCAP (sec 9.5).
+ * Therefore, we can't use pcie_flr(), which checks the VF DEVCAP.
+ */
if (probe)
return 0;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL,
- PCI_EXP_DEVCTL_BCR_FLR);
+ /* Wait for Transaction Pending bit clean */
+ for (i = 0; i < 4; i++) {
+ if (i)
+ msleep((1 << (i - 1)) * 100);
+
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
+ if (!(status & PCI_EXP_DEVSTA_TRPND))
+ goto clear;
+ }
+
+ dev_err(&dev->dev, "transaction is not cleared; "
+ "proceeding with reset anyway\n");
+
+clear:
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
+
msleep(100);
return 0;
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 04a4861..513972f 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -32,152 +32,82 @@ static void pci_stop_dev(struct pci_dev *dev)
static void pci_destroy_dev(struct pci_dev *dev)
{
- /* Remove the device from the device lists, and prevent any further
- * list accesses from this device */
down_write(&pci_bus_sem);
list_del(&dev->bus_list);
- dev->bus_list.next = dev->bus_list.prev = NULL;
up_write(&pci_bus_sem);
pci_free_resources(dev);
pci_dev_put(dev);
}
-/**
- * pci_remove_device_safe - remove an unused hotplug device
- * @dev: the device to remove
- *
- * Delete the device structure from the device lists and
- * notify userspace (/sbin/hotplug), but only if the device
- * in question is not being used by a driver.
- * Returns 0 on success.
- */
-#if 0
-int pci_remove_device_safe(struct pci_dev *dev)
-{
- if (pci_dev_driver(dev))
- return -EBUSY;
- pci_destroy_dev(dev);
- return 0;
-}
-#endif /* 0 */
-
-void pci_remove_bus(struct pci_bus *pci_bus)
+void pci_remove_bus(struct pci_bus *bus)
{
- pci_proc_detach_bus(pci_bus);
+ pci_proc_detach_bus(bus);
down_write(&pci_bus_sem);
- list_del(&pci_bus->node);
- pci_bus_release_busn_res(pci_bus);
+ list_del(&bus->node);
+ pci_bus_release_busn_res(bus);
up_write(&pci_bus_sem);
- if (!pci_bus->is_added)
+ if (!bus->is_added)
return;
- pci_remove_legacy_files(pci_bus);
- device_unregister(&pci_bus->dev);
+ pci_remove_legacy_files(bus);
+ device_unregister(&bus->dev);
}
EXPORT_SYMBOL(pci_remove_bus);
-static void __pci_remove_behind_bridge(struct pci_dev *dev);
-/**
- * pci_stop_and_remove_bus_device - remove a PCI device and any children
- * @dev: the device to remove
- *
- * Remove a PCI device from the device lists, informing the drivers
- * that the device has been removed. We also remove any subordinate
- * buses and children in a depth-first manner.
- *
- * For each device we remove, delete the device structure from the
- * device lists, remove the /proc entry, and notify userspace
- * (/sbin/hotplug).
- */
-void __pci_remove_bus_device(struct pci_dev *dev)
+static void pci_stop_bus_device(struct pci_dev *dev)
{
- if (dev->subordinate) {
- struct pci_bus *b = dev->subordinate;
+ struct pci_bus *bus = dev->subordinate;
+ struct pci_dev *child, *tmp;
- __pci_remove_behind_bridge(dev);
- pci_remove_bus(b);
- dev->subordinate = NULL;
+ /*
+ * Stopping an SR-IOV PF device removes all the associated VFs,
+ * which will update the bus->devices list and confuse the
+ * iterator. Therefore, iterate in reverse so we remove the VFs
+ * first, then the PF.
+ */
+ if (bus) {
+ list_for_each_entry_safe_reverse(child, tmp,
+ &bus->devices, bus_list)
+ pci_stop_bus_device(child);
}
- pci_destroy_dev(dev);
-}
-EXPORT_SYMBOL(__pci_remove_bus_device);
-
-void pci_stop_and_remove_bus_device(struct pci_dev *dev)
-{
- pci_stop_bus_device(dev);
- __pci_remove_bus_device(dev);
+ pci_stop_dev(dev);
}
-static void __pci_remove_behind_bridge(struct pci_dev *dev)
+static void pci_remove_bus_device(struct pci_dev *dev)
{
- struct list_head *l, *n;
+ struct pci_bus *bus = dev->subordinate;
+ struct pci_dev *child, *tmp;
- if (dev->subordinate)
- list_for_each_safe(l, n, &dev->subordinate->devices)
- __pci_remove_bus_device(pci_dev_b(l));
-}
+ if (bus) {
+ list_for_each_entry_safe(child, tmp,
+ &bus->devices, bus_list)
+ pci_remove_bus_device(child);
-static void pci_stop_behind_bridge(struct pci_dev *dev)
-{
- struct list_head *l, *n;
+ pci_remove_bus(bus);
+ dev->subordinate = NULL;
+ }
- if (dev->subordinate)
- list_for_each_safe(l, n, &dev->subordinate->devices)
- pci_stop_bus_device(pci_dev_b(l));
+ pci_destroy_dev(dev);
}
/**
- * pci_stop_and_remove_behind_bridge - stop and remove all devices behind
- * a PCI bridge
- * @dev: PCI bridge device
+ * pci_stop_and_remove_bus_device - remove a PCI device and any children
+ * @dev: the device to remove
*
- * Remove all devices on the bus, except for the parent bridge.
- * This also removes any child buses, and any devices they may
- * contain in a depth-first manner.
- */
-void pci_stop_and_remove_behind_bridge(struct pci_dev *dev)
-{
- pci_stop_behind_bridge(dev);
- __pci_remove_behind_bridge(dev);
-}
-
-static void pci_stop_bus_devices(struct pci_bus *bus)
-{
- struct list_head *l, *n;
-
- /*
- * VFs could be removed by pci_stop_and_remove_bus_device() in the
- * pci_stop_bus_devices() code path for PF.
- * aka, bus->devices get updated in the process.
- * but VFs are inserted after PFs when SRIOV is enabled for PF,
- * We can iterate the list backwards to get prev valid PF instead
- * of removed VF.
- */
- list_for_each_prev_safe(l, n, &bus->devices) {
- struct pci_dev *dev = pci_dev_b(l);
- pci_stop_bus_device(dev);
- }
-}
-
-/**
- * pci_stop_bus_device - stop a PCI device and any children
- * @dev: the device to stop
+ * Remove a PCI device from the device lists, informing the drivers
+ * that the device has been removed. We also remove any subordinate
+ * buses and children in a depth-first manner.
*
- * Stop a PCI device (detach the driver, remove from the global list
- * and so on). This also stop any subordinate buses and children in a
- * depth-first manner.
+ * For each device we remove, delete the device structure from the
+ * device lists, remove the /proc entry, and notify userspace
+ * (/sbin/hotplug).
*/
-void pci_stop_bus_device(struct pci_dev *dev)
+void pci_stop_and_remove_bus_device(struct pci_dev *dev)
{
- if (dev->subordinate)
- pci_stop_bus_devices(dev->subordinate);
-
- pci_stop_dev(dev);
+ pci_stop_bus_device(dev);
+ pci_remove_bus_device(dev);
}
-
EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
-EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge);
-EXPORT_SYMBOL_GPL(pci_stop_bus_device);
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index 48ebdb2..0b3037a 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -167,44 +167,6 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
return rom;
}
-#if 0
-/**
- * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
- * @pdev: pointer to pci device struct
- * @size: pointer to receive size of pci window over ROM
- *
- * Return: kernel virtual pointer to image of ROM
- *
- * Map a PCI ROM into kernel space. If ROM is boot video ROM,
- * the shadow BIOS copy will be returned instead of the
- * actual ROM.
- */
-void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
-{
- struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
- void __iomem *rom;
-
- rom = pci_map_rom(pdev, size);
- if (!rom)
- return NULL;
-
- if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW |
- IORESOURCE_ROM_BIOS_COPY))
- return rom;
-
- res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
- if (!res->start)
- return rom;
-
- res->end = res->start + *size;
- memcpy_fromio((void*)(unsigned long)res->start, rom, *size);
- pci_unmap_rom(pdev, rom);
- res->flags |= IORESOURCE_ROM_COPY;
-
- return (void __iomem *)(unsigned long)res->start;
-}
-#endif /* 0 */
-
/**
* pci_unmap_rom - unmap the ROM from kernel space
* @pdev: pointer to pci device struct
@@ -226,27 +188,6 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
pci_disable_rom(pdev);
}
-#if 0
-/**
- * pci_remove_rom - disable the ROM and remove its sysfs attribute
- * @pdev: pointer to pci device struct
- *
- * Remove the rom file in sysfs and disable ROM decoding.
- */
-void pci_remove_rom(struct pci_dev *pdev)
-{
- struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
-
- if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
- sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
- if (!(res->flags & (IORESOURCE_ROM_ENABLE |
- IORESOURCE_ROM_SHADOW |
- IORESOURCE_ROM_BIOS_COPY |
- IORESOURCE_ROM_COPY)))
- pci_disable_rom(pdev);
-}
-#endif /* 0 */
-
/**
* pci_cleanup_rom - free the ROM copy created by pci_map_rom_copy
* @pdev: pointer to pci device struct
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index 993d4a0..bf969ba 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -41,7 +41,7 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
continue;
}
/* PCI device should connect to a PCIe bridge */
- if (pdev->pcie_type != PCI_EXP_TYPE_PCI_BRIDGE) {
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_PCI_BRIDGE) {
/* Busted hardware? */
WARN_ON_ONCE(1);
return NULL;
@@ -130,16 +130,14 @@ pci_find_next_bus(const struct pci_bus *from)
* decrement the reference count by calling pci_dev_put().
* If no device is found, %NULL is returned.
*/
-struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)
+struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn)
{
- struct list_head *tmp;
struct pci_dev *dev;
WARN_ON(in_interrupt());
down_read(&pci_bus_sem);
- list_for_each(tmp, &bus->devices) {
- dev = pci_dev_b(tmp);
+ list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->devfn == devfn)
goto out;
}
@@ -245,30 +243,14 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
unsigned int ss_vendor, unsigned int ss_device,
struct pci_dev *from)
{
- struct pci_dev *pdev;
- struct pci_device_id *id;
-
- /*
- * pci_find_subsys() can be called on the ide_setup() path,
- * super-early in boot. But the down_read() will enable local
- * interrupts, which can cause some machines to crash. So here we
- * detect and flag that situation and bail out early.
- */
- if (unlikely(no_pci_devices()))
- return NULL;
-
- id = kzalloc(sizeof(*id), GFP_KERNEL);
- if (!id)
- return NULL;
- id->vendor = vendor;
- id->device = device;
- id->subvendor = ss_vendor;
- id->subdevice = ss_device;
-
- pdev = pci_get_dev_by_id(id, from);
- kfree(id);
-
- return pdev;
+ struct pci_device_id id = {
+ .vendor = vendor,
+ .device = device,
+ .subvendor = ss_vendor,
+ .subdevice = ss_device,
+ };
+
+ return pci_get_dev_by_id(&id, from);
}
/**
@@ -307,19 +289,16 @@ pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
*/
struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from)
{
- struct pci_dev *dev;
- struct pci_device_id *id;
-
- id = kzalloc(sizeof(*id), GFP_KERNEL);
- if (!id)
- return NULL;
- id->vendor = id->device = id->subvendor = id->subdevice = PCI_ANY_ID;
- id->class_mask = PCI_ANY_ID;
- id->class = class;
-
- dev = pci_get_dev_by_id(id, from);
- kfree(id);
- return dev;
+ struct pci_device_id id = {
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class_mask = PCI_ANY_ID,
+ .class = class,
+ };
+
+ return pci_get_dev_by_id(&id, from);
}
/**
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index fb50613..1e808ca 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -697,6 +697,38 @@ static resource_size_t calculate_memsize(resource_size_t size,
return size;
}
+resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus,
+ unsigned long type)
+{
+ return 1;
+}
+
+#define PCI_P2P_DEFAULT_MEM_ALIGN 0x100000 /* 1MiB */
+#define PCI_P2P_DEFAULT_IO_ALIGN 0x1000 /* 4KiB */
+#define PCI_P2P_DEFAULT_IO_ALIGN_1K 0x400 /* 1KiB */
+
+static resource_size_t window_alignment(struct pci_bus *bus,
+ unsigned long type)
+{
+ resource_size_t align = 1, arch_align;
+
+ if (type & IORESOURCE_MEM)
+ align = PCI_P2P_DEFAULT_MEM_ALIGN;
+ else if (type & IORESOURCE_IO) {
+ /*
+ * Per spec, I/O windows are 4K-aligned, but some
+ * bridges have an extension to support 1K alignment.
+ */
+ if (bus->self->io_window_1k)
+ align = PCI_P2P_DEFAULT_IO_ALIGN_1K;
+ else
+ align = PCI_P2P_DEFAULT_IO_ALIGN;
+ }
+
+ arch_align = pcibios_window_alignment(bus, type);
+ return max(align, arch_align);
+}
+
/**
* pbus_size_io() - size the io window of a given bus
*
@@ -717,17 +749,12 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
unsigned long size = 0, size0 = 0, size1 = 0;
resource_size_t children_add_size = 0;
- resource_size_t min_align = 4096, align;
+ resource_size_t min_align, io_align, align;
if (!b_res)
return;
- /*
- * Per spec, I/O windows are 4K-aligned, but some bridges have an
- * extension to support 1K alignment.
- */
- if (bus->self->io_window_1k)
- min_align = 1024;
+ io_align = min_align = window_alignment(bus, IORESOURCE_IO);
list_for_each_entry(dev, &bus->devices, bus_list) {
int i;
@@ -754,8 +781,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
}
}
- if (min_align > 4096)
- min_align = 4096;
+ if (min_align > io_align)
+ min_align = io_align;
size0 = calculate_iosize(size, min_size, size1,
resource_size(b_res), min_align);
@@ -785,6 +812,28 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
}
}
+static inline resource_size_t calculate_mem_align(resource_size_t *aligns,
+ int max_order)
+{
+ resource_size_t align = 0;
+ resource_size_t min_align = 0;
+ int order;
+
+ for (order = 0; order <= max_order; order++) {
+ resource_size_t align1 = 1;
+
+ align1 <<= (order + 20);
+
+ if (!align)
+ min_align = align1;
+ else if (ALIGN(align + min_align, min_align) < align1)
+ min_align = align1 >> 1;
+ align += aligns[order];
+ }
+
+ return min_align;
+}
+
/**
* pbus_size_mem() - size the memory window of a given bus
*
@@ -864,19 +913,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
children_add_size += get_res_add_size(realloc_head, r);
}
}
- align = 0;
- min_align = 0;
- for (order = 0; order <= max_order; order++) {
- resource_size_t align1 = 1;
- align1 <<= (order + 20);
-
- if (!align)
- min_align = align1;
- else if (ALIGN(align + min_align, min_align) < align1)
- min_align = align1 >> 1;
- align += aligns[order];
- }
+ min_align = calculate_mem_align(aligns, max_order);
+ min_align = max(min_align, window_alignment(bus, b_res->flags & mask));
size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
if (children_add_size > add_size)
add_size = children_add_size;
diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c
index eb219a1..9bd6864 100644
--- a/drivers/pci/setup-irq.c
+++ b/drivers/pci/setup-irq.c
@@ -17,8 +17,13 @@
#include <linux/ioport.h>
#include <linux/cache.h>
+void __weak pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+ dev_dbg(&dev->dev, "assigning IRQ %02d\n", irq);
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}
-static void __init
+static void
pdev_fixup_irq(struct pci_dev *dev,
u8 (*swizzle)(struct pci_dev *, u8 *),
int (*map_irq)(const struct pci_dev *, u8, u8))
@@ -54,7 +59,7 @@ pdev_fixup_irq(struct pci_dev *dev,
pcibios_update_irq(dev, irq);
}
-void __init
+void
pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
int (*map_irq)(const struct pci_dev *, u8, u8))
{
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index d6cc62c..def8d0b 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -982,7 +982,6 @@ static int pcifront_detach_devices(struct pcifront_device *pdev)
int err = 0;
int i, num_devs;
unsigned int domain, bus, slot, func;
- struct pci_bus *pci_bus;
struct pci_dev *pci_dev;
char str[64];
@@ -1032,13 +1031,8 @@ static int pcifront_detach_devices(struct pcifront_device *pdev)
goto out;
}
- pci_bus = pci_find_bus(domain, bus);
- if (!pci_bus) {
- dev_dbg(&pdev->xdev->dev, "Cannot get bus %04x:%02x\n",
- domain, bus);
- continue;
- }
- pci_dev = pci_get_slot(pci_bus, PCI_DEVFN(slot, func));
+ pci_dev = pci_get_domain_bus_and_slot(domain, bus,
+ PCI_DEVFN(slot, func));
if (!pci_dev) {
dev_dbg(&pdev->xdev->dev,
"Cannot get PCI device %04x:%02x:%02x.%d\n",
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
index 24caeaf..9d3ac99 100644
--- a/drivers/pcmcia/cardbus.c
+++ b/drivers/pcmcia/cardbus.c
@@ -105,8 +105,17 @@ int __ref cb_alloc(struct pcmcia_socket *s)
*/
void cb_free(struct pcmcia_socket *s)
{
- struct pci_dev *bridge = s->cb_dev;
+ struct pci_dev *bridge, *dev, *tmp;
+ struct pci_bus *bus;
- if (bridge)
- pci_stop_and_remove_behind_bridge(bridge);
+ bridge = s->cb_dev;
+ if (!bridge)
+ return;
+
+ bus = bridge->subordinate;
+ if (!bus)
+ return;
+
+ list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list)
+ pci_stop_and_remove_bus_device(dev);
}
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 5d44252..d5e1625 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -2219,9 +2219,7 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct tsi721_device *priv;
- int cap;
int err;
- u32 regval;
priv = kzalloc(sizeof(struct tsi721_device), GFP_KERNEL);
if (priv == NULL) {
@@ -2330,20 +2328,16 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
dev_info(&pdev->dev, "Unable to set consistent DMA mask\n");
}
- cap = pci_pcie_cap(pdev);
- BUG_ON(cap == 0);
+ BUG_ON(!pci_is_pcie(pdev));
/* Clear "no snoop" and "relaxed ordering" bits, use default MRRS. */
- pci_read_config_dword(pdev, cap + PCI_EXP_DEVCTL, &regval);
- regval &= ~(PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN |
- PCI_EXP_DEVCTL_NOSNOOP_EN);
- regval |= 0x2 << MAX_READ_REQUEST_SZ_SHIFT;
- pci_write_config_dword(pdev, cap + PCI_EXP_DEVCTL, regval);
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN |
+ PCI_EXP_DEVCTL_NOSNOOP_EN,
+ 0x2 << MAX_READ_REQUEST_SZ_SHIFT);
/* Adjust PCIe completion timeout. */
- pci_read_config_dword(pdev, cap + PCI_EXP_DEVCTL2, &regval);
- regval &= ~(0x0f);
- pci_write_config_dword(pdev, cap + PCI_EXP_DEVCTL2, regval | 0x2);
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2, 0xf, 0x2);
/*
* FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 4e932cc..e98a5e7 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -33,9 +33,8 @@ config REGULATOR_DUMMY
help
If this option is enabled then when a regulator lookup fails
and the board has not specified that it has provided full
- constraints then the regulator core will provide an always
- enabled dummy regulator will be provided, allowing consumer
- drivers to continue.
+ constraints the regulator core will provide an always
+ enabled dummy regulator, allowing consumer drivers to continue.
A warning will be generated when this substitution is done.
@@ -50,11 +49,11 @@ config REGULATOR_VIRTUAL_CONSUMER
tristate "Virtual regulator consumer support"
help
This driver provides a virtual consumer for the voltage and
- current regulator API which provides sysfs controls for
- configuring the supplies requested. This is mainly useful
- for test purposes.
+ current regulator API which provides sysfs controls for
+ configuring the supplies requested. This is mainly useful
+ for test purposes.
- If unsure, say no.
+ If unsure, say no.
config REGULATOR_USERSPACE_CONSUMER
tristate "Userspace regulator consumer support"
@@ -63,7 +62,7 @@ config REGULATOR_USERSPACE_CONSUMER
from user space. Userspace consumer driver provides ability to
control power supplies for such devices.
- If unsure, say no.
+ If unsure, say no.
config REGULATOR_GPIO
tristate "GPIO regulator support"
@@ -110,6 +109,17 @@ config REGULATOR_DA9052
This driver supports the voltage regulators of DA9052-BC and
DA9053-AA/Bx PMIC.
+config REGULATOR_FAN53555
+ tristate "Fairchild FAN53555 Regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver supports Fairchild FAN53555 Digitally Programmable
+ TinyBuck Regulator. The FAN53555 is a step-down switching voltage
+ regulator that delivers a digitally programmable output from an
+ input voltage supply of 2.5V to 5.5V. The output voltage is
+ programmed through an I2C interface.
+
config REGULATOR_ANATOP
tristate "Freescale i.MX on-chip ANATOP LDO regulators"
depends on MFD_ANATOP
@@ -172,6 +182,14 @@ config REGULATOR_MAX8660
This driver controls a Maxim 8660/8661 voltage output
regulator via I2C bus.
+config REGULATOR_MAX8907
+ tristate "Maxim 8907 voltage regulator"
+ depends on MFD_MAX8907
+ help
+ This driver controls a Maxim 8907 voltage output regulator
+ via I2C bus. The provided regulator is suitable for Tegra
+ chip to control Step-Down DC-DC and LDOs.
+
config REGULATOR_MAX8925
tristate "Maxim MAX8925 Power Management IC"
depends on MFD_MAX8925
@@ -247,7 +265,7 @@ config REGULATOR_LP8788
config REGULATOR_PCF50633
tristate "NXP PCF50633 regulator driver"
- depends on MFD_PCF50633
+ depends on MFD_PCF50633
help
Say Y here to support the voltage regulators and convertors
on PCF50633
@@ -416,7 +434,7 @@ config REGULATOR_WM8350
depends on MFD_WM8350
help
This driver provides support for the voltage and current regulators
- of the WM8350 AudioPlus PMIC.
+ of the WM8350 AudioPlus PMIC.
config REGULATOR_WM8400
tristate "Wolfson Microelectronics WM8400 AudioPlus PMIC"
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 3342615..e431eed 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
+obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
+obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o
obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o
obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c
index 6f45bfd..167c93f 100644
--- a/drivers/regulator/aat2870-regulator.c
+++ b/drivers/regulator/aat2870-regulator.c
@@ -162,7 +162,7 @@ static struct aat2870_regulator *aat2870_get_regulator(int id)
static int aat2870_regulator_probe(struct platform_device *pdev)
{
struct aat2870_regulator *ri;
- struct regulator_config config = { 0 };
+ struct regulator_config config = { };
struct regulator_dev *rdev;
ri = aat2870_get_regulator(pdev->id);
diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c
index c151fd5..65ad2b3 100644
--- a/drivers/regulator/ab3100.c
+++ b/drivers/regulator/ab3100.c
@@ -347,17 +347,11 @@ static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg)
return abreg->plfdata->external_voltage;
}
-static int ab3100_get_fixed_voltage_regulator(struct regulator_dev *reg)
-{
- return reg->desc->min_uV;
-}
-
static struct regulator_ops regulator_ops_fixed = {
.list_voltage = regulator_list_voltage_linear,
.enable = ab3100_enable_regulator,
.disable = ab3100_disable_regulator,
.is_enabled = ab3100_is_enabled_regulator,
- .get_voltage = ab3100_get_fixed_voltage_regulator,
};
static struct regulator_ops regulator_ops_variable = {
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 10f2f4d..e3d1d06 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -37,6 +37,7 @@
* @voltage_bank: bank to control regulator voltage
* @voltage_reg: register to control regulator voltage
* @voltage_mask: mask to control regulator voltage
+ * @voltage_shift: shift to control regulator voltage
* @delay: startup/set voltage delay in us
*/
struct ab8500_regulator_info {
@@ -50,6 +51,7 @@ struct ab8500_regulator_info {
u8 voltage_bank;
u8 voltage_reg;
u8 voltage_mask;
+ u8 voltage_shift;
unsigned int delay;
};
@@ -195,17 +197,14 @@ static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev)
}
dev_vdbg(rdev_get_dev(rdev),
- "%s-get_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
- " 0x%x\n",
- info->desc.name, info->voltage_bank, info->voltage_reg,
- info->voltage_mask, regval);
+ "%s-get_voltage (bank, reg, mask, shift, value): "
+ "0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->voltage_bank,
+ info->voltage_reg, info->voltage_mask,
+ info->voltage_shift, regval);
- /* vintcore has a different layout */
val = regval & info->voltage_mask;
- if (info->desc.id == AB8500_LDO_INTCORE)
- return val >> 0x3;
- else
- return val;
+ return val >> info->voltage_shift;
}
static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
@@ -221,7 +220,7 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
}
/* set the registers for the request */
- regval = (u8)selector;
+ regval = (u8)selector << info->voltage_shift;
ret = abx500_mask_and_set_register_interruptible(info->dev,
info->voltage_bank, info->voltage_reg,
info->voltage_mask, regval);
@@ -238,13 +237,6 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
return ret;
}
-static int ab8500_regulator_enable_time(struct regulator_dev *rdev)
-{
- struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
-
- return info->delay;
-}
-
static int ab8500_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
unsigned int old_sel,
unsigned int new_sel)
@@ -261,22 +253,14 @@ static struct regulator_ops ab8500_regulator_ops = {
.get_voltage_sel = ab8500_regulator_get_voltage_sel,
.set_voltage_sel = ab8500_regulator_set_voltage_sel,
.list_voltage = regulator_list_voltage_table,
- .enable_time = ab8500_regulator_enable_time,
.set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel,
};
-static int ab8500_fixed_get_voltage(struct regulator_dev *rdev)
-{
- return rdev->desc->min_uV;
-}
-
static struct regulator_ops ab8500_regulator_fixed_ops = {
.enable = ab8500_regulator_enable,
.disable = ab8500_regulator_disable,
.is_enabled = ab8500_regulator_is_enabled,
- .get_voltage = ab8500_fixed_get_voltage,
.list_voltage = regulator_list_voltage_linear,
- .enable_time = ab8500_regulator_enable_time,
};
static struct ab8500_regulator_info
@@ -358,6 +342,7 @@ static struct ab8500_regulator_info
.voltage_bank = 0x03,
.voltage_reg = 0x80,
.voltage_mask = 0x38,
+ .voltage_shift = 3,
},
/*
@@ -374,6 +359,7 @@ static struct ab8500_regulator_info
.owner = THIS_MODULE,
.n_voltages = 1,
.min_uV = 2000000,
+ .enable_time = 10000,
},
.delay = 10000,
.update_bank = 0x03,
diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c
index c8f95c0..d184aa3 100644
--- a/drivers/regulator/arizona-ldo1.c
+++ b/drivers/regulator/arizona-ldo1.c
@@ -39,6 +39,8 @@ static struct regulator_ops arizona_ldo1_ops = {
.map_voltage = regulator_map_voltage_linear,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
};
static const struct regulator_desc arizona_ldo1 = {
@@ -49,9 +51,11 @@ static const struct regulator_desc arizona_ldo1 = {
.vsel_reg = ARIZONA_LDO1_CONTROL_1,
.vsel_mask = ARIZONA_LDO1_VSEL_MASK,
+ .bypass_reg = ARIZONA_LDO1_CONTROL_1,
+ .bypass_mask = ARIZONA_LDO1_BYPASS,
.min_uV = 900000,
.uV_step = 50000,
- .n_voltages = 7,
+ .n_voltages = 6,
.owner = THIS_MODULE,
};
diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c
index 450a069..d9b1f82 100644
--- a/drivers/regulator/arizona-micsupp.c
+++ b/drivers/regulator/arizona-micsupp.c
@@ -82,6 +82,9 @@ static struct regulator_ops arizona_micsupp_ops = {
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
+
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
};
static const struct regulator_desc arizona_micsupp = {
@@ -95,6 +98,8 @@ static const struct regulator_desc arizona_micsupp = {
.vsel_mask = ARIZONA_LDO2_VSEL_MASK,
.enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,
.enable_mask = ARIZONA_CPMIC_ENA,
+ .bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
+ .bypass_mask = ARIZONA_CPMIC_BYPASS,
.owner = THIS_MODULE,
};
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 4838531..2e0352d 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -77,6 +77,7 @@ struct regulator {
struct device *dev;
struct list_head list;
unsigned int always_on:1;
+ unsigned int bypass:1;
int uA_load;
int min_uV;
int max_uV;
@@ -394,6 +395,9 @@ static ssize_t regulator_status_show(struct device *dev,
case REGULATOR_STATUS_STANDBY:
label = "standby";
break;
+ case REGULATOR_STATUS_BYPASS:
+ label = "bypass";
+ break;
case REGULATOR_STATUS_UNDEFINED:
label = "undefined";
break;
@@ -585,6 +589,27 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,
static DEVICE_ATTR(suspend_standby_state, 0444,
regulator_suspend_standby_state_show, NULL);
+static ssize_t regulator_bypass_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+ const char *report;
+ bool bypass;
+ int ret;
+
+ ret = rdev->desc->ops->get_bypass(rdev, &bypass);
+
+ if (ret != 0)
+ report = "unknown";
+ else if (bypass)
+ report = "enabled";
+ else
+ report = "disabled";
+
+ return sprintf(buf, "%s\n", report);
+}
+static DEVICE_ATTR(bypass, 0444,
+ regulator_bypass_show, NULL);
/*
* These are the only attributes are present for all regulators.
@@ -778,6 +803,9 @@ static void print_constraints(struct regulator_dev *rdev)
if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
count += sprintf(buf + count, "standby");
+ if (!count)
+ sprintf(buf, "no parameters");
+
rdev_info(rdev, "%s\n", buf);
if ((constraints->min_uV != constraints->max_uV) &&
@@ -974,6 +1002,7 @@ static int set_supply(struct regulator_dev *rdev,
err = -ENOMEM;
return err;
}
+ supply_rdev->open_count++;
return 0;
}
@@ -1720,6 +1749,9 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)
if (regulator->always_on)
return 0;
+ if (!ms)
+ return regulator_disable(regulator);
+
mutex_lock(&rdev->mutex);
rdev->deferred_disables++;
mutex_unlock(&rdev->mutex);
@@ -2178,9 +2210,12 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
}
}
- if (ret == 0 && best_val >= 0)
+ if (ret == 0 && best_val >= 0) {
+ unsigned long data = best_val;
+
_notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE,
- (void *)best_val);
+ (void *)data);
+ }
trace_regulator_set_voltage_complete(rdev_get_name(rdev), best_val);
@@ -2291,8 +2326,8 @@ int regulator_set_voltage_time(struct regulator *regulator,
EXPORT_SYMBOL_GPL(regulator_set_voltage_time);
/**
- *regulator_set_voltage_time_sel - get raise/fall time
- * @regulator: regulator source
+ * regulator_set_voltage_time_sel - get raise/fall time
+ * @rdev: regulator source device
* @old_selector: selector for starting voltage
* @new_selector: selector for target voltage
*
@@ -2388,6 +2423,8 @@ static int _regulator_get_voltage(struct regulator_dev *rdev)
ret = rdev->desc->ops->list_voltage(rdev, sel);
} else if (rdev->desc->ops->get_voltage) {
ret = rdev->desc->ops->get_voltage(rdev);
+ } else if (rdev->desc->ops->list_voltage) {
+ ret = rdev->desc->ops->list_voltage(rdev, 0);
} else {
return -EINVAL;
}
@@ -2674,6 +2711,100 @@ out:
EXPORT_SYMBOL_GPL(regulator_set_optimum_mode);
/**
+ * regulator_set_bypass_regmap - Default set_bypass() using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: state to set.
+ */
+int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable)
+{
+ unsigned int val;
+
+ if (enable)
+ val = rdev->desc->bypass_mask;
+ else
+ val = 0;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg,
+ rdev->desc->bypass_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap);
+
+/**
+ * regulator_get_bypass_regmap - Default get_bypass() using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: current state.
+ */
+int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val);
+ if (ret != 0)
+ return ret;
+
+ *enable = val & rdev->desc->bypass_mask;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap);
+
+/**
+ * regulator_allow_bypass - allow the regulator to go into bypass mode
+ *
+ * @regulator: Regulator to configure
+ * @allow: enable or disable bypass mode
+ *
+ * Allow the regulator to go into bypass mode if all other consumers
+ * for the regulator also enable bypass mode and the machine
+ * constraints allow this. Bypass mode means that the regulator is
+ * simply passing the input directly to the output with no regulation.
+ */
+int regulator_allow_bypass(struct regulator *regulator, bool enable)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ int ret = 0;
+
+ if (!rdev->desc->ops->set_bypass)
+ return 0;
+
+ if (rdev->constraints &&
+ !(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_BYPASS))
+ return 0;
+
+ mutex_lock(&rdev->mutex);
+
+ if (enable && !regulator->bypass) {
+ rdev->bypass_count++;
+
+ if (rdev->bypass_count == rdev->open_count) {
+ ret = rdev->desc->ops->set_bypass(rdev, enable);
+ if (ret != 0)
+ rdev->bypass_count--;
+ }
+
+ } else if (!enable && regulator->bypass) {
+ rdev->bypass_count--;
+
+ if (rdev->bypass_count != rdev->open_count) {
+ ret = rdev->desc->ops->set_bypass(rdev, enable);
+ if (ret != 0)
+ rdev->bypass_count++;
+ }
+ }
+
+ if (ret == 0)
+ regulator->bypass = enable;
+
+ mutex_unlock(&rdev->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_allow_bypass);
+
+/**
* regulator_register_notifier - register regulator event notifier
* @regulator: regulator source
* @nb: notifier block
@@ -3011,7 +3142,8 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
/* some attributes need specific methods to be displayed */
if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) ||
- (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0)) {
+ (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0) ||
+ (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0)) {
status = device_create_file(dev, &dev_attr_microvolts);
if (status < 0)
return status;
@@ -3036,6 +3168,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
if (status < 0)
return status;
}
+ if (ops->get_bypass) {
+ status = device_create_file(dev, &dev_attr_bypass);
+ if (status < 0)
+ return status;
+ }
/* some attributes are type-specific */
if (rdev->desc->type == REGULATOR_CURRENT) {
@@ -3124,6 +3261,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
&rdev->use_count);
debugfs_create_u32("open_count", 0444, rdev->debugfs,
&rdev->open_count);
+ debugfs_create_u32("bypass_count", 0444, rdev->debugfs,
+ &rdev->bypass_count);
}
/**
@@ -3189,8 +3328,10 @@ regulator_register(const struct regulator_desc *regulator_desc,
rdev->desc = regulator_desc;
if (config->regmap)
rdev->regmap = config->regmap;
- else
+ else if (dev_get_regmap(dev, NULL))
rdev->regmap = dev_get_regmap(dev, NULL);
+ else if (dev->parent)
+ rdev->regmap = dev_get_regmap(dev->parent, NULL);
INIT_LIST_HEAD(&rdev->consumer_list);
INIT_LIST_HEAD(&rdev->list);
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c
index 903299c..27355b1 100644
--- a/drivers/regulator/da9052-regulator.c
+++ b/drivers/regulator/da9052-regulator.c
@@ -133,8 +133,8 @@ static int da9052_dcdc_set_current_limit(struct regulator_dev *rdev, int min_uA,
max_uA < da9052_current_limits[row][DA9052_MIN_UA])
return -EINVAL;
- for (i = 0; i < DA9052_CURRENT_RANGE; i++) {
- if (min_uA <= da9052_current_limits[row][i]) {
+ for (i = DA9052_CURRENT_RANGE - 1; i >= 0; i--) {
+ if (da9052_current_limits[row][i] <= max_uA) {
reg_val = i;
break;
}
diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c
index 86f655c..03a1d7c 100644
--- a/drivers/regulator/dummy.c
+++ b/drivers/regulator/dummy.c
@@ -30,7 +30,7 @@ static struct regulator_init_data dummy_initdata;
static struct regulator_ops dummy_ops;
static struct regulator_desc dummy_desc = {
- .name = "dummy",
+ .name = "regulator-dummy",
.id = -1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
new file mode 100644
index 0000000..339f4d7
--- /dev/null
+++ b/drivers/regulator/fan53555.c
@@ -0,0 +1,322 @@
+/*
+ * FAN53555 Fairchild Digitally Programmable TinyBuck Regulator Driver.
+ *
+ * Supported Part Numbers:
+ * FAN53555UC00X/01X/03X/04X/05X
+ *
+ * Copyright (c) 2012 Marvell Technology Ltd.
+ * Yunfan Zhang <yfzhang@marvell.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/regulator/fan53555.h>
+
+/* Voltage setting */
+#define FAN53555_VSEL0 0x00
+#define FAN53555_VSEL1 0x01
+/* Control register */
+#define FAN53555_CONTROL 0x02
+/* IC Type */
+#define FAN53555_ID1 0x03
+/* IC mask version */
+#define FAN53555_ID2 0x04
+/* Monitor register */
+#define FAN53555_MONITOR 0x05
+
+/* VSEL bit definitions */
+#define VSEL_BUCK_EN (1 << 7)
+#define VSEL_MODE (1 << 6)
+#define VSEL_NSEL_MASK 0x3F
+/* Chip ID and Verison */
+#define DIE_ID 0x0F /* ID1 */
+#define DIE_REV 0x0F /* ID2 */
+/* Control bit definitions */
+#define CTL_OUTPUT_DISCHG (1 << 7)
+#define CTL_SLEW_MASK (0x7 << 4)
+#define CTL_SLEW_SHIFT 4
+#define CTL_RESET (1 << 2)
+
+#define FAN53555_NVOLTAGES 64 /* Numbers of voltages */
+
+/* IC Type */
+enum {
+ FAN53555_CHIP_ID_00 = 0,
+ FAN53555_CHIP_ID_01,
+ FAN53555_CHIP_ID_02,
+ FAN53555_CHIP_ID_03,
+ FAN53555_CHIP_ID_04,
+ FAN53555_CHIP_ID_05,
+};
+
+struct fan53555_device_info {
+ struct regmap *regmap;
+ struct device *dev;
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct regulator_init_data *regulator;
+ /* IC Type and Rev */
+ int chip_id;
+ int chip_rev;
+ /* Voltage setting register */
+ unsigned int vol_reg;
+ unsigned int sleep_reg;
+ /* Voltage range and step(linear) */
+ unsigned int vsel_min;
+ unsigned int vsel_step;
+ /* Voltage slew rate limiting */
+ unsigned int slew_rate;
+ /* Sleep voltage cache */
+ unsigned int sleep_vol_cache;
+};
+
+static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (di->sleep_vol_cache == uV)
+ return 0;
+ ret = regulator_map_voltage_linear(rdev, uV, uV);
+ if (ret < 0)
+ return -EINVAL;
+ ret = regmap_update_bits(di->regmap, di->sleep_reg,
+ VSEL_NSEL_MASK, ret);
+ if (ret < 0)
+ return -EINVAL;
+ /* Cache the sleep voltage setting.
+ * Might not be the real voltage which is rounded */
+ di->sleep_vol_cache = uV;
+
+ return 0;
+}
+
+static int fan53555_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ regmap_update_bits(di->regmap, di->vol_reg,
+ VSEL_MODE, VSEL_MODE);
+ break;
+ case REGULATOR_MODE_NORMAL:
+ regmap_update_bits(di->regmap, di->vol_reg, VSEL_MODE, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static unsigned int fan53555_get_mode(struct regulator_dev *rdev)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int ret = 0;
+
+ ret = regmap_read(di->regmap, di->vol_reg, &val);
+ if (ret < 0)
+ return ret;
+ if (val & VSEL_MODE)
+ return REGULATOR_MODE_FAST;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops fan53555_regulator_ops = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_suspend_voltage = fan53555_set_suspend_voltage,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_mode = fan53555_set_mode,
+ .get_mode = fan53555_get_mode,
+};
+
+/* For 00,01,03,05 options:
+ * VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V.
+ * For 04 option:
+ * VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V.
+ * */
+static int fan53555_device_setup(struct fan53555_device_info *di,
+ struct fan53555_platform_data *pdata)
+{
+ unsigned int reg, data, mask;
+
+ /* Setup voltage control register */
+ switch (pdata->sleep_vsel_id) {
+ case FAN53555_VSEL_ID_0:
+ di->sleep_reg = FAN53555_VSEL0;
+ di->vol_reg = FAN53555_VSEL1;
+ break;
+ case FAN53555_VSEL_ID_1:
+ di->sleep_reg = FAN53555_VSEL1;
+ di->vol_reg = FAN53555_VSEL0;
+ break;
+ default:
+ dev_err(di->dev, "Invalid VSEL ID!\n");
+ return -EINVAL;
+ }
+ /* Init voltage range and step */
+ switch (di->chip_id) {
+ case FAN53555_CHIP_ID_00:
+ case FAN53555_CHIP_ID_01:
+ case FAN53555_CHIP_ID_03:
+ case FAN53555_CHIP_ID_05:
+ di->vsel_min = 600000;
+ di->vsel_step = 10000;
+ break;
+ case FAN53555_CHIP_ID_04:
+ di->vsel_min = 603000;
+ di->vsel_step = 12826;
+ break;
+ default:
+ dev_err(di->dev,
+ "Chip ID[%d]\n not supported!\n", di->chip_id);
+ return -EINVAL;
+ }
+ /* Init slew rate */
+ if (pdata->slew_rate & 0x7)
+ di->slew_rate = pdata->slew_rate;
+ else
+ di->slew_rate = FAN53555_SLEW_RATE_64MV;
+ reg = FAN53555_CONTROL;
+ data = di->slew_rate << CTL_SLEW_SHIFT;
+ mask = CTL_SLEW_MASK;
+ return regmap_update_bits(di->regmap, reg, mask, data);
+}
+
+static int fan53555_regulator_register(struct fan53555_device_info *di,
+ struct regulator_config *config)
+{
+ struct regulator_desc *rdesc = &di->desc;
+
+ rdesc->name = "fan53555-reg";
+ rdesc->ops = &fan53555_regulator_ops;
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->n_voltages = FAN53555_NVOLTAGES;
+ rdesc->enable_reg = di->vol_reg;
+ rdesc->enable_mask = VSEL_BUCK_EN;
+ rdesc->min_uV = di->vsel_min;
+ rdesc->uV_step = di->vsel_step;
+ rdesc->vsel_reg = di->vol_reg;
+ rdesc->vsel_mask = VSEL_NSEL_MASK;
+ rdesc->owner = THIS_MODULE;
+
+ di->rdev = regulator_register(&di->desc, config);
+ if (IS_ERR(di->rdev))
+ return PTR_ERR(di->rdev);
+ return 0;
+
+}
+
+static struct regmap_config fan53555_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int __devinit fan53555_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct fan53555_device_info *di;
+ struct fan53555_platform_data *pdata;
+ struct regulator_config config = { };
+ unsigned int val;
+ int ret;
+
+ pdata = client->dev.platform_data;
+ if (!pdata || !pdata->regulator) {
+ dev_err(&client->dev, "Platform data not found!\n");
+ return -ENODEV;
+ }
+
+ di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info),
+ GFP_KERNEL);
+ if (!di) {
+ dev_err(&client->dev, "Failed to allocate device info data!\n");
+ return -ENOMEM;
+ }
+ di->regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config);
+ if (IS_ERR(di->regmap)) {
+ dev_err(&client->dev, "Failed to allocate regmap!\n");
+ return PTR_ERR(di->regmap);
+ }
+ di->dev = &client->dev;
+ di->regulator = pdata->regulator;
+ i2c_set_clientdata(client, di);
+ /* Get chip ID */
+ ret = regmap_read(di->regmap, FAN53555_ID1, &val);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to get chip ID!\n");
+ return -ENODEV;
+ }
+ di->chip_id = val & DIE_ID;
+ /* Get chip revision */
+ ret = regmap_read(di->regmap, FAN53555_ID2, &val);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to get chip Rev!\n");
+ return -ENODEV;
+ }
+ di->chip_rev = val & DIE_REV;
+ dev_info(&client->dev, "FAN53555 Option[%d] Rev[%d] Detected!\n",
+ di->chip_id, di->chip_rev);
+ /* Device init */
+ ret = fan53555_device_setup(di, pdata);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to setup device!\n");
+ return ret;
+ }
+ /* Register regulator */
+ config.dev = di->dev;
+ config.init_data = di->regulator;
+ config.regmap = di->regmap;
+ config.driver_data = di;
+ ret = fan53555_regulator_register(di, &config);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to register regulator!\n");
+ return ret;
+
+}
+
+static int __devexit fan53555_regulator_remove(struct i2c_client *client)
+{
+ struct fan53555_device_info *di = i2c_get_clientdata(client);
+
+ regulator_unregister(di->rdev);
+ return 0;
+}
+
+static const struct i2c_device_id fan53555_id[] = {
+ {"fan53555", -1},
+ { },
+};
+
+static struct i2c_driver fan53555_regulator_driver = {
+ .driver = {
+ .name = "fan53555-regulator",
+ },
+ .probe = fan53555_regulator_probe,
+ .remove = __devexit_p(fan53555_regulator_remove),
+ .id_table = fan53555_id,
+};
+
+module_i2c_driver(fan53555_regulator_driver);
+
+MODULE_AUTHOR("Yunfan Zhang <yfzhang@marvell.com>");
+MODULE_DESCRIPTION("FAN53555 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c
index 1d145a0..d8ecf49a 100644
--- a/drivers/regulator/isl6271a-regulator.c
+++ b/drivers/regulator/isl6271a-regulator.c
@@ -73,13 +73,7 @@ static struct regulator_ops isl_core_ops = {
.map_voltage = regulator_map_voltage_linear,
};
-static int isl6271a_get_fixed_voltage(struct regulator_dev *dev)
-{
- return dev->desc->min_uV;
-}
-
static struct regulator_ops isl_fixed_ops = {
- .get_voltage = isl6271a_get_fixed_voltage,
.list_voltage = regulator_list_voltage_linear,
};
diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c
index 212c38e..708f4b6 100644
--- a/drivers/regulator/lp872x.c
+++ b/drivers/regulator/lp872x.c
@@ -86,6 +86,10 @@
#define EXTERN_DVS_USED 0
#define MAX_DELAY 6
+/* Default DVS Mode */
+#define LP8720_DEFAULT_DVS 0
+#define LP8725_DEFAULT_DVS BIT(2)
+
/* dump registers in regmap-debugfs */
#define MAX_REGISTERS 0x0F
@@ -269,9 +273,9 @@ static int lp872x_regulator_enable_time(struct regulator_dev *rdev)
return val > MAX_DELAY ? 0 : val * time_step_us;
}
-static void lp872x_set_dvs(struct lp872x *lp, int gpio)
+static void lp872x_set_dvs(struct lp872x *lp, enum lp872x_dvs_sel dvs_sel,
+ int gpio)
{
- enum lp872x_dvs_sel dvs_sel = lp->pdata->dvs->vsel;
enum lp872x_dvs_state state;
state = dvs_sel == SEL_V1 ? DVS_HIGH : DVS_LOW;
@@ -339,10 +343,10 @@ static int lp872x_buck_set_voltage_sel(struct regulator_dev *rdev,
struct lp872x *lp = rdev_get_drvdata(rdev);
enum lp872x_regulator_id buck = rdev_get_id(rdev);
u8 addr, mask = LP872X_VOUT_M;
- struct lp872x_dvs *dvs = lp->pdata->dvs;
+ struct lp872x_dvs *dvs = lp->pdata ? lp->pdata->dvs : NULL;
if (dvs && gpio_is_valid(dvs->gpio))
- lp872x_set_dvs(lp, dvs->gpio);
+ lp872x_set_dvs(lp, dvs->vsel, dvs->gpio);
addr = lp872x_select_buck_vout_addr(lp, buck);
if (!lp872x_is_valid_buck_addr(addr))
@@ -374,8 +378,8 @@ static int lp8725_buck_set_current_limit(struct regulator_dev *rdev,
{
struct lp872x *lp = rdev_get_drvdata(rdev);
enum lp872x_regulator_id buck = rdev_get_id(rdev);
- int i, max = ARRAY_SIZE(lp8725_buck_uA);
- u8 addr, val;
+ int i;
+ u8 addr;
switch (buck) {
case LP8725_ID_BUCK1:
@@ -388,17 +392,15 @@ static int lp8725_buck_set_current_limit(struct regulator_dev *rdev,
return -EINVAL;
}
- for (i = 0 ; i < max ; i++)
+ for (i = ARRAY_SIZE(lp8725_buck_uA) - 1 ; i >= 0; i--) {
if (lp8725_buck_uA[i] >= min_uA &&
lp8725_buck_uA[i] <= max_uA)
- break;
-
- if (i == max)
- return -EINVAL;
-
- val = i << LP8725_BUCK_CL_S;
+ return lp872x_update_bits(lp, addr,
+ LP8725_BUCK_CL_M,
+ i << LP8725_BUCK_CL_S);
+ }
- return lp872x_update_bits(lp, addr, LP8725_BUCK_CL_M, val);
+ return -EINVAL;
}
static int lp8725_buck_get_current_limit(struct regulator_dev *rdev)
@@ -727,39 +729,16 @@ static struct regulator_desc lp8725_regulator_desc[] = {
},
};
-static int lp872x_check_dvs_validity(struct lp872x *lp)
-{
- struct lp872x_dvs *dvs = lp->pdata->dvs;
- u8 val = 0;
- int ret;
-
- ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val);
- if (ret)
- return ret;
-
- ret = 0;
- if (lp->chipid == LP8720) {
- if (val & LP8720_EXT_DVS_M)
- ret = dvs ? 0 : -EINVAL;
- } else {
- if ((val & LP8725_DVS1_M) == EXTERN_DVS_USED)
- ret = dvs ? 0 : -EINVAL;
- }
-
- return ret;
-}
-
static int lp872x_init_dvs(struct lp872x *lp)
{
int ret, gpio;
- struct lp872x_dvs *dvs = lp->pdata->dvs;
+ struct lp872x_dvs *dvs = lp->pdata ? lp->pdata->dvs : NULL;
enum lp872x_dvs_state pinstate;
+ u8 mask[] = { LP8720_EXT_DVS_M, LP8725_DVS1_M | LP8725_DVS2_M };
+ u8 default_dvs_mode[] = { LP8720_DEFAULT_DVS, LP8725_DEFAULT_DVS };
- ret = lp872x_check_dvs_validity(lp);
- if (ret) {
- dev_warn(lp->dev, "invalid dvs data: %d\n", ret);
- return ret;
- }
+ if (!dvs)
+ goto set_default_dvs_mode;
gpio = dvs->gpio;
if (!gpio_is_valid(gpio)) {
@@ -778,6 +757,10 @@ static int lp872x_init_dvs(struct lp872x *lp)
lp->dvs_gpio = gpio;
return 0;
+
+set_default_dvs_mode:
+ return lp872x_update_bits(lp, LP872X_GENERAL_CFG, mask[lp->chipid],
+ default_dvs_mode[lp->chipid]);
}
static int lp872x_config(struct lp872x *lp)
@@ -785,24 +768,29 @@ static int lp872x_config(struct lp872x *lp)
struct lp872x_platform_data *pdata = lp->pdata;
int ret;
- if (!pdata->update_config)
- return 0;
+ if (!pdata || !pdata->update_config)
+ goto init_dvs;
ret = lp872x_write_byte(lp, LP872X_GENERAL_CFG, pdata->general_config);
if (ret)
return ret;
+init_dvs:
return lp872x_init_dvs(lp);
}
static struct regulator_init_data
*lp872x_find_regulator_init_data(int id, struct lp872x *lp)
{
+ struct lp872x_platform_data *pdata = lp->pdata;
int i;
+ if (!pdata)
+ return NULL;
+
for (i = 0; i < lp->num_regulators; i++) {
- if (lp->pdata->regulator_data[i].id == id)
- return lp->pdata->regulator_data[i].init_data;
+ if (pdata->regulator_data[i].id == id)
+ return pdata->regulator_data[i].init_data;
}
return NULL;
@@ -863,18 +851,12 @@ static const struct regmap_config lp872x_regmap_config = {
static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
{
struct lp872x *lp;
- struct lp872x_platform_data *pdata = cl->dev.platform_data;
int ret, size, num_regulators;
const int lp872x_num_regulators[] = {
[LP8720] = LP8720_NUM_REGULATORS,
[LP8725] = LP8725_NUM_REGULATORS,
};
- if (!pdata) {
- dev_err(&cl->dev, "no platform data\n");
- return -EINVAL;
- }
-
lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL);
if (!lp)
goto err_mem;
@@ -894,7 +876,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
}
lp->dev = &cl->dev;
- lp->pdata = pdata;
+ lp->pdata = cl->dev.platform_data;
lp->chipid = id->driver_data;
lp->num_regulators = num_regulators;
i2c_set_clientdata(cl, lp);
diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c
index 6356e82..ba3e0aa 100644
--- a/drivers/regulator/lp8788-buck.c
+++ b/drivers/regulator/lp8788-buck.c
@@ -69,6 +69,9 @@
#define PIN_HIGH 1
#define ENABLE_TIME_USEC 32
+#define BUCK_FPWM_MASK(x) (1 << (x))
+#define BUCK_FPWM_SHIFT(x) (x)
+
enum lp8788_dvs_state {
DVS_LOW = GPIOF_OUT_INIT_LOW,
DVS_HIGH = GPIOF_OUT_INIT_HIGH,
@@ -86,15 +89,9 @@ enum lp8788_buck_id {
BUCK4,
};
-struct lp8788_pwm_map {
- u8 mask;
- u8 shift;
-};
-
struct lp8788_buck {
struct lp8788 *lp;
struct regulator_dev *regulator;
- struct lp8788_pwm_map *pmap;
void *dvs;
};
@@ -106,29 +103,6 @@ static const int lp8788_buck_vtbl[] = {
1950000, 2000000,
};
-/* buck pwm mode selection : used for set/get_mode in regulator ops
- * @forced pwm : fast mode
- * @auto pwm : normal mode
- */
-static struct lp8788_pwm_map buck_pmap[] = {
- [BUCK1] = {
- .mask = LP8788_FPWM_BUCK1_M,
- .shift = LP8788_FPWM_BUCK1_S,
- },
- [BUCK2] = {
- .mask = LP8788_FPWM_BUCK2_M,
- .shift = LP8788_FPWM_BUCK2_S,
- },
- [BUCK3] = {
- .mask = LP8788_FPWM_BUCK3_M,
- .shift = LP8788_FPWM_BUCK3_S,
- },
- [BUCK4] = {
- .mask = LP8788_FPWM_BUCK4_M,
- .shift = LP8788_FPWM_BUCK4_S,
- },
-};
-
static const u8 buck1_vout_addr[] = {
LP8788_BUCK1_VOUT0, LP8788_BUCK1_VOUT1,
LP8788_BUCK1_VOUT2, LP8788_BUCK1_VOUT3,
@@ -347,41 +321,37 @@ static int lp8788_buck_enable_time(struct regulator_dev *rdev)
static int lp8788_buck_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
struct lp8788_buck *buck = rdev_get_drvdata(rdev);
- struct lp8788_pwm_map *pmap = buck->pmap;
- u8 val;
-
- if (!pmap)
- return -EINVAL;
+ enum lp8788_buck_id id = rdev_get_id(rdev);
+ u8 mask, val;
+ mask = BUCK_FPWM_MASK(id);
switch (mode) {
case REGULATOR_MODE_FAST:
- val = LP8788_FORCE_PWM << pmap->shift;
+ val = LP8788_FORCE_PWM << BUCK_FPWM_SHIFT(id);
break;
case REGULATOR_MODE_NORMAL:
- val = LP8788_AUTO_PWM << pmap->shift;
+ val = LP8788_AUTO_PWM << BUCK_FPWM_SHIFT(id);
break;
default:
return -EINVAL;
}
- return lp8788_update_bits(buck->lp, LP8788_BUCK_PWM, pmap->mask, val);
+ return lp8788_update_bits(buck->lp, LP8788_BUCK_PWM, mask, val);
}
static unsigned int lp8788_buck_get_mode(struct regulator_dev *rdev)
{
struct lp8788_buck *buck = rdev_get_drvdata(rdev);
- struct lp8788_pwm_map *pmap = buck->pmap;
+ enum lp8788_buck_id id = rdev_get_id(rdev);
u8 val;
int ret;
- if (!pmap)
- return -EINVAL;
-
ret = lp8788_read_byte(buck->lp, LP8788_BUCK_PWM, &val);
if (ret)
return ret;
- return val & pmap->mask ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
+ return val & BUCK_FPWM_MASK(id) ?
+ REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
}
static struct regulator_ops lp8788_buck12_ops = {
@@ -459,27 +429,6 @@ static struct regulator_desc lp8788_buck_desc[] = {
},
};
-static int lp8788_set_default_dvs_ctrl_mode(struct lp8788 *lp,
- enum lp8788_buck_id id)
-{
- u8 mask, val;
-
- switch (id) {
- case BUCK1:
- mask = LP8788_BUCK1_DVS_SEL_M;
- val = LP8788_BUCK1_DVS_I2C;
- break;
- case BUCK2:
- mask = LP8788_BUCK2_DVS_SEL_M;
- val = LP8788_BUCK2_DVS_I2C;
- break;
- default:
- return 0;
- }
-
- return lp8788_update_bits(lp, LP8788_BUCK_DVS_SEL, mask, val);
-}
-
static int _gpio_request(struct lp8788_buck *buck, int gpio, char *name)
{
struct device *dev = buck->lp->dev;
@@ -530,6 +479,7 @@ static int lp8788_init_dvs(struct lp8788_buck *buck, enum lp8788_buck_id id)
struct lp8788_platform_data *pdata = buck->lp->pdata;
u8 mask[] = { LP8788_BUCK1_DVS_SEL_M, LP8788_BUCK2_DVS_SEL_M };
u8 val[] = { LP8788_BUCK1_DVS_PIN, LP8788_BUCK2_DVS_PIN };
+ u8 default_dvs_mode[] = { LP8788_BUCK1_DVS_I2C, LP8788_BUCK2_DVS_I2C };
/* no dvs for buck3, 4 */
if (id == BUCK3 || id == BUCK4)
@@ -550,7 +500,8 @@ static int lp8788_init_dvs(struct lp8788_buck *buck, enum lp8788_buck_id id)
val[id]);
set_default_dvs_mode:
- return lp8788_set_default_dvs_ctrl_mode(buck->lp, id);
+ return lp8788_update_bits(buck->lp, LP8788_BUCK_DVS_SEL, mask[id],
+ default_dvs_mode[id]);
}
static __devinit int lp8788_buck_probe(struct platform_device *pdev)
@@ -567,7 +518,6 @@ static __devinit int lp8788_buck_probe(struct platform_device *pdev)
return -ENOMEM;
buck->lp = lp;
- buck->pmap = &buck_pmap[id];
ret = lp8788_init_dvs(buck, id);
if (ret)
diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c
index d2122e4..6796eeb 100644
--- a/drivers/regulator/lp8788-ldo.c
+++ b/drivers/regulator/lp8788-ldo.c
@@ -496,6 +496,7 @@ static struct regulator_desc lp8788_dldo_desc[] = {
.name = "dldo12",
.id = DLDO12,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_B,
@@ -521,6 +522,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo2",
.id = ALDO2,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_B,
@@ -530,6 +532,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo3",
.id = ALDO3,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_B,
@@ -539,6 +542,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo4",
.id = ALDO4,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_B,
@@ -548,6 +552,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo5",
.id = ALDO5,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_C,
@@ -583,6 +588,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo8",
.id = ALDO8,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_C,
@@ -592,6 +598,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo9",
.id = ALDO9,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_C,
@@ -601,6 +608,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo10",
.id = ALDO10,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_C,
diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c
index c564af6..2a67d08 100644
--- a/drivers/regulator/max77686.c
+++ b/drivers/regulator/max77686.c
@@ -66,7 +66,7 @@ enum max77686_ramp_rate {
};
struct max77686_data {
- struct regulator_dev **rdev;
+ struct regulator_dev *rdev[MAX77686_REGULATORS];
};
static int max77686_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
@@ -265,6 +265,7 @@ static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev,
rmatch.of_node = NULL;
of_regulator_match(iodev->dev, regulators_np, &rmatch, 1);
rdata[i].initdata = rmatch.init_data;
+ rdata[i].of_node = rmatch.of_node;
}
pdata->regulators = rdata;
@@ -283,10 +284,8 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev)
{
struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev);
- struct regulator_dev **rdev;
struct max77686_data *max77686;
- int i, size;
- int ret = 0;
+ int i, ret = 0;
struct regulator_config config = { };
dev_dbg(&pdev->dev, "%s\n", __func__);
@@ -313,45 +312,38 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev)
if (!max77686)
return -ENOMEM;
- size = sizeof(struct regulator_dev *) * MAX77686_REGULATORS;
- max77686->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
- if (!max77686->rdev)
- return -ENOMEM;
-
- rdev = max77686->rdev;
config.dev = &pdev->dev;
config.regmap = iodev->regmap;
platform_set_drvdata(pdev, max77686);
for (i = 0; i < MAX77686_REGULATORS; i++) {
config.init_data = pdata->regulators[i].initdata;
+ config.of_node = pdata->regulators[i].of_node;
- rdev[i] = regulator_register(&regulators[i], &config);
- if (IS_ERR(rdev[i])) {
- ret = PTR_ERR(rdev[i]);
+ max77686->rdev[i] = regulator_register(&regulators[i], &config);
+ if (IS_ERR(max77686->rdev[i])) {
+ ret = PTR_ERR(max77686->rdev[i]);
dev_err(&pdev->dev,
"regulator init failed for %d\n", i);
- rdev[i] = NULL;
- goto err;
+ max77686->rdev[i] = NULL;
+ goto err;
}
}
return 0;
err:
while (--i >= 0)
- regulator_unregister(rdev[i]);
+ regulator_unregister(max77686->rdev[i]);
return ret;
}
static int __devexit max77686_pmic_remove(struct platform_device *pdev)
{
struct max77686_data *max77686 = platform_get_drvdata(pdev);
- struct regulator_dev **rdev = max77686->rdev;
int i;
for (i = 0; i < MAX77686_REGULATORS; i++)
- if (rdev[i])
- regulator_unregister(rdev[i]);
+ regulator_unregister(max77686->rdev[i]);
return 0;
}
diff --git a/drivers/regulator/max8907-regulator.c b/drivers/regulator/max8907-regulator.c
new file mode 100644
index 0000000..af76075
--- /dev/null
+++ b/drivers/regulator/max8907-regulator.c
@@ -0,0 +1,408 @@
+/*
+ * max8907-regulator.c -- support regulators in max8907
+ *
+ * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com>
+ * Copyright (C) 2010-2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Portions based on drivers/regulator/tps65910-regulator.c,
+ * Copyright 2010 Texas Instruments Inc.
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8907.h>
+#include <linux/module.h>
+#include <linux/of.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>
+
+#define MAX8907_II2RR_VERSION_MASK 0xF0
+#define MAX8907_II2RR_VERSION_REV_A 0x00
+#define MAX8907_II2RR_VERSION_REV_B 0x10
+#define MAX8907_II2RR_VERSION_REV_C 0x30
+
+struct max8907_regulator {
+ struct regulator_desc desc[MAX8907_NUM_REGULATORS];
+ struct regulator_dev *rdev[MAX8907_NUM_REGULATORS];
+};
+
+#define REG_MBATT() \
+ [MAX8907_MBATT] = { \
+ .name = "MBATT", \
+ .supply_name = "mbatt", \
+ .id = MAX8907_MBATT, \
+ .ops = &max8907_mbatt_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }
+
+#define REG_LDO(ids, supply, base, min, max, step) \
+ [MAX8907_##ids] = { \
+ .name = #ids, \
+ .supply_name = supply, \
+ .id = MAX8907_##ids, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .ops = &max8907_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .vsel_reg = (base) + MAX8907_VOUT, \
+ .vsel_mask = 0x3f, \
+ .enable_reg = (base) + MAX8907_CTL, \
+ .enable_mask = MAX8907_MASK_LDO_EN, \
+ }
+
+#define REG_FIXED(ids, supply, voltage) \
+ [MAX8907_##ids] = { \
+ .name = #ids, \
+ .supply_name = supply, \
+ .id = MAX8907_##ids, \
+ .n_voltages = 1, \
+ .ops = &max8907_fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = (voltage), \
+ }
+
+#define REG_OUT5V(ids, supply, base, voltage) \
+ [MAX8907_##ids] = { \
+ .name = #ids, \
+ .supply_name = supply, \
+ .id = MAX8907_##ids, \
+ .n_voltages = 1, \
+ .ops = &max8907_out5v_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = (voltage), \
+ .enable_reg = (base), \
+ .enable_mask = MAX8907_MASK_OUT5V_EN, \
+ }
+
+#define REG_BBAT(ids, supply, base, min, max, step) \
+ [MAX8907_##ids] = { \
+ .name = #ids, \
+ .supply_name = supply, \
+ .id = MAX8907_##ids, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .ops = &max8907_bbat_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .vsel_reg = (base), \
+ .vsel_mask = MAX8907_MASK_VBBATTCV, \
+ }
+
+#define LDO_750_50(id, supply, base) REG_LDO(id, supply, (base), \
+ 750000, 3900000, 50000)
+#define LDO_650_25(id, supply, base) REG_LDO(id, supply, (base), \
+ 650000, 2225000, 25000)
+
+static struct regulator_ops max8907_mbatt_ops = {
+};
+
+static struct regulator_ops max8907_ldo_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static struct regulator_ops max8907_ldo_hwctl_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops max8907_fixed_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops max8907_out5v_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static struct regulator_ops max8907_out5v_hwctl_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops max8907_bbat_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_desc max8907_regulators[] = {
+ REG_MBATT(),
+ REG_LDO(SD1, "in-v1", MAX8907_REG_SDCTL1, 650000, 2225000, 25000),
+ REG_LDO(SD2, "in-v2", MAX8907_REG_SDCTL2, 637500, 1425000, 12500),
+ REG_LDO(SD3, "in-v3", MAX8907_REG_SDCTL3, 750000, 3900000, 50000),
+ LDO_750_50(LDO1, "in1", MAX8907_REG_LDOCTL1),
+ LDO_650_25(LDO2, "in2", MAX8907_REG_LDOCTL2),
+ LDO_650_25(LDO3, "in3", MAX8907_REG_LDOCTL3),
+ LDO_750_50(LDO4, "in4", MAX8907_REG_LDOCTL4),
+ LDO_750_50(LDO5, "in5", MAX8907_REG_LDOCTL5),
+ LDO_750_50(LDO6, "in6", MAX8907_REG_LDOCTL6),
+ LDO_750_50(LDO7, "in7", MAX8907_REG_LDOCTL7),
+ LDO_750_50(LDO8, "in8", MAX8907_REG_LDOCTL8),
+ LDO_750_50(LDO9, "in9", MAX8907_REG_LDOCTL9),
+ LDO_750_50(LDO10, "in10", MAX8907_REG_LDOCTL10),
+ LDO_750_50(LDO11, "in11", MAX8907_REG_LDOCTL11),
+ LDO_750_50(LDO12, "in12", MAX8907_REG_LDOCTL12),
+ LDO_750_50(LDO13, "in13", MAX8907_REG_LDOCTL13),
+ LDO_750_50(LDO14, "in14", MAX8907_REG_LDOCTL14),
+ LDO_750_50(LDO15, "in15", MAX8907_REG_LDOCTL15),
+ LDO_750_50(LDO16, "in16", MAX8907_REG_LDOCTL16),
+ LDO_650_25(LDO17, "in17", MAX8907_REG_LDOCTL17),
+ LDO_650_25(LDO18, "in18", MAX8907_REG_LDOCTL18),
+ LDO_750_50(LDO19, "in19", MAX8907_REG_LDOCTL19),
+ LDO_750_50(LDO20, "in20", MAX8907_REG_LDOCTL20),
+ REG_OUT5V(OUT5V, "mbatt", MAX8907_REG_OUT5VEN, 5000000),
+ REG_OUT5V(OUT33V, "mbatt", MAX8907_REG_OUT33VEN, 3300000),
+ REG_BBAT(BBAT, "MBATT", MAX8907_REG_BBAT_CNFG,
+ 2400000, 3000000, 200000),
+ REG_FIXED(SDBY, "MBATT", 1200000),
+ REG_FIXED(VRTC, "MBATT", 3300000),
+};
+
+#ifdef CONFIG_OF
+
+#define MATCH(_name, _id) \
+ [MAX8907_##_id] = { \
+ .name = #_name, \
+ .driver_data = (void *)&max8907_regulators[MAX8907_##_id], \
+ }
+
+static struct of_regulator_match max8907_matches[] = {
+ MATCH(mbatt, MBATT),
+ MATCH(sd1, SD1),
+ MATCH(sd2, SD2),
+ MATCH(sd3, SD3),
+ MATCH(ldo1, LDO1),
+ MATCH(ldo2, LDO2),
+ MATCH(ldo3, LDO3),
+ MATCH(ldo4, LDO4),
+ MATCH(ldo5, LDO5),
+ MATCH(ldo6, LDO6),
+ MATCH(ldo7, LDO7),
+ MATCH(ldo8, LDO8),
+ MATCH(ldo9, LDO9),
+ MATCH(ldo10, LDO10),
+ MATCH(ldo11, LDO11),
+ MATCH(ldo12, LDO12),
+ MATCH(ldo13, LDO13),
+ MATCH(ldo14, LDO14),
+ MATCH(ldo15, LDO15),
+ MATCH(ldo16, LDO16),
+ MATCH(ldo17, LDO17),
+ MATCH(ldo18, LDO18),
+ MATCH(ldo19, LDO19),
+ MATCH(ldo20, LDO20),
+ MATCH(out5v, OUT5V),
+ MATCH(out33v, OUT33V),
+ MATCH(bbat, BBAT),
+ MATCH(sdby, SDBY),
+ MATCH(vrtc, VRTC),
+};
+
+static int max8907_regulator_parse_dt(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.parent->of_node;
+ struct device_node *regulators;
+ int ret;
+
+ if (!pdev->dev.parent->of_node)
+ return 0;
+
+ regulators = of_find_node_by_name(np, "regulators");
+ if (!regulators) {
+ dev_err(&pdev->dev, "regulators node not found\n");
+ return -EINVAL;
+ }
+
+ ret = of_regulator_match(pdev->dev.parent, regulators,
+ max8907_matches,
+ ARRAY_SIZE(max8907_matches));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error parsing regulator init data: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline struct regulator_init_data *match_init_data(int index)
+{
+ return max8907_matches[index].init_data;
+}
+
+static inline struct device_node *match_of_node(int index)
+{
+ return max8907_matches[index].of_node;
+}
+#else
+static int max8907_regulator_parse_dt(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static inline struct regulator_init_data *match_init_data(int index)
+{
+ return NULL;
+}
+
+static inline struct device_node *match_of_node(int index)
+{
+ return NULL;
+}
+#endif
+
+static __devinit int max8907_regulator_probe(struct platform_device *pdev)
+{
+ struct max8907 *max8907 = dev_get_drvdata(pdev->dev.parent);
+ struct max8907_platform_data *pdata = dev_get_platdata(max8907->dev);
+ int ret;
+ struct max8907_regulator *pmic;
+ unsigned int val;
+ int i;
+ struct regulator_config config = {};
+ struct regulator_init_data *idata;
+ const char *mbatt_rail_name = NULL;
+
+ ret = max8907_regulator_parse_dt(pdev);
+ if (ret)
+ return ret;
+
+ pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic) {
+ dev_err(&pdev->dev, "Failed to alloc pmic\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, pmic);
+
+ memcpy(pmic->desc, max8907_regulators, sizeof(pmic->desc));
+
+ /* Backwards compatibility with MAX8907B; SD1 uses different voltages */
+ regmap_read(max8907->regmap_gen, MAX8907_REG_II2RR, &val);
+ if ((val & MAX8907_II2RR_VERSION_MASK) ==
+ MAX8907_II2RR_VERSION_REV_B) {
+ pmic->desc[MAX8907_SD1].min_uV = 637500;
+ pmic->desc[MAX8907_SD1].uV_step = 12500;
+ pmic->desc[MAX8907_SD1].n_voltages =
+ (1425000 - 637500) / 12500 + 1;
+ }
+
+ for (i = 0; i < MAX8907_NUM_REGULATORS; i++) {
+ config.dev = pdev->dev.parent;
+ if (pdata)
+ idata = pdata->init_data[i];
+ else
+ idata = match_init_data(i);
+ config.init_data = idata;
+ config.driver_data = pmic;
+ config.regmap = max8907->regmap_gen;
+ config.of_node = match_of_node(i);
+
+ switch (pmic->desc[i].id) {
+ case MAX8907_MBATT:
+ if (idata && idata->constraints.name)
+ mbatt_rail_name = idata->constraints.name;
+ else
+ mbatt_rail_name = pmic->desc[i].name;
+ break;
+ case MAX8907_BBAT:
+ case MAX8907_SDBY:
+ case MAX8907_VRTC:
+ idata->supply_regulator = mbatt_rail_name;
+ break;
+ }
+
+ if (pmic->desc[i].ops == &max8907_ldo_ops) {
+ regmap_read(config.regmap, pmic->desc[i].enable_reg,
+ &val);
+ if ((val & MAX8907_MASK_LDO_SEQ) !=
+ MAX8907_MASK_LDO_SEQ)
+ pmic->desc[i].ops = &max8907_ldo_hwctl_ops;
+ } else if (pmic->desc[i].ops == &max8907_out5v_ops) {
+ regmap_read(config.regmap, pmic->desc[i].enable_reg,
+ &val);
+ if ((val & (MAX8907_MASK_OUT5V_VINEN |
+ MAX8907_MASK_OUT5V_ENSRC)) !=
+ MAX8907_MASK_OUT5V_ENSRC)
+ pmic->desc[i].ops = &max8907_out5v_hwctl_ops;
+ }
+
+ pmic->rdev[i] = regulator_register(&pmic->desc[i], &config);
+ if (IS_ERR(pmic->rdev[i])) {
+ dev_err(&pdev->dev,
+ "failed to register %s regulator\n",
+ pmic->desc[i].name);
+ ret = PTR_ERR(pmic->rdev[i]);
+ goto err_unregister_regulator;
+ }
+ }
+
+ return 0;
+
+err_unregister_regulator:
+ while (--i >= 0)
+ regulator_unregister(pmic->rdev[i]);
+ return ret;
+}
+
+static __devexit int max8907_regulator_remove(struct platform_device *pdev)
+{
+ struct max8907_regulator *pmic = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < MAX8907_NUM_REGULATORS; i++)
+ regulator_unregister(pmic->rdev[i]);
+
+ return 0;
+}
+
+static struct platform_driver max8907_regulator_driver = {
+ .driver = {
+ .name = "max8907-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8907_regulator_probe,
+ .remove = __devexit_p(max8907_regulator_remove),
+};
+
+static int __init max8907_regulator_init(void)
+{
+ return platform_driver_register(&max8907_regulator_driver);
+}
+
+subsys_initcall(max8907_regulator_init);
+
+static void __exit max8907_reg_exit(void)
+{
+ platform_driver_unregister(&max8907_regulator_driver);
+}
+
+module_exit(max8907_reg_exit);
+
+MODULE_DESCRIPTION("MAX8907 regulator driver");
+MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:max8907-regulator");
diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c
index 4932e34..0801a6d 100644
--- a/drivers/regulator/mc13783-regulator.c
+++ b/drivers/regulator/mc13783-regulator.c
@@ -21,6 +21,30 @@
#include <linux/module.h>
#include "mc13xxx.h"
+#define MC13783_REG_SWITCHERS0 24
+/* Enable does not exist for SW1A */
+#define MC13783_REG_SWITCHERS0_SW1AEN 0
+#define MC13783_REG_SWITCHERS0_SW1AVSEL 0
+#define MC13783_REG_SWITCHERS0_SW1AVSEL_M (63 << 0)
+
+#define MC13783_REG_SWITCHERS1 25
+/* Enable does not exist for SW1B */
+#define MC13783_REG_SWITCHERS1_SW1BEN 0
+#define MC13783_REG_SWITCHERS1_SW1BVSEL 0
+#define MC13783_REG_SWITCHERS1_SW1BVSEL_M (63 << 0)
+
+#define MC13783_REG_SWITCHERS2 26
+/* Enable does not exist for SW2A */
+#define MC13783_REG_SWITCHERS2_SW2AEN 0
+#define MC13783_REG_SWITCHERS2_SW2AVSEL 0
+#define MC13783_REG_SWITCHERS2_SW2AVSEL_M (63 << 0)
+
+#define MC13783_REG_SWITCHERS3 27
+/* Enable does not exist for SW2B */
+#define MC13783_REG_SWITCHERS3_SW2BEN 0
+#define MC13783_REG_SWITCHERS3_SW2BVSEL 0
+#define MC13783_REG_SWITCHERS3_SW2BVSEL_M (63 << 0)
+
#define MC13783_REG_SWITCHERS5 29
#define MC13783_REG_SWITCHERS5_SW3EN (1 << 20)
#define MC13783_REG_SWITCHERS5_SW3VSEL 18
@@ -93,6 +117,44 @@
/* Voltage Values */
+static const int mc13783_sw1x_val[] = {
+ 900000, 925000, 950000, 975000,
+ 1000000, 1025000, 1050000, 1075000,
+ 1100000, 1125000, 1150000, 1175000,
+ 1200000, 1225000, 1250000, 1275000,
+ 1300000, 1325000, 1350000, 1375000,
+ 1400000, 1425000, 1450000, 1475000,
+ 1500000, 1525000, 1550000, 1575000,
+ 1600000, 1625000, 1650000, 1675000,
+ 1700000, 1700000, 1700000, 1700000,
+ 1800000, 1800000, 1800000, 1800000,
+ 1850000, 1850000, 1850000, 1850000,
+ 2000000, 2000000, 2000000, 2000000,
+ 2100000, 2100000, 2100000, 2100000,
+ 2200000, 2200000, 2200000, 2200000,
+ 2200000, 2200000, 2200000, 2200000,
+ 2200000, 2200000, 2200000, 2200000,
+};
+
+static const int mc13783_sw2x_val[] = {
+ 900000, 925000, 950000, 975000,
+ 1000000, 1025000, 1050000, 1075000,
+ 1100000, 1125000, 1150000, 1175000,
+ 1200000, 1225000, 1250000, 1275000,
+ 1300000, 1325000, 1350000, 1375000,
+ 1400000, 1425000, 1450000, 1475000,
+ 1500000, 1525000, 1550000, 1575000,
+ 1600000, 1625000, 1650000, 1675000,
+ 1700000, 1700000, 1700000, 1700000,
+ 1800000, 1800000, 1800000, 1800000,
+ 1900000, 1900000, 1900000, 1900000,
+ 2000000, 2000000, 2000000, 2000000,
+ 2100000, 2100000, 2100000, 2100000,
+ 2200000, 2200000, 2200000, 2200000,
+ 2200000, 2200000, 2200000, 2200000,
+ 2200000, 2200000, 2200000, 2200000,
+};
+
static const unsigned int mc13783_sw3_val[] = {
5000000, 5000000, 5000000, 5500000,
};
@@ -188,6 +250,10 @@ static struct regulator_ops mc13783_gpo_regulator_ops;
MC13783_DEFINE(REG, _name, _reg, _vsel_reg, _voltages)
static struct mc13xxx_regulator mc13783_regulators[] = {
+ MC13783_DEFINE_SW(SW1A, SWITCHERS0, SWITCHERS0, mc13783_sw1x_val),
+ MC13783_DEFINE_SW(SW1B, SWITCHERS1, SWITCHERS1, mc13783_sw1x_val),
+ MC13783_DEFINE_SW(SW2A, SWITCHERS2, SWITCHERS2, mc13783_sw2x_val),
+ MC13783_DEFINE_SW(SW2B, SWITCHERS3, SWITCHERS3, mc13783_sw2x_val),
MC13783_DEFINE_SW(SW3, SWITCHERS5, SWITCHERS5, mc13783_sw3_val),
MC13783_FIXED_DEFINE(REG, VAUDIO, REGULATORMODE0, mc13783_vaudio_val),
@@ -238,9 +304,10 @@ static int mc13783_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
BUG_ON(val & ~mask);
+ mc13xxx_lock(priv->mc13xxx);
ret = mc13xxx_reg_read(mc13783, MC13783_REG_POWERMISC, &valread);
if (ret)
- return ret;
+ goto out;
/* Update the stored state for Power Gates. */
priv->powermisc_pwgt_state =
@@ -253,7 +320,10 @@ static int mc13783_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
valread = (valread & ~MC13783_REG_POWERMISC_PWGTSPI_M) |
priv->powermisc_pwgt_state;
- return mc13xxx_reg_write(mc13783, MC13783_REG_POWERMISC, valread);
+ ret = mc13xxx_reg_write(mc13783, MC13783_REG_POWERMISC, valread);
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+ return ret;
}
static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
@@ -261,7 +331,6 @@ static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
int id = rdev_get_id(rdev);
- int ret;
u32 en_val = mc13xxx_regulators[id].enable_bit;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -271,12 +340,8 @@ static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
id == MC13783_REG_PWGT2SPI)
en_val = 0;
- mc13xxx_lock(priv->mc13xxx);
- ret = mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
+ return mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
en_val);
- mc13xxx_unlock(priv->mc13xxx);
-
- return ret;
}
static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
@@ -284,7 +349,6 @@ static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
int id = rdev_get_id(rdev);
- int ret;
u32 dis_val = 0;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -294,12 +358,8 @@ static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
id == MC13783_REG_PWGT2SPI)
dis_val = mc13xxx_regulators[id].enable_bit;
- mc13xxx_lock(priv->mc13xxx);
- ret = mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
+ return mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
dis_val);
- mc13xxx_unlock(priv->mc13xxx);
-
- return ret;
}
static int mc13783_gpo_regulator_is_enabled(struct regulator_dev *rdev)
@@ -330,7 +390,6 @@ static struct regulator_ops mc13783_gpo_regulator_ops = {
.is_enabled = mc13783_gpo_regulator_is_enabled,
.list_voltage = regulator_list_voltage_table,
.set_voltage = mc13xxx_fixed_regulator_set_voltage,
- .get_voltage = mc13xxx_fixed_regulator_get_voltage,
};
static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c
index b388b74..1fa6381 100644
--- a/drivers/regulator/mc13892-regulator.c
+++ b/drivers/regulator/mc13892-regulator.c
@@ -305,9 +305,10 @@ static int mc13892_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
BUG_ON(val & ~mask);
+ mc13xxx_lock(priv->mc13xxx);
ret = mc13xxx_reg_read(mc13892, MC13892_POWERMISC, &valread);
if (ret)
- return ret;
+ goto out;
/* Update the stored state for Power Gates. */
priv->powermisc_pwgt_state =
@@ -320,14 +321,16 @@ static int mc13892_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
valread = (valread & ~MC13892_POWERMISC_PWGTSPI_M) |
priv->powermisc_pwgt_state;
- return mc13xxx_reg_write(mc13892, MC13892_POWERMISC, valread);
+ ret = mc13xxx_reg_write(mc13892, MC13892_POWERMISC, valread);
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+ return ret;
}
static int mc13892_gpo_regulator_enable(struct regulator_dev *rdev)
{
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
- int ret;
u32 en_val = mc13892_regulators[id].enable_bit;
u32 mask = mc13892_regulators[id].enable_bit;
@@ -340,18 +343,13 @@ static int mc13892_gpo_regulator_enable(struct regulator_dev *rdev)
if (id == MC13892_GPO4)
mask |= MC13892_POWERMISC_GPO4ADINEN;
- mc13xxx_lock(priv->mc13xxx);
- ret = mc13892_powermisc_rmw(priv, mask, en_val);
- mc13xxx_unlock(priv->mc13xxx);
-
- return ret;
+ return mc13892_powermisc_rmw(priv, mask, en_val);
}
static int mc13892_gpo_regulator_disable(struct regulator_dev *rdev)
{
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
- int ret;
u32 dis_val = 0;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -360,12 +358,8 @@ static int mc13892_gpo_regulator_disable(struct regulator_dev *rdev)
if (id == MC13892_PWGT1SPI || id == MC13892_PWGT2SPI)
dis_val = mc13892_regulators[id].enable_bit;
- mc13xxx_lock(priv->mc13xxx);
- ret = mc13892_powermisc_rmw(priv, mc13892_regulators[id].enable_bit,
+ return mc13892_powermisc_rmw(priv, mc13892_regulators[id].enable_bit,
dis_val);
- mc13xxx_unlock(priv->mc13xxx);
-
- return ret;
}
static int mc13892_gpo_regulator_is_enabled(struct regulator_dev *rdev)
@@ -396,14 +390,13 @@ static struct regulator_ops mc13892_gpo_regulator_ops = {
.is_enabled = mc13892_gpo_regulator_is_enabled,
.list_voltage = regulator_list_voltage_table,
.set_voltage = mc13xxx_fixed_regulator_set_voltage,
- .get_voltage = mc13xxx_fixed_regulator_get_voltage,
};
-static int mc13892_sw_regulator_get_voltage(struct regulator_dev *rdev)
+static int mc13892_sw_regulator_get_voltage_sel(struct regulator_dev *rdev)
{
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
int ret, id = rdev_get_id(rdev);
- unsigned int val, hi;
+ unsigned int val;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -414,17 +407,11 @@ static int mc13892_sw_regulator_get_voltage(struct regulator_dev *rdev)
if (ret)
return ret;
- hi = val & MC13892_SWITCHERS0_SWxHI;
val = (val & mc13892_regulators[id].vsel_mask)
>> mc13892_regulators[id].vsel_shift;
dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val);
- if (hi)
- val = (25000 * val) + 1100000;
- else
- val = (25000 * val) + 600000;
-
return val;
}
@@ -432,37 +419,25 @@ static int mc13892_sw_regulator_set_voltage_sel(struct regulator_dev *rdev,
unsigned selector)
{
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
- int hi, value, mask, id = rdev_get_id(rdev);
- u32 valread;
+ int volt, mask, id = rdev_get_id(rdev);
+ u32 reg_value;
int ret;
- value = rdev->desc->volt_table[selector];
+ volt = rdev->desc->volt_table[selector];
+ mask = mc13892_regulators[id].vsel_mask;
+ reg_value = selector << mc13892_regulators[id].vsel_shift;
+
+ if (volt > 1375000) {
+ mask |= MC13892_SWITCHERS0_SWxHI;
+ reg_value |= MC13892_SWITCHERS0_SWxHI;
+ } else if (volt < 1100000) {
+ mask |= MC13892_SWITCHERS0_SWxHI;
+ reg_value &= ~MC13892_SWITCHERS0_SWxHI;
+ }
mc13xxx_lock(priv->mc13xxx);
- ret = mc13xxx_reg_read(priv->mc13xxx,
- mc13892_regulators[id].vsel_reg, &valread);
- if (ret)
- goto err;
-
- if (value > 1375000)
- hi = 1;
- else if (value < 1100000)
- hi = 0;
- else
- hi = valread & MC13892_SWITCHERS0_SWxHI;
-
- if (hi) {
- value = (value - 1100000) / 25000;
- value |= MC13892_SWITCHERS0_SWxHI;
- } else
- value = (value - 600000) / 25000;
-
- mask = mc13892_regulators[id].vsel_mask | MC13892_SWITCHERS0_SWxHI;
- valread = (valread & ~mask) |
- (value << mc13892_regulators[id].vsel_shift);
- ret = mc13xxx_reg_write(priv->mc13xxx, mc13892_regulators[id].vsel_reg,
- valread);
-err:
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].reg, mask,
+ reg_value);
mc13xxx_unlock(priv->mc13xxx);
return ret;
@@ -471,7 +446,7 @@ err:
static struct regulator_ops mc13892_sw_regulator_ops = {
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = mc13892_sw_regulator_set_voltage_sel,
- .get_voltage = mc13892_sw_regulator_get_voltage,
+ .get_voltage_sel = mc13892_sw_regulator_get_voltage_sel,
};
static int mc13892_vcam_set_mode(struct regulator_dev *rdev, unsigned int mode)
diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c
index d6eda28..88cbb83 100644
--- a/drivers/regulator/mc13xxx-regulator-core.c
+++ b/drivers/regulator/mc13xxx-regulator-core.c
@@ -143,30 +143,21 @@ int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
__func__, id, min_uV, max_uV);
if (min_uV <= rdev->desc->volt_table[0] &&
- rdev->desc->volt_table[0] <= max_uV)
+ rdev->desc->volt_table[0] <= max_uV) {
+ *selector = 0;
return 0;
- else
+ } else {
return -EINVAL;
+ }
}
EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_set_voltage);
-int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev)
-{
- int id = rdev_get_id(rdev);
-
- dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
-
- return rdev->desc->volt_table[0];
-}
-EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_get_voltage);
-
struct regulator_ops mc13xxx_fixed_regulator_ops = {
.enable = mc13xxx_regulator_enable,
.disable = mc13xxx_regulator_disable,
.is_enabled = mc13xxx_regulator_is_enabled,
.list_voltage = regulator_list_voltage_table,
.set_voltage = mc13xxx_fixed_regulator_set_voltage,
- .get_voltage = mc13xxx_fixed_regulator_get_voltage,
};
EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_ops);
diff --git a/drivers/regulator/mc13xxx.h b/drivers/regulator/mc13xxx.h
index eaff551..06c8903 100644
--- a/drivers/regulator/mc13xxx.h
+++ b/drivers/regulator/mc13xxx.h
@@ -34,7 +34,6 @@ struct mc13xxx_regulator_priv {
extern int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV, unsigned *selector);
-extern int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev);
#ifdef CONFIG_OF
extern int mc13xxx_get_num_regulators_dt(struct platform_device *pdev);
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 3e4106f..6f68491 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -92,16 +92,18 @@ struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
/**
- * of_regulator_match - extract regulator init data when node
- * property "regulator-compatible" matches with the regulator name.
+ * of_regulator_match - extract multiple regulator init data from device tree.
* @dev: device requesting the data
* @node: parent device node of the regulators
* @matches: match table for the regulators
* @num_matches: number of entries in match table
*
- * This function uses a match table specified by the regulator driver and
- * looks up the corresponding init data in the device tree if
- * regulator-compatible matches. Note that the match table is modified
+ * This function uses a match table specified by the regulator driver to
+ * parse regulator init data from the device tree. @node is expected to
+ * contain a set of child nodes, each providing the init data for one
+ * regulator. The data parsed from a child node will be matched to a regulator
+ * based on either the deprecated property regulator-compatible if present,
+ * or otherwise the child node's name. Note that the match table is modified
* in place.
*
* Returns the number of matches found or a negative error code on failure.
@@ -112,26 +114,23 @@ int of_regulator_match(struct device *dev, struct device_node *node,
{
unsigned int count = 0;
unsigned int i;
- const char *regulator_comp;
+ const char *name;
struct device_node *child;
if (!dev || !node)
return -EINVAL;
for_each_child_of_node(node, child) {
- regulator_comp = of_get_property(child,
+ name = of_get_property(child,
"regulator-compatible", NULL);
- if (!regulator_comp) {
- dev_err(dev, "regulator-compatible is missing for node %s\n",
- child->name);
- continue;
- }
+ if (!name)
+ name = child->name;
for (i = 0; i < num_matches; i++) {
struct of_regulator_match *match = &matches[i];
if (match->of_node)
continue;
- if (strcmp(match->name, regulator_comp))
+ if (strcmp(match->name, name))
continue;
match->init_data =
diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c
index 46c7e88..2ba7502 100644
--- a/drivers/regulator/palmas-regulator.c
+++ b/drivers/regulator/palmas-regulator.c
@@ -443,44 +443,6 @@ static int palmas_list_voltage_ldo(struct regulator_dev *dev,
return 850000 + (selector * 50000);
}
-static int palmas_get_voltage_ldo_sel(struct regulator_dev *dev)
-{
- struct palmas_pmic *pmic = rdev_get_drvdata(dev);
- int id = rdev_get_id(dev);
- int selector;
- unsigned int reg;
- unsigned int addr;
-
- addr = palmas_regs_info[id].vsel_addr;
-
- palmas_ldo_read(pmic->palmas, addr, &reg);
-
- selector = reg & PALMAS_LDO1_VOLTAGE_VSEL_MASK;
-
- /* Adjust selector to match list_voltage ranges */
- if (selector > 49)
- selector = 49;
-
- return selector;
-}
-
-static int palmas_set_voltage_ldo_sel(struct regulator_dev *dev,
- unsigned selector)
-{
- struct palmas_pmic *pmic = rdev_get_drvdata(dev);
- int id = rdev_get_id(dev);
- unsigned int reg = 0;
- unsigned int addr;
-
- addr = palmas_regs_info[id].vsel_addr;
-
- reg = selector;
-
- palmas_ldo_write(pmic->palmas, addr, reg);
-
- return 0;
-}
-
static int palmas_map_voltage_ldo(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
@@ -505,8 +467,8 @@ static struct regulator_ops palmas_ops_ldo = {
.is_enabled = palmas_is_enabled_ldo,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
- .get_voltage_sel = palmas_get_voltage_ldo_sel,
- .set_voltage_sel = palmas_set_voltage_ldo_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
.list_voltage = palmas_list_voltage_ldo,
.map_voltage = palmas_map_voltage_ldo,
};
@@ -757,6 +719,9 @@ static __devinit int palmas_probe(struct platform_device *pdev)
pmic->desc[id].type = REGULATOR_VOLTAGE;
pmic->desc[id].owner = THIS_MODULE;
+ pmic->desc[id].vsel_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
+ palmas_regs_info[id].vsel_addr);
+ pmic->desc[id].vsel_mask = PALMAS_LDO1_VOLTAGE_VSEL_MASK;
pmic->desc[id].enable_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
palmas_regs_info[id].ctrl_addr);
pmic->desc[id].enable_mask = PALMAS_LDO1_CTRL_MODE_ACTIVE;
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index 4669dc9..926f9c8 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -24,7 +24,7 @@
#include <linux/mfd/samsung/s2mps11.h>
struct s2mps11_info {
- struct regulator_dev **rdev;
+ struct regulator_dev *rdev[S2MPS11_REGULATOR_MAX];
int ramp_delay2;
int ramp_delay34;
@@ -236,9 +236,8 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
struct regulator_config config = { };
- struct regulator_dev **rdev;
struct s2mps11_info *s2mps11;
- int i, ret, size;
+ int i, ret;
unsigned char ramp_enable, ramp_reg = 0;
if (!pdata) {
@@ -251,13 +250,6 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
if (!s2mps11)
return -ENOMEM;
- size = sizeof(struct regulator_dev *) * S2MPS11_REGULATOR_MAX;
- s2mps11->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
- if (!s2mps11->rdev) {
- return -ENOMEM;
- }
-
- rdev = s2mps11->rdev;
platform_set_drvdata(pdev, s2mps11);
s2mps11->ramp_delay2 = pdata->buck2_ramp_delay;
@@ -297,12 +289,12 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
config.init_data = pdata->regulators[i].initdata;
config.driver_data = s2mps11;
- rdev[i] = regulator_register(&regulators[i], &config);
- if (IS_ERR(rdev[i])) {
- ret = PTR_ERR(rdev[i]);
+ s2mps11->rdev[i] = regulator_register(&regulators[i], &config);
+ if (IS_ERR(s2mps11->rdev[i])) {
+ ret = PTR_ERR(s2mps11->rdev[i]);
dev_err(&pdev->dev, "regulator init failed for %d\n",
i);
- rdev[i] = NULL;
+ s2mps11->rdev[i] = NULL;
goto err;
}
}
@@ -310,8 +302,7 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
return 0;
err:
for (i = 0; i < S2MPS11_REGULATOR_MAX; i++)
- if (rdev[i])
- regulator_unregister(rdev[i]);
+ regulator_unregister(s2mps11->rdev[i]);
return ret;
}
@@ -319,12 +310,10 @@ err:
static int __devexit s2mps11_pmic_remove(struct platform_device *pdev)
{
struct s2mps11_info *s2mps11 = platform_get_drvdata(pdev);
- struct regulator_dev **rdev = s2mps11->rdev;
int i;
for (i = 0; i < S2MPS11_REGULATOR_MAX; i++)
- if (rdev[i])
- regulator_unregister(rdev[i]);
+ regulator_unregister(s2mps11->rdev[i]);
return 0;
}
diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c
index 947ece9..058d2f2 100644
--- a/drivers/regulator/tps6524x-regulator.c
+++ b/drivers/regulator/tps6524x-regulator.c
@@ -502,15 +502,13 @@ static int set_current_limit(struct regulator_dev *rdev, int min_uA,
if (info->n_ilimsels == 1)
return -EINVAL;
- for (i = 0; i < info->n_ilimsels; i++)
+ for (i = info->n_ilimsels - 1; i >= 0; i--) {
if (min_uA <= info->ilimsels[i] &&
max_uA >= info->ilimsels[i])
- break;
-
- if (i >= info->n_ilimsels)
- return -EINVAL;
+ return write_field(hw, &info->ilimsel, i);
+ }
- return write_field(hw, &info->ilimsel, i);
+ return -EINVAL;
}
static int get_current_limit(struct regulator_dev *rdev)
diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c
index 19241fc..ce1e7cb 100644
--- a/drivers/regulator/tps6586x-regulator.c
+++ b/drivers/regulator/tps6586x-regulator.c
@@ -57,9 +57,6 @@
struct tps6586x_regulator {
struct regulator_desc desc;
- int volt_reg;
- int volt_shift;
- int volt_nbits;
int enable_bit[2];
int enable_reg[2];
@@ -81,10 +78,10 @@ static int tps6586x_set_voltage_sel(struct regulator_dev *rdev,
int ret, val, rid = rdev_get_id(rdev);
uint8_t mask;
- val = selector << ri->volt_shift;
- mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
+ val = selector << (ffs(rdev->desc->vsel_mask) - 1);
+ mask = rdev->desc->vsel_mask;
- ret = tps6586x_update(parent, ri->volt_reg, val, mask);
+ ret = tps6586x_update(parent, rdev->desc->vsel_reg, val, mask);
if (ret)
return ret;
@@ -100,66 +97,17 @@ static int tps6586x_set_voltage_sel(struct regulator_dev *rdev,
return ret;
}
-static int tps6586x_get_voltage_sel(struct regulator_dev *rdev)
-{
- struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
- struct device *parent = to_tps6586x_dev(rdev);
- uint8_t val, mask;
- int ret;
-
- ret = tps6586x_read(parent, ri->volt_reg, &val);
- if (ret)
- return ret;
-
- mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
- val = (val & mask) >> ri->volt_shift;
-
- if (val >= ri->desc.n_voltages)
- BUG();
-
- return val;
-}
-
-static int tps6586x_regulator_enable(struct regulator_dev *rdev)
-{
- struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
- struct device *parent = to_tps6586x_dev(rdev);
-
- return tps6586x_set_bits(parent, ri->enable_reg[0],
- 1 << ri->enable_bit[0]);
-}
-
-static int tps6586x_regulator_disable(struct regulator_dev *rdev)
-{
- struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
- struct device *parent = to_tps6586x_dev(rdev);
-
- return tps6586x_clr_bits(parent, ri->enable_reg[0],
- 1 << ri->enable_bit[0]);
-}
-
-static int tps6586x_regulator_is_enabled(struct regulator_dev *rdev)
-{
- struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
- struct device *parent = to_tps6586x_dev(rdev);
- uint8_t reg_val;
- int ret;
-
- ret = tps6586x_read(parent, ri->enable_reg[0], &reg_val);
- if (ret)
- return ret;
-
- return !!(reg_val & (1 << ri->enable_bit[0]));
-}
-
static struct regulator_ops tps6586x_regulator_ops = {
.list_voltage = regulator_list_voltage_table,
- .get_voltage_sel = tps6586x_get_voltage_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = tps6586x_set_voltage_sel,
- .is_enabled = tps6586x_regulator_is_enabled,
- .enable = tps6586x_regulator_enable,
- .disable = tps6586x_regulator_disable,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+};
+
+static struct regulator_ops tps6586x_sys_regulator_ops = {
};
static const unsigned int tps6586x_ldo0_voltages[] = {
@@ -202,10 +150,11 @@ static const unsigned int tps6586x_dvm_voltages[] = {
.n_voltages = ARRAY_SIZE(tps6586x_##vdata##_voltages), \
.volt_table = tps6586x_##vdata##_voltages, \
.owner = THIS_MODULE, \
+ .enable_reg = TPS6586X_SUPPLY##ereg0, \
+ .enable_mask = 1 << (ebit0), \
+ .vsel_reg = TPS6586X_##vreg, \
+ .vsel_mask = ((1 << (nbits)) - 1) << (shift), \
}, \
- .volt_reg = TPS6586X_##vreg, \
- .volt_shift = (shift), \
- .volt_nbits = (nbits), \
.enable_reg[0] = TPS6586X_SUPPLY##ereg0, \
.enable_bit[0] = (ebit0), \
.enable_reg[1] = TPS6586X_SUPPLY##ereg1, \
@@ -230,15 +179,28 @@ static const unsigned int tps6586x_dvm_voltages[] = {
TPS6586X_REGULATOR_DVM_GOREG(goreg, gobit) \
}
+#define TPS6586X_SYS_REGULATOR() \
+{ \
+ .desc = { \
+ .supply_name = "sys", \
+ .name = "REG-SYS", \
+ .ops = &tps6586x_sys_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = TPS6586X_ID_SYS, \
+ .owner = THIS_MODULE, \
+ }, \
+}
+
static struct tps6586x_regulator tps6586x_regulator[] = {
+ TPS6586X_SYS_REGULATOR(),
TPS6586X_LDO(LDO_0, "vinldo01", ldo0, SUPPLYV1, 5, 3, ENC, 0, END, 0),
TPS6586X_LDO(LDO_3, "vinldo23", ldo, SUPPLYV4, 0, 3, ENC, 2, END, 2),
- TPS6586X_LDO(LDO_5, NULL, ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6),
+ TPS6586X_LDO(LDO_5, "REG-SYS", ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6),
TPS6586X_LDO(LDO_6, "vinldo678", ldo, SUPPLYV3, 0, 3, ENC, 4, END, 4),
TPS6586X_LDO(LDO_7, "vinldo678", ldo, SUPPLYV3, 3, 3, ENC, 5, END, 5),
TPS6586X_LDO(LDO_8, "vinldo678", ldo, SUPPLYV2, 5, 3, ENC, 6, END, 6),
TPS6586X_LDO(LDO_9, "vinldo9", ldo, SUPPLYV6, 3, 3, ENE, 7, ENE, 7),
- TPS6586X_LDO(LDO_RTC, NULL, ldo, SUPPLYV4, 3, 3, V4, 7, V4, 7),
+ TPS6586X_LDO(LDO_RTC, "REG-SYS", ldo, SUPPLYV4, 3, 3, V4, 7, V4, 7),
TPS6586X_LDO(LDO_1, "vinldo01", dvm, SUPPLYV1, 0, 5, ENC, 1, END, 1),
TPS6586X_LDO(SM_2, "vin-sm2", sm2, SUPPLYV2, 0, 5, ENC, 7, END, 7),
diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c
index 77a71a5..7eb986a 100644
--- a/drivers/regulator/twl-regulator.c
+++ b/drivers/regulator/twl-regulator.c
@@ -10,6 +10,8 @@
*/
#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
@@ -624,18 +626,9 @@ static int twlfixed_list_voltage(struct regulator_dev *rdev, unsigned index)
return info->min_mV * 1000;
}
-static int twlfixed_get_voltage(struct regulator_dev *rdev)
-{
- struct twlreg_info *info = rdev_get_drvdata(rdev);
-
- return info->min_mV * 1000;
-}
-
static struct regulator_ops twl4030fixed_ops = {
.list_voltage = twlfixed_list_voltage,
- .get_voltage = twlfixed_get_voltage,
-
.enable = twl4030reg_enable,
.disable = twl4030reg_disable,
.is_enabled = twl4030reg_is_enabled,
@@ -648,8 +641,6 @@ static struct regulator_ops twl4030fixed_ops = {
static struct regulator_ops twl6030fixed_ops = {
.list_voltage = twlfixed_list_voltage,
- .get_voltage = twlfixed_get_voltage,
-
.enable = twl6030reg_enable,
.disable = twl6030reg_disable,
.is_enabled = twl6030reg_is_enabled,
@@ -659,13 +650,6 @@ static struct regulator_ops twl6030fixed_ops = {
.get_status = twl6030reg_get_status,
};
-static struct regulator_ops twl6030_fixed_resource = {
- .enable = twl6030reg_enable,
- .disable = twl6030reg_disable,
- .is_enabled = twl6030reg_is_enabled,
- .get_status = twl6030reg_get_status,
-};
-
/*
* SMPS status and control
*/
@@ -757,37 +741,32 @@ static int twl6030smps_list_voltage(struct regulator_dev *rdev, unsigned index)
return voltage;
}
-static int
-twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
- unsigned int *selector)
+static int twl6030smps_map_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV)
{
- struct twlreg_info *info = rdev_get_drvdata(rdev);
- int vsel = 0;
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+ int vsel = 0;
switch (info->flags) {
case 0:
if (min_uV == 0)
vsel = 0;
else if ((min_uV >= 600000) && (min_uV <= 1300000)) {
- int calc_uV;
vsel = DIV_ROUND_UP(min_uV - 600000, 12500);
vsel++;
- calc_uV = twl6030smps_list_voltage(rdev, vsel);
- if (calc_uV > max_uV)
- return -EINVAL;
}
/* Values 1..57 for vsel are linear and can be calculated
* values 58..62 are non linear.
*/
- else if ((min_uV > 1900000) && (max_uV >= 2100000))
+ else if ((min_uV > 1900000) && (min_uV <= 2100000))
vsel = 62;
- else if ((min_uV > 1800000) && (max_uV >= 1900000))
+ else if ((min_uV > 1800000) && (min_uV <= 1900000))
vsel = 61;
- else if ((min_uV > 1500000) && (max_uV >= 1800000))
+ else if ((min_uV > 1500000) && (min_uV <= 1800000))
vsel = 60;
- else if ((min_uV > 1350000) && (max_uV >= 1500000))
+ else if ((min_uV > 1350000) && (min_uV <= 1500000))
vsel = 59;
- else if ((min_uV > 1300000) && (max_uV >= 1350000))
+ else if ((min_uV > 1300000) && (min_uV <= 1350000))
vsel = 58;
else
return -EINVAL;
@@ -796,25 +775,21 @@ twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
if (min_uV == 0)
vsel = 0;
else if ((min_uV >= 700000) && (min_uV <= 1420000)) {
- int calc_uV;
vsel = DIV_ROUND_UP(min_uV - 700000, 12500);
vsel++;
- calc_uV = twl6030smps_list_voltage(rdev, vsel);
- if (calc_uV > max_uV)
- return -EINVAL;
}
/* Values 1..57 for vsel are linear and can be calculated
* values 58..62 are non linear.
*/
- else if ((min_uV > 1900000) && (max_uV >= 2100000))
+ else if ((min_uV > 1900000) && (min_uV <= 2100000))
vsel = 62;
- else if ((min_uV > 1800000) && (max_uV >= 1900000))
+ else if ((min_uV > 1800000) && (min_uV <= 1900000))
vsel = 61;
- else if ((min_uV > 1350000) && (max_uV >= 1800000))
+ else if ((min_uV > 1350000) && (min_uV <= 1800000))
vsel = 60;
- else if ((min_uV > 1350000) && (max_uV >= 1500000))
+ else if ((min_uV > 1350000) && (min_uV <= 1500000))
vsel = 59;
- else if ((min_uV > 1300000) && (max_uV >= 1350000))
+ else if ((min_uV > 1300000) && (min_uV <= 1350000))
vsel = 58;
else
return -EINVAL;
@@ -830,17 +805,23 @@ twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
case SMPS_OFFSET_EN|SMPS_EXTENDED_EN:
if (min_uV == 0) {
vsel = 0;
- } else if ((min_uV >= 2161000) && (max_uV <= 4321000)) {
+ } else if ((min_uV >= 2161000) && (min_uV <= 4321000)) {
vsel = DIV_ROUND_UP(min_uV - 2161000, 38600);
vsel++;
}
break;
}
- *selector = vsel;
+ return vsel;
+}
+
+static int twl6030smps_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS,
- vsel);
+ selector);
}
static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev)
@@ -852,8 +833,9 @@ static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev)
static struct regulator_ops twlsmps_ops = {
.list_voltage = twl6030smps_list_voltage,
+ .map_voltage = twl6030smps_map_voltage,
- .set_voltage = twl6030smps_set_voltage,
+ .set_voltage_sel = twl6030smps_set_voltage_sel,
.get_voltage_sel = twl6030smps_get_voltage_sel,
.enable = twl6030reg_enable,
@@ -876,7 +858,7 @@ static struct regulator_ops twlsmps_ops = {
0x0, TWL6030, twl6030fixed_ops)
#define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) \
-static struct twlreg_info TWL4030_INFO_##label = { \
+static const struct twlreg_info TWL4030_INFO_##label = { \
.base = offset, \
.id = num, \
.table_len = ARRAY_SIZE(label##_VSEL_table), \
@@ -894,7 +876,7 @@ static struct twlreg_info TWL4030_INFO_##label = { \
}
#define TWL4030_ADJUSTABLE_SMPS(label, offset, num, turnon_delay, remap_conf) \
-static struct twlreg_info TWL4030_INFO_##label = { \
+static const struct twlreg_info TWL4030_INFO_##label = { \
.base = offset, \
.id = num, \
.remap = remap_conf, \
@@ -909,7 +891,7 @@ static struct twlreg_info TWL4030_INFO_##label = { \
}
#define TWL6030_ADJUSTABLE_SMPS(label) \
-static struct twlreg_info TWL6030_INFO_##label = { \
+static const struct twlreg_info TWL6030_INFO_##label = { \
.desc = { \
.name = #label, \
.id = TWL6030_REG_##label, \
@@ -920,7 +902,7 @@ static struct twlreg_info TWL6030_INFO_##label = { \
}
#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \
-static struct twlreg_info TWL6030_INFO_##label = { \
+static const struct twlreg_info TWL6030_INFO_##label = { \
.base = offset, \
.min_mV = min_mVolts, \
.max_mV = max_mVolts, \
@@ -935,7 +917,7 @@ static struct twlreg_info TWL6030_INFO_##label = { \
}
#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \
-static struct twlreg_info TWL6025_INFO_##label = { \
+static const struct twlreg_info TWL6025_INFO_##label = { \
.base = offset, \
.min_mV = min_mVolts, \
.max_mV = max_mVolts, \
@@ -951,7 +933,7 @@ static struct twlreg_info TWL6025_INFO_##label = { \
#define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \
family, operations) \
-static struct twlreg_info TWLFIXED_INFO_##label = { \
+static const struct twlreg_info TWLFIXED_INFO_##label = { \
.base = offset, \
.id = num, \
.min_mV = mVolts, \
@@ -981,7 +963,7 @@ static struct twlreg_info TWLRES_INFO_##label = { \
}
#define TWL6025_ADJUSTABLE_SMPS(label, offset) \
-static struct twlreg_info TWLSMPS_INFO_##label = { \
+static const struct twlreg_info TWLSMPS_INFO_##label = { \
.base = offset, \
.min_mV = 600, \
.max_mV = 2100, \
@@ -1138,6 +1120,7 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
{
int i, id;
struct twlreg_info *info;
+ const struct twlreg_info *template;
struct regulator_init_data *initdata;
struct regulation_constraints *c;
struct regulator_dev *rdev;
@@ -1147,17 +1130,17 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
match = of_match_device(twl_of_match, &pdev->dev);
if (match) {
- info = match->data;
- id = info->desc.id;
+ template = match->data;
+ id = template->desc.id;
initdata = of_get_regulator_init_data(&pdev->dev,
pdev->dev.of_node);
drvdata = NULL;
} else {
id = pdev->id;
initdata = pdev->dev.platform_data;
- for (i = 0, info = NULL; i < ARRAY_SIZE(twl_of_match); i++) {
- info = twl_of_match[i].data;
- if (info && info->desc.id == id)
+ for (i = 0, template = NULL; i < ARRAY_SIZE(twl_of_match); i++) {
+ template = twl_of_match[i].data;
+ if (template && template->desc.id == id)
break;
}
if (i == ARRAY_SIZE(twl_of_match))
@@ -1168,12 +1151,16 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
return -EINVAL;
}
- if (!info)
+ if (!template)
return -ENODEV;
if (!initdata)
return -EINVAL;
+ info = kmemdup(template, sizeof (*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
if (drvdata) {
/* copy the driver data into regulator data */
info->features = drvdata->features;
@@ -1234,6 +1221,7 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "can't register %s, %ld\n",
info->desc.name, PTR_ERR(rdev));
+ kfree(info);
return PTR_ERR(rdev);
}
platform_set_drvdata(pdev, rdev);
@@ -1255,7 +1243,11 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
static int __devexit twlreg_remove(struct platform_device *pdev)
{
- regulator_unregister(platform_get_drvdata(pdev));
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct twlreg_info *info = rdev->reg_data;
+
+ regulator_unregister(rdev);
+ kfree(info);
return 0;
}
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
index 7413885..90cbcc6 100644
--- a/drivers/regulator/wm831x-dcdc.c
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -339,16 +339,15 @@ static int wm831x_buckv_set_current_limit(struct regulator_dev *rdev,
u16 reg = dcdc->base + WM831X_DCDC_CONTROL_2;
int i;
- for (i = 0; i < ARRAY_SIZE(wm831x_dcdc_ilim); i++) {
+ for (i = ARRAY_SIZE(wm831x_dcdc_ilim) - 1; i >= 0; i--) {
if ((min_uA <= wm831x_dcdc_ilim[i]) &&
(wm831x_dcdc_ilim[i] <= max_uA))
- break;
+ return wm831x_set_bits(wm831x, reg,
+ WM831X_DC1_HC_THR_MASK,
+ i << WM831X_DC1_HC_THR_SHIFT);
}
- if (i == ARRAY_SIZE(wm831x_dcdc_ilim))
- return -EINVAL;
- return wm831x_set_bits(wm831x, reg, WM831X_DC1_HC_THR_MASK,
- i << WM831X_DC1_HC_THR_SHIFT);
+ return -EINVAL;
}
static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev)
diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c
index 5cb70ca..9af5126 100644
--- a/drivers/regulator/wm831x-ldo.c
+++ b/drivers/regulator/wm831x-ldo.c
@@ -205,6 +205,8 @@ static int wm831x_gp_ldo_get_status(struct regulator_dev *rdev)
/* Is it reporting under voltage? */
ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS);
+ if (ret < 0)
+ return ret;
if (ret & mask)
return REGULATOR_STATUS_ERROR;
@@ -237,6 +239,8 @@ static struct regulator_ops wm831x_gp_ldo_ops = {
.set_mode = wm831x_gp_ldo_set_mode,
.get_status = wm831x_gp_ldo_get_status,
.get_optimum_mode = wm831x_gp_ldo_get_optimum_mode,
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
@@ -293,6 +297,8 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
ldo->desc.vsel_mask = WM831X_LDO1_ON_VSEL_MASK;
ldo->desc.enable_reg = WM831X_LDO_ENABLE;
ldo->desc.enable_mask = 1 << id;
+ ldo->desc.bypass_reg = ldo->base;
+ ldo->desc.bypass_mask = WM831X_LDO1_SWI;
config.dev = pdev->dev.parent;
if (pdata)
@@ -469,6 +475,8 @@ static int wm831x_aldo_get_status(struct regulator_dev *rdev)
/* Is it reporting under voltage? */
ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS);
+ if (ret < 0)
+ return ret;
if (ret & mask)
return REGULATOR_STATUS_ERROR;
@@ -488,6 +496,8 @@ static struct regulator_ops wm831x_aldo_ops = {
.get_mode = wm831x_aldo_get_mode,
.set_mode = wm831x_aldo_set_mode,
.get_status = wm831x_aldo_get_status,
+ .set_bypass = regulator_set_bypass_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
@@ -544,6 +554,8 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
ldo->desc.vsel_mask = WM831X_LDO7_ON_VSEL_MASK;
ldo->desc.enable_reg = WM831X_LDO_ENABLE;
ldo->desc.enable_mask = 1 << id;
+ ldo->desc.bypass_reg = ldo->base;
+ ldo->desc.bypass_mask = WM831X_LDO7_SWI;
config.dev = pdev->dev.parent;
if (pdata)
diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c
index 9035dd0..27c746e 100644
--- a/drivers/regulator/wm8400-regulator.c
+++ b/drivers/regulator/wm8400-regulator.c
@@ -120,13 +120,8 @@ static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)
case REGULATOR_MODE_IDLE:
/* Datasheet: standby */
- ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
- WM8400_DC1_ACTIVE, 0);
- if (ret != 0)
- return ret;
return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
- WM8400_DC1_SLEEP, 0);
-
+ WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 0);
default:
return -EINVAL;
}
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 590cfaf..1859f71 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -1008,8 +1008,8 @@ static int rpmsg_probe(struct virtio_device *vdev)
return 0;
free_coherent:
- dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, bufs_va,
- vrp->bufs_dma);
+ dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE,
+ bufs_va, vrp->bufs_dma);
vqs_del:
vdev->config->del_vqs(vrp->vdev);
free_vrp:
@@ -1043,7 +1043,7 @@ static void __devexit rpmsg_remove(struct virtio_device *vdev)
vdev->config->del_vqs(vrp->vdev);
- dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE,
+ dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE,
vrp->rbufs, vrp->bufs_dma);
kfree(vrp);
diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
index c5d06fe..9277d94 100644
--- a/drivers/rtc/rtc-twl.c
+++ b/drivers/rtc/rtc-twl.c
@@ -495,6 +495,11 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
if (ret < 0)
goto out1;
+ /* ensure interrupts are disabled, bootloaders can be strange */
+ ret = twl_rtc_write_u8(0, REG_RTC_INTERRUPTS_REG);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "unable to disable interrupt\n");
+
/* init cached IRQ enable bits */
ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
if (ret < 0)
diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig
index 8e477bb..4a3b623 100644
--- a/drivers/s390/block/Kconfig
+++ b/drivers/s390/block/Kconfig
@@ -70,3 +70,21 @@ config DASD_EER
This driver provides a character device interface to the
DASD extended error reporting. This is only needed if you want to
use applications written for the EER facility.
+
+config SCM_BLOCK
+ def_tristate m
+ prompt "Support for Storage Class Memory"
+ depends on S390 && BLOCK && EADM_SCH && SCM_BUS
+ help
+ Block device driver for Storage Class Memory (SCM). This driver
+ provides a block device interface for each available SCM increment.
+
+ To compile this driver as a module, choose M here: the
+ module will be called scm_block.
+
+config SCM_BLOCK_CLUSTER_WRITE
+ def_bool y
+ prompt "SCM force cluster writes"
+ depends on SCM_BLOCK
+ help
+ Force writes to Storage Class Memory (SCM) to be in done in clusters.
diff --git a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile
index 0a89e08..c2f4e67 100644
--- a/drivers/s390/block/Makefile
+++ b/drivers/s390/block/Makefile
@@ -17,3 +17,9 @@ obj-$(CONFIG_DASD_ECKD) += dasd_eckd_mod.o
obj-$(CONFIG_DASD_FBA) += dasd_fba_mod.o
obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
obj-$(CONFIG_DCSSBLK) += dcssblk.o
+
+scm_block-objs := scm_drv.o scm_blk.o
+ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
+scm_block-objs += scm_blk_cluster.o
+endif
+obj-$(CONFIG_SCM_BLOCK) += scm_block.o
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 15370a2..0595c76 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -534,11 +534,11 @@ static void dasd_change_state(struct dasd_device *device)
if (rc)
device->target = device->state;
- if (device->state == device->target)
- wake_up(&dasd_init_waitq);
-
/* let user-space know that the device status changed */
kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE);
+
+ if (device->state == device->target)
+ wake_up(&dasd_init_waitq);
}
/*
@@ -2157,6 +2157,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
(!dasd_eer_enabled(device))) {
cqr->status = DASD_CQR_FAILED;
+ cqr->intrc = -EAGAIN;
continue;
}
/* Don't try to start requests if device is stopped */
@@ -3270,6 +3271,16 @@ void dasd_generic_path_event(struct ccw_device *cdev, int *path_event)
dasd_schedule_device_bh(device);
}
if (path_event[chp] & PE_PATHGROUP_ESTABLISHED) {
+ if (!(device->path_data.opm & eventlpm) &&
+ !(device->path_data.tbvpm & eventlpm)) {
+ /*
+ * we can not establish a pathgroup on an
+ * unavailable path, so trigger a path
+ * verification first
+ */
+ device->path_data.tbvpm |= eventlpm;
+ dasd_schedule_device_bh(device);
+ }
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Pathgroup re-established\n");
if (device->discipline->kick_validate)
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index 157defe..6b55699 100644
--- a/drivers/s390/block/dasd_alias.c
+++ b/drivers/s390/block/dasd_alias.c
@@ -384,6 +384,29 @@ static void _remove_device_from_lcu(struct alias_lcu *lcu,
group->next = NULL;
};
+static int
+suborder_not_supported(struct dasd_ccw_req *cqr)
+{
+ char *sense;
+ char reason;
+ char msg_format;
+ char msg_no;
+
+ sense = dasd_get_sense(&cqr->irb);
+ if (!sense)
+ return 0;
+
+ reason = sense[0];
+ msg_format = (sense[7] & 0xF0);
+ msg_no = (sense[7] & 0x0F);
+
+ /* command reject, Format 0 MSG 4 - invalid parameter */
+ if ((reason == 0x80) && (msg_format == 0x00) && (msg_no == 0x04))
+ return 1;
+
+ return 0;
+}
+
static int read_unit_address_configuration(struct dasd_device *device,
struct alias_lcu *lcu)
{
@@ -435,6 +458,8 @@ static int read_unit_address_configuration(struct dasd_device *device,
do {
rc = dasd_sleep_on(cqr);
+ if (rc && suborder_not_supported(cqr))
+ return -EOPNOTSUPP;
} while (rc && (cqr->retries > 0));
if (rc) {
spin_lock_irqsave(&lcu->lock, flags);
@@ -521,7 +546,7 @@ static void lcu_update_work(struct work_struct *work)
* processing the data
*/
spin_lock_irqsave(&lcu->lock, flags);
- if (rc || (lcu->flags & NEED_UAC_UPDATE)) {
+ if ((rc && (rc != -EOPNOTSUPP)) || (lcu->flags & NEED_UAC_UPDATE)) {
DBF_DEV_EVENT(DBF_WARNING, device, "could not update"
" alias data in lcu (rc = %d), retry later", rc);
schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 2fb2b9e..108332b 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -20,6 +20,7 @@
#include <linux/compat.h>
#include <linux/init.h>
+#include <asm/css_chars.h>
#include <asm/debug.h>
#include <asm/idals.h>
#include <asm/ebcdic.h>
@@ -31,8 +32,6 @@
#include "dasd_int.h"
#include "dasd_eckd.h"
-#include "../cio/chsc.h"
-
#ifdef PRINTK_HEADER
#undef PRINTK_HEADER
@@ -140,6 +139,10 @@ dasd_eckd_set_online(struct ccw_device *cdev)
static const int sizes_trk0[] = { 28, 148, 84 };
#define LABEL_SIZE 140
+/* head and record addresses of count_area read in analysis ccw */
+static const int count_area_head[] = { 0, 0, 0, 0, 2 };
+static const int count_area_rec[] = { 1, 2, 3, 4, 1 };
+
static inline unsigned int
round_up_multiple(unsigned int no, unsigned int mult)
{
@@ -212,7 +215,7 @@ check_XRC (struct ccw1 *de_ccw,
rc = get_sync_clock(&data->ep_sys_time);
/* Ignore return code if sync clock is switched off. */
- if (rc == -ENOSYS || rc == -EACCES)
+ if (rc == -EOPNOTSUPP || rc == -EACCES)
rc = 0;
de_ccw->count = sizeof(struct DE_eckd_data);
@@ -323,7 +326,7 @@ static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata,
rc = get_sync_clock(&pfxdata->define_extent.ep_sys_time);
/* Ignore return code if sync clock is switched off. */
- if (rc == -ENOSYS || rc == -EACCES)
+ if (rc == -EOPNOTSUPP || rc == -EACCES)
rc = 0;
return rc;
}
@@ -1507,7 +1510,8 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
* call might change behaviour of DASD devices.
*/
static int
-dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
+dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav,
+ unsigned long flags)
{
struct dasd_ccw_req *cqr;
int rc;
@@ -1516,10 +1520,19 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
if (IS_ERR(cqr))
return PTR_ERR(cqr);
+ /*
+ * set flags e.g. turn on failfast, to prevent blocking
+ * the calling function should handle failed requests
+ */
+ cqr->flags |= flags;
+
rc = dasd_sleep_on(cqr);
if (!rc)
/* trigger CIO to reprobe devices */
css_schedule_reprobe();
+ else if (cqr->intrc == -EAGAIN)
+ rc = -EAGAIN;
+
dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -1527,7 +1540,8 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
/*
* Valide storage server of current device.
*/
-static void dasd_eckd_validate_server(struct dasd_device *device)
+static int dasd_eckd_validate_server(struct dasd_device *device,
+ unsigned long flags)
{
int rc;
struct dasd_eckd_private *private;
@@ -1536,17 +1550,18 @@ static void dasd_eckd_validate_server(struct dasd_device *device)
private = (struct dasd_eckd_private *) device->private;
if (private->uid.type == UA_BASE_PAV_ALIAS ||
private->uid.type == UA_HYPER_PAV_ALIAS)
- return;
+ return 0;
if (dasd_nopav || MACHINE_IS_VM)
enable_pav = 0;
else
enable_pav = 1;
- rc = dasd_eckd_psf_ssc(device, enable_pav);
+ rc = dasd_eckd_psf_ssc(device, enable_pav, flags);
/* may be requested feature is not available on server,
* therefore just report error and go ahead */
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x "
"returned rc=%d", private->uid.ssid, rc);
+ return rc;
}
/*
@@ -1556,7 +1571,13 @@ static void dasd_eckd_do_validate_server(struct work_struct *work)
{
struct dasd_device *device = container_of(work, struct dasd_device,
kick_validate);
- dasd_eckd_validate_server(device);
+ if (dasd_eckd_validate_server(device, DASD_CQR_FLAGS_FAILFAST)
+ == -EAGAIN) {
+ /* schedule worker again if failed */
+ schedule_work(&device->kick_validate);
+ return;
+ }
+
dasd_put_device(device);
}
@@ -1685,7 +1706,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
if (rc)
goto out_err2;
- dasd_eckd_validate_server(device);
+ dasd_eckd_validate_server(device, 0);
/* device may report different configuration data after LCU setup */
rc = dasd_eckd_read_conf(device);
@@ -1922,7 +1943,10 @@ static int dasd_eckd_end_analysis(struct dasd_block *block)
count_area = NULL;
for (i = 0; i < 3; i++) {
if (private->count_area[i].kl != 4 ||
- private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4) {
+ private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4 ||
+ private->count_area[i].cyl != 0 ||
+ private->count_area[i].head != count_area_head[i] ||
+ private->count_area[i].record != count_area_rec[i]) {
private->uses_cdl = 0;
break;
}
@@ -1934,7 +1958,10 @@ static int dasd_eckd_end_analysis(struct dasd_block *block)
for (i = 0; i < 5; i++) {
if ((private->count_area[i].kl != 0) ||
(private->count_area[i].dl !=
- private->count_area[0].dl))
+ private->count_area[0].dl) ||
+ private->count_area[i].cyl != 0 ||
+ private->count_area[i].head != count_area_head[i] ||
+ private->count_area[i].record != count_area_rec[i])
break;
}
if (i == 5)
@@ -4153,7 +4180,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
rc = dasd_alias_make_device_known_to_lcu(device);
if (rc)
return rc;
- dasd_eckd_validate_server(device);
+ dasd_eckd_validate_server(device, DASD_CQR_FLAGS_FAILFAST);
/* RE-Read Configuration Data */
rc = dasd_eckd_read_conf(device);
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index 654c692..8252f37 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -292,12 +292,12 @@ out:
#else
static int dasd_ioctl_reset_profile(struct dasd_block *block)
{
- return -ENOSYS;
+ return -ENOTTY;
}
static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
{
- return -ENOSYS;
+ return -ENOTTY;
}
#endif
diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c
new file mode 100644
index 0000000..9978ad4
--- /dev/null
+++ b/drivers/s390/block/scm_blk.c
@@ -0,0 +1,445 @@
+/*
+ * Block driver for s390 storage class memory.
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#define KMSG_COMPONENT "scm_block"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <asm/eadm.h>
+#include "scm_blk.h"
+
+debug_info_t *scm_debug;
+static int scm_major;
+static DEFINE_SPINLOCK(list_lock);
+static LIST_HEAD(inactive_requests);
+static unsigned int nr_requests = 64;
+static atomic_t nr_devices = ATOMIC_INIT(0);
+module_param(nr_requests, uint, S_IRUGO);
+MODULE_PARM_DESC(nr_requests, "Number of parallel requests.");
+
+MODULE_DESCRIPTION("Block driver for s390 storage class memory.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("scm:scmdev*");
+
+static void __scm_free_rq(struct scm_request *scmrq)
+{
+ struct aob_rq_header *aobrq = to_aobrq(scmrq);
+
+ free_page((unsigned long) scmrq->aob);
+ free_page((unsigned long) scmrq->aidaw);
+ __scm_free_rq_cluster(scmrq);
+ kfree(aobrq);
+}
+
+static void scm_free_rqs(void)
+{
+ struct list_head *iter, *safe;
+ struct scm_request *scmrq;
+
+ spin_lock_irq(&list_lock);
+ list_for_each_safe(iter, safe, &inactive_requests) {
+ scmrq = list_entry(iter, struct scm_request, list);
+ list_del(&scmrq->list);
+ __scm_free_rq(scmrq);
+ }
+ spin_unlock_irq(&list_lock);
+}
+
+static int __scm_alloc_rq(void)
+{
+ struct aob_rq_header *aobrq;
+ struct scm_request *scmrq;
+
+ aobrq = kzalloc(sizeof(*aobrq) + sizeof(*scmrq), GFP_KERNEL);
+ if (!aobrq)
+ return -ENOMEM;
+
+ scmrq = (void *) aobrq->data;
+ scmrq->aidaw = (void *) get_zeroed_page(GFP_DMA);
+ scmrq->aob = (void *) get_zeroed_page(GFP_DMA);
+ if (!scmrq->aob || !scmrq->aidaw) {
+ __scm_free_rq(scmrq);
+ return -ENOMEM;
+ }
+
+ if (__scm_alloc_rq_cluster(scmrq)) {
+ __scm_free_rq(scmrq);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&scmrq->list);
+ spin_lock_irq(&list_lock);
+ list_add(&scmrq->list, &inactive_requests);
+ spin_unlock_irq(&list_lock);
+
+ return 0;
+}
+
+static int scm_alloc_rqs(unsigned int nrqs)
+{
+ int ret = 0;
+
+ while (nrqs-- && !ret)
+ ret = __scm_alloc_rq();
+
+ return ret;
+}
+
+static struct scm_request *scm_request_fetch(void)
+{
+ struct scm_request *scmrq = NULL;
+
+ spin_lock(&list_lock);
+ if (list_empty(&inactive_requests))
+ goto out;
+ scmrq = list_first_entry(&inactive_requests, struct scm_request, list);
+ list_del(&scmrq->list);
+out:
+ spin_unlock(&list_lock);
+ return scmrq;
+}
+
+static void scm_request_done(struct scm_request *scmrq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&list_lock, flags);
+ list_add(&scmrq->list, &inactive_requests);
+ spin_unlock_irqrestore(&list_lock, flags);
+}
+
+static int scm_open(struct block_device *blkdev, fmode_t mode)
+{
+ return scm_get_ref();
+}
+
+static int scm_release(struct gendisk *gendisk, fmode_t mode)
+{
+ scm_put_ref();
+ return 0;
+}
+
+static const struct block_device_operations scm_blk_devops = {
+ .owner = THIS_MODULE,
+ .open = scm_open,
+ .release = scm_release,
+};
+
+static void scm_request_prepare(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+ struct scm_device *scmdev = bdev->gendisk->private_data;
+ struct aidaw *aidaw = scmrq->aidaw;
+ struct msb *msb = &scmrq->aob->msb[0];
+ struct req_iterator iter;
+ struct bio_vec *bv;
+
+ msb->bs = MSB_BS_4K;
+ scmrq->aob->request.msb_count = 1;
+ msb->scm_addr = scmdev->address +
+ ((u64) blk_rq_pos(scmrq->request) << 9);
+ msb->oc = (rq_data_dir(scmrq->request) == READ) ?
+ MSB_OC_READ : MSB_OC_WRITE;
+ msb->flags |= MSB_FLAG_IDA;
+ msb->data_addr = (u64) aidaw;
+
+ rq_for_each_segment(bv, scmrq->request, iter) {
+ WARN_ON(bv->bv_offset);
+ msb->blk_count += bv->bv_len >> 12;
+ aidaw->data_addr = (u64) page_address(bv->bv_page);
+ aidaw++;
+ }
+}
+
+static inline void scm_request_init(struct scm_blk_dev *bdev,
+ struct scm_request *scmrq,
+ struct request *req)
+{
+ struct aob_rq_header *aobrq = to_aobrq(scmrq);
+ struct aob *aob = scmrq->aob;
+
+ memset(aob, 0, sizeof(*aob));
+ memset(scmrq->aidaw, 0, PAGE_SIZE);
+ aobrq->scmdev = bdev->scmdev;
+ aob->request.cmd_code = ARQB_CMD_MOVE;
+ aob->request.data = (u64) aobrq;
+ scmrq->request = req;
+ scmrq->bdev = bdev;
+ scmrq->retries = 4;
+ scmrq->error = 0;
+ scm_request_cluster_init(scmrq);
+}
+
+static void scm_ensure_queue_restart(struct scm_blk_dev *bdev)
+{
+ if (atomic_read(&bdev->queued_reqs)) {
+ /* Queue restart is triggered by the next interrupt. */
+ return;
+ }
+ blk_delay_queue(bdev->rq, SCM_QUEUE_DELAY);
+}
+
+void scm_request_requeue(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+
+ scm_release_cluster(scmrq);
+ blk_requeue_request(bdev->rq, scmrq->request);
+ scm_request_done(scmrq);
+ scm_ensure_queue_restart(bdev);
+}
+
+void scm_request_finish(struct scm_request *scmrq)
+{
+ scm_release_cluster(scmrq);
+ blk_end_request_all(scmrq->request, scmrq->error);
+ scm_request_done(scmrq);
+}
+
+static void scm_blk_request(struct request_queue *rq)
+{
+ struct scm_device *scmdev = rq->queuedata;
+ struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev);
+ struct scm_request *scmrq;
+ struct request *req;
+ int ret;
+
+ while ((req = blk_peek_request(rq))) {
+ if (req->cmd_type != REQ_TYPE_FS)
+ continue;
+
+ scmrq = scm_request_fetch();
+ if (!scmrq) {
+ SCM_LOG(5, "no request");
+ scm_ensure_queue_restart(bdev);
+ return;
+ }
+ scm_request_init(bdev, scmrq, req);
+ if (!scm_reserve_cluster(scmrq)) {
+ SCM_LOG(5, "cluster busy");
+ scm_request_done(scmrq);
+ return;
+ }
+ if (scm_need_cluster_request(scmrq)) {
+ blk_start_request(req);
+ scm_initiate_cluster_request(scmrq);
+ return;
+ }
+ scm_request_prepare(scmrq);
+ blk_start_request(req);
+
+ ret = scm_start_aob(scmrq->aob);
+ if (ret) {
+ SCM_LOG(5, "no subchannel");
+ scm_request_requeue(scmrq);
+ return;
+ }
+ atomic_inc(&bdev->queued_reqs);
+ }
+}
+
+static void __scmrq_log_error(struct scm_request *scmrq)
+{
+ struct aob *aob = scmrq->aob;
+
+ if (scmrq->error == -ETIMEDOUT)
+ SCM_LOG(1, "Request timeout");
+ else {
+ SCM_LOG(1, "Request error");
+ SCM_LOG_HEX(1, &aob->response, sizeof(aob->response));
+ }
+ if (scmrq->retries)
+ SCM_LOG(1, "Retry request");
+ else
+ pr_err("An I/O operation to SCM failed with rc=%d\n",
+ scmrq->error);
+}
+
+void scm_blk_irq(struct scm_device *scmdev, void *data, int error)
+{
+ struct scm_request *scmrq = data;
+ struct scm_blk_dev *bdev = scmrq->bdev;
+
+ scmrq->error = error;
+ if (error)
+ __scmrq_log_error(scmrq);
+
+ spin_lock(&bdev->lock);
+ list_add_tail(&scmrq->list, &bdev->finished_requests);
+ spin_unlock(&bdev->lock);
+ tasklet_hi_schedule(&bdev->tasklet);
+}
+
+static void scm_blk_tasklet(struct scm_blk_dev *bdev)
+{
+ struct scm_request *scmrq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bdev->lock, flags);
+ while (!list_empty(&bdev->finished_requests)) {
+ scmrq = list_first_entry(&bdev->finished_requests,
+ struct scm_request, list);
+ list_del(&scmrq->list);
+ spin_unlock_irqrestore(&bdev->lock, flags);
+
+ if (scmrq->error && scmrq->retries-- > 0) {
+ if (scm_start_aob(scmrq->aob)) {
+ spin_lock_irqsave(&bdev->rq_lock, flags);
+ scm_request_requeue(scmrq);
+ spin_unlock_irqrestore(&bdev->rq_lock, flags);
+ }
+ /* Request restarted or requeued, handle next. */
+ spin_lock_irqsave(&bdev->lock, flags);
+ continue;
+ }
+
+ if (scm_test_cluster_request(scmrq)) {
+ scm_cluster_request_irq(scmrq);
+ spin_lock_irqsave(&bdev->lock, flags);
+ continue;
+ }
+
+ scm_request_finish(scmrq);
+ atomic_dec(&bdev->queued_reqs);
+ spin_lock_irqsave(&bdev->lock, flags);
+ }
+ spin_unlock_irqrestore(&bdev->lock, flags);
+ /* Look out for more requests. */
+ blk_run_queue(bdev->rq);
+}
+
+int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)
+{
+ struct request_queue *rq;
+ int len, ret = -ENOMEM;
+ unsigned int devindex, nr_max_blk;
+
+ devindex = atomic_inc_return(&nr_devices) - 1;
+ /* scma..scmz + scmaa..scmzz */
+ if (devindex > 701) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ bdev->scmdev = scmdev;
+ spin_lock_init(&bdev->rq_lock);
+ spin_lock_init(&bdev->lock);
+ INIT_LIST_HEAD(&bdev->finished_requests);
+ atomic_set(&bdev->queued_reqs, 0);
+ tasklet_init(&bdev->tasklet,
+ (void (*)(unsigned long)) scm_blk_tasklet,
+ (unsigned long) bdev);
+
+ rq = blk_init_queue(scm_blk_request, &bdev->rq_lock);
+ if (!rq)
+ goto out;
+
+ bdev->rq = rq;
+ nr_max_blk = min(scmdev->nr_max_block,
+ (unsigned int) (PAGE_SIZE / sizeof(struct aidaw)));
+
+ blk_queue_logical_block_size(rq, 1 << 12);
+ blk_queue_max_hw_sectors(rq, nr_max_blk << 3); /* 8 * 512 = blk_size */
+ blk_queue_max_segments(rq, nr_max_blk);
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, rq);
+ scm_blk_dev_cluster_setup(bdev);
+
+ bdev->gendisk = alloc_disk(SCM_NR_PARTS);
+ if (!bdev->gendisk)
+ goto out_queue;
+
+ rq->queuedata = scmdev;
+ bdev->gendisk->driverfs_dev = &scmdev->dev;
+ bdev->gendisk->private_data = scmdev;
+ bdev->gendisk->fops = &scm_blk_devops;
+ bdev->gendisk->queue = rq;
+ bdev->gendisk->major = scm_major;
+ bdev->gendisk->first_minor = devindex * SCM_NR_PARTS;
+
+ len = snprintf(bdev->gendisk->disk_name, DISK_NAME_LEN, "scm");
+ if (devindex > 25) {
+ len += snprintf(bdev->gendisk->disk_name + len,
+ DISK_NAME_LEN - len, "%c",
+ 'a' + (devindex / 26) - 1);
+ devindex = devindex % 26;
+ }
+ snprintf(bdev->gendisk->disk_name + len, DISK_NAME_LEN - len, "%c",
+ 'a' + devindex);
+
+ /* 512 byte sectors */
+ set_capacity(bdev->gendisk, scmdev->size >> 9);
+ add_disk(bdev->gendisk);
+ return 0;
+
+out_queue:
+ blk_cleanup_queue(rq);
+out:
+ atomic_dec(&nr_devices);
+ return ret;
+}
+
+void scm_blk_dev_cleanup(struct scm_blk_dev *bdev)
+{
+ tasklet_kill(&bdev->tasklet);
+ del_gendisk(bdev->gendisk);
+ blk_cleanup_queue(bdev->gendisk->queue);
+ put_disk(bdev->gendisk);
+}
+
+static int __init scm_blk_init(void)
+{
+ int ret = -EINVAL;
+
+ if (!scm_cluster_size_valid())
+ goto out;
+
+ ret = register_blkdev(0, "scm");
+ if (ret < 0)
+ goto out;
+
+ scm_major = ret;
+ if (scm_alloc_rqs(nr_requests))
+ goto out_unreg;
+
+ scm_debug = debug_register("scm_log", 16, 1, 16);
+ if (!scm_debug)
+ goto out_free;
+
+ debug_register_view(scm_debug, &debug_hex_ascii_view);
+ debug_set_level(scm_debug, 2);
+
+ ret = scm_drv_init();
+ if (ret)
+ goto out_dbf;
+
+ return ret;
+
+out_dbf:
+ debug_unregister(scm_debug);
+out_free:
+ scm_free_rqs();
+out_unreg:
+ unregister_blkdev(scm_major, "scm");
+out:
+ return ret;
+}
+module_init(scm_blk_init);
+
+static void __exit scm_blk_cleanup(void)
+{
+ scm_drv_cleanup();
+ debug_unregister(scm_debug);
+ scm_free_rqs();
+ unregister_blkdev(scm_major, "scm");
+}
+module_exit(scm_blk_cleanup);
diff --git a/drivers/s390/block/scm_blk.h b/drivers/s390/block/scm_blk.h
new file mode 100644
index 0000000..7ac6bad
--- /dev/null
+++ b/drivers/s390/block/scm_blk.h
@@ -0,0 +1,117 @@
+#ifndef SCM_BLK_H
+#define SCM_BLK_H
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/list.h>
+
+#include <asm/debug.h>
+#include <asm/eadm.h>
+
+#define SCM_NR_PARTS 8
+#define SCM_QUEUE_DELAY 5
+
+struct scm_blk_dev {
+ struct tasklet_struct tasklet;
+ struct request_queue *rq;
+ struct gendisk *gendisk;
+ struct scm_device *scmdev;
+ spinlock_t rq_lock; /* guard the request queue */
+ spinlock_t lock; /* guard the rest of the blockdev */
+ atomic_t queued_reqs;
+ struct list_head finished_requests;
+#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
+ struct list_head cluster_list;
+#endif
+};
+
+struct scm_request {
+ struct scm_blk_dev *bdev;
+ struct request *request;
+ struct aidaw *aidaw;
+ struct aob *aob;
+ struct list_head list;
+ u8 retries;
+ int error;
+#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
+ struct {
+ enum {CLUSTER_NONE, CLUSTER_READ, CLUSTER_WRITE} state;
+ struct list_head list;
+ void **buf;
+ } cluster;
+#endif
+};
+
+#define to_aobrq(rq) container_of((void *) rq, struct aob_rq_header, data)
+
+int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *);
+void scm_blk_dev_cleanup(struct scm_blk_dev *);
+void scm_blk_irq(struct scm_device *, void *, int);
+
+void scm_request_finish(struct scm_request *);
+void scm_request_requeue(struct scm_request *);
+
+int scm_drv_init(void);
+void scm_drv_cleanup(void);
+
+#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
+void __scm_free_rq_cluster(struct scm_request *);
+int __scm_alloc_rq_cluster(struct scm_request *);
+void scm_request_cluster_init(struct scm_request *);
+bool scm_reserve_cluster(struct scm_request *);
+void scm_release_cluster(struct scm_request *);
+void scm_blk_dev_cluster_setup(struct scm_blk_dev *);
+bool scm_need_cluster_request(struct scm_request *);
+void scm_initiate_cluster_request(struct scm_request *);
+void scm_cluster_request_irq(struct scm_request *);
+bool scm_test_cluster_request(struct scm_request *);
+bool scm_cluster_size_valid(void);
+#else
+#define __scm_free_rq_cluster(scmrq) {}
+#define __scm_alloc_rq_cluster(scmrq) 0
+#define scm_request_cluster_init(scmrq) {}
+#define scm_reserve_cluster(scmrq) true
+#define scm_release_cluster(scmrq) {}
+#define scm_blk_dev_cluster_setup(bdev) {}
+#define scm_need_cluster_request(scmrq) false
+#define scm_initiate_cluster_request(scmrq) {}
+#define scm_cluster_request_irq(scmrq) {}
+#define scm_test_cluster_request(scmrq) false
+#define scm_cluster_size_valid() true
+#endif
+
+extern debug_info_t *scm_debug;
+
+#define SCM_LOG(imp, txt) do { \
+ debug_text_event(scm_debug, imp, txt); \
+ } while (0)
+
+static inline void SCM_LOG_HEX(int level, void *data, int length)
+{
+ if (level > scm_debug->level)
+ return;
+ while (length > 0) {
+ debug_event(scm_debug, level, data, length);
+ length -= scm_debug->buf_size;
+ data += scm_debug->buf_size;
+ }
+}
+
+static inline void SCM_LOG_STATE(int level, struct scm_device *scmdev)
+{
+ struct {
+ u64 address;
+ u8 oper_state;
+ u8 rank;
+ } __packed data = {
+ .address = scmdev->address,
+ .oper_state = scmdev->attrs.oper_state,
+ .rank = scmdev->attrs.rank,
+ };
+
+ SCM_LOG_HEX(level, &data, sizeof(data));
+}
+
+#endif /* SCM_BLK_H */
diff --git a/drivers/s390/block/scm_blk_cluster.c b/drivers/s390/block/scm_blk_cluster.c
new file mode 100644
index 0000000..f4bb61b
--- /dev/null
+++ b/drivers/s390/block/scm_blk_cluster.c
@@ -0,0 +1,228 @@
+/*
+ * Block driver for s390 storage class memory.
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <asm/eadm.h>
+#include "scm_blk.h"
+
+static unsigned int write_cluster_size = 64;
+module_param(write_cluster_size, uint, S_IRUGO);
+MODULE_PARM_DESC(write_cluster_size,
+ "Number of pages used for contiguous writes.");
+
+#define CLUSTER_SIZE (write_cluster_size * PAGE_SIZE)
+
+void __scm_free_rq_cluster(struct scm_request *scmrq)
+{
+ int i;
+
+ if (!scmrq->cluster.buf)
+ return;
+
+ for (i = 0; i < 2 * write_cluster_size; i++)
+ free_page((unsigned long) scmrq->cluster.buf[i]);
+
+ kfree(scmrq->cluster.buf);
+}
+
+int __scm_alloc_rq_cluster(struct scm_request *scmrq)
+{
+ int i;
+
+ scmrq->cluster.buf = kzalloc(sizeof(void *) * 2 * write_cluster_size,
+ GFP_KERNEL);
+ if (!scmrq->cluster.buf)
+ return -ENOMEM;
+
+ for (i = 0; i < 2 * write_cluster_size; i++) {
+ scmrq->cluster.buf[i] = (void *) get_zeroed_page(GFP_DMA);
+ if (!scmrq->cluster.buf[i])
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&scmrq->cluster.list);
+ return 0;
+}
+
+void scm_request_cluster_init(struct scm_request *scmrq)
+{
+ scmrq->cluster.state = CLUSTER_NONE;
+}
+
+static bool clusters_intersect(struct scm_request *A, struct scm_request *B)
+{
+ unsigned long firstA, lastA, firstB, lastB;
+
+ firstA = ((u64) blk_rq_pos(A->request) << 9) / CLUSTER_SIZE;
+ lastA = (((u64) blk_rq_pos(A->request) << 9) +
+ blk_rq_bytes(A->request) - 1) / CLUSTER_SIZE;
+
+ firstB = ((u64) blk_rq_pos(B->request) << 9) / CLUSTER_SIZE;
+ lastB = (((u64) blk_rq_pos(B->request) << 9) +
+ blk_rq_bytes(B->request) - 1) / CLUSTER_SIZE;
+
+ return (firstB <= lastA && firstA <= lastB);
+}
+
+bool scm_reserve_cluster(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+ struct scm_request *iter;
+
+ if (write_cluster_size == 0)
+ return true;
+
+ spin_lock(&bdev->lock);
+ list_for_each_entry(iter, &bdev->cluster_list, cluster.list) {
+ if (clusters_intersect(scmrq, iter) &&
+ (rq_data_dir(scmrq->request) == WRITE ||
+ rq_data_dir(iter->request) == WRITE)) {
+ spin_unlock(&bdev->lock);
+ return false;
+ }
+ }
+ list_add(&scmrq->cluster.list, &bdev->cluster_list);
+ spin_unlock(&bdev->lock);
+
+ return true;
+}
+
+void scm_release_cluster(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+ unsigned long flags;
+
+ if (write_cluster_size == 0)
+ return;
+
+ spin_lock_irqsave(&bdev->lock, flags);
+ list_del(&scmrq->cluster.list);
+ spin_unlock_irqrestore(&bdev->lock, flags);
+}
+
+void scm_blk_dev_cluster_setup(struct scm_blk_dev *bdev)
+{
+ INIT_LIST_HEAD(&bdev->cluster_list);
+ blk_queue_io_opt(bdev->rq, CLUSTER_SIZE);
+}
+
+static void scm_prepare_cluster_request(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+ struct scm_device *scmdev = bdev->gendisk->private_data;
+ struct request *req = scmrq->request;
+ struct aidaw *aidaw = scmrq->aidaw;
+ struct msb *msb = &scmrq->aob->msb[0];
+ struct req_iterator iter;
+ struct bio_vec *bv;
+ int i = 0;
+ u64 addr;
+
+ switch (scmrq->cluster.state) {
+ case CLUSTER_NONE:
+ scmrq->cluster.state = CLUSTER_READ;
+ /* fall through */
+ case CLUSTER_READ:
+ scmrq->aob->request.msb_count = 1;
+ msb->bs = MSB_BS_4K;
+ msb->oc = MSB_OC_READ;
+ msb->flags = MSB_FLAG_IDA;
+ msb->data_addr = (u64) aidaw;
+ msb->blk_count = write_cluster_size;
+
+ addr = scmdev->address + ((u64) blk_rq_pos(req) << 9);
+ msb->scm_addr = round_down(addr, CLUSTER_SIZE);
+
+ if (msb->scm_addr !=
+ round_down(addr + (u64) blk_rq_bytes(req) - 1,
+ CLUSTER_SIZE))
+ msb->blk_count = 2 * write_cluster_size;
+
+ for (i = 0; i < msb->blk_count; i++) {
+ aidaw->data_addr = (u64) scmrq->cluster.buf[i];
+ aidaw++;
+ }
+
+ break;
+ case CLUSTER_WRITE:
+ msb->oc = MSB_OC_WRITE;
+
+ for (addr = msb->scm_addr;
+ addr < scmdev->address + ((u64) blk_rq_pos(req) << 9);
+ addr += PAGE_SIZE) {
+ aidaw->data_addr = (u64) scmrq->cluster.buf[i];
+ aidaw++;
+ i++;
+ }
+ rq_for_each_segment(bv, req, iter) {
+ aidaw->data_addr = (u64) page_address(bv->bv_page);
+ aidaw++;
+ i++;
+ }
+ for (; i < msb->blk_count; i++) {
+ aidaw->data_addr = (u64) scmrq->cluster.buf[i];
+ aidaw++;
+ }
+ break;
+ }
+}
+
+bool scm_need_cluster_request(struct scm_request *scmrq)
+{
+ if (rq_data_dir(scmrq->request) == READ)
+ return false;
+
+ return blk_rq_bytes(scmrq->request) < CLUSTER_SIZE;
+}
+
+/* Called with queue lock held. */
+void scm_initiate_cluster_request(struct scm_request *scmrq)
+{
+ scm_prepare_cluster_request(scmrq);
+ if (scm_start_aob(scmrq->aob))
+ scm_request_requeue(scmrq);
+}
+
+bool scm_test_cluster_request(struct scm_request *scmrq)
+{
+ return scmrq->cluster.state != CLUSTER_NONE;
+}
+
+void scm_cluster_request_irq(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+ unsigned long flags;
+
+ switch (scmrq->cluster.state) {
+ case CLUSTER_NONE:
+ BUG();
+ break;
+ case CLUSTER_READ:
+ if (scmrq->error) {
+ scm_request_finish(scmrq);
+ break;
+ }
+ scmrq->cluster.state = CLUSTER_WRITE;
+ spin_lock_irqsave(&bdev->rq_lock, flags);
+ scm_initiate_cluster_request(scmrq);
+ spin_unlock_irqrestore(&bdev->rq_lock, flags);
+ break;
+ case CLUSTER_WRITE:
+ scm_request_finish(scmrq);
+ break;
+ }
+}
+
+bool scm_cluster_size_valid(void)
+{
+ return write_cluster_size == 0 || write_cluster_size == 32 ||
+ write_cluster_size == 64 || write_cluster_size == 128;
+}
diff --git a/drivers/s390/block/scm_drv.c b/drivers/s390/block/scm_drv.c
new file mode 100644
index 0000000..9fa0a90
--- /dev/null
+++ b/drivers/s390/block/scm_drv.c
@@ -0,0 +1,81 @@
+/*
+ * Device driver for s390 storage class memory.
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#define KMSG_COMPONENT "scm_block"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/eadm.h>
+#include "scm_blk.h"
+
+static void notify(struct scm_device *scmdev)
+{
+ pr_info("%lu: The capabilities of the SCM increment changed\n",
+ (unsigned long) scmdev->address);
+ SCM_LOG(2, "State changed");
+ SCM_LOG_STATE(2, scmdev);
+}
+
+static int scm_probe(struct scm_device *scmdev)
+{
+ struct scm_blk_dev *bdev;
+ int ret;
+
+ SCM_LOG(2, "probe");
+ SCM_LOG_STATE(2, scmdev);
+
+ if (scmdev->attrs.oper_state != OP_STATE_GOOD)
+ return -EINVAL;
+
+ bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
+ if (!bdev)
+ return -ENOMEM;
+
+ dev_set_drvdata(&scmdev->dev, bdev);
+ ret = scm_blk_dev_setup(bdev, scmdev);
+ if (ret) {
+ dev_set_drvdata(&scmdev->dev, NULL);
+ kfree(bdev);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static int scm_remove(struct scm_device *scmdev)
+{
+ struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev);
+
+ scm_blk_dev_cleanup(bdev);
+ dev_set_drvdata(&scmdev->dev, NULL);
+ kfree(bdev);
+
+ return 0;
+}
+
+static struct scm_driver scm_drv = {
+ .drv = {
+ .name = "scm_block",
+ .owner = THIS_MODULE,
+ },
+ .notify = notify,
+ .probe = scm_probe,
+ .remove = scm_remove,
+ .handler = scm_blk_irq,
+};
+
+int __init scm_drv_init(void)
+{
+ return scm_driver_register(&scm_drv);
+}
+
+void scm_drv_cleanup(void)
+{
+ scm_driver_unregister(&scm_drv);
+}
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index bb07577..699fd3e 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -35,7 +35,6 @@ static struct raw3270_fn con3270_fn;
*/
struct con3270 {
struct raw3270_view view;
- spinlock_t lock;
struct list_head freemem; /* list of free memory for strings. */
/* Output stuff. */
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index 5b8b859..f4ff515 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -571,8 +571,11 @@ static int __init mon_init(void)
if (rc)
goto out_iucv;
monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL);
- if (!monreader_device)
+ if (!monreader_device) {
+ rc = -ENOMEM;
goto out_driver;
+ }
+
dev_set_name(monreader_device, "monreader-dev");
monreader_device->bus = &iucv_bus;
monreader_device->parent = iucv_root;
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 3fcc000..4fa21f7 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -334,7 +334,7 @@ sclp_dispatch_evbufs(struct sccb_header *sccb)
reg->receiver_fn(evbuf);
spin_lock_irqsave(&sclp_lock, flags);
} else if (reg == NULL)
- rc = -ENOSYS;
+ rc = -EOPNOTSUPP;
}
spin_unlock_irqrestore(&sclp_lock, flags);
return rc;
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 4be63be..3b13d58 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -463,7 +463,7 @@ sclp_emit_buffer(struct sclp_buffer *buffer,
/* Use write priority message */
sccb->msg_buf.header.type = EVTYP_PMSGCMD;
else
- return -ENOSYS;
+ return -EOPNOTSUPP;
buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;
buffer->request.status = SCLP_REQ_FILLED;
buffer->request.callback = sclp_writedata_callback;
diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h
index c06be6c..ea664dd 100644
--- a/drivers/s390/char/tape.h
+++ b/drivers/s390/char/tape.h
@@ -15,7 +15,6 @@
#include <asm/ccwdev.h>
#include <asm/debug.h>
#include <asm/idals.h>
-#include <linux/blkdev.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtio.h>
diff --git a/drivers/s390/char/tape_std.h b/drivers/s390/char/tape_std.h
index c5816ad..8c760c0 100644
--- a/drivers/s390/char/tape_std.h
+++ b/drivers/s390/char/tape_std.h
@@ -100,11 +100,7 @@ struct tape_request *tape_std_read_block(struct tape_device *, size_t);
void tape_std_read_backward(struct tape_device *device,
struct tape_request *request);
struct tape_request *tape_std_write_block(struct tape_device *, size_t);
-struct tape_request *tape_std_bread(struct tape_device *, struct request *);
-void tape_std_free_bread(struct tape_request *);
void tape_std_check_locate(struct tape_device *, struct tape_request *);
-struct tape_request *tape_std_bwrite(struct request *,
- struct tape_device *, int);
/* Some non-mtop commands. */
int tape_std_assign(struct tape_device *);
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index c131bc4..9b3a24e 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -321,7 +321,7 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
* only allow for blocking reads to be open
*/
if (filp->f_flags & O_NONBLOCK)
- return -ENOSYS;
+ return -EOPNOTSUPP;
/* Besure this device hasn't already been opened */
spin_lock_bh(&logptr->priv_lock);
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index e1b700a..8c4a386 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -8,6 +8,8 @@ ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o
obj-$(CONFIG_CHSC_SCH) += chsc_sch.o
+obj-$(CONFIG_EADM_SCH) += eadm_sch.o
+obj-$(CONFIG_SCM_BUS) += scm.o
obj-$(CONFIG_CCWGROUP) += ccwgroup.o
qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index cfe0c08..4d51a7c 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -52,6 +52,11 @@ int chsc_error_from_response(int response)
return -EINVAL;
case 0x0004:
return -EOPNOTSUPP;
+ case 0x000b:
+ return -EBUSY;
+ case 0x0100:
+ case 0x0102:
+ return -ENOMEM;
default:
return -EIO;
}
@@ -393,6 +398,20 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
}
}
+static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area)
+{
+ int ret;
+
+ CIO_CRW_EVENT(4, "chsc: scm change notification\n");
+ if (sei_area->rs != 7)
+ return;
+
+ ret = scm_update_information();
+ if (ret)
+ CIO_CRW_EVENT(0, "chsc: updating change notification"
+ " failed (rc=%d).\n", ret);
+}
+
static void chsc_process_sei(struct chsc_sei_area *sei_area)
{
/* Check if we might have lost some information. */
@@ -414,6 +433,9 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
case 8: /* channel-path-configuration notification */
chsc_process_sei_chp_config(sei_area);
break;
+ case 12: /* scm change notification */
+ chsc_process_sei_scm_change(sei_area);
+ break;
default: /* other stuff */
CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
sei_area->cc);
@@ -1047,3 +1069,33 @@ out:
return rc;
}
EXPORT_SYMBOL_GPL(chsc_siosl);
+
+/**
+ * chsc_scm_info() - store SCM information (SSI)
+ * @scm_area: request and response block for SSI
+ * @token: continuation token
+ *
+ * Returns 0 on success.
+ */
+int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token)
+{
+ int ccode, ret;
+
+ memset(scm_area, 0, sizeof(*scm_area));
+ scm_area->request.length = 0x0020;
+ scm_area->request.code = 0x004C;
+ scm_area->reqtok = token;
+
+ ccode = chsc(scm_area);
+ if (ccode > 0) {
+ ret = (ccode == 3) ? -ENODEV : -EBUSY;
+ goto out;
+ }
+ ret = chsc_error_from_response(scm_area->response.code);
+ if (ret != 0)
+ CIO_MSG_EVENT(2, "chsc: scm info failed (rc=%04x)\n",
+ scm_area->response.code);
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(chsc_scm_info);
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 3f15b2a..662dab4 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -3,6 +3,7 @@
#include <linux/types.h>
#include <linux/device.h>
+#include <asm/css_chars.h>
#include <asm/chpid.h>
#include <asm/chsc.h>
#include <asm/schid.h>
@@ -118,4 +119,46 @@ int chsc_error_from_response(int response);
int chsc_siosl(struct subchannel_id schid);
+/* Functions and definitions to query storage-class memory. */
+struct sale {
+ u64 sa;
+ u32 p:4;
+ u32 op_state:4;
+ u32 data_state:4;
+ u32 rank:4;
+ u32 r:1;
+ u32:7;
+ u32 rid:8;
+ u32:32;
+} __packed;
+
+struct chsc_scm_info {
+ struct chsc_header request;
+ u32:32;
+ u64 reqtok;
+ u32 reserved1[4];
+ struct chsc_header response;
+ u64:56;
+ u8 rq;
+ u32 mbc;
+ u64 msa;
+ u16 is;
+ u16 mmc;
+ u32 mci;
+ u64 nr_scm_ini;
+ u64 nr_scm_unini;
+ u32 reserved2[10];
+ u64 restok;
+ struct sale scmal[248];
+} __packed;
+
+int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);
+
+#ifdef CONFIG_SCM_BUS
+int scm_update_information(void);
+#else /* CONFIG_SCM_BUS */
+#define scm_update_information() 0
+#endif /* CONFIG_SCM_BUS */
+
+
#endif
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 33d1ef7..8e927b9 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -1029,7 +1029,7 @@ extern void do_reipl_asm(__u32 schid);
/* Make sure all subchannels are quiet before we re-ipl an lpar. */
void reipl_ccw_dev(struct ccw_dev_id *devid)
{
- struct subchannel_id schid;
+ struct subchannel_id uninitialized_var(schid);
s390_reset_system(NULL, NULL);
if (reipl_find_schid(devid, &schid) != 0)
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 21908e6..b4d572f 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -445,6 +445,7 @@ void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo)
put_device(&sch->dev);
}
}
+EXPORT_SYMBOL_GPL(css_sched_sch_todo);
static void css_sch_todo(struct work_struct *work)
{
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index ed25c87..fc916f5 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -1426,6 +1426,8 @@ static enum io_sch_action sch_get_action(struct subchannel *sch)
return IO_SCH_REPROBE;
if (cdev->online)
return IO_SCH_VERIFY;
+ if (cdev->private->state == DEV_STATE_NOT_OPER)
+ return IO_SCH_UNREG_ATTACH;
return IO_SCH_NOP;
}
@@ -1519,11 +1521,14 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
goto out;
break;
case IO_SCH_UNREG_ATTACH:
+ spin_lock_irqsave(sch->lock, flags);
if (cdev->private->flags.resuming) {
/* Device will be handled later. */
rc = 0;
- goto out;
+ goto out_unlock;
}
+ sch_set_cdev(sch, NULL);
+ spin_unlock_irqrestore(sch->lock, flags);
/* Unregister ccw device. */
ccw_device_unregister(cdev);
break;
diff --git a/drivers/s390/cio/eadm_sch.c b/drivers/s390/cio/eadm_sch.c
new file mode 100644
index 0000000..6c96734
--- /dev/null
+++ b/drivers/s390/cio/eadm_sch.c
@@ -0,0 +1,401 @@
+/*
+ * Driver for s390 eadm subchannels
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#include <linux/kernel_stat.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <asm/css_chars.h>
+#include <asm/debug.h>
+#include <asm/isc.h>
+#include <asm/cio.h>
+#include <asm/scsw.h>
+#include <asm/eadm.h>
+
+#include "eadm_sch.h"
+#include "ioasm.h"
+#include "cio.h"
+#include "css.h"
+#include "orb.h"
+
+MODULE_DESCRIPTION("driver for s390 eadm subchannels");
+MODULE_LICENSE("GPL");
+
+#define EADM_TIMEOUT (5 * HZ)
+static DEFINE_SPINLOCK(list_lock);
+static LIST_HEAD(eadm_list);
+
+static debug_info_t *eadm_debug;
+
+#define EADM_LOG(imp, txt) do { \
+ debug_text_event(eadm_debug, imp, txt); \
+ } while (0)
+
+static void EADM_LOG_HEX(int level, void *data, int length)
+{
+ if (level > eadm_debug->level)
+ return;
+ while (length > 0) {
+ debug_event(eadm_debug, level, data, length);
+ length -= eadm_debug->buf_size;
+ data += eadm_debug->buf_size;
+ }
+}
+
+static void orb_init(union orb *orb)
+{
+ memset(orb, 0, sizeof(union orb));
+ orb->eadm.compat1 = 1;
+ orb->eadm.compat2 = 1;
+ orb->eadm.fmt = 1;
+ orb->eadm.x = 1;
+}
+
+static int eadm_subchannel_start(struct subchannel *sch, struct aob *aob)
+{
+ union orb *orb = &get_eadm_private(sch)->orb;
+ int cc;
+
+ orb_init(orb);
+ orb->eadm.aob = (u32)__pa(aob);
+ orb->eadm.intparm = (u32)(addr_t)sch;
+ orb->eadm.key = PAGE_DEFAULT_KEY >> 4;
+
+ EADM_LOG(6, "start");
+ EADM_LOG_HEX(6, &sch->schid, sizeof(sch->schid));
+
+ cc = ssch(sch->schid, orb);
+ switch (cc) {
+ case 0:
+ sch->schib.scsw.eadm.actl |= SCSW_ACTL_START_PEND;
+ break;
+ case 1: /* status pending */
+ case 2: /* busy */
+ return -EBUSY;
+ case 3: /* not operational */
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int eadm_subchannel_clear(struct subchannel *sch)
+{
+ int cc;
+
+ cc = csch(sch->schid);
+ if (cc)
+ return -ENODEV;
+
+ sch->schib.scsw.eadm.actl |= SCSW_ACTL_CLEAR_PEND;
+ return 0;
+}
+
+static void eadm_subchannel_timeout(unsigned long data)
+{
+ struct subchannel *sch = (struct subchannel *) data;
+
+ spin_lock_irq(sch->lock);
+ EADM_LOG(1, "timeout");
+ EADM_LOG_HEX(1, &sch->schid, sizeof(sch->schid));
+ if (eadm_subchannel_clear(sch))
+ EADM_LOG(0, "clear failed");
+ spin_unlock_irq(sch->lock);
+}
+
+static void eadm_subchannel_set_timeout(struct subchannel *sch, int expires)
+{
+ struct eadm_private *private = get_eadm_private(sch);
+
+ if (expires == 0) {
+ del_timer(&private->timer);
+ return;
+ }
+ if (timer_pending(&private->timer)) {
+ if (mod_timer(&private->timer, jiffies + expires))
+ return;
+ }
+ private->timer.function = eadm_subchannel_timeout;
+ private->timer.data = (unsigned long) sch;
+ private->timer.expires = jiffies + expires;
+ add_timer(&private->timer);
+}
+
+static void eadm_subchannel_irq(struct subchannel *sch)
+{
+ struct eadm_private *private = get_eadm_private(sch);
+ struct eadm_scsw *scsw = &sch->schib.scsw.eadm;
+ struct irb *irb = (struct irb *)&S390_lowcore.irb;
+ int error = 0;
+
+ EADM_LOG(6, "irq");
+ EADM_LOG_HEX(6, irb, sizeof(*irb));
+
+ kstat_cpu(smp_processor_id()).irqs[IOINT_ADM]++;
+
+ if ((scsw->stctl & (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))
+ && scsw->eswf == 1 && irb->esw.eadm.erw.r)
+ error = -EIO;
+
+ if (scsw->fctl & SCSW_FCTL_CLEAR_FUNC)
+ error = -ETIMEDOUT;
+
+ eadm_subchannel_set_timeout(sch, 0);
+
+ if (private->state != EADM_BUSY) {
+ EADM_LOG(1, "irq unsol");
+ EADM_LOG_HEX(1, irb, sizeof(*irb));
+ private->state = EADM_NOT_OPER;
+ css_sched_sch_todo(sch, SCH_TODO_EVAL);
+ return;
+ }
+ scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error);
+ private->state = EADM_IDLE;
+}
+
+static struct subchannel *eadm_get_idle_sch(void)
+{
+ struct eadm_private *private;
+ struct subchannel *sch;
+ unsigned long flags;
+
+ spin_lock_irqsave(&list_lock, flags);
+ list_for_each_entry(private, &eadm_list, head) {
+ sch = private->sch;
+ spin_lock(sch->lock);
+ if (private->state == EADM_IDLE) {
+ private->state = EADM_BUSY;
+ list_move_tail(&private->head, &eadm_list);
+ spin_unlock(sch->lock);
+ spin_unlock_irqrestore(&list_lock, flags);
+
+ return sch;
+ }
+ spin_unlock(sch->lock);
+ }
+ spin_unlock_irqrestore(&list_lock, flags);
+
+ return NULL;
+}
+
+static int eadm_start_aob(struct aob *aob)
+{
+ struct eadm_private *private;
+ struct subchannel *sch;
+ unsigned long flags;
+ int ret;
+
+ sch = eadm_get_idle_sch();
+ if (!sch)
+ return -EBUSY;
+
+ spin_lock_irqsave(sch->lock, flags);
+ eadm_subchannel_set_timeout(sch, EADM_TIMEOUT);
+ ret = eadm_subchannel_start(sch, aob);
+ if (!ret)
+ goto out_unlock;
+
+ /* Handle start subchannel failure. */
+ eadm_subchannel_set_timeout(sch, 0);
+ private = get_eadm_private(sch);
+ private->state = EADM_NOT_OPER;
+ css_sched_sch_todo(sch, SCH_TODO_EVAL);
+
+out_unlock:
+ spin_unlock_irqrestore(sch->lock, flags);
+
+ return ret;
+}
+
+static int eadm_subchannel_probe(struct subchannel *sch)
+{
+ struct eadm_private *private;
+ int ret;
+
+ private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
+ if (!private)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&private->head);
+ init_timer(&private->timer);
+
+ spin_lock_irq(sch->lock);
+ set_eadm_private(sch, private);
+ private->state = EADM_IDLE;
+ private->sch = sch;
+ sch->isc = EADM_SCH_ISC;
+ ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
+ if (ret) {
+ set_eadm_private(sch, NULL);
+ spin_unlock_irq(sch->lock);
+ kfree(private);
+ goto out;
+ }
+ spin_unlock_irq(sch->lock);
+
+ spin_lock_irq(&list_lock);
+ list_add(&private->head, &eadm_list);
+ spin_unlock_irq(&list_lock);
+
+ if (dev_get_uevent_suppress(&sch->dev)) {
+ dev_set_uevent_suppress(&sch->dev, 0);
+ kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
+ }
+out:
+ return ret;
+}
+
+static void eadm_quiesce(struct subchannel *sch)
+{
+ int ret;
+
+ do {
+ spin_lock_irq(sch->lock);
+ ret = cio_disable_subchannel(sch);
+ spin_unlock_irq(sch->lock);
+ } while (ret == -EBUSY);
+}
+
+static int eadm_subchannel_remove(struct subchannel *sch)
+{
+ struct eadm_private *private = get_eadm_private(sch);
+
+ spin_lock_irq(&list_lock);
+ list_del(&private->head);
+ spin_unlock_irq(&list_lock);
+
+ eadm_quiesce(sch);
+
+ spin_lock_irq(sch->lock);
+ set_eadm_private(sch, NULL);
+ spin_unlock_irq(sch->lock);
+
+ kfree(private);
+
+ return 0;
+}
+
+static void eadm_subchannel_shutdown(struct subchannel *sch)
+{
+ eadm_quiesce(sch);
+}
+
+static int eadm_subchannel_freeze(struct subchannel *sch)
+{
+ return cio_disable_subchannel(sch);
+}
+
+static int eadm_subchannel_restore(struct subchannel *sch)
+{
+ return cio_enable_subchannel(sch, (u32)(unsigned long)sch);
+}
+
+/**
+ * eadm_subchannel_sch_event - process subchannel event
+ * @sch: subchannel
+ * @process: non-zero if function is called in process context
+ *
+ * An unspecified event occurred for this subchannel. Adjust data according
+ * to the current operational state of the subchannel. Return zero when the
+ * event has been handled sufficiently or -EAGAIN when this function should
+ * be called again in process context.
+ */
+static int eadm_subchannel_sch_event(struct subchannel *sch, int process)
+{
+ struct eadm_private *private;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(sch->lock, flags);
+ if (!device_is_registered(&sch->dev))
+ goto out_unlock;
+
+ if (work_pending(&sch->todo_work))
+ goto out_unlock;
+
+ if (cio_update_schib(sch)) {
+ css_sched_sch_todo(sch, SCH_TODO_UNREG);
+ goto out_unlock;
+ }
+ private = get_eadm_private(sch);
+ if (private->state == EADM_NOT_OPER)
+ private->state = EADM_IDLE;
+
+out_unlock:
+ spin_unlock_irqrestore(sch->lock, flags);
+
+ return ret;
+}
+
+static struct css_device_id eadm_subchannel_ids[] = {
+ { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_ADM, },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(css, eadm_subchannel_ids);
+
+static struct css_driver eadm_subchannel_driver = {
+ .drv = {
+ .name = "eadm_subchannel",
+ .owner = THIS_MODULE,
+ },
+ .subchannel_type = eadm_subchannel_ids,
+ .irq = eadm_subchannel_irq,
+ .probe = eadm_subchannel_probe,
+ .remove = eadm_subchannel_remove,
+ .shutdown = eadm_subchannel_shutdown,
+ .sch_event = eadm_subchannel_sch_event,
+ .freeze = eadm_subchannel_freeze,
+ .thaw = eadm_subchannel_restore,
+ .restore = eadm_subchannel_restore,
+};
+
+static struct eadm_ops eadm_ops = {
+ .eadm_start = eadm_start_aob,
+ .owner = THIS_MODULE,
+};
+
+static int __init eadm_sch_init(void)
+{
+ int ret;
+
+ if (!css_general_characteristics.eadm)
+ return -ENXIO;
+
+ eadm_debug = debug_register("eadm_log", 16, 1, 16);
+ if (!eadm_debug)
+ return -ENOMEM;
+
+ debug_register_view(eadm_debug, &debug_hex_ascii_view);
+ debug_set_level(eadm_debug, 2);
+
+ isc_register(EADM_SCH_ISC);
+ ret = css_driver_register(&eadm_subchannel_driver);
+ if (ret)
+ goto cleanup;
+
+ register_eadm_ops(&eadm_ops);
+ return ret;
+
+cleanup:
+ isc_unregister(EADM_SCH_ISC);
+ debug_unregister(eadm_debug);
+ return ret;
+}
+
+static void __exit eadm_sch_exit(void)
+{
+ unregister_eadm_ops(&eadm_ops);
+ css_driver_unregister(&eadm_subchannel_driver);
+ isc_unregister(EADM_SCH_ISC);
+ debug_unregister(eadm_debug);
+}
+module_init(eadm_sch_init);
+module_exit(eadm_sch_exit);
diff --git a/drivers/s390/cio/eadm_sch.h b/drivers/s390/cio/eadm_sch.h
new file mode 100644
index 0000000..2779be0
--- /dev/null
+++ b/drivers/s390/cio/eadm_sch.h
@@ -0,0 +1,20 @@
+#ifndef EADM_SCH_H
+#define EADM_SCH_H
+
+#include <linux/device.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include "orb.h"
+
+struct eadm_private {
+ union orb orb;
+ enum {EADM_IDLE, EADM_BUSY, EADM_NOT_OPER} state;
+ struct timer_list timer;
+ struct list_head head;
+ struct subchannel *sch;
+} __aligned(8);
+
+#define get_eadm_private(n) ((struct eadm_private *)dev_get_drvdata(&n->dev))
+#define set_eadm_private(n, p) (dev_set_drvdata(&n->dev, p))
+
+#endif
diff --git a/drivers/s390/cio/orb.h b/drivers/s390/cio/orb.h
index 45a9865..7a64053 100644
--- a/drivers/s390/cio/orb.h
+++ b/drivers/s390/cio/orb.h
@@ -59,9 +59,33 @@ struct tm_orb {
u32:32;
} __packed __aligned(4);
+/*
+ * eadm operation request block
+ */
+struct eadm_orb {
+ u32 intparm;
+ u32 key:4;
+ u32:4;
+ u32 compat1:1;
+ u32 compat2:1;
+ u32:21;
+ u32 x:1;
+ u32 aob;
+ u32 css_prio:8;
+ u32:8;
+ u32 scm_prio:8;
+ u32:8;
+ u32:29;
+ u32 fmt:3;
+ u32:32;
+ u32:32;
+ u32:32;
+} __packed __aligned(4);
+
union orb {
struct cmd_orb cmd;
struct tm_orb tm;
+ struct eadm_orb eadm;
} __packed __aligned(4);
#endif /* S390_ORB_H */
diff --git a/drivers/s390/cio/qdio_debug.h b/drivers/s390/cio/qdio_debug.h
index e1f6468..7f8b973 100644
--- a/drivers/s390/cio/qdio_debug.h
+++ b/drivers/s390/cio/qdio_debug.h
@@ -37,10 +37,14 @@ static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
debug_text_event(qdio_dbf_setup, DBF_ERR, debug_buffer); \
} while (0)
-#define DBF_HEX(addr, len) \
- do { \
- debug_event(qdio_dbf_setup, DBF_ERR, (void*)(addr), len); \
- } while (0)
+static inline void DBF_HEX(void *addr, int len)
+{
+ while (len > 0) {
+ debug_event(qdio_dbf_setup, DBF_ERR, addr, len);
+ len -= qdio_dbf_setup->buf_size;
+ addr += qdio_dbf_setup->buf_size;
+ }
+}
#define DBF_ERROR(text...) \
do { \
@@ -49,11 +53,14 @@ static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
debug_text_event(qdio_dbf_error, DBF_ERR, debug_buffer); \
} while (0)
-#define DBF_ERROR_HEX(addr, len) \
- do { \
- debug_event(qdio_dbf_error, DBF_ERR, (void*)(addr), len); \
- } while (0)
-
+static inline void DBF_ERROR_HEX(void *addr, int len)
+{
+ while (len > 0) {
+ debug_event(qdio_dbf_error, DBF_ERR, addr, len);
+ len -= qdio_dbf_error->buf_size;
+ addr += qdio_dbf_error->buf_size;
+ }
+}
#define DBF_DEV_EVENT(level, device, text...) \
do { \
@@ -64,10 +71,15 @@ static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
} \
} while (0)
-#define DBF_DEV_HEX(level, device, addr, len) \
- do { \
- debug_event(device->debug_area, level, (void*)(addr), len); \
- } while (0)
+static inline void DBF_DEV_HEX(struct qdio_irq *dev, void *addr,
+ int len, int level)
+{
+ while (len > 0) {
+ debug_event(dev->debug_area, level, addr, len);
+ len -= dev->debug_area->buf_size;
+ addr += dev->debug_area->buf_size;
+ }
+}
void qdio_allocate_dbf(struct qdio_initialize *init_data,
struct qdio_irq *irq_ptr);
diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c
new file mode 100644
index 0000000..bcf20f3
--- /dev/null
+++ b/drivers/s390/cio/scm.c
@@ -0,0 +1,317 @@
+/*
+ * Recognize and maintain s390 storage class memory.
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <asm/eadm.h>
+#include "chsc.h"
+
+static struct device *scm_root;
+static struct eadm_ops *eadm_ops;
+static DEFINE_MUTEX(eadm_ops_mutex);
+
+#define to_scm_dev(n) container_of(n, struct scm_device, dev)
+#define to_scm_drv(d) container_of(d, struct scm_driver, drv)
+
+static int scmdev_probe(struct device *dev)
+{
+ struct scm_device *scmdev = to_scm_dev(dev);
+ struct scm_driver *scmdrv = to_scm_drv(dev->driver);
+
+ return scmdrv->probe ? scmdrv->probe(scmdev) : -ENODEV;
+}
+
+static int scmdev_remove(struct device *dev)
+{
+ struct scm_device *scmdev = to_scm_dev(dev);
+ struct scm_driver *scmdrv = to_scm_drv(dev->driver);
+
+ return scmdrv->remove ? scmdrv->remove(scmdev) : -ENODEV;
+}
+
+static int scmdev_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ return add_uevent_var(env, "MODALIAS=scm:scmdev");
+}
+
+static struct bus_type scm_bus_type = {
+ .name = "scm",
+ .probe = scmdev_probe,
+ .remove = scmdev_remove,
+ .uevent = scmdev_uevent,
+};
+
+/**
+ * scm_driver_register() - register a scm driver
+ * @scmdrv: driver to be registered
+ */
+int scm_driver_register(struct scm_driver *scmdrv)
+{
+ struct device_driver *drv = &scmdrv->drv;
+
+ drv->bus = &scm_bus_type;
+
+ return driver_register(drv);
+}
+EXPORT_SYMBOL_GPL(scm_driver_register);
+
+/**
+ * scm_driver_unregister() - deregister a scm driver
+ * @scmdrv: driver to be deregistered
+ */
+void scm_driver_unregister(struct scm_driver *scmdrv)
+{
+ driver_unregister(&scmdrv->drv);
+}
+EXPORT_SYMBOL_GPL(scm_driver_unregister);
+
+int scm_get_ref(void)
+{
+ int ret = 0;
+
+ mutex_lock(&eadm_ops_mutex);
+ if (!eadm_ops || !try_module_get(eadm_ops->owner))
+ ret = -ENOENT;
+ mutex_unlock(&eadm_ops_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(scm_get_ref);
+
+void scm_put_ref(void)
+{
+ mutex_lock(&eadm_ops_mutex);
+ module_put(eadm_ops->owner);
+ mutex_unlock(&eadm_ops_mutex);
+}
+EXPORT_SYMBOL_GPL(scm_put_ref);
+
+void register_eadm_ops(struct eadm_ops *ops)
+{
+ mutex_lock(&eadm_ops_mutex);
+ eadm_ops = ops;
+ mutex_unlock(&eadm_ops_mutex);
+}
+EXPORT_SYMBOL_GPL(register_eadm_ops);
+
+void unregister_eadm_ops(struct eadm_ops *ops)
+{
+ mutex_lock(&eadm_ops_mutex);
+ eadm_ops = NULL;
+ mutex_unlock(&eadm_ops_mutex);
+}
+EXPORT_SYMBOL_GPL(unregister_eadm_ops);
+
+int scm_start_aob(struct aob *aob)
+{
+ return eadm_ops->eadm_start(aob);
+}
+EXPORT_SYMBOL_GPL(scm_start_aob);
+
+void scm_irq_handler(struct aob *aob, int error)
+{
+ struct aob_rq_header *aobrq = (void *) aob->request.data;
+ struct scm_device *scmdev = aobrq->scmdev;
+ struct scm_driver *scmdrv = to_scm_drv(scmdev->dev.driver);
+
+ scmdrv->handler(scmdev, aobrq->data, error);
+}
+EXPORT_SYMBOL_GPL(scm_irq_handler);
+
+#define scm_attr(name) \
+static ssize_t show_##name(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct scm_device *scmdev = to_scm_dev(dev); \
+ int ret; \
+ \
+ device_lock(dev); \
+ ret = sprintf(buf, "%u\n", scmdev->attrs.name); \
+ device_unlock(dev); \
+ \
+ return ret; \
+} \
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+
+scm_attr(persistence);
+scm_attr(oper_state);
+scm_attr(data_state);
+scm_attr(rank);
+scm_attr(release);
+scm_attr(res_id);
+
+static struct attribute *scmdev_attrs[] = {
+ &dev_attr_persistence.attr,
+ &dev_attr_oper_state.attr,
+ &dev_attr_data_state.attr,
+ &dev_attr_rank.attr,
+ &dev_attr_release.attr,
+ &dev_attr_res_id.attr,
+ NULL,
+};
+
+static struct attribute_group scmdev_attr_group = {
+ .attrs = scmdev_attrs,
+};
+
+static const struct attribute_group *scmdev_attr_groups[] = {
+ &scmdev_attr_group,
+ NULL,
+};
+
+static void scmdev_release(struct device *dev)
+{
+ struct scm_device *scmdev = to_scm_dev(dev);
+
+ kfree(scmdev);
+}
+
+static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
+ unsigned int size, unsigned int max_blk_count)
+{
+ dev_set_name(&scmdev->dev, "%016llx", (unsigned long long) sale->sa);
+ scmdev->nr_max_block = max_blk_count;
+ scmdev->address = sale->sa;
+ scmdev->size = 1UL << size;
+ scmdev->attrs.rank = sale->rank;
+ scmdev->attrs.persistence = sale->p;
+ scmdev->attrs.oper_state = sale->op_state;
+ scmdev->attrs.data_state = sale->data_state;
+ scmdev->attrs.rank = sale->rank;
+ scmdev->attrs.release = sale->r;
+ scmdev->attrs.res_id = sale->rid;
+ scmdev->dev.parent = scm_root;
+ scmdev->dev.bus = &scm_bus_type;
+ scmdev->dev.release = scmdev_release;
+ scmdev->dev.groups = scmdev_attr_groups;
+}
+
+/*
+ * Check for state-changes, notify the driver and userspace.
+ */
+static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
+{
+ struct scm_driver *scmdrv;
+ bool changed;
+
+ device_lock(&scmdev->dev);
+ changed = scmdev->attrs.rank != sale->rank ||
+ scmdev->attrs.oper_state != sale->op_state;
+ scmdev->attrs.rank = sale->rank;
+ scmdev->attrs.oper_state = sale->op_state;
+ if (!scmdev->dev.driver)
+ goto out;
+ scmdrv = to_scm_drv(scmdev->dev.driver);
+ if (changed && scmdrv->notify)
+ scmdrv->notify(scmdev);
+out:
+ device_unlock(&scmdev->dev);
+ if (changed)
+ kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE);
+}
+
+static int check_address(struct device *dev, void *data)
+{
+ struct scm_device *scmdev = to_scm_dev(dev);
+ struct sale *sale = data;
+
+ return scmdev->address == sale->sa;
+}
+
+static struct scm_device *scmdev_find(struct sale *sale)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&scm_bus_type, NULL, sale, check_address);
+
+ return dev ? to_scm_dev(dev) : NULL;
+}
+
+static int scm_add(struct chsc_scm_info *scm_info, size_t num)
+{
+ struct sale *sale, *scmal = scm_info->scmal;
+ struct scm_device *scmdev;
+ int ret;
+
+ for (sale = scmal; sale < scmal + num; sale++) {
+ scmdev = scmdev_find(sale);
+ if (scmdev) {
+ scmdev_update(scmdev, sale);
+ /* Release reference from scm_find(). */
+ put_device(&scmdev->dev);
+ continue;
+ }
+ scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
+ if (!scmdev)
+ return -ENODEV;
+ scmdev_setup(scmdev, sale, scm_info->is, scm_info->mbc);
+ ret = device_register(&scmdev->dev);
+ if (ret) {
+ /* Release reference from device_initialize(). */
+ put_device(&scmdev->dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int scm_update_information(void)
+{
+ struct chsc_scm_info *scm_info;
+ u64 token = 0;
+ size_t num;
+ int ret;
+
+ scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+ if (!scm_info)
+ return -ENOMEM;
+
+ do {
+ ret = chsc_scm_info(scm_info, token);
+ if (ret)
+ break;
+
+ num = (scm_info->response.length -
+ (offsetof(struct chsc_scm_info, scmal) -
+ offsetof(struct chsc_scm_info, response))
+ ) / sizeof(struct sale);
+
+ ret = scm_add(scm_info, num);
+ if (ret)
+ break;
+
+ token = scm_info->restok;
+ } while (token);
+
+ free_page((unsigned long)scm_info);
+
+ return ret;
+}
+
+static int __init scm_init(void)
+{
+ int ret;
+
+ ret = bus_register(&scm_bus_type);
+ if (ret)
+ return ret;
+
+ scm_root = root_device_register("scm");
+ if (IS_ERR(scm_root)) {
+ bus_unregister(&scm_bus_type);
+ return PTR_ERR(scm_root);
+ }
+
+ scm_update_information();
+ return 0;
+}
+subsys_initcall_sync(scm_init);
diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile
index af3c7f1..771faf7 100644
--- a/drivers/s390/crypto/Makefile
+++ b/drivers/s390/crypto/Makefile
@@ -4,4 +4,5 @@
ap-objs := ap_bus.o
obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcicc.o zcrypt_pcixcc.o
-obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o
+obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o zcrypt_cex4.o
+obj-$(CONFIG_ZCRYPT) += zcrypt_msgtype6.o zcrypt_msgtype50.o
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index ae258a4..7b865a7 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -1,5 +1,5 @@
/*
- * Copyright IBM Corp. 2006
+ * Copyright IBM Corp. 2006, 2012
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
@@ -62,13 +62,14 @@ static void ap_interrupt_handler(void *unused1, void *unused2);
static void ap_reset(struct ap_device *ap_dev);
static void ap_config_timeout(unsigned long ptr);
static int ap_select_domain(void);
+static void ap_query_configuration(void);
/*
* Module description.
*/
MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("Adjunct Processor Bus driver, "
- "Copyright IBM Corp. 2006");
+MODULE_DESCRIPTION("Adjunct Processor Bus driver, " \
+ "Copyright IBM Corp. 2006, 2012");
MODULE_LICENSE("GPL");
/*
@@ -84,6 +85,7 @@ module_param_named(poll_thread, ap_thread_flag, int, 0000);
MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");
static struct device *ap_root_device = NULL;
+static struct ap_config_info *ap_configuration;
static DEFINE_SPINLOCK(ap_device_list_lock);
static LIST_HEAD(ap_device_list);
@@ -158,6 +160,19 @@ static int ap_interrupts_available(void)
}
/**
+ * ap_configuration_available(): Test if AP configuration
+ * information is available.
+ *
+ * Returns 1 if AP configuration information is available.
+ */
+#ifdef CONFIG_64BIT
+static int ap_configuration_available(void)
+{
+ return test_facility(2) && test_facility(12);
+}
+#endif
+
+/**
* ap_test_queue(): Test adjunct processor queue.
* @qid: The AP queue number
* @queue_depth: Pointer to queue depth value
@@ -242,6 +257,26 @@ __ap_query_functions(ap_qid_t qid, unsigned int *functions)
}
#endif
+#ifdef CONFIG_64BIT
+static inline int __ap_query_configuration(struct ap_config_info *config)
+{
+ register unsigned long reg0 asm ("0") = 0x04000000UL;
+ register unsigned long reg1 asm ("1") = -EINVAL;
+ register unsigned char *reg2 asm ("2") = (unsigned char *)config;
+
+ asm volatile(
+ ".long 0xb2af0000\n" /* PQAP(QCI) */
+ "0: la %1,0\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (reg0), "+d" (reg1), "+d" (reg2)
+ :
+ : "cc");
+
+ return reg1;
+}
+#endif
+
/**
* ap_query_functions(): Query supported functions.
* @qid: The AP queue number
@@ -292,25 +327,6 @@ static int ap_query_functions(ap_qid_t qid, unsigned int *functions)
}
/**
- * ap_4096_commands_availablen(): Check for availability of 4096 bit RSA
- * support.
- * @qid: The AP queue number
- *
- * Returns 1 if 4096 bit RSA keys are support fo the AP, returns 0 if not.
- */
-int ap_4096_commands_available(ap_qid_t qid)
-{
- unsigned int functions;
-
- if (ap_query_functions(qid, &functions))
- return 0;
-
- return test_ap_facility(functions, 1) &&
- test_ap_facility(functions, 2);
-}
-EXPORT_SYMBOL(ap_4096_commands_available);
-
-/**
* ap_queue_enable_interruption(): Enable interruption on an AP.
* @qid: The AP queue number
* @ind: the notification indicator byte
@@ -657,6 +673,34 @@ static ssize_t ap_request_count_show(struct device *dev,
static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL);
+static ssize_t ap_requestq_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ int rc;
+
+ spin_lock_bh(&ap_dev->lock);
+ rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->requestq_count);
+ spin_unlock_bh(&ap_dev->lock);
+ return rc;
+}
+
+static DEVICE_ATTR(requestq_count, 0444, ap_requestq_count_show, NULL);
+
+static ssize_t ap_pendingq_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ int rc;
+
+ spin_lock_bh(&ap_dev->lock);
+ rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->pendingq_count);
+ spin_unlock_bh(&ap_dev->lock);
+ return rc;
+}
+
+static DEVICE_ATTR(pendingq_count, 0444, ap_pendingq_count_show, NULL);
+
static ssize_t ap_modalias_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -665,11 +709,23 @@ static ssize_t ap_modalias_show(struct device *dev,
static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL);
+static ssize_t ap_functions_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ return snprintf(buf, PAGE_SIZE, "0x%08X\n", ap_dev->functions);
+}
+
+static DEVICE_ATTR(ap_functions, 0444, ap_functions_show, NULL);
+
static struct attribute *ap_dev_attrs[] = {
&dev_attr_hwtype.attr,
&dev_attr_depth.attr,
&dev_attr_request_count.attr,
+ &dev_attr_requestq_count.attr,
+ &dev_attr_pendingq_count.attr,
&dev_attr_modalias.attr,
+ &dev_attr_ap_functions.attr,
NULL
};
static struct attribute_group ap_dev_attr_group = {
@@ -772,6 +828,7 @@ static int ap_bus_resume(struct device *dev)
ap_suspend_flag = 0;
if (!ap_interrupts_available())
ap_interrupt_indicator = NULL;
+ ap_query_configuration();
if (!user_set_domain) {
ap_domain_index = -1;
ap_select_domain();
@@ -895,6 +952,20 @@ void ap_driver_unregister(struct ap_driver *ap_drv)
}
EXPORT_SYMBOL(ap_driver_unregister);
+void ap_bus_force_rescan(void)
+{
+ /* Delete the AP bus rescan timer. */
+ del_timer(&ap_config_timer);
+
+ /* processing a synchonuous bus rescan */
+ ap_scan_bus(NULL);
+
+ /* Setup the AP bus rescan timer again. */
+ ap_config_timer.expires = jiffies + ap_config_time * HZ;
+ add_timer(&ap_config_timer);
+}
+EXPORT_SYMBOL(ap_bus_force_rescan);
+
/*
* AP bus attributes.
*/
@@ -997,6 +1068,65 @@ static struct bus_attribute *const ap_bus_attrs[] = {
NULL,
};
+static inline int ap_test_config(unsigned int *field, unsigned int nr)
+{
+ if (nr > 0xFFu)
+ return 0;
+ return ap_test_bit((field + (nr >> 5)), (nr & 0x1f));
+}
+
+/*
+ * ap_test_config_card_id(): Test, whether an AP card ID is configured.
+ * @id AP card ID
+ *
+ * Returns 0 if the card is not configured
+ * 1 if the card is configured or
+ * if the configuration information is not available
+ */
+static inline int ap_test_config_card_id(unsigned int id)
+{
+ if (!ap_configuration)
+ return 1;
+ return ap_test_config(ap_configuration->apm, id);
+}
+
+/*
+ * ap_test_config_domain(): Test, whether an AP usage domain is configured.
+ * @domain AP usage domain ID
+ *
+ * Returns 0 if the usage domain is not configured
+ * 1 if the usage domain is configured or
+ * if the configuration information is not available
+ */
+static inline int ap_test_config_domain(unsigned int domain)
+{
+ if (!ap_configuration)
+ return 1;
+ return ap_test_config(ap_configuration->aqm, domain);
+}
+
+/**
+ * ap_query_configuration(): Query AP configuration information.
+ *
+ * Query information of installed cards and configured domains from AP.
+ */
+static void ap_query_configuration(void)
+{
+#ifdef CONFIG_64BIT
+ if (ap_configuration_available()) {
+ if (!ap_configuration)
+ ap_configuration =
+ kzalloc(sizeof(struct ap_config_info),
+ GFP_KERNEL);
+ if (ap_configuration)
+ __ap_query_configuration(ap_configuration);
+ } else
+ ap_configuration = NULL;
+#else
+ ap_configuration = NULL;
+#endif
+}
+
/**
* ap_select_domain(): Select an AP domain.
*
@@ -1005,6 +1135,7 @@ static struct bus_attribute *const ap_bus_attrs[] = {
static int ap_select_domain(void)
{
int queue_depth, device_type, count, max_count, best_domain;
+ ap_qid_t qid;
int rc, i, j;
/*
@@ -1018,9 +1149,13 @@ static int ap_select_domain(void)
best_domain = -1;
max_count = 0;
for (i = 0; i < AP_DOMAINS; i++) {
+ if (!ap_test_config_domain(i))
+ continue;
count = 0;
for (j = 0; j < AP_DEVICES; j++) {
- ap_qid_t qid = AP_MKQID(j, i);
+ if (!ap_test_config_card_id(j))
+ continue;
+ qid = AP_MKQID(j, i);
rc = ap_query_queue(qid, &queue_depth, &device_type);
if (rc)
continue;
@@ -1169,6 +1304,7 @@ static void ap_scan_bus(struct work_struct *unused)
unsigned int device_functions;
int rc, i;
+ ap_query_configuration();
if (ap_select_domain() != 0)
return;
for (i = 0; i < AP_DEVICES; i++) {
@@ -1176,7 +1312,10 @@ static void ap_scan_bus(struct work_struct *unused)
dev = bus_find_device(&ap_bus_type, NULL,
(void *)(unsigned long)qid,
__ap_scan_bus);
- rc = ap_query_queue(qid, &queue_depth, &device_type);
+ if (ap_test_config_card_id(i))
+ rc = ap_query_queue(qid, &queue_depth, &device_type);
+ else
+ rc = -ENODEV;
if (dev) {
if (rc == -EBUSY) {
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1217,29 +1356,22 @@ static void ap_scan_bus(struct work_struct *unused)
(unsigned long) ap_dev);
switch (device_type) {
case 0:
+ /* device type probing for old cards */
if (ap_probe_device_type(ap_dev)) {
kfree(ap_dev);
continue;
}
break;
- case 10:
- if (ap_query_functions(qid, &device_functions)) {
- kfree(ap_dev);
- continue;
- }
- if (test_ap_facility(device_functions, 3))
- ap_dev->device_type = AP_DEVICE_TYPE_CEX3C;
- else if (test_ap_facility(device_functions, 4))
- ap_dev->device_type = AP_DEVICE_TYPE_CEX3A;
- else {
- kfree(ap_dev);
- continue;
- }
- break;
default:
ap_dev->device_type = device_type;
}
+ rc = ap_query_functions(qid, &device_functions);
+ if (!rc)
+ ap_dev->functions = device_functions;
+ else
+ ap_dev->functions = 0u;
+
ap_dev->device.bus = &ap_bus_type;
ap_dev->device.parent = ap_root_device;
if (dev_set_name(&ap_dev->device, "card%02x",
@@ -1785,6 +1917,7 @@ int __init ap_module_init(void)
goto out_root;
}
+ ap_query_configuration();
if (ap_select_domain() == 0)
ap_scan_bus(NULL);
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index 52d6199..685f6cc0 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -1,5 +1,5 @@
/*
- * Copyright IBM Corp. 2006
+ * Copyright IBM Corp. 2006, 2012
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
@@ -83,13 +83,12 @@ int ap_queue_status_invalid_test(struct ap_queue_status *status)
return !(memcmp(status, &invalid, sizeof(struct ap_queue_status)));
}
-#define MAX_AP_FACILITY 31
-
-static inline int test_ap_facility(unsigned int function, unsigned int nr)
+#define AP_MAX_BITS 31
+static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
{
- if (nr > MAX_AP_FACILITY)
+ if (nr > AP_MAX_BITS)
return 0;
- return function & (unsigned int)(0x80000000 >> nr);
+ return (*ptr & (0x80000000u >> nr)) != 0;
}
#define AP_RESPONSE_NORMAL 0x00
@@ -117,6 +116,15 @@ static inline int test_ap_facility(unsigned int function, unsigned int nr)
#define AP_DEVICE_TYPE_CEX2C 7
#define AP_DEVICE_TYPE_CEX3A 8
#define AP_DEVICE_TYPE_CEX3C 9
+#define AP_DEVICE_TYPE_CEX4 10
+
+/*
+ * Known function facilities
+ */
+#define AP_FUNC_MEX4K 1
+#define AP_FUNC_CRT4K 2
+#define AP_FUNC_COPRO 3
+#define AP_FUNC_ACCEL 4
/*
* AP reset flag states
@@ -151,6 +159,7 @@ struct ap_device {
ap_qid_t qid; /* AP queue id. */
int queue_depth; /* AP queue depth.*/
int device_type; /* AP device type. */
+ unsigned int functions; /* AP device function bitfield. */
int unregistered; /* marks AP device as unregistered */
struct timer_list timeout; /* Timer for request timeouts. */
int reset; /* Reset required after req. timeout. */
@@ -183,6 +192,17 @@ struct ap_message {
struct ap_message *);
};
+struct ap_config_info {
+ unsigned int special_command:1;
+ unsigned int ap_extended:1;
+ unsigned char reserved1:6;
+ unsigned char reserved2[15];
+ unsigned int apm[8]; /* AP ID mask */
+ unsigned int aqm[8]; /* AP queue mask */
+ unsigned int adm[8]; /* AP domain mask */
+ unsigned char reserved4[16];
+} __packed;
+
#define AP_DEVICE(dt) \
.dev_type=(dt), \
.match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE,
@@ -211,10 +231,9 @@ int ap_recv(ap_qid_t, unsigned long long *, void *, size_t);
void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg);
void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg);
void ap_flush_queue(struct ap_device *ap_dev);
+void ap_bus_force_rescan(void);
int ap_module_init(void);
void ap_module_exit(void);
-int ap_4096_commands_available(ap_qid_t qid);
-
#endif /* _AP_BUS_H_ */
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 2f94132..31cfaa5 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -1,7 +1,7 @@
/*
* zcrypt 2.1.0
*
- * Copyright IBM Corp. 2001, 2006
+ * Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
* Cornelia Huck <cornelia.huck@de.ibm.com>
@@ -9,6 +9,7 @@
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@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
@@ -37,25 +38,39 @@
#include <linux/atomic.h>
#include <asm/uaccess.h>
#include <linux/hw_random.h>
+#include <linux/debugfs.h>
+#include <asm/debug.h>
+#include "zcrypt_debug.h"
#include "zcrypt_api.h"
/*
* Module description.
*/
MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("Cryptographic Coprocessor interface, "
- "Copyright IBM Corp. 2001, 2006");
+MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " \
+ "Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
static DEFINE_SPINLOCK(zcrypt_device_lock);
static LIST_HEAD(zcrypt_device_list);
static int zcrypt_device_count = 0;
static atomic_t zcrypt_open_count = ATOMIC_INIT(0);
+static atomic_t zcrypt_rescan_count = ATOMIC_INIT(0);
+
+atomic_t zcrypt_rescan_req = ATOMIC_INIT(0);
+EXPORT_SYMBOL(zcrypt_rescan_req);
static int zcrypt_rng_device_add(void);
static void zcrypt_rng_device_remove(void);
+static DEFINE_SPINLOCK(zcrypt_ops_list_lock);
+static LIST_HEAD(zcrypt_ops_list);
+
+static debug_info_t *zcrypt_dbf_common;
+static debug_info_t *zcrypt_dbf_devices;
+static struct dentry *debugfs_root;
+
/*
* Device attributes common for all crypto devices.
*/
@@ -85,6 +100,8 @@ static ssize_t zcrypt_online_store(struct device *dev,
if (sscanf(buf, "%d\n", &online) != 1 || online < 0 || online > 1)
return -EINVAL;
zdev->online = online;
+ ZCRYPT_DBF_DEV(DBF_INFO, zdev, "dev%04xo%dman", zdev->ap_dev->qid,
+ zdev->online);
if (!online)
ap_flush_queue(zdev->ap_dev);
return count;
@@ -103,6 +120,24 @@ static struct attribute_group zcrypt_device_attr_group = {
};
/**
+ * Process a rescan of the transport layer.
+ *
+ * Returns 1, if the rescan has been processed, otherwise 0.
+ */
+static inline int zcrypt_process_rescan(void)
+{
+ if (atomic_read(&zcrypt_rescan_req)) {
+ atomic_set(&zcrypt_rescan_req, 0);
+ atomic_inc(&zcrypt_rescan_count);
+ ap_bus_force_rescan();
+ ZCRYPT_DBF_COMMON(DBF_INFO, "rescan%07d",
+ atomic_inc_return(&zcrypt_rescan_count));
+ return 1;
+ }
+ return 0;
+}
+
+/**
* __zcrypt_increase_preference(): Increase preference of a crypto device.
* @zdev: Pointer the crypto device
*
@@ -190,6 +225,7 @@ struct zcrypt_device *zcrypt_device_alloc(size_t max_response_size)
zdev->reply.length = max_response_size;
spin_lock_init(&zdev->lock);
INIT_LIST_HEAD(&zdev->list);
+ zdev->dbf_area = zcrypt_dbf_devices;
return zdev;
out_free:
@@ -215,6 +251,8 @@ int zcrypt_device_register(struct zcrypt_device *zdev)
{
int rc;
+ if (!zdev->ops)
+ return -ENODEV;
rc = sysfs_create_group(&zdev->ap_dev->device.kobj,
&zcrypt_device_attr_group);
if (rc)
@@ -223,6 +261,8 @@ int zcrypt_device_register(struct zcrypt_device *zdev)
kref_init(&zdev->refcount);
spin_lock_bh(&zcrypt_device_lock);
zdev->online = 1; /* New devices are online by default. */
+ ZCRYPT_DBF_DEV(DBF_INFO, zdev, "dev%04xo%dreg", zdev->ap_dev->qid,
+ zdev->online);
list_add_tail(&zdev->list, &zcrypt_device_list);
__zcrypt_increase_preference(zdev);
zcrypt_device_count++;
@@ -269,6 +309,67 @@ void zcrypt_device_unregister(struct zcrypt_device *zdev)
}
EXPORT_SYMBOL(zcrypt_device_unregister);
+void zcrypt_msgtype_register(struct zcrypt_ops *zops)
+{
+ if (zops->owner) {
+ spin_lock_bh(&zcrypt_ops_list_lock);
+ list_add_tail(&zops->list, &zcrypt_ops_list);
+ spin_unlock_bh(&zcrypt_ops_list_lock);
+ }
+}
+EXPORT_SYMBOL(zcrypt_msgtype_register);
+
+void zcrypt_msgtype_unregister(struct zcrypt_ops *zops)
+{
+ spin_lock_bh(&zcrypt_ops_list_lock);
+ list_del_init(&zops->list);
+ spin_unlock_bh(&zcrypt_ops_list_lock);
+}
+EXPORT_SYMBOL(zcrypt_msgtype_unregister);
+
+static inline
+struct zcrypt_ops *__ops_lookup(unsigned char *name, int variant)
+{
+ struct zcrypt_ops *zops;
+ int found = 0;
+
+ spin_lock_bh(&zcrypt_ops_list_lock);
+ list_for_each_entry(zops, &zcrypt_ops_list, list) {
+ if ((zops->variant == variant) &&
+ (!strncmp(zops->owner->name, name, MODULE_NAME_LEN))) {
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&zcrypt_ops_list_lock);
+
+ if (!found)
+ return NULL;
+ return zops;
+}
+
+struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *name, int variant)
+{
+ struct zcrypt_ops *zops = NULL;
+
+ zops = __ops_lookup(name, variant);
+ if (!zops) {
+ request_module(name);
+ zops = __ops_lookup(name, variant);
+ }
+ if ((!zops) || (!try_module_get(zops->owner)))
+ return NULL;
+ return zops;
+}
+EXPORT_SYMBOL(zcrypt_msgtype_request);
+
+void zcrypt_msgtype_release(struct zcrypt_ops *zops)
+{
+ if (zops)
+ module_put(zops->owner);
+}
+EXPORT_SYMBOL(zcrypt_msgtype_release);
+
/**
* zcrypt_read (): Not supported beyond zcrypt 1.3.1.
*
@@ -640,6 +741,11 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_rsa_modexpo(&mex);
} while (rc == -EAGAIN);
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_rsa_modexpo(&mex);
+ } while (rc == -EAGAIN);
if (rc)
return rc;
return put_user(mex.outputdatalength, &umex->outputdatalength);
@@ -652,6 +758,11 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_rsa_crt(&crt);
} while (rc == -EAGAIN);
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_rsa_crt(&crt);
+ } while (rc == -EAGAIN);
if (rc)
return rc;
return put_user(crt.outputdatalength, &ucrt->outputdatalength);
@@ -664,6 +775,11 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_send_cprb(&xcRB);
} while (rc == -EAGAIN);
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_send_cprb(&xcRB);
+ } while (rc == -EAGAIN);
if (copy_to_user(uxcRB, &xcRB, sizeof(xcRB)))
return -EFAULT;
return rc;
@@ -770,10 +886,15 @@ static long trans_modexpo32(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_rsa_modexpo(&mex64);
} while (rc == -EAGAIN);
- if (!rc)
- rc = put_user(mex64.outputdatalength,
- &umex32->outputdatalength);
- return rc;
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_rsa_modexpo(&mex64);
+ } while (rc == -EAGAIN);
+ if (rc)
+ return rc;
+ return put_user(mex64.outputdatalength,
+ &umex32->outputdatalength);
}
struct compat_ica_rsa_modexpo_crt {
@@ -810,10 +931,15 @@ static long trans_modexpo_crt32(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_rsa_crt(&crt64);
} while (rc == -EAGAIN);
- if (!rc)
- rc = put_user(crt64.outputdatalength,
- &ucrt32->outputdatalength);
- return rc;
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_rsa_crt(&crt64);
+ } while (rc == -EAGAIN);
+ if (rc)
+ return rc;
+ return put_user(crt64.outputdatalength,
+ &ucrt32->outputdatalength);
}
struct compat_ica_xcRB {
@@ -869,6 +995,11 @@ static long trans_xcRB32(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_send_cprb(&xcRB64);
} while (rc == -EAGAIN);
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_send_cprb(&xcRB64);
+ } while (rc == -EAGAIN);
xcRB32.reply_control_blk_length = xcRB64.reply_control_blk_length;
xcRB32.reply_data_length = xcRB64.reply_data_length;
xcRB32.status = xcRB64.status;
@@ -1126,6 +1257,9 @@ static int zcrypt_rng_data_read(struct hwrng *rng, u32 *data)
*/
if (zcrypt_rng_buffer_index == 0) {
rc = zcrypt_rng((char *) zcrypt_rng_buffer);
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ rc = zcrypt_rng((char *) zcrypt_rng_buffer);
if (rc < 0)
return -EIO;
zcrypt_rng_buffer_index = rc / sizeof *data;
@@ -1178,6 +1312,30 @@ static void zcrypt_rng_device_remove(void)
mutex_unlock(&zcrypt_rng_mutex);
}
+int __init zcrypt_debug_init(void)
+{
+ debugfs_root = debugfs_create_dir("zcrypt", NULL);
+
+ zcrypt_dbf_common = debug_register("zcrypt_common", 1, 1, 16);
+ debug_register_view(zcrypt_dbf_common, &debug_hex_ascii_view);
+ debug_set_level(zcrypt_dbf_common, DBF_ERR);
+
+ zcrypt_dbf_devices = debug_register("zcrypt_devices", 1, 1, 16);
+ debug_register_view(zcrypt_dbf_devices, &debug_hex_ascii_view);
+ debug_set_level(zcrypt_dbf_devices, DBF_ERR);
+
+ return 0;
+}
+
+void zcrypt_debug_exit(void)
+{
+ debugfs_remove(debugfs_root);
+ if (zcrypt_dbf_common)
+ debug_unregister(zcrypt_dbf_common);
+ if (zcrypt_dbf_devices)
+ debug_unregister(zcrypt_dbf_devices);
+}
+
/**
* zcrypt_api_init(): Module initialization.
*
@@ -1187,6 +1345,12 @@ int __init zcrypt_api_init(void)
{
int rc;
+ rc = zcrypt_debug_init();
+ if (rc)
+ goto out;
+
+ atomic_set(&zcrypt_rescan_req, 0);
+
/* Register the request sprayer. */
rc = misc_register(&zcrypt_misc_device);
if (rc < 0)
@@ -1216,6 +1380,7 @@ void zcrypt_api_exit(void)
{
remove_proc_entry("driver/z90crypt", NULL);
misc_deregister(&zcrypt_misc_device);
+ zcrypt_debug_exit();
}
module_init(zcrypt_api_init);
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 7a32c4b..8963291 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -1,7 +1,7 @@
/*
* zcrypt 2.1.0
*
- * Copyright IBM Corp. 2001, 2006
+ * Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
* Cornelia Huck <cornelia.huck@de.ibm.com>
@@ -9,6 +9,7 @@
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@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
@@ -28,8 +29,10 @@
#ifndef _ZCRYPT_API_H_
#define _ZCRYPT_API_H_
-#include "ap_bus.h"
+#include <linux/atomic.h>
+#include <asm/debug.h>
#include <asm/zcrypt.h>
+#include "ap_bus.h"
/* deprecated status calls */
#define ICAZ90STATUS _IOR(ZCRYPT_IOCTL_MAGIC, 0x10, struct ica_z90_status)
@@ -87,6 +90,9 @@ struct zcrypt_ops {
struct ica_rsa_modexpo_crt *);
long (*send_cprb)(struct zcrypt_device *, struct ica_xcRB *);
long (*rng)(struct zcrypt_device *, char *);
+ struct list_head list; /* zcrypt ops list. */
+ struct module *owner;
+ int variant;
};
struct zcrypt_device {
@@ -108,14 +114,23 @@ struct zcrypt_device {
struct ap_message reply; /* Per-device reply structure. */
int max_exp_bit_length;
+
+ debug_info_t *dbf_area; /* debugging */
};
+/* transport layer rescanning */
+extern atomic_t zcrypt_rescan_req;
+
struct zcrypt_device *zcrypt_device_alloc(size_t);
void zcrypt_device_free(struct zcrypt_device *);
void zcrypt_device_get(struct zcrypt_device *);
int zcrypt_device_put(struct zcrypt_device *);
int zcrypt_device_register(struct zcrypt_device *);
void zcrypt_device_unregister(struct zcrypt_device *);
+void zcrypt_msgtype_register(struct zcrypt_ops *);
+void zcrypt_msgtype_unregister(struct zcrypt_ops *);
+struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *, int);
+void zcrypt_msgtype_release(struct zcrypt_ops *);
int zcrypt_api_init(void);
void zcrypt_api_exit(void);
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index 744c668..1e849d6 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -1,13 +1,14 @@
/*
* zcrypt 2.1.0
*
- * Copyright IBM Corp. 2001, 2006
+ * Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@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
@@ -35,6 +36,7 @@
#include "zcrypt_api.h"
#include "zcrypt_error.h"
#include "zcrypt_cex2a.h"
+#include "zcrypt_msgtype50.h"
#define CEX2A_MIN_MOD_SIZE 1 /* 8 bits */
#define CEX2A_MAX_MOD_SIZE 256 /* 2048 bits */
@@ -63,14 +65,12 @@ static struct ap_device_id zcrypt_cex2a_ids[] = {
MODULE_DEVICE_TABLE(ap, zcrypt_cex2a_ids);
MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, "
- "Copyright IBM Corp. 2001, 2006");
+MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, " \
+ "Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
static int zcrypt_cex2a_probe(struct ap_device *ap_dev);
static void zcrypt_cex2a_remove(struct ap_device *ap_dev);
-static void zcrypt_cex2a_receive(struct ap_device *, struct ap_message *,
- struct ap_message *);
static struct ap_driver zcrypt_cex2a_driver = {
.probe = zcrypt_cex2a_probe,
@@ -80,344 +80,6 @@ static struct ap_driver zcrypt_cex2a_driver = {
};
/**
- * Convert a ICAMEX message to a type50 MEX message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @mex: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo *mex)
-{
- unsigned char *mod, *exp, *inp;
- int mod_len;
-
- mod_len = mex->inputdatalength;
-
- if (mod_len <= 128) {
- struct type50_meb1_msg *meb1 = ap_msg->message;
- memset(meb1, 0, sizeof(*meb1));
- ap_msg->length = sizeof(*meb1);
- meb1->header.msg_type_code = TYPE50_TYPE_CODE;
- meb1->header.msg_len = sizeof(*meb1);
- meb1->keyblock_type = TYPE50_MEB1_FMT;
- mod = meb1->modulus + sizeof(meb1->modulus) - mod_len;
- exp = meb1->exponent + sizeof(meb1->exponent) - mod_len;
- inp = meb1->message + sizeof(meb1->message) - mod_len;
- } else if (mod_len <= 256) {
- struct type50_meb2_msg *meb2 = ap_msg->message;
- memset(meb2, 0, sizeof(*meb2));
- ap_msg->length = sizeof(*meb2);
- meb2->header.msg_type_code = TYPE50_TYPE_CODE;
- meb2->header.msg_len = sizeof(*meb2);
- meb2->keyblock_type = TYPE50_MEB2_FMT;
- mod = meb2->modulus + sizeof(meb2->modulus) - mod_len;
- exp = meb2->exponent + sizeof(meb2->exponent) - mod_len;
- inp = meb2->message + sizeof(meb2->message) - mod_len;
- } else {
- /* mod_len > 256 = 4096 bit RSA Key */
- struct type50_meb3_msg *meb3 = ap_msg->message;
- memset(meb3, 0, sizeof(*meb3));
- ap_msg->length = sizeof(*meb3);
- meb3->header.msg_type_code = TYPE50_TYPE_CODE;
- meb3->header.msg_len = sizeof(*meb3);
- meb3->keyblock_type = TYPE50_MEB3_FMT;
- mod = meb3->modulus + sizeof(meb3->modulus) - mod_len;
- exp = meb3->exponent + sizeof(meb3->exponent) - mod_len;
- inp = meb3->message + sizeof(meb3->message) - mod_len;
- }
-
- if (copy_from_user(mod, mex->n_modulus, mod_len) ||
- copy_from_user(exp, mex->b_key, mod_len) ||
- copy_from_user(inp, mex->inputdata, mod_len))
- return -EFAULT;
- return 0;
-}
-
-/**
- * Convert a ICACRT message to a type50 CRT message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @crt: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo_crt *crt)
-{
- int mod_len, short_len, long_len, long_offset, limit;
- unsigned char *p, *q, *dp, *dq, *u, *inp;
-
- mod_len = crt->inputdatalength;
- short_len = mod_len / 2;
- long_len = mod_len / 2 + 8;
-
- /*
- * CEX2A cannot handle p, dp, or U > 128 bytes.
- * If we have one of these, we need to do extra checking.
- * For CEX3A the limit is 256 bytes.
- */
- if (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE)
- limit = 256;
- else
- limit = 128;
-
- if (long_len > limit) {
- /*
- * zcrypt_rsa_crt already checked for the leading
- * zeroes of np_prime, bp_key and u_mult_inc.
- */
- long_offset = long_len - limit;
- long_len = limit;
- } else
- long_offset = 0;
-
- /*
- * Instead of doing extra work for p, dp, U > 64 bytes, we'll just use
- * the larger message structure.
- */
- if (long_len <= 64) {
- struct type50_crb1_msg *crb1 = ap_msg->message;
- memset(crb1, 0, sizeof(*crb1));
- ap_msg->length = sizeof(*crb1);
- crb1->header.msg_type_code = TYPE50_TYPE_CODE;
- crb1->header.msg_len = sizeof(*crb1);
- crb1->keyblock_type = TYPE50_CRB1_FMT;
- p = crb1->p + sizeof(crb1->p) - long_len;
- q = crb1->q + sizeof(crb1->q) - short_len;
- dp = crb1->dp + sizeof(crb1->dp) - long_len;
- dq = crb1->dq + sizeof(crb1->dq) - short_len;
- u = crb1->u + sizeof(crb1->u) - long_len;
- inp = crb1->message + sizeof(crb1->message) - mod_len;
- } else if (long_len <= 128) {
- struct type50_crb2_msg *crb2 = ap_msg->message;
- memset(crb2, 0, sizeof(*crb2));
- ap_msg->length = sizeof(*crb2);
- crb2->header.msg_type_code = TYPE50_TYPE_CODE;
- crb2->header.msg_len = sizeof(*crb2);
- crb2->keyblock_type = TYPE50_CRB2_FMT;
- p = crb2->p + sizeof(crb2->p) - long_len;
- q = crb2->q + sizeof(crb2->q) - short_len;
- dp = crb2->dp + sizeof(crb2->dp) - long_len;
- dq = crb2->dq + sizeof(crb2->dq) - short_len;
- u = crb2->u + sizeof(crb2->u) - long_len;
- inp = crb2->message + sizeof(crb2->message) - mod_len;
- } else {
- /* long_len >= 256 */
- struct type50_crb3_msg *crb3 = ap_msg->message;
- memset(crb3, 0, sizeof(*crb3));
- ap_msg->length = sizeof(*crb3);
- crb3->header.msg_type_code = TYPE50_TYPE_CODE;
- crb3->header.msg_len = sizeof(*crb3);
- crb3->keyblock_type = TYPE50_CRB3_FMT;
- p = crb3->p + sizeof(crb3->p) - long_len;
- q = crb3->q + sizeof(crb3->q) - short_len;
- dp = crb3->dp + sizeof(crb3->dp) - long_len;
- dq = crb3->dq + sizeof(crb3->dq) - short_len;
- u = crb3->u + sizeof(crb3->u) - long_len;
- inp = crb3->message + sizeof(crb3->message) - mod_len;
- }
-
- if (copy_from_user(p, crt->np_prime + long_offset, long_len) ||
- copy_from_user(q, crt->nq_prime, short_len) ||
- copy_from_user(dp, crt->bp_key + long_offset, long_len) ||
- copy_from_user(dq, crt->bq_key, short_len) ||
- copy_from_user(u, crt->u_mult_inv + long_offset, long_len) ||
- copy_from_user(inp, crt->inputdata, mod_len))
- return -EFAULT;
-
- return 0;
-}
-
-/**
- * Copy results from a type 80 reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @data: pointer to user output data
- * @length: size of user output data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int convert_type80(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- struct type80_hdr *t80h = reply->message;
- unsigned char *data;
-
- if (t80h->len < sizeof(*t80h) + outputdatalength) {
- /* The result is too short, the CEX2A card may not do that.. */
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
- if (zdev->user_space_type == ZCRYPT_CEX2A)
- BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE);
- else
- BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE);
- data = reply->message + t80h->len - outputdatalength;
- if (copy_to_user(outputdata, data, outputdatalength))
- return -EFAULT;
- return 0;
-}
-
-static int convert_response(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- /* Response type byte is the second byte in the response. */
- switch (((unsigned char *) reply->message)[1]) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- return convert_error(zdev, reply);
- case TYPE80_RSP_CODE:
- return convert_type80(zdev, reply,
- outputdata, outputdatalength);
- default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-/**
- * This function is called from the AP bus code after a crypto request
- * "msg" has finished with the reply message "reply".
- * It is called from tasklet context.
- * @ap_dev: pointer to the AP device
- * @msg: pointer to the AP message
- * @reply: pointer to the AP reply message
- */
-static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
- struct ap_message *msg,
- struct ap_message *reply)
-{
- static struct error_hdr error_reply = {
- .type = TYPE82_RSP_CODE,
- .reply_code = REP82_ERROR_MACHINE_FAILURE,
- };
- struct type80_hdr *t80h;
- int length;
-
- /* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
- t80h = reply->message;
- if (t80h->type == TYPE80_RSP_CODE) {
- if (ap_dev->device_type == AP_DEVICE_TYPE_CEX2A)
- length = min(CEX2A_MAX_RESPONSE_SIZE, (int) t80h->len);
- else
- length = min(CEX3A_MAX_RESPONSE_SIZE, (int) t80h->len);
- memcpy(msg->message, reply->message, length);
- } else
- memcpy(msg->message, reply->message, sizeof error_reply);
-out:
- complete((struct completion *) msg->private);
-}
-
-static atomic_t zcrypt_step = ATOMIC_INIT(0);
-
-/**
- * The request distributor calls this function if it picked the CEX2A
- * device to handle a modexpo request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * CEX2A device to the request distributor
- * @mex: pointer to the modexpo request buffer
- */
-static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo *mex)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- if (zdev->user_space_type == ZCRYPT_CEX2A)
- ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
- else
- ap_msg.message = kmalloc(CEX3A_MAX_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_cex2a_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICAMEX_msg_to_type50MEX_msg(zdev, &ap_msg, mex);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the CEX2A
- * device to handle a modexpo_crt request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * CEX2A device to the request distributor
- * @crt: pointer to the modexpoc_crt request buffer
- */
-static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo_crt *crt)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- if (zdev->user_space_type == ZCRYPT_CEX2A)
- ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
- else
- ap_msg.message = kmalloc(CEX3A_MAX_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_cex2a_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICACRT_msg_to_type50CRT_msg(zdev, &ap_msg, crt);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The crypto operations for a CEX2A card.
- */
-static struct zcrypt_ops zcrypt_cex2a_ops = {
- .rsa_modexpo = zcrypt_cex2a_modexpo,
- .rsa_modexpo_crt = zcrypt_cex2a_modexpo_crt,
-};
-
-/**
* Probe function for CEX2A cards. It always accepts the AP device
* since the bus_match already checked the hardware type.
* @ap_dev: pointer to the AP device.
@@ -449,7 +111,8 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
zdev->min_mod_size = CEX2A_MIN_MOD_SIZE;
zdev->max_mod_size = CEX2A_MAX_MOD_SIZE;
zdev->max_exp_bit_length = CEX2A_MAX_MOD_SIZE;
- if (ap_4096_commands_available(ap_dev->qid)) {
+ if (ap_test_bit(&ap_dev->functions, AP_FUNC_MEX4K) &&
+ ap_test_bit(&ap_dev->functions, AP_FUNC_CRT4K)) {
zdev->max_mod_size = CEX3A_MAX_MOD_SIZE;
zdev->max_exp_bit_length = CEX3A_MAX_MOD_SIZE;
}
@@ -457,16 +120,18 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
zdev->speed_rating = CEX3A_SPEED_RATING;
break;
}
- if (zdev != NULL) {
- zdev->ap_dev = ap_dev;
- zdev->ops = &zcrypt_cex2a_ops;
- zdev->online = 1;
- ap_dev->reply = &zdev->reply;
- ap_dev->private = zdev;
- rc = zcrypt_device_register(zdev);
- }
+ if (!zdev)
+ return -ENODEV;
+ zdev->ops = zcrypt_msgtype_request(MSGTYPE50_NAME,
+ MSGTYPE50_VARIANT_DEFAULT);
+ zdev->ap_dev = ap_dev;
+ zdev->online = 1;
+ ap_dev->reply = &zdev->reply;
+ ap_dev->private = zdev;
+ rc = zcrypt_device_register(zdev);
if (rc) {
ap_dev->private = NULL;
+ zcrypt_msgtype_release(zdev->ops);
zcrypt_device_free(zdev);
}
return rc;
@@ -479,8 +144,10 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
static void zcrypt_cex2a_remove(struct ap_device *ap_dev)
{
struct zcrypt_device *zdev = ap_dev->private;
+ struct zcrypt_ops *zops = zdev->ops;
zcrypt_device_unregister(zdev);
+ zcrypt_msgtype_release(zops);
}
int __init zcrypt_cex2a_init(void)
diff --git a/drivers/s390/crypto/zcrypt_cex4.c b/drivers/s390/crypto/zcrypt_cex4.c
new file mode 100644
index 0000000..ce12263
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_cex4.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright IBM Corp. 2012
+ * Author(s): Holger Dengler <hd@linux.vnet.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_msgtype6.h"
+#include "zcrypt_msgtype50.h"
+#include "zcrypt_error.h"
+#include "zcrypt_cex4.h"
+
+#define CEX4A_MIN_MOD_SIZE 1 /* 8 bits */
+#define CEX4A_MAX_MOD_SIZE_2K 256 /* 2048 bits */
+#define CEX4A_MAX_MOD_SIZE_4K 512 /* 4096 bits */
+
+#define CEX4C_MIN_MOD_SIZE 16 /* 256 bits */
+#define CEX4C_MAX_MOD_SIZE 512 /* 4096 bits */
+
+#define CEX4A_SPEED_RATING 900 /* TODO new card, new speed rating */
+#define CEX4C_SPEED_RATING 6500 /* TODO new card, new speed rating */
+
+#define CEX4A_MAX_MESSAGE_SIZE MSGTYPE50_CRB3_MAX_MSG_SIZE
+#define CEX4C_MAX_MESSAGE_SIZE MSGTYPE06_MAX_MSG_SIZE
+
+#define CEX4_CLEANUP_TIME (15*HZ)
+
+static struct ap_device_id zcrypt_cex4_ids[] = {
+ { AP_DEVICE(AP_DEVICE_TYPE_CEX4) },
+ { /* end of list */ },
+};
+
+MODULE_DEVICE_TABLE(ap, zcrypt_cex4_ids);
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("CEX4 Cryptographic Card device driver, " \
+ "Copyright IBM Corp. 2012");
+MODULE_LICENSE("GPL");
+
+static int zcrypt_cex4_probe(struct ap_device *ap_dev);
+static void zcrypt_cex4_remove(struct ap_device *ap_dev);
+
+static struct ap_driver zcrypt_cex4_driver = {
+ .probe = zcrypt_cex4_probe,
+ .remove = zcrypt_cex4_remove,
+ .ids = zcrypt_cex4_ids,
+ .request_timeout = CEX4_CLEANUP_TIME,
+};
+
+/**
+ * Probe function for CEX4 cards. It always accepts the AP device
+ * since the bus_match already checked the hardware type.
+ * @ap_dev: pointer to the AP device.
+ */
+static int zcrypt_cex4_probe(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev = NULL;
+ int rc = 0;
+
+ switch (ap_dev->device_type) {
+ case AP_DEVICE_TYPE_CEX4:
+ if (ap_test_bit(&ap_dev->functions, AP_FUNC_ACCEL)) {
+ zdev = zcrypt_device_alloc(CEX4A_MAX_MESSAGE_SIZE);
+ if (!zdev)
+ return -ENOMEM;
+ zdev->type_string = "CEX4A";
+ zdev->user_space_type = ZCRYPT_CEX3A;
+ zdev->min_mod_size = CEX4A_MIN_MOD_SIZE;
+ if (ap_test_bit(&ap_dev->functions, AP_FUNC_MEX4K) &&
+ ap_test_bit(&ap_dev->functions, AP_FUNC_CRT4K)) {
+ zdev->max_mod_size =
+ CEX4A_MAX_MOD_SIZE_4K;
+ zdev->max_exp_bit_length =
+ CEX4A_MAX_MOD_SIZE_4K;
+ } else {
+ zdev->max_mod_size =
+ CEX4A_MAX_MOD_SIZE_2K;
+ zdev->max_exp_bit_length =
+ CEX4A_MAX_MOD_SIZE_2K;
+ }
+ zdev->short_crt = 1;
+ zdev->speed_rating = CEX4A_SPEED_RATING;
+ zdev->ops = zcrypt_msgtype_request(MSGTYPE50_NAME,
+ MSGTYPE50_VARIANT_DEFAULT);
+ } else if (ap_test_bit(&ap_dev->functions, AP_FUNC_COPRO)) {
+ zdev = zcrypt_device_alloc(CEX4C_MAX_MESSAGE_SIZE);
+ if (!zdev)
+ return -ENOMEM;
+ zdev->type_string = "CEX4C";
+ zdev->user_space_type = ZCRYPT_CEX3C;
+ zdev->min_mod_size = CEX4C_MIN_MOD_SIZE;
+ zdev->max_mod_size = CEX4C_MAX_MOD_SIZE;
+ zdev->max_exp_bit_length = CEX4C_MAX_MOD_SIZE;
+ zdev->short_crt = 0;
+ zdev->speed_rating = CEX4C_SPEED_RATING;
+ zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
+ MSGTYPE06_VARIANT_DEFAULT);
+ }
+ break;
+ }
+ if (!zdev)
+ return -ENODEV;
+ zdev->ap_dev = ap_dev;
+ zdev->online = 1;
+ ap_dev->reply = &zdev->reply;
+ ap_dev->private = zdev;
+ rc = zcrypt_device_register(zdev);
+ if (rc) {
+ zcrypt_msgtype_release(zdev->ops);
+ ap_dev->private = NULL;
+ zcrypt_device_free(zdev);
+ }
+ return rc;
+}
+
+/**
+ * This is called to remove the extended CEX4 driver information
+ * if an AP device is removed.
+ */
+static void zcrypt_cex4_remove(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev = ap_dev->private;
+ struct zcrypt_ops *zops;
+
+ if (zdev) {
+ zops = zdev->ops;
+ zcrypt_device_unregister(zdev);
+ zcrypt_msgtype_release(zops);
+ }
+}
+
+int __init zcrypt_cex4_init(void)
+{
+ return ap_driver_register(&zcrypt_cex4_driver, THIS_MODULE, "cex4");
+}
+
+void __exit zcrypt_cex4_exit(void)
+{
+ ap_driver_unregister(&zcrypt_cex4_driver);
+}
+
+module_init(zcrypt_cex4_init);
+module_exit(zcrypt_cex4_exit);
diff --git a/drivers/s390/crypto/zcrypt_cex4.h b/drivers/s390/crypto/zcrypt_cex4.h
new file mode 100644
index 0000000..7195713
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_cex4.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright IBM Corp. 2012
+ * Author(s): Holger Dengler <hd@linux.vnet.ibm.com>
+ */
+
+#ifndef _ZCRYPT_CEX4_H_
+#define _ZCRYPT_CEX4_H_
+
+int zcrypt_cex4_init(void);
+void zcrypt_cex4_exit(void);
+
+#endif /* _ZCRYPT_CEX4_H_ */
diff --git a/drivers/s390/crypto/zcrypt_debug.h b/drivers/s390/crypto/zcrypt_debug.h
new file mode 100644
index 0000000..841ea72
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_debug.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright IBM Corp. 2012
+ * Author(s): Holger Dengler (hd@linux.vnet.ibm.com)
+ */
+#ifndef ZCRYPT_DEBUG_H
+#define ZCRYPT_DEBUG_H
+
+#include <asm/debug.h>
+#include "zcrypt_api.h"
+
+/* that gives us 15 characters in the text event views */
+#define ZCRYPT_DBF_LEN 16
+
+/* sort out low debug levels early to avoid wasted sprints */
+static inline int zcrypt_dbf_passes(debug_info_t *dbf_grp, int level)
+{
+ return (level <= dbf_grp->level);
+}
+
+#define DBF_ERR 3 /* error conditions */
+#define DBF_WARN 4 /* warning conditions */
+#define DBF_INFO 6 /* informational */
+
+#define RC2WARN(rc) ((rc) ? DBF_WARN : DBF_INFO)
+
+#define ZCRYPT_DBF_COMMON(level, text...) \
+ do { \
+ if (zcrypt_dbf_passes(zcrypt_dbf_common, level)) { \
+ char debug_buffer[ZCRYPT_DBF_LEN]; \
+ snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
+ debug_text_event(zcrypt_dbf_common, level, \
+ debug_buffer); \
+ } \
+ } while (0)
+
+#define ZCRYPT_DBF_DEVICES(level, text...) \
+ do { \
+ if (zcrypt_dbf_passes(zcrypt_dbf_devices, level)) { \
+ char debug_buffer[ZCRYPT_DBF_LEN]; \
+ snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
+ debug_text_event(zcrypt_dbf_devices, level, \
+ debug_buffer); \
+ } \
+ } while (0)
+
+#define ZCRYPT_DBF_DEV(level, device, text...) \
+ do { \
+ if (zcrypt_dbf_passes(device->dbf_area, level)) { \
+ char debug_buffer[ZCRYPT_DBF_LEN]; \
+ snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
+ debug_text_event(device->dbf_area, level, \
+ debug_buffer); \
+ } \
+ } while (0)
+
+int zcrypt_debug_init(void);
+void zcrypt_debug_exit(void);
+
+#endif /* ZCRYPT_DEBUG_H */
diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h
index 0965e26..0079b66 100644
--- a/drivers/s390/crypto/zcrypt_error.h
+++ b/drivers/s390/crypto/zcrypt_error.h
@@ -26,6 +26,8 @@
#ifndef _ZCRYPT_ERROR_H_
#define _ZCRYPT_ERROR_H_
+#include <linux/atomic.h>
+#include "zcrypt_debug.h"
#include "zcrypt_api.h"
/**
@@ -108,16 +110,27 @@ static inline int convert_error(struct zcrypt_device *zdev,
* and then repeat the request.
*/
WARN_ON(1);
+ atomic_set(&zcrypt_rescan_req, 1);
zdev->online = 0;
+ ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
+ zdev->ap_dev->qid,
+ zdev->online, ehdr->reply_code);
return -EAGAIN;
case REP82_ERROR_TRANSPORT_FAIL:
case REP82_ERROR_MACHINE_FAILURE:
// REP88_ERROR_MODULE_FAILURE // '10' CEX2A
/* If a card fails disable it and repeat the request. */
+ atomic_set(&zcrypt_rescan_req, 1);
zdev->online = 0;
+ ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
+ zdev->ap_dev->qid,
+ zdev->online, ehdr->reply_code);
return -EAGAIN;
default:
zdev->online = 0;
+ ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
+ zdev->ap_dev->qid,
+ zdev->online, ehdr->reply_code);
return -EAGAIN; /* repeat the request on a different device. */
}
}
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.c b/drivers/s390/crypto/zcrypt_msgtype50.c
new file mode 100644
index 0000000..035b6dc
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype50.c
@@ -0,0 +1,531 @@
+/*
+ * zcrypt 2.1.0
+ *
+ * Copyright IBM Corp. 2001, 2012
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_error.h"
+#include "zcrypt_msgtype50.h"
+
+#define CEX3A_MAX_MOD_SIZE 512 /* 4096 bits */
+
+#define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */
+
+#define CEX3A_MAX_RESPONSE_SIZE 0x210 /* 512 bit modulus
+ * (max outputdatalength) +
+ * type80_hdr*/
+
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("Cryptographic Accelerator (message type 50), " \
+ "Copyright IBM Corp. 2001, 2012");
+MODULE_LICENSE("GPL");
+
+static void zcrypt_cex2a_receive(struct ap_device *, struct ap_message *,
+ struct ap_message *);
+
+/**
+ * The type 50 message family is associated with a CEX2A card.
+ *
+ * The four members of the family are described below.
+ *
+ * Note that all unsigned char arrays are right-justified and left-padded
+ * with zeroes.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type50_hdr {
+ unsigned char reserved1;
+ unsigned char msg_type_code; /* 0x50 */
+ unsigned short msg_len;
+ unsigned char reserved2;
+ unsigned char ignored;
+ unsigned short reserved3;
+} __packed;
+
+#define TYPE50_TYPE_CODE 0x50
+
+#define TYPE50_MEB1_FMT 0x0001
+#define TYPE50_MEB2_FMT 0x0002
+#define TYPE50_MEB3_FMT 0x0003
+#define TYPE50_CRB1_FMT 0x0011
+#define TYPE50_CRB2_FMT 0x0012
+#define TYPE50_CRB3_FMT 0x0013
+
+/* Mod-Exp, with a small modulus */
+struct type50_meb1_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0001 */
+ unsigned char reserved[6];
+ unsigned char exponent[128];
+ unsigned char modulus[128];
+ unsigned char message[128];
+} __packed;
+
+/* Mod-Exp, with a large modulus */
+struct type50_meb2_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0002 */
+ unsigned char reserved[6];
+ unsigned char exponent[256];
+ unsigned char modulus[256];
+ unsigned char message[256];
+} __packed;
+
+/* Mod-Exp, with a larger modulus */
+struct type50_meb3_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0003 */
+ unsigned char reserved[6];
+ unsigned char exponent[512];
+ unsigned char modulus[512];
+ unsigned char message[512];
+} __packed;
+
+/* CRT, with a small modulus */
+struct type50_crb1_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0011 */
+ unsigned char reserved[6];
+ unsigned char p[64];
+ unsigned char q[64];
+ unsigned char dp[64];
+ unsigned char dq[64];
+ unsigned char u[64];
+ unsigned char message[128];
+} __packed;
+
+/* CRT, with a large modulus */
+struct type50_crb2_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0012 */
+ unsigned char reserved[6];
+ unsigned char p[128];
+ unsigned char q[128];
+ unsigned char dp[128];
+ unsigned char dq[128];
+ unsigned char u[128];
+ unsigned char message[256];
+} __packed;
+
+/* CRT, with a larger modulus */
+struct type50_crb3_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0013 */
+ unsigned char reserved[6];
+ unsigned char p[256];
+ unsigned char q[256];
+ unsigned char dp[256];
+ unsigned char dq[256];
+ unsigned char u[256];
+ unsigned char message[512];
+} __packed;
+
+/**
+ * The type 80 response family is associated with a CEX2A card.
+ *
+ * Note that all unsigned char arrays are right-justified and left-padded
+ * with zeroes.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+
+#define TYPE80_RSP_CODE 0x80
+
+struct type80_hdr {
+ unsigned char reserved1;
+ unsigned char type; /* 0x80 */
+ unsigned short len;
+ unsigned char code; /* 0x00 */
+ unsigned char reserved2[3];
+ unsigned char reserved3[8];
+} __packed;
+
+/**
+ * Convert a ICAMEX message to a type50 MEX message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @mex: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo *mex)
+{
+ unsigned char *mod, *exp, *inp;
+ int mod_len;
+
+ mod_len = mex->inputdatalength;
+
+ if (mod_len <= 128) {
+ struct type50_meb1_msg *meb1 = ap_msg->message;
+ memset(meb1, 0, sizeof(*meb1));
+ ap_msg->length = sizeof(*meb1);
+ meb1->header.msg_type_code = TYPE50_TYPE_CODE;
+ meb1->header.msg_len = sizeof(*meb1);
+ meb1->keyblock_type = TYPE50_MEB1_FMT;
+ mod = meb1->modulus + sizeof(meb1->modulus) - mod_len;
+ exp = meb1->exponent + sizeof(meb1->exponent) - mod_len;
+ inp = meb1->message + sizeof(meb1->message) - mod_len;
+ } else if (mod_len <= 256) {
+ struct type50_meb2_msg *meb2 = ap_msg->message;
+ memset(meb2, 0, sizeof(*meb2));
+ ap_msg->length = sizeof(*meb2);
+ meb2->header.msg_type_code = TYPE50_TYPE_CODE;
+ meb2->header.msg_len = sizeof(*meb2);
+ meb2->keyblock_type = TYPE50_MEB2_FMT;
+ mod = meb2->modulus + sizeof(meb2->modulus) - mod_len;
+ exp = meb2->exponent + sizeof(meb2->exponent) - mod_len;
+ inp = meb2->message + sizeof(meb2->message) - mod_len;
+ } else {
+ /* mod_len > 256 = 4096 bit RSA Key */
+ struct type50_meb3_msg *meb3 = ap_msg->message;
+ memset(meb3, 0, sizeof(*meb3));
+ ap_msg->length = sizeof(*meb3);
+ meb3->header.msg_type_code = TYPE50_TYPE_CODE;
+ meb3->header.msg_len = sizeof(*meb3);
+ meb3->keyblock_type = TYPE50_MEB3_FMT;
+ mod = meb3->modulus + sizeof(meb3->modulus) - mod_len;
+ exp = meb3->exponent + sizeof(meb3->exponent) - mod_len;
+ inp = meb3->message + sizeof(meb3->message) - mod_len;
+ }
+
+ if (copy_from_user(mod, mex->n_modulus, mod_len) ||
+ copy_from_user(exp, mex->b_key, mod_len) ||
+ copy_from_user(inp, mex->inputdata, mod_len))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Convert a ICACRT message to a type50 CRT message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @crt: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ int mod_len, short_len, long_len, long_offset, limit;
+ unsigned char *p, *q, *dp, *dq, *u, *inp;
+
+ mod_len = crt->inputdatalength;
+ short_len = mod_len / 2;
+ long_len = mod_len / 2 + 8;
+
+ /*
+ * CEX2A cannot handle p, dp, or U > 128 bytes.
+ * If we have one of these, we need to do extra checking.
+ * For CEX3A the limit is 256 bytes.
+ */
+ if (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE)
+ limit = 256;
+ else
+ limit = 128;
+
+ if (long_len > limit) {
+ /*
+ * zcrypt_rsa_crt already checked for the leading
+ * zeroes of np_prime, bp_key and u_mult_inc.
+ */
+ long_offset = long_len - limit;
+ long_len = limit;
+ } else
+ long_offset = 0;
+
+ /*
+ * Instead of doing extra work for p, dp, U > 64 bytes, we'll just use
+ * the larger message structure.
+ */
+ if (long_len <= 64) {
+ struct type50_crb1_msg *crb1 = ap_msg->message;
+ memset(crb1, 0, sizeof(*crb1));
+ ap_msg->length = sizeof(*crb1);
+ crb1->header.msg_type_code = TYPE50_TYPE_CODE;
+ crb1->header.msg_len = sizeof(*crb1);
+ crb1->keyblock_type = TYPE50_CRB1_FMT;
+ p = crb1->p + sizeof(crb1->p) - long_len;
+ q = crb1->q + sizeof(crb1->q) - short_len;
+ dp = crb1->dp + sizeof(crb1->dp) - long_len;
+ dq = crb1->dq + sizeof(crb1->dq) - short_len;
+ u = crb1->u + sizeof(crb1->u) - long_len;
+ inp = crb1->message + sizeof(crb1->message) - mod_len;
+ } else if (long_len <= 128) {
+ struct type50_crb2_msg *crb2 = ap_msg->message;
+ memset(crb2, 0, sizeof(*crb2));
+ ap_msg->length = sizeof(*crb2);
+ crb2->header.msg_type_code = TYPE50_TYPE_CODE;
+ crb2->header.msg_len = sizeof(*crb2);
+ crb2->keyblock_type = TYPE50_CRB2_FMT;
+ p = crb2->p + sizeof(crb2->p) - long_len;
+ q = crb2->q + sizeof(crb2->q) - short_len;
+ dp = crb2->dp + sizeof(crb2->dp) - long_len;
+ dq = crb2->dq + sizeof(crb2->dq) - short_len;
+ u = crb2->u + sizeof(crb2->u) - long_len;
+ inp = crb2->message + sizeof(crb2->message) - mod_len;
+ } else {
+ /* long_len >= 256 */
+ struct type50_crb3_msg *crb3 = ap_msg->message;
+ memset(crb3, 0, sizeof(*crb3));
+ ap_msg->length = sizeof(*crb3);
+ crb3->header.msg_type_code = TYPE50_TYPE_CODE;
+ crb3->header.msg_len = sizeof(*crb3);
+ crb3->keyblock_type = TYPE50_CRB3_FMT;
+ p = crb3->p + sizeof(crb3->p) - long_len;
+ q = crb3->q + sizeof(crb3->q) - short_len;
+ dp = crb3->dp + sizeof(crb3->dp) - long_len;
+ dq = crb3->dq + sizeof(crb3->dq) - short_len;
+ u = crb3->u + sizeof(crb3->u) - long_len;
+ inp = crb3->message + sizeof(crb3->message) - mod_len;
+ }
+
+ if (copy_from_user(p, crt->np_prime + long_offset, long_len) ||
+ copy_from_user(q, crt->nq_prime, short_len) ||
+ copy_from_user(dp, crt->bp_key + long_offset, long_len) ||
+ copy_from_user(dq, crt->bq_key, short_len) ||
+ copy_from_user(u, crt->u_mult_inv + long_offset, long_len) ||
+ copy_from_user(inp, crt->inputdata, mod_len))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * Copy results from a type 80 reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @data: pointer to user output data
+ * @length: size of user output data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int convert_type80(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ struct type80_hdr *t80h = reply->message;
+ unsigned char *data;
+
+ if (t80h->len < sizeof(*t80h) + outputdatalength) {
+ /* The result is too short, the CEX2A card may not do that.. */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+ if (zdev->user_space_type == ZCRYPT_CEX2A)
+ BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE);
+ else
+ BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE);
+ data = reply->message + t80h->len - outputdatalength;
+ if (copy_to_user(outputdata, data, outputdatalength))
+ return -EFAULT;
+ return 0;
+}
+
+static int convert_response(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ /* Response type byte is the second byte in the response. */
+ switch (((unsigned char *) reply->message)[1]) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ return convert_error(zdev, reply);
+ case TYPE80_RSP_CODE:
+ return convert_type80(zdev, reply,
+ outputdata, outputdatalength);
+ default: /* Unknown response type, this should NEVER EVER happen */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+}
+
+/**
+ * This function is called from the AP bus code after a crypto request
+ * "msg" has finished with the reply message "reply".
+ * It is called from tasklet context.
+ * @ap_dev: pointer to the AP device
+ * @msg: pointer to the AP message
+ * @reply: pointer to the AP reply message
+ */
+static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
+ struct ap_message *msg,
+ struct ap_message *reply)
+{
+ static struct error_hdr error_reply = {
+ .type = TYPE82_RSP_CODE,
+ .reply_code = REP82_ERROR_MACHINE_FAILURE,
+ };
+ struct type80_hdr *t80h;
+ int length;
+
+ /* Copy the reply message to the request message buffer. */
+ if (IS_ERR(reply)) {
+ memcpy(msg->message, &error_reply, sizeof(error_reply));
+ goto out;
+ }
+ t80h = reply->message;
+ if (t80h->type == TYPE80_RSP_CODE) {
+ if (ap_dev->device_type == AP_DEVICE_TYPE_CEX2A)
+ length = min_t(int,
+ CEX2A_MAX_RESPONSE_SIZE, t80h->len);
+ else
+ length = min_t(int,
+ CEX3A_MAX_RESPONSE_SIZE, t80h->len);
+ memcpy(msg->message, reply->message, length);
+ } else
+ memcpy(msg->message, reply->message, sizeof(error_reply));
+out:
+ complete((struct completion *) msg->private);
+}
+
+static atomic_t zcrypt_step = ATOMIC_INIT(0);
+
+/**
+ * The request distributor calls this function if it picked the CEX2A
+ * device to handle a modexpo request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * CEX2A device to the request distributor
+ * @mex: pointer to the modexpo request buffer
+ */
+static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo *mex)
+{
+ struct ap_message ap_msg;
+ struct completion work;
+ int rc;
+
+ ap_init_message(&ap_msg);
+ if (zdev->user_space_type == ZCRYPT_CEX2A)
+ ap_msg.message = kmalloc(MSGTYPE50_CRB2_MAX_MSG_SIZE,
+ GFP_KERNEL);
+ else
+ ap_msg.message = kmalloc(MSGTYPE50_CRB3_MAX_MSG_SIZE,
+ GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_cex2a_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &work;
+ rc = ICAMEX_msg_to_type50MEX_msg(zdev, &ap_msg, mex);
+ if (rc)
+ goto out_free;
+ init_completion(&work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&work);
+ if (rc == 0)
+ rc = convert_response(zdev, &ap_msg, mex->outputdata,
+ mex->outputdatalength);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the CEX2A
+ * device to handle a modexpo_crt request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * CEX2A device to the request distributor
+ * @crt: pointer to the modexpoc_crt request buffer
+ */
+static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ struct ap_message ap_msg;
+ struct completion work;
+ int rc;
+
+ ap_init_message(&ap_msg);
+ if (zdev->user_space_type == ZCRYPT_CEX2A)
+ ap_msg.message = kmalloc(MSGTYPE50_CRB2_MAX_MSG_SIZE,
+ GFP_KERNEL);
+ else
+ ap_msg.message = kmalloc(MSGTYPE50_CRB3_MAX_MSG_SIZE,
+ GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_cex2a_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &work;
+ rc = ICACRT_msg_to_type50CRT_msg(zdev, &ap_msg, crt);
+ if (rc)
+ goto out_free;
+ init_completion(&work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&work);
+ if (rc == 0)
+ rc = convert_response(zdev, &ap_msg, crt->outputdata,
+ crt->outputdatalength);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The crypto operations for message type 50.
+ */
+static struct zcrypt_ops zcrypt_msgtype50_ops = {
+ .rsa_modexpo = zcrypt_cex2a_modexpo,
+ .rsa_modexpo_crt = zcrypt_cex2a_modexpo_crt,
+ .owner = THIS_MODULE,
+ .variant = MSGTYPE50_VARIANT_DEFAULT,
+};
+
+int __init zcrypt_msgtype50_init(void)
+{
+ zcrypt_msgtype_register(&zcrypt_msgtype50_ops);
+ return 0;
+}
+
+void __exit zcrypt_msgtype50_exit(void)
+{
+ zcrypt_msgtype_unregister(&zcrypt_msgtype50_ops);
+}
+
+module_init(zcrypt_msgtype50_init);
+module_exit(zcrypt_msgtype50_exit);
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.h b/drivers/s390/crypto/zcrypt_msgtype50.h
new file mode 100644
index 0000000..e56dc72
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype50.h
@@ -0,0 +1,39 @@
+/*
+ * zcrypt 2.1.0
+ *
+ * Copyright IBM Corp. 2001, 2012
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ZCRYPT_MSGTYPE50_H_
+#define _ZCRYPT_MSGTYPE50_H_
+
+#define MSGTYPE50_NAME "zcrypt_msgtype50"
+#define MSGTYPE50_VARIANT_DEFAULT 0
+
+#define MSGTYPE50_CRB2_MAX_MSG_SIZE 0x390 /*sizeof(struct type50_crb2_msg)*/
+#define MSGTYPE50_CRB3_MAX_MSG_SIZE 0x710 /*sizeof(struct type50_crb3_msg)*/
+
+int zcrypt_msgtype50_init(void);
+void zcrypt_msgtype50_exit(void);
+
+#endif /* _ZCRYPT_MSGTYPE50_H_ */
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
new file mode 100644
index 0000000..7d97fa5
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -0,0 +1,856 @@
+/*
+ * zcrypt 2.1.0
+ *
+ * Copyright IBM Corp. 2001, 2012
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_error.h"
+#include "zcrypt_msgtype6.h"
+#include "zcrypt_cca_key.h"
+
+#define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */
+#define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */
+
+#define CEIL4(x) ((((x)+3)/4)*4)
+
+struct response_type {
+ struct completion work;
+ int type;
+};
+#define PCIXCC_RESPONSE_TYPE_ICA 0
+#define PCIXCC_RESPONSE_TYPE_XCRB 1
+
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("Cryptographic Coprocessor (message type 6), " \
+ "Copyright IBM Corp. 2001, 2012");
+MODULE_LICENSE("GPL");
+
+static void zcrypt_msgtype6_receive(struct ap_device *, struct ap_message *,
+ struct ap_message *);
+
+/**
+ * CPRB
+ * Note that all shorts, ints and longs are little-endian.
+ * All pointer fields are 32-bits long, and mean nothing
+ *
+ * A request CPRB is followed by a request_parameter_block.
+ *
+ * The request (or reply) parameter block is organized thus:
+ * function code
+ * VUD block
+ * key block
+ */
+struct CPRB {
+ unsigned short cprb_len; /* CPRB length */
+ unsigned char cprb_ver_id; /* CPRB version id. */
+ unsigned char pad_000; /* Alignment pad byte. */
+ unsigned char srpi_rtcode[4]; /* SRPI return code LELONG */
+ unsigned char srpi_verb; /* SRPI verb type */
+ unsigned char flags; /* flags */
+ unsigned char func_id[2]; /* function id */
+ unsigned char checkpoint_flag; /* */
+ unsigned char resv2; /* reserved */
+ unsigned short req_parml; /* request parameter buffer */
+ /* length 16-bit little endian */
+ unsigned char req_parmp[4]; /* request parameter buffer *
+ * pointer (means nothing: the *
+ * parameter buffer follows *
+ * the CPRB). */
+ unsigned char req_datal[4]; /* request data buffer */
+ /* length ULELONG */
+ unsigned char req_datap[4]; /* request data buffer */
+ /* pointer */
+ unsigned short rpl_parml; /* reply parameter buffer */
+ /* length 16-bit little endian */
+ unsigned char pad_001[2]; /* Alignment pad bytes. ULESHORT */
+ unsigned char rpl_parmp[4]; /* reply parameter buffer *
+ * pointer (means nothing: the *
+ * parameter buffer follows *
+ * the CPRB). */
+ unsigned char rpl_datal[4]; /* reply data buffer len ULELONG */
+ unsigned char rpl_datap[4]; /* reply data buffer */
+ /* pointer */
+ unsigned short ccp_rscode; /* server reason code ULESHORT */
+ unsigned short ccp_rtcode; /* server return code ULESHORT */
+ unsigned char repd_parml[2]; /* replied parameter len ULESHORT*/
+ unsigned char mac_data_len[2]; /* Mac Data Length ULESHORT */
+ unsigned char repd_datal[4]; /* replied data length ULELONG */
+ unsigned char req_pc[2]; /* PC identifier */
+ unsigned char res_origin[8]; /* resource origin */
+ unsigned char mac_value[8]; /* Mac Value */
+ unsigned char logon_id[8]; /* Logon Identifier */
+ unsigned char usage_domain[2]; /* cdx */
+ unsigned char resv3[18]; /* reserved for requestor */
+ unsigned short svr_namel; /* server name length ULESHORT */
+ unsigned char svr_name[8]; /* server name */
+} __packed;
+
+struct function_and_rules_block {
+ unsigned char function_code[2];
+ unsigned short ulen;
+ unsigned char only_rule[8];
+} __packed;
+
+/**
+ * The following is used to initialize the CPRBX passed to the PCIXCC/CEX2C
+ * card in a type6 message. The 3 fields that must be filled in at execution
+ * time are req_parml, rpl_parml and usage_domain.
+ * Everything about this interface is ascii/big-endian, since the
+ * device does *not* have 'Intel inside'.
+ *
+ * The CPRBX is followed immediately by the parm block.
+ * The parm block contains:
+ * - function code ('PD' 0x5044 or 'PK' 0x504B)
+ * - rule block (one of:)
+ * + 0x000A 'PKCS-1.2' (MCL2 'PD')
+ * + 0x000A 'ZERO-PAD' (MCL2 'PK')
+ * + 0x000A 'ZERO-PAD' (MCL3 'PD' or CEX2C 'PD')
+ * + 0x000A 'MRP ' (MCL3 'PK' or CEX2C 'PK')
+ * - VUD block
+ */
+static struct CPRBX static_cprbx = {
+ .cprb_len = 0x00DC,
+ .cprb_ver_id = 0x02,
+ .func_id = {0x54, 0x32},
+};
+
+/**
+ * Convert a ICAMEX message to a type6 MEX message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @mex: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo *mex)
+{
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ .agent_id = {'C', 'A',},
+ .function_code = {'P', 'K'},
+ };
+ static struct function_and_rules_block static_pke_fnr = {
+ .function_code = {'P', 'K'},
+ .ulen = 10,
+ .only_rule = {'M', 'R', 'P', ' ', ' ', ' ', ' ', ' '}
+ };
+ static struct function_and_rules_block static_pke_fnr_MCL2 = {
+ .function_code = {'P', 'K'},
+ .ulen = 10,
+ .only_rule = {'Z', 'E', 'R', 'O', '-', 'P', 'A', 'D'}
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ struct function_and_rules_block fr;
+ unsigned short length;
+ char text[0];
+ } __packed * msg = ap_msg->message;
+ int size;
+
+ /* VUD.ciphertext */
+ msg->length = mex->inputdatalength + 2;
+ if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength))
+ return -EFAULT;
+
+ /* Set up key which is located after the variable length text. */
+ size = zcrypt_type6_mex_key_en(mex, msg->text+mex->inputdatalength, 1);
+ if (size < 0)
+ return size;
+ size += sizeof(*msg) + mex->inputdatalength;
+
+ /* message header, cprbx and f&r */
+ msg->hdr = static_type6_hdrX;
+ msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
+ msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
+
+ msg->cprbx = static_cprbx;
+ msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
+ msg->cprbx.rpl_msgbl = msg->hdr.FromCardLen1;
+
+ msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
+ static_pke_fnr_MCL2 : static_pke_fnr;
+
+ msg->cprbx.req_parml = size - sizeof(msg->hdr) - sizeof(msg->cprbx);
+
+ ap_msg->length = size;
+ return 0;
+}
+
+/**
+ * Convert a ICACRT message to a type6 CRT message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @crt: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ .agent_id = {'C', 'A',},
+ .function_code = {'P', 'D'},
+ };
+ static struct function_and_rules_block static_pkd_fnr = {
+ .function_code = {'P', 'D'},
+ .ulen = 10,
+ .only_rule = {'Z', 'E', 'R', 'O', '-', 'P', 'A', 'D'}
+ };
+
+ static struct function_and_rules_block static_pkd_fnr_MCL2 = {
+ .function_code = {'P', 'D'},
+ .ulen = 10,
+ .only_rule = {'P', 'K', 'C', 'S', '-', '1', '.', '2'}
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ struct function_and_rules_block fr;
+ unsigned short length;
+ char text[0];
+ } __packed * msg = ap_msg->message;
+ int size;
+
+ /* VUD.ciphertext */
+ msg->length = crt->inputdatalength + 2;
+ if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength))
+ return -EFAULT;
+
+ /* Set up key which is located after the variable length text. */
+ size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 1);
+ if (size < 0)
+ return size;
+ size += sizeof(*msg) + crt->inputdatalength; /* total size of msg */
+
+ /* message header, cprbx and f&r */
+ msg->hdr = static_type6_hdrX;
+ msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
+ msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
+
+ msg->cprbx = static_cprbx;
+ msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
+ msg->cprbx.req_parml = msg->cprbx.rpl_msgbl =
+ size - sizeof(msg->hdr) - sizeof(msg->cprbx);
+
+ msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
+ static_pkd_fnr_MCL2 : static_pkd_fnr;
+
+ ap_msg->length = size;
+ return 0;
+}
+
+/**
+ * Convert a XCRB message to a type6 CPRB message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @xcRB: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT, -EINVAL.
+ */
+struct type86_fmt2_msg {
+ struct type86_hdr hdr;
+ struct type86_fmt2_ext fmt2;
+} __packed;
+
+static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_xcRB *xcRB)
+{
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ } __packed * msg = ap_msg->message;
+
+ int rcblen = CEIL4(xcRB->request_control_blk_length);
+ int replylen;
+ char *req_data = ap_msg->message + sizeof(struct type6_hdr) + rcblen;
+ char *function_code;
+
+ /* length checks */
+ ap_msg->length = sizeof(struct type6_hdr) +
+ CEIL4(xcRB->request_control_blk_length) +
+ xcRB->request_data_length;
+ if (ap_msg->length > MSGTYPE06_MAX_MSG_SIZE)
+ return -EINVAL;
+ replylen = sizeof(struct type86_fmt2_msg) +
+ CEIL4(xcRB->reply_control_blk_length) +
+ xcRB->reply_data_length;
+ if (replylen > MSGTYPE06_MAX_MSG_SIZE)
+ return -EINVAL;
+
+ /* prepare type6 header */
+ msg->hdr = static_type6_hdrX;
+ memcpy(msg->hdr.agent_id , &(xcRB->agent_ID), sizeof(xcRB->agent_ID));
+ msg->hdr.ToCardLen1 = xcRB->request_control_blk_length;
+ if (xcRB->request_data_length) {
+ msg->hdr.offset2 = msg->hdr.offset1 + rcblen;
+ msg->hdr.ToCardLen2 = xcRB->request_data_length;
+ }
+ msg->hdr.FromCardLen1 = xcRB->reply_control_blk_length;
+ msg->hdr.FromCardLen2 = xcRB->reply_data_length;
+
+ /* prepare CPRB */
+ if (copy_from_user(&(msg->cprbx), xcRB->request_control_blk_addr,
+ xcRB->request_control_blk_length))
+ return -EFAULT;
+ if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) >
+ xcRB->request_control_blk_length)
+ return -EINVAL;
+ function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
+ memcpy(msg->hdr.function_code, function_code,
+ sizeof(msg->hdr.function_code));
+
+ if (memcmp(function_code, "US", 2) == 0)
+ ap_msg->special = 1;
+ else
+ ap_msg->special = 0;
+
+ /* copy data block */
+ if (xcRB->request_data_length &&
+ copy_from_user(req_data, xcRB->request_data_address,
+ xcRB->request_data_length))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Copy results from a type 86 ICA reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @data: pointer to user output data
+ * @length: size of user output data
+ *
+ * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
+ */
+struct type86x_reply {
+ struct type86_hdr hdr;
+ struct type86_fmt2_ext fmt2;
+ struct CPRBX cprbx;
+ unsigned char pad[4]; /* 4 byte function code/rules block ? */
+ unsigned short length;
+ char text[0];
+} __packed;
+
+static int convert_type86_ica(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ static unsigned char static_pad[] = {
+ 0x00, 0x02,
+ 0x1B, 0x7B, 0x5D, 0xB5, 0x75, 0x01, 0x3D, 0xFD,
+ 0x8D, 0xD1, 0xC7, 0x03, 0x2D, 0x09, 0x23, 0x57,
+ 0x89, 0x49, 0xB9, 0x3F, 0xBB, 0x99, 0x41, 0x5B,
+ 0x75, 0x21, 0x7B, 0x9D, 0x3B, 0x6B, 0x51, 0x39,
+ 0xBB, 0x0D, 0x35, 0xB9, 0x89, 0x0F, 0x93, 0xA5,
+ 0x0B, 0x47, 0xF1, 0xD3, 0xBB, 0xCB, 0xF1, 0x9D,
+ 0x23, 0x73, 0x71, 0xFF, 0xF3, 0xF5, 0x45, 0xFB,
+ 0x61, 0x29, 0x23, 0xFD, 0xF1, 0x29, 0x3F, 0x7F,
+ 0x17, 0xB7, 0x1B, 0xA9, 0x19, 0xBD, 0x57, 0xA9,
+ 0xD7, 0x95, 0xA3, 0xCB, 0xED, 0x1D, 0xDB, 0x45,
+ 0x7D, 0x11, 0xD1, 0x51, 0x1B, 0xED, 0x71, 0xE9,
+ 0xB1, 0xD1, 0xAB, 0xAB, 0x21, 0x2B, 0x1B, 0x9F,
+ 0x3B, 0x9F, 0xF7, 0xF7, 0xBD, 0x63, 0xEB, 0xAD,
+ 0xDF, 0xB3, 0x6F, 0x5B, 0xDB, 0x8D, 0xA9, 0x5D,
+ 0xE3, 0x7D, 0x77, 0x49, 0x47, 0xF5, 0xA7, 0xFD,
+ 0xAB, 0x2F, 0x27, 0x35, 0x77, 0xD3, 0x49, 0xC9,
+ 0x09, 0xEB, 0xB1, 0xF9, 0xBF, 0x4B, 0xCB, 0x2B,
+ 0xEB, 0xEB, 0x05, 0xFF, 0x7D, 0xC7, 0x91, 0x8B,
+ 0x09, 0x83, 0xB9, 0xB9, 0x69, 0x33, 0x39, 0x6B,
+ 0x79, 0x75, 0x19, 0xBF, 0xBB, 0x07, 0x1D, 0xBD,
+ 0x29, 0xBF, 0x39, 0x95, 0x93, 0x1D, 0x35, 0xC7,
+ 0xC9, 0x4D, 0xE5, 0x97, 0x0B, 0x43, 0x9B, 0xF1,
+ 0x16, 0x93, 0x03, 0x1F, 0xA5, 0xFB, 0xDB, 0xF3,
+ 0x27, 0x4F, 0x27, 0x61, 0x05, 0x1F, 0xB9, 0x23,
+ 0x2F, 0xC3, 0x81, 0xA9, 0x23, 0x71, 0x55, 0x55,
+ 0xEB, 0xED, 0x41, 0xE5, 0xF3, 0x11, 0xF1, 0x43,
+ 0x69, 0x03, 0xBD, 0x0B, 0x37, 0x0F, 0x51, 0x8F,
+ 0x0B, 0xB5, 0x89, 0x5B, 0x67, 0xA9, 0xD9, 0x4F,
+ 0x01, 0xF9, 0x21, 0x77, 0x37, 0x73, 0x79, 0xC5,
+ 0x7F, 0x51, 0xC1, 0xCF, 0x97, 0xA1, 0x75, 0xAD,
+ 0x35, 0x9D, 0xD3, 0xD3, 0xA7, 0x9D, 0x5D, 0x41,
+ 0x6F, 0x65, 0x1B, 0xCF, 0xA9, 0x87, 0x91, 0x09
+ };
+ struct type86x_reply *msg = reply->message;
+ unsigned short service_rc, service_rs;
+ unsigned int reply_len, pad_len;
+ char *data;
+
+ service_rc = msg->cprbx.ccp_rtcode;
+ if (unlikely(service_rc != 0)) {
+ service_rs = msg->cprbx.ccp_rscode;
+ if (service_rc == 8 && service_rs == 66)
+ return -EINVAL;
+ if (service_rc == 8 && service_rs == 65)
+ return -EINVAL;
+ if (service_rc == 8 && service_rs == 770)
+ return -EINVAL;
+ if (service_rc == 8 && service_rs == 783) {
+ zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
+ return -EAGAIN;
+ }
+ if (service_rc == 12 && service_rs == 769)
+ return -EINVAL;
+ if (service_rc == 8 && service_rs == 72)
+ return -EINVAL;
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+ data = msg->text;
+ reply_len = msg->length - 2;
+ if (reply_len > outputdatalength)
+ return -EINVAL;
+ /*
+ * For all encipher requests, the length of the ciphertext (reply_len)
+ * will always equal the modulus length. For MEX decipher requests
+ * the output needs to get padded. Minimum pad size is 10.
+ *
+ * Currently, the cases where padding will be added is for:
+ * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support
+ * ZERO-PAD and CRT is only supported for PKD requests)
+ * - PCICC, always
+ */
+ pad_len = outputdatalength - reply_len;
+ if (pad_len > 0) {
+ if (pad_len < 10)
+ return -EINVAL;
+ /* 'restore' padding left in the PCICC/PCIXCC card. */
+ if (copy_to_user(outputdata, static_pad, pad_len - 1))
+ return -EFAULT;
+ if (put_user(0, outputdata + pad_len - 1))
+ return -EFAULT;
+ }
+ /* Copy the crypto response to user space. */
+ if (copy_to_user(outputdata + pad_len, data, reply_len))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Copy results from a type 86 XCRB reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @xcRB: pointer to XCRB
+ *
+ * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
+ */
+static int convert_type86_xcrb(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ struct ica_xcRB *xcRB)
+{
+ struct type86_fmt2_msg *msg = reply->message;
+ char *data = reply->message;
+
+ /* Copy CPRB to user */
+ if (copy_to_user(xcRB->reply_control_blk_addr,
+ data + msg->fmt2.offset1, msg->fmt2.count1))
+ return -EFAULT;
+ xcRB->reply_control_blk_length = msg->fmt2.count1;
+
+ /* Copy data buffer to user */
+ if (msg->fmt2.count2)
+ if (copy_to_user(xcRB->reply_data_addr,
+ data + msg->fmt2.offset2, msg->fmt2.count2))
+ return -EFAULT;
+ xcRB->reply_data_length = msg->fmt2.count2;
+ return 0;
+}
+
+static int convert_type86_rng(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char *buffer)
+{
+ struct {
+ struct type86_hdr hdr;
+ struct type86_fmt2_ext fmt2;
+ struct CPRBX cprbx;
+ } __packed * msg = reply->message;
+ char *data = reply->message;
+
+ if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0)
+ return -EINVAL;
+ memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2);
+ return msg->fmt2.count2;
+}
+
+static int convert_response_ica(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ struct type86x_reply *msg = reply->message;
+
+ /* Response type byte is the second byte in the response. */
+ switch (((unsigned char *) reply->message)[1]) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ return convert_error(zdev, reply);
+ case TYPE86_RSP_CODE:
+ if (msg->cprbx.ccp_rtcode &&
+ (msg->cprbx.ccp_rscode == 0x14f) &&
+ (outputdatalength > 256)) {
+ if (zdev->max_exp_bit_length <= 17) {
+ zdev->max_exp_bit_length = 17;
+ return -EAGAIN;
+ } else
+ return -EINVAL;
+ }
+ if (msg->hdr.reply_code)
+ return convert_error(zdev, reply);
+ if (msg->cprbx.cprb_ver_id == 0x02)
+ return convert_type86_ica(zdev, reply,
+ outputdata, outputdatalength);
+ /* Fall through, no break, incorrect cprb version is an unknown
+ * response */
+ default: /* Unknown response type, this should NEVER EVER happen */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+}
+
+static int convert_response_xcrb(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ struct ica_xcRB *xcRB)
+{
+ struct type86x_reply *msg = reply->message;
+
+ /* Response type byte is the second byte in the response. */
+ switch (((unsigned char *) reply->message)[1]) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
+ return convert_error(zdev, reply);
+ case TYPE86_RSP_CODE:
+ if (msg->hdr.reply_code) {
+ memcpy(&(xcRB->status), msg->fmt2.apfs, sizeof(u32));
+ return convert_error(zdev, reply);
+ }
+ if (msg->cprbx.cprb_ver_id == 0x02)
+ return convert_type86_xcrb(zdev, reply, xcRB);
+ /* Fall through, no break, incorrect cprb version is an unknown
+ * response */
+ default: /* Unknown response type, this should NEVER EVER happen */
+ xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+}
+
+static int convert_response_rng(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char *data)
+{
+ struct type86x_reply *msg = reply->message;
+
+ switch (msg->hdr.type) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ return -EINVAL;
+ case TYPE86_RSP_CODE:
+ if (msg->hdr.reply_code)
+ return -EINVAL;
+ if (msg->cprbx.cprb_ver_id == 0x02)
+ return convert_type86_rng(zdev, reply, data);
+ /* Fall through, no break, incorrect cprb version is an unknown
+ * response */
+ default: /* Unknown response type, this should NEVER EVER happen */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+}
+
+/**
+ * This function is called from the AP bus code after a crypto request
+ * "msg" has finished with the reply message "reply".
+ * It is called from tasklet context.
+ * @ap_dev: pointer to the AP device
+ * @msg: pointer to the AP message
+ * @reply: pointer to the AP reply message
+ */
+static void zcrypt_msgtype6_receive(struct ap_device *ap_dev,
+ struct ap_message *msg,
+ struct ap_message *reply)
+{
+ static struct error_hdr error_reply = {
+ .type = TYPE82_RSP_CODE,
+ .reply_code = REP82_ERROR_MACHINE_FAILURE,
+ };
+ struct response_type *resp_type =
+ (struct response_type *) msg->private;
+ struct type86x_reply *t86r;
+ int length;
+
+ /* Copy the reply message to the request message buffer. */
+ if (IS_ERR(reply)) {
+ memcpy(msg->message, &error_reply, sizeof(error_reply));
+ goto out;
+ }
+ t86r = reply->message;
+ if (t86r->hdr.type == TYPE86_RSP_CODE &&
+ t86r->cprbx.cprb_ver_id == 0x02) {
+ switch (resp_type->type) {
+ case PCIXCC_RESPONSE_TYPE_ICA:
+ length = sizeof(struct type86x_reply)
+ + t86r->length - 2;
+ length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length);
+ memcpy(msg->message, reply->message, length);
+ break;
+ case PCIXCC_RESPONSE_TYPE_XCRB:
+ length = t86r->fmt2.offset2 + t86r->fmt2.count2;
+ length = min(MSGTYPE06_MAX_MSG_SIZE, length);
+ memcpy(msg->message, reply->message, length);
+ break;
+ default:
+ memcpy(msg->message, &error_reply,
+ sizeof(error_reply));
+ }
+ } else
+ memcpy(msg->message, reply->message, sizeof(error_reply));
+out:
+ complete(&(resp_type->work));
+}
+
+static atomic_t zcrypt_step = ATOMIC_INIT(0);
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a modexpo request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @mex: pointer to the modexpo request buffer
+ */
+static long zcrypt_msgtype6_modexpo(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo *mex)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_ICA,
+ };
+ int rc;
+
+ ap_init_message(&ap_msg);
+ ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_msgtype6_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rc = ICAMEX_msg_to_type6MEX_msgX(zdev, &ap_msg, mex);
+ if (rc)
+ goto out_free;
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
+ rc = convert_response_ica(zdev, &ap_msg, mex->outputdata,
+ mex->outputdatalength);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+ free_page((unsigned long) ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a modexpo_crt request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @crt: pointer to the modexpoc_crt request buffer
+ */
+static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_ICA,
+ };
+ int rc;
+
+ ap_init_message(&ap_msg);
+ ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_msgtype6_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rc = ICACRT_msg_to_type6CRT_msgX(zdev, &ap_msg, crt);
+ if (rc)
+ goto out_free;
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
+ rc = convert_response_ica(zdev, &ap_msg, crt->outputdata,
+ crt->outputdatalength);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+ free_page((unsigned long) ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a send_cprb request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @xcRB: pointer to the send_cprb request buffer
+ */
+static long zcrypt_msgtype6_send_cprb(struct zcrypt_device *zdev,
+ struct ica_xcRB *xcRB)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_XCRB,
+ };
+ int rc;
+
+ ap_init_message(&ap_msg);
+ ap_msg.message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_msgtype6_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rc = XCRB_msg_to_type6CPRB_msgX(zdev, &ap_msg, xcRB);
+ if (rc)
+ goto out_free;
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
+ rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+ kzfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to generate random data.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @buffer: pointer to a memory page to return random data
+ */
+
+static long zcrypt_msgtype6_rng(struct zcrypt_device *zdev,
+ char *buffer)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_XCRB,
+ };
+ int rc;
+
+ ap_init_message(&ap_msg);
+ ap_msg.message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_msgtype6_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rng_type6CPRB_msgX(zdev->ap_dev, &ap_msg, ZCRYPT_RNG_BUFFER_SIZE);
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
+ rc = convert_response_rng(zdev, &ap_msg, buffer);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The crypto operations for a PCIXCC/CEX2C card.
+ */
+static struct zcrypt_ops zcrypt_msgtype6_norng_ops = {
+ .owner = THIS_MODULE,
+ .variant = MSGTYPE06_VARIANT_NORNG,
+ .rsa_modexpo = zcrypt_msgtype6_modexpo,
+ .rsa_modexpo_crt = zcrypt_msgtype6_modexpo_crt,
+ .send_cprb = zcrypt_msgtype6_send_cprb,
+};
+
+static struct zcrypt_ops zcrypt_msgtype6_ops = {
+ .owner = THIS_MODULE,
+ .variant = MSGTYPE06_VARIANT_DEFAULT,
+ .rsa_modexpo = zcrypt_msgtype6_modexpo,
+ .rsa_modexpo_crt = zcrypt_msgtype6_modexpo_crt,
+ .send_cprb = zcrypt_msgtype6_send_cprb,
+ .rng = zcrypt_msgtype6_rng,
+};
+
+int __init zcrypt_msgtype6_init(void)
+{
+ zcrypt_msgtype_register(&zcrypt_msgtype6_norng_ops);
+ zcrypt_msgtype_register(&zcrypt_msgtype6_ops);
+ return 0;
+}
+
+void __exit zcrypt_msgtype6_exit(void)
+{
+ zcrypt_msgtype_unregister(&zcrypt_msgtype6_norng_ops);
+ zcrypt_msgtype_unregister(&zcrypt_msgtype6_ops);
+}
+
+module_init(zcrypt_msgtype6_init);
+module_exit(zcrypt_msgtype6_exit);
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.h b/drivers/s390/crypto/zcrypt_msgtype6.h
new file mode 100644
index 0000000..1e500d3
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype6.h
@@ -0,0 +1,169 @@
+/*
+ * zcrypt 2.1.0
+ *
+ * Copyright IBM Corp. 2001, 2012
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ZCRYPT_MSGTYPE6_H_
+#define _ZCRYPT_MSGTYPE6_H_
+
+#include <asm/zcrypt.h>
+
+#define MSGTYPE06_NAME "zcrypt_msgtype6"
+#define MSGTYPE06_VARIANT_DEFAULT 0
+#define MSGTYPE06_VARIANT_NORNG 1
+
+#define MSGTYPE06_MAX_MSG_SIZE (12*1024)
+
+/**
+ * The type 6 message family is associated with PCICC or PCIXCC cards.
+ *
+ * It contains a message header followed by a CPRB, both of which
+ * are described below.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type6_hdr {
+ unsigned char reserved1; /* 0x00 */
+ unsigned char type; /* 0x06 */
+ unsigned char reserved2[2]; /* 0x0000 */
+ unsigned char right[4]; /* 0x00000000 */
+ unsigned char reserved3[2]; /* 0x0000 */
+ unsigned char reserved4[2]; /* 0x0000 */
+ unsigned char apfs[4]; /* 0x00000000 */
+ unsigned int offset1; /* 0x00000058 (offset to CPRB) */
+ unsigned int offset2; /* 0x00000000 */
+ unsigned int offset3; /* 0x00000000 */
+ unsigned int offset4; /* 0x00000000 */
+ unsigned char agent_id[16]; /* PCICC: */
+ /* 0x0100 */
+ /* 0x4343412d4150504c202020 */
+ /* 0x010101 */
+ /* PCIXCC: */
+ /* 0x4341000000000000 */
+ /* 0x0000000000000000 */
+ unsigned char rqid[2]; /* rqid. internal to 603 */
+ unsigned char reserved5[2]; /* 0x0000 */
+ unsigned char function_code[2]; /* for PKD, 0x5044 (ascii 'PD') */
+ unsigned char reserved6[2]; /* 0x0000 */
+ unsigned int ToCardLen1; /* (request CPRB len + 3) & -4 */
+ unsigned int ToCardLen2; /* db len 0x00000000 for PKD */
+ unsigned int ToCardLen3; /* 0x00000000 */
+ unsigned int ToCardLen4; /* 0x00000000 */
+ unsigned int FromCardLen1; /* response buffer length */
+ unsigned int FromCardLen2; /* db len 0x00000000 for PKD */
+ unsigned int FromCardLen3; /* 0x00000000 */
+ unsigned int FromCardLen4; /* 0x00000000 */
+} __packed;
+
+/**
+ * The type 86 message family is associated with PCICC and PCIXCC cards.
+ *
+ * It contains a message header followed by a CPRB. The CPRB is
+ * the same as the request CPRB, which is described above.
+ *
+ * If format is 1, an error condition exists and no data beyond
+ * the 8-byte message header is of interest.
+ *
+ * The non-error message is shown below.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type86_hdr {
+ unsigned char reserved1; /* 0x00 */
+ unsigned char type; /* 0x86 */
+ unsigned char format; /* 0x01 (error) or 0x02 (ok) */
+ unsigned char reserved2; /* 0x00 */
+ unsigned char reply_code; /* reply code (see above) */
+ unsigned char reserved3[3]; /* 0x000000 */
+} __packed;
+
+#define TYPE86_RSP_CODE 0x86
+#define TYPE86_FMT2 0x02
+
+struct type86_fmt2_ext {
+ unsigned char reserved[4]; /* 0x00000000 */
+ unsigned char apfs[4]; /* final status */
+ unsigned int count1; /* length of CPRB + parameters */
+ unsigned int offset1; /* offset to CPRB */
+ unsigned int count2; /* 0x00000000 */
+ unsigned int offset2; /* db offset 0x00000000 for PKD */
+ unsigned int count3; /* 0x00000000 */
+ unsigned int offset3; /* 0x00000000 */
+ unsigned int count4; /* 0x00000000 */
+ unsigned int offset4; /* 0x00000000 */
+} __packed;
+
+/**
+ * Prepare a type6 CPRB message for random number generation
+ *
+ * @ap_dev: AP device pointer
+ * @ap_msg: pointer to AP message
+ */
+static inline void rng_type6CPRB_msgX(struct ap_device *ap_dev,
+ struct ap_message *ap_msg,
+ unsigned random_number_length)
+{
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ char function_code[2];
+ short int rule_length;
+ char rule[8];
+ short int verb_length;
+ short int key_length;
+ } __packed * msg = ap_msg->message;
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ .agent_id = {'C', 'A'},
+ .function_code = {'R', 'L'},
+ .ToCardLen1 = sizeof(*msg) - sizeof(msg->hdr),
+ .FromCardLen1 = sizeof(*msg) - sizeof(msg->hdr),
+ };
+ static struct CPRBX local_cprbx = {
+ .cprb_len = 0x00dc,
+ .cprb_ver_id = 0x02,
+ .func_id = {0x54, 0x32},
+ .req_parml = sizeof(*msg) - sizeof(msg->hdr) -
+ sizeof(msg->cprbx),
+ .rpl_msgbl = sizeof(*msg) - sizeof(msg->hdr),
+ };
+
+ msg->hdr = static_type6_hdrX;
+ msg->hdr.FromCardLen2 = random_number_length,
+ msg->cprbx = local_cprbx;
+ msg->cprbx.rpl_datal = random_number_length,
+ msg->cprbx.domain = AP_QID_QUEUE(ap_dev->qid);
+ memcpy(msg->function_code, msg->hdr.function_code, 0x02);
+ msg->rule_length = 0x0a;
+ memcpy(msg->rule, "RANDOM ", 8);
+ msg->verb_length = 0x02;
+ msg->key_length = 0x02;
+ ap_msg->length = sizeof(*msg);
+}
+
+int zcrypt_msgtype6_init(void);
+void zcrypt_msgtype6_exit(void);
+
+#endif /* _ZCRYPT_MSGTYPE6_H_ */
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index ccb4f8b6..c7275e3 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -1,13 +1,14 @@
/*
* zcrypt 2.1.0
*
- * Copyright IBM Corp. 2001, 2006
+ * Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@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
@@ -35,9 +36,10 @@
#include "ap_bus.h"
#include "zcrypt_api.h"
#include "zcrypt_error.h"
-#include "zcrypt_pcicc.h"
+#include "zcrypt_msgtype6.h"
#include "zcrypt_pcixcc.h"
#include "zcrypt_cca_key.h"
+#include "zcrypt_msgtype6.h"
#define PCIXCC_MIN_MOD_SIZE 16 /* 128 bits */
#define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */
@@ -75,14 +77,12 @@ static struct ap_device_id zcrypt_pcixcc_ids[] = {
MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_ids);
MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, "
- "Copyright IBM Corp. 2001, 2006");
+MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, " \
+ "Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
static int zcrypt_pcixcc_probe(struct ap_device *ap_dev);
static void zcrypt_pcixcc_remove(struct ap_device *ap_dev);
-static void zcrypt_pcixcc_receive(struct ap_device *, struct ap_message *,
- struct ap_message *);
static struct ap_driver zcrypt_pcixcc_driver = {
.probe = zcrypt_pcixcc_probe,
@@ -92,766 +92,6 @@ static struct ap_driver zcrypt_pcixcc_driver = {
};
/**
- * The following is used to initialize the CPRBX passed to the PCIXCC/CEX2C
- * card in a type6 message. The 3 fields that must be filled in at execution
- * time are req_parml, rpl_parml and usage_domain.
- * Everything about this interface is ascii/big-endian, since the
- * device does *not* have 'Intel inside'.
- *
- * The CPRBX is followed immediately by the parm block.
- * The parm block contains:
- * - function code ('PD' 0x5044 or 'PK' 0x504B)
- * - rule block (one of:)
- * + 0x000A 'PKCS-1.2' (MCL2 'PD')
- * + 0x000A 'ZERO-PAD' (MCL2 'PK')
- * + 0x000A 'ZERO-PAD' (MCL3 'PD' or CEX2C 'PD')
- * + 0x000A 'MRP ' (MCL3 'PK' or CEX2C 'PK')
- * - VUD block
- */
-static struct CPRBX static_cprbx = {
- .cprb_len = 0x00DC,
- .cprb_ver_id = 0x02,
- .func_id = {0x54,0x32},
-};
-
-/**
- * Convert a ICAMEX message to a type6 MEX message.
- *
- * @zdev: crypto device pointer
- * @ap_msg: pointer to AP message
- * @mex: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo *mex)
-{
- static struct type6_hdr static_type6_hdrX = {
- .type = 0x06,
- .offset1 = 0x00000058,
- .agent_id = {'C','A',},
- .function_code = {'P','K'},
- };
- static struct function_and_rules_block static_pke_fnr = {
- .function_code = {'P','K'},
- .ulen = 10,
- .only_rule = {'M','R','P',' ',' ',' ',' ',' '}
- };
- static struct function_and_rules_block static_pke_fnr_MCL2 = {
- .function_code = {'P','K'},
- .ulen = 10,
- .only_rule = {'Z','E','R','O','-','P','A','D'}
- };
- struct {
- struct type6_hdr hdr;
- struct CPRBX cprbx;
- struct function_and_rules_block fr;
- unsigned short length;
- char text[0];
- } __attribute__((packed)) *msg = ap_msg->message;
- int size;
-
- /* VUD.ciphertext */
- msg->length = mex->inputdatalength + 2;
- if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength))
- return -EFAULT;
-
- /* Set up key which is located after the variable length text. */
- size = zcrypt_type6_mex_key_en(mex, msg->text+mex->inputdatalength, 1);
- if (size < 0)
- return size;
- size += sizeof(*msg) + mex->inputdatalength;
-
- /* message header, cprbx and f&r */
- msg->hdr = static_type6_hdrX;
- msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
- msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
-
- msg->cprbx = static_cprbx;
- msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
- msg->cprbx.rpl_msgbl = msg->hdr.FromCardLen1;
-
- msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
- static_pke_fnr_MCL2 : static_pke_fnr;
-
- msg->cprbx.req_parml = size - sizeof(msg->hdr) - sizeof(msg->cprbx);
-
- ap_msg->length = size;
- return 0;
-}
-
-/**
- * Convert a ICACRT message to a type6 CRT message.
- *
- * @zdev: crypto device pointer
- * @ap_msg: pointer to AP message
- * @crt: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo_crt *crt)
-{
- static struct type6_hdr static_type6_hdrX = {
- .type = 0x06,
- .offset1 = 0x00000058,
- .agent_id = {'C','A',},
- .function_code = {'P','D'},
- };
- static struct function_and_rules_block static_pkd_fnr = {
- .function_code = {'P','D'},
- .ulen = 10,
- .only_rule = {'Z','E','R','O','-','P','A','D'}
- };
-
- static struct function_and_rules_block static_pkd_fnr_MCL2 = {
- .function_code = {'P','D'},
- .ulen = 10,
- .only_rule = {'P','K','C','S','-','1','.','2'}
- };
- struct {
- struct type6_hdr hdr;
- struct CPRBX cprbx;
- struct function_and_rules_block fr;
- unsigned short length;
- char text[0];
- } __attribute__((packed)) *msg = ap_msg->message;
- int size;
-
- /* VUD.ciphertext */
- msg->length = crt->inputdatalength + 2;
- if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength))
- return -EFAULT;
-
- /* Set up key which is located after the variable length text. */
- size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 1);
- if (size < 0)
- return size;
- size += sizeof(*msg) + crt->inputdatalength; /* total size of msg */
-
- /* message header, cprbx and f&r */
- msg->hdr = static_type6_hdrX;
- msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
- msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
-
- msg->cprbx = static_cprbx;
- msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
- msg->cprbx.req_parml = msg->cprbx.rpl_msgbl =
- size - sizeof(msg->hdr) - sizeof(msg->cprbx);
-
- msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
- static_pkd_fnr_MCL2 : static_pkd_fnr;
-
- ap_msg->length = size;
- return 0;
-}
-
-/**
- * Convert a XCRB message to a type6 CPRB message.
- *
- * @zdev: crypto device pointer
- * @ap_msg: pointer to AP message
- * @xcRB: pointer to user input data
- *
- * Returns 0 on success or -EFAULT, -EINVAL.
- */
-struct type86_fmt2_msg {
- struct type86_hdr hdr;
- struct type86_fmt2_ext fmt2;
-} __attribute__((packed));
-
-static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_xcRB *xcRB)
-{
- static struct type6_hdr static_type6_hdrX = {
- .type = 0x06,
- .offset1 = 0x00000058,
- };
- struct {
- struct type6_hdr hdr;
- struct CPRBX cprbx;
- } __attribute__((packed)) *msg = ap_msg->message;
-
- int rcblen = CEIL4(xcRB->request_control_blk_length);
- int replylen;
- char *req_data = ap_msg->message + sizeof(struct type6_hdr) + rcblen;
- char *function_code;
-
- /* length checks */
- ap_msg->length = sizeof(struct type6_hdr) +
- CEIL4(xcRB->request_control_blk_length) +
- xcRB->request_data_length;
- if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE)
- return -EINVAL;
- replylen = sizeof(struct type86_fmt2_msg) +
- CEIL4(xcRB->reply_control_blk_length) +
- xcRB->reply_data_length;
- if (replylen > PCIXCC_MAX_XCRB_MESSAGE_SIZE)
- return -EINVAL;
-
- /* prepare type6 header */
- msg->hdr = static_type6_hdrX;
- memcpy(msg->hdr.agent_id , &(xcRB->agent_ID), sizeof(xcRB->agent_ID));
- msg->hdr.ToCardLen1 = xcRB->request_control_blk_length;
- if (xcRB->request_data_length) {
- msg->hdr.offset2 = msg->hdr.offset1 + rcblen;
- msg->hdr.ToCardLen2 = xcRB->request_data_length;
- }
- msg->hdr.FromCardLen1 = xcRB->reply_control_blk_length;
- msg->hdr.FromCardLen2 = xcRB->reply_data_length;
-
- /* prepare CPRB */
- if (copy_from_user(&(msg->cprbx), xcRB->request_control_blk_addr,
- xcRB->request_control_blk_length))
- return -EFAULT;
- if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) >
- xcRB->request_control_blk_length)
- return -EINVAL;
- function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
- memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code));
-
- if (memcmp(function_code, "US", 2) == 0)
- ap_msg->special = 1;
- else
- ap_msg->special = 0;
-
- /* copy data block */
- if (xcRB->request_data_length &&
- copy_from_user(req_data, xcRB->request_data_address,
- xcRB->request_data_length))
- return -EFAULT;
- return 0;
-}
-
-/**
- * Prepare a type6 CPRB message for random number generation
- *
- * @ap_dev: AP device pointer
- * @ap_msg: pointer to AP message
- */
-static void rng_type6CPRB_msgX(struct ap_device *ap_dev,
- struct ap_message *ap_msg,
- unsigned random_number_length)
-{
- struct {
- struct type6_hdr hdr;
- struct CPRBX cprbx;
- char function_code[2];
- short int rule_length;
- char rule[8];
- short int verb_length;
- short int key_length;
- } __attribute__((packed)) *msg = ap_msg->message;
- static struct type6_hdr static_type6_hdrX = {
- .type = 0x06,
- .offset1 = 0x00000058,
- .agent_id = {'C', 'A'},
- .function_code = {'R', 'L'},
- .ToCardLen1 = sizeof *msg - sizeof(msg->hdr),
- .FromCardLen1 = sizeof *msg - sizeof(msg->hdr),
- };
- static struct CPRBX local_cprbx = {
- .cprb_len = 0x00dc,
- .cprb_ver_id = 0x02,
- .func_id = {0x54, 0x32},
- .req_parml = sizeof *msg - sizeof(msg->hdr) -
- sizeof(msg->cprbx),
- .rpl_msgbl = sizeof *msg - sizeof(msg->hdr),
- };
-
- msg->hdr = static_type6_hdrX;
- msg->hdr.FromCardLen2 = random_number_length,
- msg->cprbx = local_cprbx;
- msg->cprbx.rpl_datal = random_number_length,
- msg->cprbx.domain = AP_QID_QUEUE(ap_dev->qid);
- memcpy(msg->function_code, msg->hdr.function_code, 0x02);
- msg->rule_length = 0x0a;
- memcpy(msg->rule, "RANDOM ", 8);
- msg->verb_length = 0x02;
- msg->key_length = 0x02;
- ap_msg->length = sizeof *msg;
-}
-
-/**
- * Copy results from a type 86 ICA reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @data: pointer to user output data
- * @length: size of user output data
- *
- * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
- */
-struct type86x_reply {
- struct type86_hdr hdr;
- struct type86_fmt2_ext fmt2;
- struct CPRBX cprbx;
- unsigned char pad[4]; /* 4 byte function code/rules block ? */
- unsigned short length;
- char text[0];
-} __attribute__((packed));
-
-static int convert_type86_ica(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- static unsigned char static_pad[] = {
- 0x00,0x02,
- 0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,
- 0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57,
- 0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,
- 0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39,
- 0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5,
- 0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D,
- 0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB,
- 0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F,
- 0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9,
- 0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45,
- 0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9,
- 0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F,
- 0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD,
- 0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D,
- 0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD,
- 0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9,
- 0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B,
- 0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B,
- 0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B,
- 0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD,
- 0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7,
- 0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1,
- 0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3,
- 0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23,
- 0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55,
- 0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43,
- 0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F,
- 0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F,
- 0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,
- 0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD,
- 0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,
- 0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09
- };
- struct type86x_reply *msg = reply->message;
- unsigned short service_rc, service_rs;
- unsigned int reply_len, pad_len;
- char *data;
-
- service_rc = msg->cprbx.ccp_rtcode;
- if (unlikely(service_rc != 0)) {
- service_rs = msg->cprbx.ccp_rscode;
- if (service_rc == 8 && service_rs == 66)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 65)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 770)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 783) {
- zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
- return -EAGAIN;
- }
- if (service_rc == 12 && service_rs == 769)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 72)
- return -EINVAL;
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
- data = msg->text;
- reply_len = msg->length - 2;
- if (reply_len > outputdatalength)
- return -EINVAL;
- /*
- * For all encipher requests, the length of the ciphertext (reply_len)
- * will always equal the modulus length. For MEX decipher requests
- * the output needs to get padded. Minimum pad size is 10.
- *
- * Currently, the cases where padding will be added is for:
- * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support
- * ZERO-PAD and CRT is only supported for PKD requests)
- * - PCICC, always
- */
- pad_len = outputdatalength - reply_len;
- if (pad_len > 0) {
- if (pad_len < 10)
- return -EINVAL;
- /* 'restore' padding left in the PCICC/PCIXCC card. */
- if (copy_to_user(outputdata, static_pad, pad_len - 1))
- return -EFAULT;
- if (put_user(0, outputdata + pad_len - 1))
- return -EFAULT;
- }
- /* Copy the crypto response to user space. */
- if (copy_to_user(outputdata + pad_len, data, reply_len))
- return -EFAULT;
- return 0;
-}
-
-/**
- * Copy results from a type 86 XCRB reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @xcRB: pointer to XCRB
- *
- * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
- */
-static int convert_type86_xcrb(struct zcrypt_device *zdev,
- struct ap_message *reply,
- struct ica_xcRB *xcRB)
-{
- struct type86_fmt2_msg *msg = reply->message;
- char *data = reply->message;
-
- /* Copy CPRB to user */
- if (copy_to_user(xcRB->reply_control_blk_addr,
- data + msg->fmt2.offset1, msg->fmt2.count1))
- return -EFAULT;
- xcRB->reply_control_blk_length = msg->fmt2.count1;
-
- /* Copy data buffer to user */
- if (msg->fmt2.count2)
- if (copy_to_user(xcRB->reply_data_addr,
- data + msg->fmt2.offset2, msg->fmt2.count2))
- return -EFAULT;
- xcRB->reply_data_length = msg->fmt2.count2;
- return 0;
-}
-
-static int convert_type86_rng(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char *buffer)
-{
- struct {
- struct type86_hdr hdr;
- struct type86_fmt2_ext fmt2;
- struct CPRBX cprbx;
- } __attribute__((packed)) *msg = reply->message;
- char *data = reply->message;
-
- if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0)
- return -EINVAL;
- memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2);
- return msg->fmt2.count2;
-}
-
-static int convert_response_ica(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- struct type86x_reply *msg = reply->message;
-
- /* Response type byte is the second byte in the response. */
- switch (((unsigned char *) reply->message)[1]) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- return convert_error(zdev, reply);
- case TYPE86_RSP_CODE:
- if (msg->cprbx.ccp_rtcode &&
- (msg->cprbx.ccp_rscode == 0x14f) &&
- (outputdatalength > 256)) {
- if (zdev->max_exp_bit_length <= 17) {
- zdev->max_exp_bit_length = 17;
- return -EAGAIN;
- } else
- return -EINVAL;
- }
- if (msg->hdr.reply_code)
- return convert_error(zdev, reply);
- if (msg->cprbx.cprb_ver_id == 0x02)
- return convert_type86_ica(zdev, reply,
- outputdata, outputdatalength);
- /* Fall through, no break, incorrect cprb version is an unknown
- * response */
- default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-static int convert_response_xcrb(struct zcrypt_device *zdev,
- struct ap_message *reply,
- struct ica_xcRB *xcRB)
-{
- struct type86x_reply *msg = reply->message;
-
- /* Response type byte is the second byte in the response. */
- switch (((unsigned char *) reply->message)[1]) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
- return convert_error(zdev, reply);
- case TYPE86_RSP_CODE:
- if (msg->hdr.reply_code) {
- memcpy(&(xcRB->status), msg->fmt2.apfs, sizeof(u32));
- return convert_error(zdev, reply);
- }
- if (msg->cprbx.cprb_ver_id == 0x02)
- return convert_type86_xcrb(zdev, reply, xcRB);
- /* Fall through, no break, incorrect cprb version is an unknown
- * response */
- default: /* Unknown response type, this should NEVER EVER happen */
- xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-static int convert_response_rng(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char *data)
-{
- struct type86x_reply *msg = reply->message;
-
- switch (msg->hdr.type) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- return -EINVAL;
- case TYPE86_RSP_CODE:
- if (msg->hdr.reply_code)
- return -EINVAL;
- if (msg->cprbx.cprb_ver_id == 0x02)
- return convert_type86_rng(zdev, reply, data);
- /* Fall through, no break, incorrect cprb version is an unknown
- * response */
- default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-/**
- * This function is called from the AP bus code after a crypto request
- * "msg" has finished with the reply message "reply".
- * It is called from tasklet context.
- * @ap_dev: pointer to the AP device
- * @msg: pointer to the AP message
- * @reply: pointer to the AP reply message
- */
-static void zcrypt_pcixcc_receive(struct ap_device *ap_dev,
- struct ap_message *msg,
- struct ap_message *reply)
-{
- static struct error_hdr error_reply = {
- .type = TYPE82_RSP_CODE,
- .reply_code = REP82_ERROR_MACHINE_FAILURE,
- };
- struct response_type *resp_type =
- (struct response_type *) msg->private;
- struct type86x_reply *t86r;
- int length;
-
- /* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
- t86r = reply->message;
- if (t86r->hdr.type == TYPE86_RSP_CODE &&
- t86r->cprbx.cprb_ver_id == 0x02) {
- switch (resp_type->type) {
- case PCIXCC_RESPONSE_TYPE_ICA:
- length = sizeof(struct type86x_reply)
- + t86r->length - 2;
- length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length);
- memcpy(msg->message, reply->message, length);
- break;
- case PCIXCC_RESPONSE_TYPE_XCRB:
- length = t86r->fmt2.offset2 + t86r->fmt2.count2;
- length = min(PCIXCC_MAX_XCRB_MESSAGE_SIZE, length);
- memcpy(msg->message, reply->message, length);
- break;
- default:
- memcpy(msg->message, &error_reply, sizeof error_reply);
- }
- } else
- memcpy(msg->message, reply->message, sizeof error_reply);
-out:
- complete(&(resp_type->work));
-}
-
-static atomic_t zcrypt_step = ATOMIC_INIT(0);
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to handle a modexpo request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCIXCC/CEX2C device to the request distributor
- * @mex: pointer to the modexpo request buffer
- */
-static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo *mex)
-{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_ICA,
- };
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcixcc_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &resp_type;
- rc = ICAMEX_msg_to_type6MEX_msgX(zdev, &ap_msg, mex);
- if (rc)
- goto out_free;
- init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_ica(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- free_page((unsigned long) ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to handle a modexpo_crt request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCIXCC/CEX2C device to the request distributor
- * @crt: pointer to the modexpoc_crt request buffer
- */
-static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo_crt *crt)
-{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_ICA,
- };
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcixcc_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &resp_type;
- rc = ICACRT_msg_to_type6CRT_msgX(zdev, &ap_msg, crt);
- if (rc)
- goto out_free;
- init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_ica(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- free_page((unsigned long) ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to handle a send_cprb request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCIXCC/CEX2C device to the request distributor
- * @xcRB: pointer to the send_cprb request buffer
- */
-static long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev,
- struct ica_xcRB *xcRB)
-{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_XCRB,
- };
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcixcc_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &resp_type;
- rc = XCRB_msg_to_type6CPRB_msgX(zdev, &ap_msg, xcRB);
- if (rc)
- goto out_free;
- init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kzfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to generate random data.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCIXCC/CEX2C device to the request distributor
- * @buffer: pointer to a memory page to return random data
- */
-
-static long zcrypt_pcixcc_rng(struct zcrypt_device *zdev,
- char *buffer)
-{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_XCRB,
- };
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcixcc_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &resp_type;
- rng_type6CPRB_msgX(zdev->ap_dev, &ap_msg, ZCRYPT_RNG_BUFFER_SIZE);
- init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_rng(zdev, &ap_msg, buffer);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
- kfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The crypto operations for a PCIXCC/CEX2C card.
- */
-static struct zcrypt_ops zcrypt_pcixcc_ops = {
- .rsa_modexpo = zcrypt_pcixcc_modexpo,
- .rsa_modexpo_crt = zcrypt_pcixcc_modexpo_crt,
- .send_cprb = zcrypt_pcixcc_send_cprb,
-};
-
-static struct zcrypt_ops zcrypt_pcixcc_with_rng_ops = {
- .rsa_modexpo = zcrypt_pcixcc_modexpo,
- .rsa_modexpo_crt = zcrypt_pcixcc_modexpo_crt,
- .send_cprb = zcrypt_pcixcc_send_cprb,
- .rng = zcrypt_pcixcc_rng,
-};
-
-/**
* Micro-code detection function. Its sends a message to a pcixcc card
* to find out the microcode level.
* @ap_dev: pointer to the AP device.
@@ -1083,9 +323,11 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
return rc;
}
if (rc)
- zdev->ops = &zcrypt_pcixcc_with_rng_ops;
+ zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
+ MSGTYPE06_VARIANT_DEFAULT);
else
- zdev->ops = &zcrypt_pcixcc_ops;
+ zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
+ MSGTYPE06_VARIANT_NORNG);
ap_dev->reply = &zdev->reply;
ap_dev->private = zdev;
rc = zcrypt_device_register(zdev);
@@ -1095,6 +337,7 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
out_free:
ap_dev->private = NULL;
+ zcrypt_msgtype_release(zdev->ops);
zcrypt_device_free(zdev);
return rc;
}
@@ -1106,8 +349,10 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
static void zcrypt_pcixcc_remove(struct ap_device *ap_dev)
{
struct zcrypt_device *zdev = ap_dev->private;
+ struct zcrypt_ops *zops = zdev->ops;
zcrypt_device_unregister(zdev);
+ zcrypt_msgtype_release(zops);
}
int __init zcrypt_pcixcc_init(void)
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.h b/drivers/s390/crypto/zcrypt_pcixcc.h
index c7cdf59..eacafc8 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.h
+++ b/drivers/s390/crypto/zcrypt_pcixcc.h
@@ -1,12 +1,13 @@
/*
* zcrypt 2.1.0
*
- * Copyright IBM Corp. 2001, 2006
+ * Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@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
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 7a8b096..cf6da7f 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -2993,7 +2993,7 @@ static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid)
struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info;
struct sysinfo_3_2_2 *info322 = (struct sysinfo_3_2_2 *)info;
struct ccw_dev_id ccwid;
- int level, rc;
+ int level;
tid->chpid = card->info.chpid;
ccw_device_get_id(CARD_RDEV(card), &ccwid);
@@ -3001,17 +3001,10 @@ static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid)
tid->devno = ccwid.devno;
if (!info)
return;
-
- rc = stsi(NULL, 0, 0, 0);
- if (rc == -ENOSYS)
- level = rc;
- else
- level = (((unsigned int) rc) >> 28);
-
- if ((level >= 2) && (stsi(info222, 2, 2, 2) != -ENOSYS))
+ level = stsi(NULL, 0, 0, 0);
+ if ((level >= 2) && (stsi(info222, 2, 2, 2) == 0))
tid->lparnr = info222->lpar_number;
-
- if ((level >= 3) && (stsi(info322, 3, 2, 2) != -ENOSYS)) {
+ if ((level >= 3) && (stsi(info322, 3, 2, 2) == 0)) {
EBCASC(info322->vm[0].name, sizeof(info322->vm[0].name));
memcpy(tid->vmname, info322->vm[0].name, sizeof(tid->vmname));
}
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
index 25417d0..0bcacf7 100644
--- a/drivers/scsi/aic7xxx/aic79xx_core.c
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -2888,7 +2888,7 @@ ahd_handle_lqiphase_error(struct ahd_softc *ahd, u_int lqistat1)
ahd_outb(ahd, CLRINT, CLRSCSIINT);
ahd_unpause(ahd);
} else {
- printk("Reseting Channel for LQI Phase error\n");
+ printk("Resetting Channel for LQI Phase error\n");
ahd_dump_card_state(ahd);
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
}
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index 8cdb79c..21ad290 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -5587,7 +5587,7 @@ static bfa_status_t bfa_dconf_flash_write(struct bfa_dconf_mod_s *dconf);
static void bfa_dconf_init_cb(void *arg, bfa_status_t status);
/*
- * Begining state of dconf module. Waiting for an event to start.
+ * Beginning state of dconf module. Waiting for an event to start.
*/
static void
bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h
index 1a99d4b..7b916e0 100644
--- a/drivers/scsi/bfa/bfa_ioc.h
+++ b/drivers/scsi/bfa/bfa_ioc.h
@@ -530,7 +530,7 @@ struct bfa_diag_results_fwping {
struct bfa_diag_qtest_result_s {
u32 status;
- u16 count; /* sucessful queue test count */
+ u16 count; /* successful queue test count */
u8 queue;
u8 rsvd; /* 64-bit align */
};
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index ae1cb76..e055865 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -908,7 +908,7 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
return;
default:
- printk(KERN_ERR PFX "Unkonwn netevent %ld", event);
+ printk(KERN_ERR PFX "Unknown netevent %ld", event);
return;
}
@@ -1738,7 +1738,7 @@ static int bnx2fc_ulp_get_stats(void *handle)
/**
* bnx2fc_ulp_start - cnic callback to initialize & start adapter instance
*
- * @handle: transport handle pointing to adapter struture
+ * @handle: transport handle pointing to adapter structure
*
* This function maps adapter structure to pcidev structure and initiates
* firmware handshake to enable/initialize on-chip FCoE components.
diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c
index 33d6630..91eec60 100644
--- a/drivers/scsi/bnx2i/bnx2i_hwi.c
+++ b/drivers/scsi/bnx2i/bnx2i_hwi.c
@@ -1264,6 +1264,9 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
int rc = 0;
u64 mask64;
+ memset(&iscsi_init, 0x00, sizeof(struct iscsi_kwqe_init1));
+ memset(&iscsi_init2, 0x00, sizeof(struct iscsi_kwqe_init2));
+
bnx2i_adjust_qp_size(hba);
iscsi_init.flags =
diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h
index d3e4d7c..fbf6f0f4 100644
--- a/drivers/scsi/gdth.h
+++ b/drivers/scsi/gdth.h
@@ -49,15 +49,6 @@
/* GDT_ISA */
#define GDT2_ID 0x0120941c /* GDT2000/2020 */
-/* vendor ID, device IDs (PCI) */
-/* these defines should already exist in <linux/pci.h> */
-#ifndef PCI_VENDOR_ID_VORTEX
-#define PCI_VENDOR_ID_VORTEX 0x1119 /* PCI controller vendor ID */
-#endif
-#ifndef PCI_VENDOR_ID_INTEL
-#define PCI_VENDOR_ID_INTEL 0x8086
-#endif
-
#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0
/* GDT_PCI */
#define PCI_DEVICE_ID_VORTEX_GDT60x0 0 /* GDT6000/6020/6050 */
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 796482b..2b4261c 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -1315,8 +1315,9 @@ static void complete_scsi_command(struct CommandList *cp)
}
break;
case CMD_PROTOCOL_ERR:
+ cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev, "cp %p has "
- "protocol error \n", cp);
+ "protocol error\n", cp);
break;
case CMD_HARDWARE_ERR:
cmd->result = DID_ERROR << 16;
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 467dc38..0a2c5a8 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -192,7 +192,7 @@ static const struct ipr_chip_t ipr_chip[] = {
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE, IPR_USE_MSI, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] }
};
-static int ipr_max_bus_speeds [] = {
+static int ipr_max_bus_speeds[] = {
IPR_80MBs_SCSI_RATE, IPR_U160_SCSI_RATE, IPR_U320_SCSI_RATE
};
@@ -562,7 +562,7 @@ static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd,
trace_entry->u.add_data = add_data;
}
#else
-#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while(0)
+#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while (0)
#endif
/**
@@ -1002,7 +1002,7 @@ static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
**/
static void ipr_update_ata_class(struct ipr_resource_entry *res, unsigned int proto)
{
- switch(proto) {
+ switch (proto) {
case IPR_PROTO_SATA:
case IPR_PROTO_SAS_STP:
res->ata_class = ATA_DEV_ATA;
@@ -3043,7 +3043,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
}
#else
-#define ipr_get_ioa_dump(ioa_cfg, dump) do { } while(0)
+#define ipr_get_ioa_dump(ioa_cfg, dump) do { } while (0)
#endif
/**
@@ -3055,7 +3055,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
**/
static void ipr_release_dump(struct kref *kref)
{
- struct ipr_dump *dump = container_of(kref,struct ipr_dump,kref);
+ struct ipr_dump *dump = container_of(kref, struct ipr_dump, kref);
struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg;
unsigned long lock_flags = 0;
int i;
@@ -3142,7 +3142,7 @@ restart:
break;
}
}
- } while(did_work);
+ } while (did_work);
list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
if (res->add_to_ml) {
@@ -3268,7 +3268,7 @@ static ssize_t ipr_show_log_level(struct device *dev,
* number of bytes printed to buffer
**/
static ssize_t ipr_store_log_level(struct device *dev,
- struct device_attribute *attr,
+ struct device_attribute *attr,
const char *buf, size_t count)
{
struct Scsi_Host *shost = class_to_shost(dev);
@@ -3315,7 +3315,7 @@ static ssize_t ipr_store_diagnostics(struct device *dev,
return -EACCES;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -3682,7 +3682,7 @@ static int ipr_update_ioa_ucode(struct ipr_ioa_cfg *ioa_cfg,
unsigned long lock_flags;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -3746,7 +3746,7 @@ static ssize_t ipr_store_update_fw(struct device *dev,
len = snprintf(fname, 99, "%s", buf);
fname[len-1] = '\0';
- if(request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
+ if (request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
dev_err(&ioa_cfg->pdev->dev, "Firmware file %s not found\n", fname);
return -EIO;
}
@@ -4612,7 +4612,7 @@ static int ipr_slave_alloc(struct scsi_device *sdev)
* Return value:
* SUCCESS / FAILED
**/
-static int __ipr_eh_host_reset(struct scsi_cmnd * scsi_cmd)
+static int __ipr_eh_host_reset(struct scsi_cmnd *scsi_cmd)
{
struct ipr_ioa_cfg *ioa_cfg;
int rc;
@@ -4634,7 +4634,7 @@ static int __ipr_eh_host_reset(struct scsi_cmnd * scsi_cmd)
return rc;
}
-static int ipr_eh_host_reset(struct scsi_cmnd * cmd)
+static int ipr_eh_host_reset(struct scsi_cmnd *cmd)
{
int rc;
@@ -4701,7 +4701,7 @@ static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
}
LEAVE;
- return (IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0);
+ return IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0;
}
/**
@@ -4725,7 +4725,7 @@ static int ipr_sata_reset(struct ata_link *link, unsigned int *classes,
ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -4753,7 +4753,7 @@ static int ipr_sata_reset(struct ata_link *link, unsigned int *classes,
* Return value:
* SUCCESS / FAILED
**/
-static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd)
+static int __ipr_eh_dev_reset(struct scsi_cmnd *scsi_cmd)
{
struct ipr_cmnd *ipr_cmd;
struct ipr_ioa_cfg *ioa_cfg;
@@ -4811,10 +4811,10 @@ static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd)
res->resetting_device = 0;
LEAVE;
- return (rc ? FAILED : SUCCESS);
+ return rc ? FAILED : SUCCESS;
}
-static int ipr_eh_dev_reset(struct scsi_cmnd * cmd)
+static int ipr_eh_dev_reset(struct scsi_cmnd *cmd)
{
int rc;
@@ -4910,7 +4910,7 @@ static void ipr_abort_timeout(struct ipr_cmnd *ipr_cmd)
* Return value:
* SUCCESS / FAILED
**/
-static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
+static int ipr_cancel_op(struct scsi_cmnd *scsi_cmd)
{
struct ipr_cmnd *ipr_cmd;
struct ipr_ioa_cfg *ioa_cfg;
@@ -4979,7 +4979,7 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
res->needs_sync_complete = 1;
LEAVE;
- return (IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS);
+ return IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
}
/**
@@ -4989,7 +4989,7 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
* Return value:
* SUCCESS / FAILED
**/
-static int ipr_eh_abort(struct scsi_cmnd * scsi_cmd)
+static int ipr_eh_abort(struct scsi_cmnd *scsi_cmd)
{
unsigned long flags;
int rc;
@@ -5907,7 +5907,7 @@ static int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
* Return value:
* pointer to buffer with description string
**/
-static const char * ipr_ioa_info(struct Scsi_Host *host)
+static const char *ipr_ioa_info(struct Scsi_Host *host)
{
static char buffer[512];
struct ipr_ioa_cfg *ioa_cfg;
@@ -5965,7 +5965,7 @@ static void ipr_ata_phy_reset(struct ata_port *ap)
ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
@@ -6005,7 +6005,7 @@ static void ipr_ata_post_internal(struct ata_queued_cmd *qc)
unsigned long flags;
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
@@ -6330,7 +6330,7 @@ static int ipr_invalid_adapter(struct ipr_ioa_cfg *ioa_cfg)
int i;
if ((ioa_cfg->type == 0x5702) && (ioa_cfg->pdev->revision < 4)) {
- for (i = 0; i < ARRAY_SIZE(ipr_blocked_processors); i++){
+ for (i = 0; i < ARRAY_SIZE(ipr_blocked_processors); i++) {
if (__is_processor(ipr_blocked_processors[i]))
return 1;
}
@@ -6608,7 +6608,7 @@ static void ipr_scsi_bus_speed_limit(struct ipr_ioa_cfg *ioa_cfg)
* none
**/
static void ipr_modify_ioafp_mode_page_28(struct ipr_ioa_cfg *ioa_cfg,
- struct ipr_mode_pages *mode_pages)
+ struct ipr_mode_pages *mode_pages)
{
int i, entry_length;
struct ipr_dev_bus_entry *bus;
@@ -8022,7 +8022,7 @@ static void ipr_reset_ioa_job(struct ipr_cmnd *ipr_cmd)
ipr_reinit_ipr_cmnd(ipr_cmd);
ipr_cmd->job_step_failed = ipr_reset_cmd_failed;
rc = ipr_cmd->job_step(ipr_cmd);
- } while(rc == IPR_RC_JOB_CONTINUE);
+ } while (rc == IPR_RC_JOB_CONTINUE);
}
/**
@@ -8283,7 +8283,7 @@ static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
}
if (ioa_cfg->ipr_cmd_pool)
- pci_pool_destroy (ioa_cfg->ipr_cmd_pool);
+ pci_pool_destroy(ioa_cfg->ipr_cmd_pool);
kfree(ioa_cfg->ipr_cmnd_list);
kfree(ioa_cfg->ipr_cmnd_list_dma);
@@ -8363,8 +8363,8 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
dma_addr_t dma_addr;
int i;
- ioa_cfg->ipr_cmd_pool = pci_pool_create (IPR_NAME, ioa_cfg->pdev,
- sizeof(struct ipr_cmnd), 512, 0);
+ ioa_cfg->ipr_cmd_pool = pci_pool_create(IPR_NAME, ioa_cfg->pdev,
+ sizeof(struct ipr_cmnd), 512, 0);
if (!ioa_cfg->ipr_cmd_pool)
return -ENOMEM;
@@ -8378,7 +8378,7 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
}
for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
- ipr_cmd = pci_pool_alloc (ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
+ ipr_cmd = pci_pool_alloc(ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
if (!ipr_cmd) {
ipr_free_cmd_blks(ioa_cfg);
@@ -8964,7 +8964,7 @@ static void ipr_scan_vsets(struct ipr_ioa_cfg *ioa_cfg)
int target, lun;
for (target = 0; target < IPR_MAX_NUM_TARGETS_PER_BUS; target++)
- for (lun = 0; lun < IPR_MAX_NUM_VSET_LUNS_PER_TARGET; lun++ )
+ for (lun = 0; lun < IPR_MAX_NUM_VSET_LUNS_PER_TARGET; lun++)
scsi_add_device(ioa_cfg->host, IPR_VSET_BUS, target, lun);
}
@@ -9010,7 +9010,7 @@ static void __ipr_remove(struct pci_dev *pdev)
ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
@@ -9139,7 +9139,7 @@ static void ipr_shutdown(struct pci_dev *pdev)
unsigned long lock_flags = 0;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -9228,7 +9228,7 @@ static struct pci_device_id ipr_pci_table[] __devinitdata = {
};
MODULE_DEVICE_TABLE(pci, ipr_pci_table);
-static struct pci_error_handlers ipr_err_handler = {
+static const struct pci_error_handlers ipr_err_handler = {
.error_detected = ipr_pci_error_detected,
.slot_reset = ipr_pci_slot_reset,
};
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 45385f5..b334fdc 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -492,7 +492,7 @@ static void sci_controller_process_completions(struct isci_host *ihost)
u32 event_cycle;
dev_dbg(&ihost->pdev->dev,
- "%s: completion queue begining get:0x%08x\n",
+ "%s: completion queue beginning get:0x%08x\n",
__func__,
ihost->completion_queue_get);
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
index 92c1d86..9be45a2 100644
--- a/drivers/scsi/isci/init.c
+++ b/drivers/scsi/isci/init.c
@@ -222,7 +222,7 @@ static struct sas_domain_function_template isci_transport_ops = {
* @isci_host: This parameter specifies the lldd specific wrapper for the
* libsas sas_ha struct.
*
- * This method returns an error code indicating sucess or failure. The user
+ * This method returns an error code indicating success or failure. The user
* should check for possible memory allocation error return otherwise, a zero
* indicates success.
*/
diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c
index 2fb85bf..13098b0 100644
--- a/drivers/scsi/isci/port.c
+++ b/drivers/scsi/isci/port.c
@@ -212,7 +212,7 @@ static void isci_port_link_up(struct isci_host *isci_host,
memcpy(iphy->sas_phy.attached_sas_addr,
iphy->frame_rcvd.iaf.sas_addr, SAS_ADDR_SIZE);
} else {
- dev_err(&isci_host->pdev->dev, "%s: unkown target\n", __func__);
+ dev_err(&isci_host->pdev->dev, "%s: unknown target\n", __func__);
success = false;
}
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c
index 7a0431c..c1bafc3 100644
--- a/drivers/scsi/isci/request.c
+++ b/drivers/scsi/isci/request.c
@@ -2240,7 +2240,7 @@ static enum sci_status atapi_data_tc_completion_handler(struct isci_request *ire
status = ireq->sci_status;
sci_change_state(&idev->sm, SCI_STP_DEV_ATAPI_ERROR);
} else {
- /* If receiving any non-sucess TC status, no UF
+ /* If receiving any non-success TC status, no UF
* received yet, then an UF for the status fis
* is coming after (XXX: suspect this is
* actually a protocol error or a bug like the
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 6bc74eb..b6f19a1 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -532,7 +532,7 @@ int isci_task_abort_task(struct sas_task *task)
/* The request has already completed and there
* is nothing to do here other than to set the task
* done bit, and indicate that the task abort function
- * was sucessful.
+ * was successful.
*/
spin_lock_irqsave(&task->task_state_lock, flags);
task->task_state_flags |= SAS_TASK_STATE_DONE;
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 45c1520..628a703 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -6607,7 +6607,7 @@ out_error:
* we just use some constant number as place holder.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* -ENOMEM - No availble memory
* -EIO - The mailbox failed to complete successfully.
**/
@@ -10425,7 +10425,7 @@ static struct pci_device_id lpfc_id_table[] = {
MODULE_DEVICE_TABLE(pci, lpfc_id_table);
-static struct pci_error_handlers lpfc_err_handler = {
+static const struct pci_error_handlers lpfc_err_handler = {
.error_detected = lpfc_io_error_detected,
.slot_reset = lpfc_io_slot_reset,
.resume = lpfc_io_resume,
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 9cbd20b..0e7e144 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -4739,7 +4739,7 @@ lpfc_sli4_read_rev(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
* is attached to.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* otherwise - failed to retrieve physical port name
**/
static int
@@ -15209,7 +15209,7 @@ lpfc_check_next_fcf_pri_level(struct lpfc_hba *phba)
/*
* if next_fcf_pri was not set above and the list is not empty then
* we have failed flogis on all of them. So reset flogi failed
- * and start at the begining.
+ * and start at the beginning.
*/
if (!next_fcf_pri && !list_empty(&phba->fcf.fcf_pri_list)) {
list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) {
diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c
index 97825f1..76ad72d 100644
--- a/drivers/scsi/megaraid.c
+++ b/drivers/scsi/megaraid.c
@@ -305,12 +305,11 @@ mega_query_adapter(adapter_t *adapter)
adapter->host->sg_tablesize = adapter->sglen;
-
/* use HP firmware and bios version encoding
Note: fw_version[0|1] and bios_version[0|1] were originally shifted
right 8 bits making them zero. This 0 value was hardcoded to fix
sparse warnings. */
- if (adapter->product_info.subsysvid == HP_SUBSYS_VID) {
+ if (adapter->product_info.subsysvid == PCI_VENDOR_ID_HP) {
sprintf (adapter->fw_version, "%c%d%d.%d%d",
adapter->product_info.fw_version[2],
0,
@@ -4716,7 +4715,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
* support, since this firmware cannot handle 64 bit
* addressing
*/
- if ((subsysvid == HP_SUBSYS_VID) &&
+ if ((subsysvid == PCI_VENDOR_ID_HP) &&
((subsysid == 0x60E7) || (subsysid == 0x60E8))) {
/*
* which firmware
diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h
index 9a7897f..4fb2adf 100644
--- a/drivers/scsi/megaraid.h
+++ b/drivers/scsi/megaraid.h
@@ -45,45 +45,10 @@
#define MAX_DEV_TYPE 32
-#ifndef PCI_VENDOR_ID_LSI_LOGIC
-#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
-#endif
-
-#ifndef PCI_VENDOR_ID_AMI
-#define PCI_VENDOR_ID_AMI 0x101E
-#endif
-
-#ifndef PCI_VENDOR_ID_DELL
-#define PCI_VENDOR_ID_DELL 0x1028
-#endif
-
-#ifndef PCI_VENDOR_ID_INTEL
-#define PCI_VENDOR_ID_INTEL 0x8086
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID
-#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID2
-#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID3
-#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960
-#endif
-
#define PCI_DEVICE_ID_DISCOVERY 0x000E
#define PCI_DEVICE_ID_PERC4_DI 0x000F
#define PCI_DEVICE_ID_PERC4_QC_VERDE 0x0407
-/* Sub-System Vendor IDs */
-#define AMI_SUBSYS_VID 0x101E
-#define DELL_SUBSYS_VID 0x1028
-#define HP_SUBSYS_VID 0x103C
-#define LSI_SUBSYS_VID 0x1000
-#define INTEL_SUBSYS_VID 0x8086
-
#define HBA_SIGNATURE 0x3344
#define HBA_SIGNATURE_471 0xCCCC
#define HBA_SIGNATURE_64BIT 0x0299
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index b25757d..9d5a56c 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -1209,6 +1209,13 @@ _base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc)
u16 message_control;
+ /* Check whether controller SAS2008 B0 controller,
+ if it is SAS2008 B0 controller use IO-APIC instead of MSIX */
+ if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 &&
+ ioc->pdev->revision == 0x01) {
+ return -EINVAL;
+ }
+
base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX);
if (!base) {
dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not "
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index b1ebd6f..1ccae45 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -8306,7 +8306,7 @@ _scsih_pci_mmio_enabled(struct pci_dev *pdev)
return PCI_ERS_RESULT_NEED_RESET;
}
-static struct pci_error_handlers _scsih_err_handler = {
+static const struct pci_error_handlers _scsih_err_handler = {
.error_detected = _scsih_pci_error_detected,
.mmio_enabled = _scsih_pci_mmio_enabled,
.slot_reset = _scsih_pci_slot_reset,
diff --git a/drivers/scsi/mvumi.c b/drivers/scsi/mvumi.c
index 88cf1db..783edc7 100644
--- a/drivers/scsi/mvumi.c
+++ b/drivers/scsi/mvumi.c
@@ -122,7 +122,7 @@ static struct mvumi_res *mvumi_alloc_mem_resource(struct mvumi_hba *mhba,
if (!res) {
dev_err(&mhba->pdev->dev,
- "Failed to allocate memory for resouce manager.\n");
+ "Failed to allocate memory for resource manager.\n");
return NULL;
}
@@ -1007,13 +1007,13 @@ static int mvumi_handshake(struct mvumi_hba *mhba)
tmp |= INT_MAP_COMAOUT | INT_MAP_COMAERR;
iowrite32(tmp, regs + CPU_ENPOINTA_MASK_REG);
iowrite32(mhba->list_num_io, mhba->ib_shadow);
- /* Set InBound List Avaliable count shadow */
+ /* Set InBound List Available count shadow */
iowrite32(lower_32_bits(mhba->ib_shadow_phys),
regs + CLA_INB_AVAL_COUNT_BASEL);
iowrite32(upper_32_bits(mhba->ib_shadow_phys),
regs + CLA_INB_AVAL_COUNT_BASEH);
- /* Set OutBound List Avaliable count shadow */
+ /* Set OutBound List Available count shadow */
iowrite32((mhba->list_num_io-1) | CL_POINTER_TOGGLE,
mhba->ob_shadow);
iowrite32(lower_32_bits(mhba->ob_shadow_phys), regs + 0x5B0);
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index 9ce3a8f..7cfdf2b 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -1615,13 +1615,11 @@ qla82xx_get_fw_offs(struct qla_hw_data *ha)
char *
qla82xx_pci_info_str(struct scsi_qla_host *vha, char *str)
{
- int pcie_reg;
struct qla_hw_data *ha = vha->hw;
char lwstr[6];
uint16_t lnk;
- pcie_reg = pci_pcie_cap(ha->pdev);
- pci_read_config_word(ha->pdev, pcie_reg + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(ha->pdev, PCI_EXP_LNKSTA, &lnk);
ha->link_width = (lnk >> 4) & 0x3f;
strcpy(str, "PCIe (");
@@ -2497,7 +2495,6 @@ fw_load_failed:
int
qla82xx_start_firmware(scsi_qla_host_t *vha)
{
- int pcie_cap;
uint16_t lnk;
struct qla_hw_data *ha = vha->hw;
@@ -2528,8 +2525,7 @@ qla82xx_start_firmware(scsi_qla_host_t *vha)
}
/* Negotiated Link width */
- pcie_cap = pci_pcie_cap(ha->pdev);
- pci_read_config_word(ha->pdev, pcie_cap + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(ha->pdev, PCI_EXP_LNKSTA, &lnk);
ha->link_width = (lnk >> 4) & 0x3f;
/* Synchronize with Receive peg */
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index fb8cd38..d305262 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4471,7 +4471,7 @@ qla2xxx_pci_resume(struct pci_dev *pdev)
ha->flags.eeh_busy = 0;
}
-static struct pci_error_handlers qla2xxx_err_handler = {
+static const struct pci_error_handlers qla2xxx_err_handler = {
.error_detected = qla2xxx_pci_error_detected,
.mmio_enabled = qla2xxx_pci_mmio_enabled,
.slot_reset = qla2xxx_pci_slot_reset,
diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c
index 939d726..807bf76 100644
--- a/drivers/scsi/qla4xxx/ql4_nx.c
+++ b/drivers/scsi/qla4xxx/ql4_nx.c
@@ -1566,7 +1566,6 @@ qla4_8xxx_set_qsnt_ready(struct scsi_qla_host *ha)
static int
qla4_8xxx_start_firmware(struct scsi_qla_host *ha, uint32_t image_start)
{
- int pcie_cap;
uint16_t lnk;
/* scrub dma mask expansion register */
@@ -1590,8 +1589,7 @@ qla4_8xxx_start_firmware(struct scsi_qla_host *ha, uint32_t image_start)
}
/* Negotiated Link width */
- pcie_cap = pci_pcie_cap(ha->pdev);
- pci_read_config_word(ha->pdev, pcie_cap + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(ha->pdev, PCI_EXP_LNKSTA, &lnk);
ha->link_width = (lnk >> 4) & 0x3f;
/* Synchronize with Receive peg */
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 9da4266..79243b7 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -803,7 +803,7 @@ static void qla4xxx_conn_get_stats(struct iscsi_cls_conn *cls_conn,
iscsi_stats_dma);
if (ret != QLA_SUCCESS) {
ql4_printk(KERN_ERR, ha,
- "Unable to retreive iscsi stats\n");
+ "Unable to retrieve iscsi stats\n");
goto free_stats;
}
@@ -4338,7 +4338,7 @@ static int qla4xxx_compare_tuple_ddb(struct scsi_qla_host *ha,
return QLA_ERROR;
/* For multi sessions, driver generates the ISID, so do not compare
- * ISID in reset path since it would be a comparision between the
+ * ISID in reset path since it would be a comparison between the
* driver generated ISID and firmware generated ISID. This could
* lead to adding duplicated DDBs in the list as driver generated
* ISID would not match firmware generated ISID.
@@ -5326,7 +5326,7 @@ static void qla4xxx_destroy_fw_ddb_session(struct scsi_qla_host *ha)
}
}
/**
- * qla4xxx_remove_adapter - calback function to remove adapter.
+ * qla4xxx_remove_adapter - callback function to remove adapter.
* @pci_dev: PCI device pointer
**/
static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
@@ -6148,7 +6148,7 @@ qla4xxx_pci_resume(struct pci_dev *pdev)
clear_bit(AF_EEH_BUSY, &ha->flags);
}
-static struct pci_error_handlers qla4xxx_err_handler = {
+static const struct pci_error_handlers qla4xxx_err_handler = {
.error_detected = qla4xxx_pci_error_detected,
.mmio_enabled = qla4xxx_pci_mmio_enabled,
.slot_reset = qla4xxx_pci_slot_reset,
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c
index 36d1ed7..e2b8e68 100644
--- a/drivers/scsi/sym53c8xx_2/sym_glue.c
+++ b/drivers/scsi/sym53c8xx_2/sym_glue.c
@@ -2117,7 +2117,7 @@ static struct pci_device_id sym2_id_table[] __devinitdata = {
MODULE_DEVICE_TABLE(pci, sym2_id_table);
-static struct pci_error_handlers sym2_err_handler = {
+static const struct pci_error_handlers sym2_err_handler = {
.error_detected = sym2_io_error_detected,
.mmio_enabled = sym2_io_slot_dump,
.slot_reset = sym2_io_slot_reset,
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index c7030fb..3e79a2f 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -331,7 +331,7 @@ static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx,
int i;
for_each_sg(table->sgl, sg_elem, table->nents, i)
- sg_set_buf(&sg[idx++], sg_virt(sg_elem), sg_elem->length);
+ sg[idx++] = *sg_elem;
*p_idx = idx;
}
diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c
index 4411d42..20b3a48 100644
--- a/drivers/scsi/vmw_pvscsi.c
+++ b/drivers/scsi/vmw_pvscsi.c
@@ -295,7 +295,7 @@ static void ll_adapter_reset(const struct pvscsi_adapter *adapter)
static void ll_bus_reset(const struct pvscsi_adapter *adapter)
{
- dev_dbg(pvscsi_dev(adapter), "Reseting bus on %p\n", adapter);
+ dev_dbg(pvscsi_dev(adapter), "Resetting bus on %p\n", adapter);
pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_BUS, NULL, 0);
}
@@ -304,7 +304,7 @@ static void ll_device_reset(const struct pvscsi_adapter *adapter, u32 target)
{
struct PVSCSICmdDescResetDevice cmd = { 0 };
- dev_dbg(pvscsi_dev(adapter), "Reseting device: target=%u\n", target);
+ dev_dbg(pvscsi_dev(adapter), "Resetting device: target=%u\n", target);
cmd.target = target;
diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c
index 32c26d7..8f32a13 100644
--- a/drivers/sh/intc/core.c
+++ b/drivers/sh/intc/core.c
@@ -355,7 +355,7 @@ int __init register_intc_controller(struct intc_desc *desc)
if (unlikely(res)) {
if (res == -EEXIST) {
res = irq_domain_associate(d->domain,
- irq, irq);
+ irq2, irq2);
if (unlikely(res)) {
pr_err("domain association "
"failure\n");
diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c
index 2804eaa..0646bf6 100644
--- a/drivers/sh/pfc/pinctrl.c
+++ b/drivers/sh/pfc/pinctrl.c
@@ -208,10 +208,13 @@ static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev,
break;
case PINMUX_TYPE_GPIO:
+ case PINMUX_TYPE_INPUT:
+ case PINMUX_TYPE_OUTPUT:
break;
default:
pr_err("Unsupported mux type (%d), bailing...\n", pinmux_type);
- return -ENOTSUPP;
+ ret = -ENOTSUPP;
+ goto err;
}
ret = 0;
diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c
index 5784c87..4de66d1 100644
--- a/drivers/spi/spi-au1550.c
+++ b/drivers/spi/spi-au1550.c
@@ -475,7 +475,7 @@ static irqreturn_t au1550_spi_dma_irq_callback(struct au1550_spi *hw)
/*
* due to an spi error we consider transfer as done,
* so mask all events until before next transfer start
- * and stop the possibly running dma immediatelly
+ * and stop the possibly running dma immediately
*/
au1550_spi_mask_ack_all(hw);
au1xxx_dbdma_stop(hw->dma_rx_ch);
diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c
index 1fe5119..6555ecd 100644
--- a/drivers/spi/spi-bfin-sport.c
+++ b/drivers/spi/spi-bfin-sport.c
@@ -467,7 +467,7 @@ bfin_sport_spi_pump_transfers(unsigned long data)
dev_dbg(drv_data->dev, "IO write error!\n");
drv_data->state = ERROR_STATE;
} else {
- /* Update total byte transfered */
+ /* Update total byte transferred */
message->actual_length += transfer->len;
/* Move to next transfer of this msg */
drv_data->state = bfin_sport_spi_next_transfer(drv_data);
diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c
index 698018f..9d9071b 100644
--- a/drivers/spi/spi-oc-tiny.c
+++ b/drivers/spi/spi-oc-tiny.c
@@ -129,7 +129,7 @@ static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
unsigned int i;
if (hw->irq >= 0) {
- /* use intrrupt driven data transfer */
+ /* use interrupt driven data transfer */
hw->len = t->len;
hw->txp = t->tx_buf;
hw->rxp = t->rx_buf;
diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c
index 75ac9d4..7a85f22 100644
--- a/drivers/spi/spi-ppc4xx.c
+++ b/drivers/spi/spi-ppc4xx.c
@@ -101,7 +101,7 @@ struct spi_ppc4xx_regs {
u8 dummy;
/*
* Clock divisor modulus register
- * This uses the follwing formula:
+ * This uses the following formula:
* SCPClkOut = OPBCLK/(4(CDM + 1))
* or
* CDM = (OPBCLK/4*SCPClkOut) - 1
@@ -201,7 +201,7 @@ static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct spi_transfer *t)
return -EINVAL;
}
- /* Write new configration */
+ /* Write new configuration */
out_8(&hw->regs->mode, cs->mode);
/* Set the clock */
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index cd56dcf..1284c9b 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -505,7 +505,7 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
}
if (unlikely(pspi->max_speed_hz == 0)) {
- dev_err(&pspi->dev, "%s pch_spi_tranfer maxspeed=%d\n",
+ dev_err(&pspi->dev, "%s pch_spi_transfer maxspeed=%d\n",
__func__, pspi->max_speed_hz);
retval = -EINVAL;
goto err_out;
diff --git a/drivers/staging/et131x/et131x.c b/drivers/staging/et131x/et131x.c
index c13499d..413da0d 100644
--- a/drivers/staging/et131x/et131x.c
+++ b/drivers/staging/et131x/et131x.c
@@ -3990,16 +3990,14 @@ static void et131x_hwaddr_init(struct et131x_adapter *adapter)
static int et131x_pci_init(struct et131x_adapter *adapter,
struct pci_dev *pdev)
{
- int cap = pci_pcie_cap(pdev);
u16 max_payload;
- u16 ctl;
int i, rc;
rc = et131x_init_eeprom(adapter);
if (rc < 0)
goto out;
- if (!cap) {
+ if (!pci_is_pcie(pdev)) {
dev_err(&pdev->dev, "Missing PCIe capabilities\n");
goto err_out;
}
@@ -4007,7 +4005,7 @@ static int et131x_pci_init(struct et131x_adapter *adapter,
/* Let's set up the PORT LOGIC Register. First we need to know what
* the max_payload_size is
*/
- if (pci_read_config_word(pdev, cap + PCI_EXP_DEVCAP, &max_payload)) {
+ if (pcie_capability_read_word(pdev, PCI_EXP_DEVCAP, &max_payload)) {
dev_err(&pdev->dev,
"Could not read PCI config space for Max Payload Size\n");
goto err_out;
@@ -4044,17 +4042,10 @@ static int et131x_pci_init(struct et131x_adapter *adapter,
}
/* Change the max read size to 2k */
- if (pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl)) {
+ if (pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_READRQ, 0x4 << 12)) {
dev_err(&pdev->dev,
- "Could not read PCI config space for Max read size\n");
- goto err_out;
- }
-
- ctl = (ctl & ~PCI_EXP_DEVCTL_READRQ) | (0x04 << 12);
-
- if (pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl)) {
- dev_err(&pdev->dev,
- "Could not write PCI config space for Max read size\n");
+ "Couldn't change PCI config space for Max read size\n");
goto err_out;
}
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
index ddadcc3..5abbee3 100644
--- a/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
@@ -31,12 +31,10 @@ static void rtl8192_parse_pci_configuration(struct pci_dev *pdev,
struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
u8 tmp;
- int pos;
- u8 LinkCtrlReg;
+ u16 LinkCtrlReg;
- pos = pci_find_capability(priv->pdev, PCI_CAP_ID_EXP);
- pci_read_config_byte(priv->pdev, pos + PCI_EXP_LNKCTL, &LinkCtrlReg);
- priv->NdisAdapter.LinkCtrlReg = LinkCtrlReg;
+ pcie_capability_read_word(priv->pdev, PCI_EXP_LNKCTL, &LinkCtrlReg);
+ priv->NdisAdapter.LinkCtrlReg = (u8)LinkCtrlReg;
RT_TRACE(COMP_INIT, "Link Control Register =%x\n",
priv->NdisAdapter.LinkCtrlReg);
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
index 0c4760f..240f7aa 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.c
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -662,7 +662,7 @@ int iscsi_extract_key_value(char *textbuf, char **key, char **value)
{
*value = strchr(textbuf, '=');
if (!*value) {
- pr_err("Unable to locate \"=\" seperator for key,"
+ pr_err("Unable to locate \"=\" separator for key,"
" ignoring request.\n");
return -1;
}
@@ -1269,7 +1269,7 @@ static int iscsi_check_value(struct iscsi_param *param, char *value)
comma_ptr = strchr(value, ',');
if (comma_ptr && !IS_TYPE_VALUE_LIST(param)) {
- pr_err("Detected value seperator \",\", but"
+ pr_err("Detected value separator \",\", but"
" key \"%s\" does not allow a value list,"
" protocol error.\n", param->name);
return -1;
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 28e7c7c..452278e 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -4236,7 +4236,7 @@ static void serial8250_io_resume(struct pci_dev *dev)
pciserial_resume_ports(priv);
}
-static struct pci_error_handlers serial8250_err_handler = {
+static const struct pci_error_handlers serial8250_err_handler = {
.error_detected = serial8250_io_error_detected,
.slot_reset = serial8250_io_slot_reset,
.resume = serial8250_io_resume,
diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c
index 7545fe1..5ab3c3b 100644
--- a/drivers/tty/serial/jsm/jsm_driver.c
+++ b/drivers/tty/serial/jsm/jsm_driver.c
@@ -54,7 +54,7 @@ static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev,
static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev);
static void jsm_io_resume(struct pci_dev *pdev);
-static struct pci_error_handlers jsm_err_handler = {
+static const struct pci_error_handlers jsm_err_handler = {
.error_detected = jsm_io_error_detected,
.slot_reset = jsm_io_slot_reset,
.resume = jsm_io_resume,
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index d956965..3440812 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -624,7 +624,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
/* print devices for all busses */
list_for_each_entry(bus, &usb_bus_list, bus_list) {
/* recurse through all children of the root hub */
- if (!bus->root_hub)
+ if (!bus_to_hcd(bus)->rh_registered)
continue;
usb_lock_device(bus->root_hub);
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos,
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index bc84106..75ba209 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1011,10 +1011,7 @@ static int register_root_hub(struct usb_hcd *hcd)
if (retval) {
dev_err (parent_dev, "can't register root hub for %s, %d\n",
dev_name(&usb_dev->dev), retval);
- }
- mutex_unlock(&usb_bus_list_lock);
-
- if (retval == 0) {
+ } else {
spin_lock_irq (&hcd_root_hub_lock);
hcd->rh_registered = 1;
spin_unlock_irq (&hcd_root_hub_lock);
@@ -1023,6 +1020,7 @@ static int register_root_hub(struct usb_hcd *hcd)
if (HCD_DEAD(hcd))
usb_hc_died (hcd); /* This time clean up */
}
+ mutex_unlock(&usb_bus_list_lock);
return retval;
}
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index aaa8d2b..0bf72f9 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -467,7 +467,8 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
/* From the GPIO notifying the over-current situation, find
* out the corresponding port */
at91_for_each_port(port) {
- if (gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
+ if (gpio_is_valid(pdata->overcurrent_pin[port]) &&
+ gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
gpio = pdata->overcurrent_pin[port];
break;
}
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c
index 211a492..d8dedc7 100644
--- a/drivers/vfio/pci/vfio_pci_intrs.c
+++ b/drivers/vfio/pci/vfio_pci_intrs.c
@@ -76,9 +76,24 @@ static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
schedule_work(&virqfd->inject);
}
- if (flags & POLLHUP)
- /* The eventfd is closing, detach from VFIO */
- virqfd_deactivate(virqfd);
+ if (flags & POLLHUP) {
+ unsigned long flags;
+ spin_lock_irqsave(&virqfd->vdev->irqlock, flags);
+
+ /*
+ * The eventfd is closing, if the virqfd has not yet been
+ * queued for release, as determined by testing whether the
+ * vdev pointer to it is still valid, queue it now. As
+ * with kvm irqfds, we know we won't race against the virqfd
+ * going away because we hold wqh->lock to get here.
+ */
+ if (*(virqfd->pvirqfd) == virqfd) {
+ *(virqfd->pvirqfd) = NULL;
+ virqfd_deactivate(virqfd);
+ }
+
+ spin_unlock_irqrestore(&virqfd->vdev->irqlock, flags);
+ }
return 0;
}
@@ -93,7 +108,6 @@ static void virqfd_ptable_queue_proc(struct file *file,
static void virqfd_shutdown(struct work_struct *work)
{
struct virqfd *virqfd = container_of(work, struct virqfd, shutdown);
- struct virqfd **pvirqfd = virqfd->pvirqfd;
u64 cnt;
eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt);
@@ -101,7 +115,6 @@ static void virqfd_shutdown(struct work_struct *work)
eventfd_ctx_put(virqfd->eventfd);
kfree(virqfd);
- *pvirqfd = NULL;
}
static void virqfd_inject(struct work_struct *work)
@@ -122,15 +135,11 @@ static int virqfd_enable(struct vfio_pci_device *vdev,
int ret = 0;
unsigned int events;
- if (*pvirqfd)
- return -EBUSY;
-
virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL);
if (!virqfd)
return -ENOMEM;
virqfd->pvirqfd = pvirqfd;
- *pvirqfd = virqfd;
virqfd->vdev = vdev;
virqfd->handler = handler;
virqfd->thread = thread;
@@ -154,6 +163,23 @@ static int virqfd_enable(struct vfio_pci_device *vdev,
virqfd->eventfd = ctx;
/*
+ * virqfds can be released by closing the eventfd or directly
+ * through ioctl. These are both done through a workqueue, so
+ * we update the pointer to the virqfd under lock to avoid
+ * pushing multiple jobs to release the same virqfd.
+ */
+ spin_lock_irq(&vdev->irqlock);
+
+ if (*pvirqfd) {
+ spin_unlock_irq(&vdev->irqlock);
+ ret = -EBUSY;
+ goto fail;
+ }
+ *pvirqfd = virqfd;
+
+ spin_unlock_irq(&vdev->irqlock);
+
+ /*
* Install our own custom wake-up handling so we are notified via
* a callback whenever someone signals the underlying eventfd.
*/
@@ -187,19 +213,29 @@ fail:
fput(file);
kfree(virqfd);
- *pvirqfd = NULL;
return ret;
}
-static void virqfd_disable(struct virqfd *virqfd)
+static void virqfd_disable(struct vfio_pci_device *vdev,
+ struct virqfd **pvirqfd)
{
- if (!virqfd)
- return;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vdev->irqlock, flags);
+
+ if (*pvirqfd) {
+ virqfd_deactivate(*pvirqfd);
+ *pvirqfd = NULL;
+ }
- virqfd_deactivate(virqfd);
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
- /* Block until we know all outstanding shutdown jobs have completed. */
+ /*
+ * Block until we know all outstanding shutdown jobs have completed.
+ * Even if we don't queue the job, flush the wq to be sure it's
+ * been released.
+ */
flush_workqueue(vfio_irqfd_cleanup_wq);
}
@@ -392,8 +428,8 @@ static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd)
static void vfio_intx_disable(struct vfio_pci_device *vdev)
{
vfio_intx_set_signal(vdev, -1);
- virqfd_disable(vdev->ctx[0].unmask);
- virqfd_disable(vdev->ctx[0].mask);
+ virqfd_disable(vdev, &vdev->ctx[0].unmask);
+ virqfd_disable(vdev, &vdev->ctx[0].mask);
vdev->irq_type = VFIO_PCI_NUM_IRQS;
vdev->num_ctx = 0;
kfree(vdev->ctx);
@@ -539,8 +575,8 @@ static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix)
vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix);
for (i = 0; i < vdev->num_ctx; i++) {
- virqfd_disable(vdev->ctx[i].unmask);
- virqfd_disable(vdev->ctx[i].mask);
+ virqfd_disable(vdev, &vdev->ctx[i].unmask);
+ virqfd_disable(vdev, &vdev->ctx[i].mask);
}
if (msix) {
@@ -577,7 +613,7 @@ static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev,
vfio_send_intx_eventfd, NULL,
&vdev->ctx[0].unmask, fd);
- virqfd_disable(vdev->ctx[0].unmask);
+ virqfd_disable(vdev, &vdev->ctx[0].unmask);
}
return 0;
diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c
index f75da87..f49181c 100644
--- a/drivers/video/backlight/88pm860x_bl.c
+++ b/drivers/video/backlight/88pm860x_bl.c
@@ -228,7 +228,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
data->port = pdata->flags;
if (data->port < 0) {
dev_err(&pdev->dev, "wrong platform data is assigned");
- kfree(data);
return -EINVAL;
}
diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c
index b4a632a..932abaa 100644
--- a/drivers/video/efifb.c
+++ b/drivers/video/efifb.c
@@ -553,7 +553,9 @@ static int __init efifb_init(void)
int ret;
char *option = NULL;
- dmi_check_system(dmi_system_table);
+ if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI ||
+ !(screen_info.capabilities & VIDEO_CAPABILITY_SKIP_QUIRKS))
+ dmi_check_system(dmi_system_table);
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
return -ENODEV;
diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c
index 4bc2b8a..663c308 100644
--- a/drivers/video/exynos/exynos_mipi_dsi.c
+++ b/drivers/video/exynos/exynos_mipi_dsi.c
@@ -461,7 +461,7 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
done:
platform_set_drvdata(pdev, dsim);
- dev_dbg(&pdev->dev, "%s() completed sucessfuly (%s mode)\n", __func__,
+ dev_dbg(&pdev->dev, "%s() completed successfully (%s mode)\n", __func__,
dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB");
return 0;
diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
index 8e4a446..b244f06 100644
--- a/drivers/video/tmiofb.c
+++ b/drivers/video/tmiofb.c
@@ -694,6 +694,10 @@ static int __devinit tmiofb_probe(struct platform_device *dev)
dev_err(&dev->dev, "NULL platform data!\n");
return -EINVAL;
}
+ if (ccr == NULL || lcr == NULL || vram == NULL || irq < 0) {
+ dev_err(&dev->dev, "missing resources\n");
+ return -EINVAL;
+ }
info = framebuffer_alloc(sizeof(struct tmiofb_par), &dev->dev);
diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c
index e0df92e..1425d22c 100644
--- a/drivers/vme/bridges/vme_ca91cx42.c
+++ b/drivers/vme/bridges/vme_ca91cx42.c
@@ -1603,7 +1603,7 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int retval, i;
u32 data;
- struct list_head *pos = NULL;
+ struct list_head *pos = NULL, *n;
struct vme_bridge *ca91cx42_bridge;
struct ca91cx42_driver *ca91cx42_device;
struct vme_master_resource *master_image;
@@ -1821,28 +1821,28 @@ err_reg:
ca91cx42_crcsr_exit(ca91cx42_bridge, pdev);
err_lm:
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->lm_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->lm_resources) {
lm = list_entry(pos, struct vme_lm_resource, list);
list_del(pos);
kfree(lm);
}
err_dma:
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->dma_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->dma_resources) {
dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
list_del(pos);
kfree(dma_ctrlr);
}
err_slave:
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->slave_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->slave_resources) {
slave_image = list_entry(pos, struct vme_slave_resource, list);
list_del(pos);
kfree(slave_image);
}
err_master:
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->master_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->master_resources) {
master_image = list_entry(pos, struct vme_master_resource,
list);
list_del(pos);
@@ -1868,7 +1868,7 @@ err_struct:
static void ca91cx42_remove(struct pci_dev *pdev)
{
- struct list_head *pos = NULL;
+ struct list_head *pos = NULL, *n;
struct vme_master_resource *master_image;
struct vme_slave_resource *slave_image;
struct vme_dma_resource *dma_ctrlr;
@@ -1905,28 +1905,28 @@ static void ca91cx42_remove(struct pci_dev *pdev)
ca91cx42_crcsr_exit(ca91cx42_bridge, pdev);
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->lm_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->lm_resources) {
lm = list_entry(pos, struct vme_lm_resource, list);
list_del(pos);
kfree(lm);
}
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->dma_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->dma_resources) {
dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
list_del(pos);
kfree(dma_ctrlr);
}
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->slave_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->slave_resources) {
slave_image = list_entry(pos, struct vme_slave_resource, list);
list_del(pos);
kfree(slave_image);
}
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->master_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->master_resources) {
master_image = list_entry(pos, struct vme_master_resource,
list);
list_del(pos);
diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c
index 880d924..5fbd08f 100644
--- a/drivers/vme/bridges/vme_tsi148.c
+++ b/drivers/vme/bridges/vme_tsi148.c
@@ -2350,7 +2350,7 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int retval, i, master_num;
u32 data;
- struct list_head *pos = NULL;
+ struct list_head *pos = NULL, *n;
struct vme_bridge *tsi148_bridge;
struct tsi148_driver *tsi148_device;
struct vme_master_resource *master_image;
@@ -2615,28 +2615,28 @@ err_reg:
err_crcsr:
err_lm:
/* resources are stored in link list */
- list_for_each(pos, &tsi148_bridge->lm_resources) {
+ list_for_each_safe(pos, n, &tsi148_bridge->lm_resources) {
lm = list_entry(pos, struct vme_lm_resource, list);
list_del(pos);
kfree(lm);
}
err_dma:
/* resources are stored in link list */
- list_for_each(pos, &tsi148_bridge->dma_resources) {
+ list_for_each_safe(pos, n, &tsi148_bridge->dma_resources) {
dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
list_del(pos);
kfree(dma_ctrlr);
}
err_slave:
/* resources are stored in link list */
- list_for_each(pos, &tsi148_bridge->slave_resources) {
+ list_for_each_safe(pos, n, &tsi148_bridge->slave_resources) {
slave_image = list_entry(pos, struct vme_slave_resource, list);
list_del(pos);
kfree(slave_image);
}
err_master:
/* resources are stored in link list */
- list_for_each(pos, &tsi148_bridge->master_resources) {
+ list_for_each_safe(pos, n, &tsi148_bridge->master_resources) {
master_image = list_entry(pos, struct vme_master_resource,
list);
list_del(pos);
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig
index 5ceb1cd..7e98403 100644
--- a/drivers/w1/masters/Kconfig
+++ b/drivers/w1/masters/Kconfig
@@ -60,7 +60,6 @@ config W1_MASTER_GPIO
config HDQ_MASTER_OMAP
tristate "OMAP HDQ driver"
- depends on ARCH_OMAP2PLUS
help
Say Y here if you want support for the 1-wire or HDQ Interface
on an OMAP processor.
diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c
index 530a2d3..7c294f4 100644
--- a/drivers/w1/masters/ds1wm.c
+++ b/drivers/w1/masters/ds1wm.c
@@ -349,7 +349,7 @@ static void ds1wm_search(void *data, struct w1_master *master_dev,
"pass: %d entering ASM\n", pass);
ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA);
dev_dbg(&ds1wm_data->pdev->dev,
- "pass: %d begining nibble loop\n", pass);
+ "pass: %d beginning nibble loop\n", pass);
r_prime = 0;
d = 0;
diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c
index 4b0fcf3..ca8e60b 100644
--- a/drivers/w1/masters/omap_hdq.c
+++ b/drivers/w1/masters/omap_hdq.c
@@ -18,9 +18,6 @@
#include <linux/sched.h>
#include <linux/pm_runtime.h>
-#include <asm/irq.h>
-#include <mach/hardware.h>
-
#include "../w1.h"
#include "../w1_int.h"
@@ -73,11 +70,11 @@ struct hdq_data {
};
static int __devinit omap_hdq_probe(struct platform_device *pdev);
-static int omap_hdq_remove(struct platform_device *pdev);
+static int __devexit omap_hdq_remove(struct platform_device *pdev);
static struct platform_driver omap_hdq_driver = {
.probe = omap_hdq_probe,
- .remove = omap_hdq_remove,
+ .remove = __devexit_p(omap_hdq_remove),
.driver = {
.name = "omap_hdq",
},
@@ -538,39 +535,35 @@ static void omap_w1_write_byte(void *_hdq, u8 byte)
hdq_data->init_trans = 0;
mutex_unlock(&hdq_data->hdq_mutex);
}
-
- return;
}
static int __devinit omap_hdq_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct hdq_data *hdq_data;
struct resource *res;
int ret, irq;
u8 rev;
- hdq_data = kmalloc(sizeof(*hdq_data), GFP_KERNEL);
+ hdq_data = devm_kzalloc(dev, sizeof(*hdq_data), GFP_KERNEL);
if (!hdq_data) {
dev_dbg(&pdev->dev, "unable to allocate memory\n");
- ret = -ENOMEM;
- goto err_kmalloc;
+ return -ENOMEM;
}
- hdq_data->dev = &pdev->dev;
+ hdq_data->dev = dev;
platform_set_drvdata(pdev, hdq_data);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_dbg(&pdev->dev, "unable to get resource\n");
- ret = -ENXIO;
- goto err_resource;
+ return -ENXIO;
}
- hdq_data->hdq_base = ioremap(res->start, SZ_4K);
+ hdq_data->hdq_base = devm_request_and_ioremap(dev, res);
if (!hdq_data->hdq_base) {
dev_dbg(&pdev->dev, "ioremap failed\n");
- ret = -EINVAL;
- goto err_ioremap;
+ return -ENOMEM;
}
hdq_data->hdq_usecount = 0;
@@ -591,7 +584,8 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev)
goto err_irq;
}
- ret = request_irq(irq, hdq_isr, IRQF_DISABLED, "omap_hdq", hdq_data);
+ ret = devm_request_irq(dev, irq, hdq_isr, IRQF_DISABLED,
+ "omap_hdq", hdq_data);
if (ret < 0) {
dev_dbg(&pdev->dev, "could not request irq\n");
goto err_irq;
@@ -616,19 +610,10 @@ err_irq:
err_w1:
pm_runtime_disable(&pdev->dev);
- iounmap(hdq_data->hdq_base);
-
-err_ioremap:
-err_resource:
- platform_set_drvdata(pdev, NULL);
- kfree(hdq_data);
-
-err_kmalloc:
return ret;
-
}
-static int omap_hdq_remove(struct platform_device *pdev)
+static int __devexit omap_hdq_remove(struct platform_device *pdev)
{
struct hdq_data *hdq_data = platform_get_drvdata(pdev);
@@ -644,27 +629,11 @@ static int omap_hdq_remove(struct platform_device *pdev)
/* remove module dependency */
pm_runtime_disable(&pdev->dev);
- free_irq(INT_24XX_HDQ_IRQ, hdq_data);
- platform_set_drvdata(pdev, NULL);
- iounmap(hdq_data->hdq_base);
- kfree(hdq_data);
return 0;
}
-static int __init
-omap_hdq_init(void)
-{
- return platform_driver_register(&omap_hdq_driver);
-}
-module_init(omap_hdq_init);
-
-static void __exit
-omap_hdq_exit(void)
-{
- platform_driver_unregister(&omap_hdq_driver);
-}
-module_exit(omap_hdq_exit);
+module_platform_driver(omap_hdq_driver);
module_param(w1_id, int, S_IRUSR);
MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection");
diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c
index df600d1..6012c4e 100644
--- a/drivers/w1/masters/w1-gpio.c
+++ b/drivers/w1/masters/w1-gpio.c
@@ -14,6 +14,8 @@
#include <linux/slab.h>
#include <linux/w1-gpio.h>
#include <linux/gpio.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
#include "../w1.h"
#include "../w1_int.h"
@@ -42,12 +44,55 @@ static u8 w1_gpio_read_bit(void *data)
return gpio_get_value(pdata->pin) ? 1 : 0;
}
+#ifdef CONFIG_OF
+static struct of_device_id w1_gpio_dt_ids[] = {
+ { .compatible = "w1-gpio" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids);
+
+static int w1_gpio_probe_dt(struct platform_device *pdev)
+{
+ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(w1_gpio_dt_ids, &pdev->dev);
+
+ if (!of_id)
+ return 0;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (of_get_property(np, "linux,open-drain", NULL))
+ pdata->is_open_drain = 1;
+
+ pdata->pin = of_get_gpio(np, 0);
+ pdata->ext_pullup_enable_pin = of_get_gpio(np, 1);
+ pdev->dev.platform_data = pdata;
+
+ return 0;
+}
+#else
+static int w1_gpio_probe_dt(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
static int __init w1_gpio_probe(struct platform_device *pdev)
{
struct w1_bus_master *master;
- struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct w1_gpio_platform_data *pdata;
int err;
+ err = w1_gpio_probe_dt(pdev);
+ if (err < 0)
+ return err;
+
+ pdata = pdev->dev.platform_data;
+
if (!pdata)
return -ENXIO;
@@ -59,6 +104,13 @@ static int __init w1_gpio_probe(struct platform_device *pdev)
if (err)
goto free_master;
+ if (gpio_is_valid(pdata->ext_pullup_enable_pin)) {
+ err = gpio_request_one(pdata->ext_pullup_enable_pin,
+ GPIOF_INIT_LOW, "w1 pullup");
+ if (err < 0)
+ goto free_gpio;
+ }
+
master->data = pdata;
master->read_bit = w1_gpio_read_bit;
@@ -72,15 +124,21 @@ static int __init w1_gpio_probe(struct platform_device *pdev)
err = w1_add_master_device(master);
if (err)
- goto free_gpio;
+ goto free_gpio_ext_pu;
if (pdata->enable_external_pullup)
pdata->enable_external_pullup(1);
+ if (gpio_is_valid(pdata->ext_pullup_enable_pin))
+ gpio_set_value(pdata->ext_pullup_enable_pin, 1);
+
platform_set_drvdata(pdev, master);
return 0;
+ free_gpio_ext_pu:
+ if (gpio_is_valid(pdata->ext_pullup_enable_pin))
+ gpio_free(pdata->ext_pullup_enable_pin);
free_gpio:
gpio_free(pdata->pin);
free_master:
@@ -97,6 +155,9 @@ static int __exit w1_gpio_remove(struct platform_device *pdev)
if (pdata->enable_external_pullup)
pdata->enable_external_pullup(0);
+ if (gpio_is_valid(pdata->ext_pullup_enable_pin))
+ gpio_set_value(pdata->ext_pullup_enable_pin, 0);
+
w1_remove_master_device(master);
gpio_free(pdata->pin);
kfree(master);
@@ -135,6 +196,7 @@ static struct platform_driver w1_gpio_driver = {
.driver = {
.name = "w1-gpio",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(w1_gpio_dt_ids),
},
.remove = __exit_p(w1_gpio_remove),
.suspend = w1_gpio_suspend,
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 1eff743..ae60406 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -814,6 +814,9 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
hpwdt_timer_reg = pci_mem_addr + 0x70;
hpwdt_timer_con = pci_mem_addr + 0x72;
+ /* Make sure that timer is disabled until /dev/watchdog is opened */
+ hpwdt_stop();
+
/* Make sure that we have a valid soft_margin */
if (hpwdt_change_timer(soft_margin))
hpwdt_change_timer(DEFAULT_MARGIN);
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index 6aa46a9..3796434 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -128,11 +128,12 @@ EXPORT_SYMBOL_GPL(watchdog_register_device);
void watchdog_unregister_device(struct watchdog_device *wdd)
{
int ret;
- int devno = wdd->cdev.dev;
+ int devno;
if (wdd == NULL)
return;
+ devno = wdd->cdev.dev;
ret = watchdog_dev_unregister(wdd);
if (ret)
pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 1ffd03b..7f12416 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -314,8 +314,9 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages)
}
}
- err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset,
- pages, true);
+ err = gnttab_unmap_refs(map->unmap_ops + offset,
+ use_ptemod ? map->kmap_ops + offset : NULL, map->pages + offset,
+ pages);
if (err)
return err;
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 0bfc1ef..0067266 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -870,7 +870,8 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
EXPORT_SYMBOL_GPL(gnttab_map_refs);
int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
- struct page **pages, unsigned int count, bool clear_pte)
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count)
{
int i, ret;
bool lazy = false;
@@ -888,7 +889,8 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
}
for (i = 0; i < count; i++) {
- ret = m2p_remove_override(pages[i], clear_pte);
+ ret = m2p_remove_override(pages[i], kmap_ops ?
+ &kmap_ops[i] : NULL);
if (ret)
return ret;
}
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index 0334272..92ff01d 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -871,7 +871,7 @@ end:
}
/*add xen_pcibk AER handling*/
-static struct pci_error_handlers xen_pcibk_error_handler = {
+static const struct pci_error_handlers xen_pcibk_error_handler = {
.error_detected = xen_pcibk_error_detected,
.mmio_enabled = xen_pcibk_mmio_enabled,
.slot_reset = xen_pcibk_slot_reset,
OpenPOWER on IntegriCloud